diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 6e1821178f39..a09f9f2ed2f2 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1361,7 +1361,7 @@ pub const Dir = struct { .share_access = share_access, .creation = creation, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |er| switch (er) { error.WouldBlock => unreachable, else => |e2| return e2, diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig index c103925bdd8c..e2ec8b8061a5 100644 --- a/lib/std/fs/watch.zig +++ b/lib/std/fs/watch.zig @@ -401,7 +401,7 @@ pub fn Watch(comptime V: type) type { .access_mask = windows.FILE_LIST_DIRECTORY, .creation = windows.FILE_OPEN, .io_mode = .evented, - .open_dir = true, + .filter = .dir_only, }); errdefer windows.CloseHandle(dir_handle); diff --git a/lib/std/os.zig b/lib/std/os.zig index 1728c2ac0d65..7be8825fcc3c 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1353,7 +1353,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; } - const open_dir: bool = flags & O.DIRECTORY != 0; + const filter: windows.OpenFileOptions.Filter = if (flags & O.DIRECTORY != 0) .dir_only else .file_only; const follow_symlinks: bool = flags & O.NOFOLLOW == 0; const creation: w.ULONG = blk: { @@ -1369,7 +1369,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { .access_mask = access_mask, .io_mode = .blocking, .creation = creation, - .open_dir = open_dir, + .filter = filter, .follow_symlinks = follow_symlinks, }; } @@ -2324,6 +2324,7 @@ pub fn renameatW( .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, .creation = windows.FILE_OPEN, .io_mode = .blocking, + .filter = .any, // This function is supposed to rename both files and directories. }) catch |err| switch (err) { error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. else => |e| return e, @@ -2435,7 +2436,7 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!v .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, .creation = windows.FILE_CREATE, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |err| switch (err) { error.IsDir => unreachable, error.PipeBusy => unreachable, @@ -2511,7 +2512,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void { .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, .creation = windows.FILE_CREATE, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |err| switch (err) { error.IsDir => unreachable, error.PipeBusy => unreachable, @@ -4693,7 +4694,7 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat .share_access = share_access, .creation = creation, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |er| switch (er) { error.WouldBlock => unreachable, else => |e2| return e2, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 59e65ed54c18..0d9907893cd5 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -53,17 +53,26 @@ pub const OpenFileOptions = struct { io_mode: std.io.ModeOverride, /// If true, tries to open path as a directory. /// Defaults to false. - open_dir: bool = false, + filter: Filter = .file_only, /// If false, tries to open path as a reparse point without dereferencing it. /// Defaults to true. follow_symlinks: bool = true, + + pub const Filter = enum { + /// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory. + file_only, + /// Causes `OpenFile` to return `error.NotDir` if the opened handle would be a file. + dir_only, + /// `OpenFile` does not discriminate between opening files and directories. + any, + }; }; pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { - if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) { + if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and options.filter == .file_only) { return error.IsDir; } - if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) { + if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and options.filter == .file_only) { return error.IsDir; } @@ -87,7 +96,11 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN }; var io: IO_STATUS_BLOCK = undefined; const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; - const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE; + const file_or_dir_flag: ULONG = switch (options.filter) { + .file_only => FILE_NON_DIRECTORY_FILE, + .dir_only => FILE_DIRECTORY_FILE, + .any => 0, + }; // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; @@ -695,7 +708,7 @@ pub fn CreateSymbolicLink( .dir = dir, .creation = FILE_CREATE, .io_mode = .blocking, - .open_dir = is_directory, + .filter = if (is_directory) .dir_only else .file_only, }) catch |err| switch (err) { error.IsDir => return error.PathAlreadyExists, error.NotDir => unreachable, diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 297fd1cb39d7..78017726479f 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -22,6 +22,12 @@ else const long_double_is_f128 = builtin.target.longDoubleIsF128(); comptime { + // These files do their own comptime exporting logic. + if (!builtin.zig_is_stage2) { + _ = @import("compiler_rt/atomics.zig"); + } + _ = @import("compiler_rt/clear_cache.zig").clear_cache; + const __extenddftf2 = @import("compiler_rt/extendXfYf2.zig").__extenddftf2; @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); const __extendsftf2 = @import("compiler_rt/extendXfYf2.zig").__extendsftf2; @@ -171,16 +177,16 @@ comptime { const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); - if (!builtin.zig_is_stage2) { - if (!long_double_is_f128) { - // TODO implement these - //const __extendxftf2 = @import("compiler_rt/extendXfYf2.zig").__extendxftf2; - //@export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); + if (!long_double_is_f128) { + // TODO implement these + //const __extendxftf2 = @import("compiler_rt/extendXfYf2.zig").__extendxftf2; + //@export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); - //const __trunctfxf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfxf2; - //@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); - } + //const __trunctfxf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfxf2; + //@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); + } + if (!builtin.zig_is_stage2) { switch (arch) { .i386, .x86_64, @@ -193,531 +199,533 @@ comptime { }, else => {}, } + } - // __clear_cache manages its own logic about whether to be exported or not. - _ = @import("compiler_rt/clear_cache.zig").clear_cache; - - const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2; - @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); - const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2; - @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); - const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2; - @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); - - const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3; - @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); - const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3; - @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); - const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; - @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); - const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; - @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); - const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; - @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); - const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; - @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); - - const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; - @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); - const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3; - @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); - const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3; - @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); - - const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3; - @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); - const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3; - @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); - const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3; - @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); - - // Integral bit manipulation - const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3; - @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); - const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3; - @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); - const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3; - @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); - const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3; - @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); - const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3; - @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); - const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3; - @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); - - const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2; - @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); - const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2; - @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); - const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2; - @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); - const __ctzsi2 = @import("compiler_rt/count0bits.zig").__ctzsi2; - @export(__ctzsi2, .{ .name = "__ctzsi2", .linkage = linkage }); - const __ctzdi2 = @import("compiler_rt/count0bits.zig").__ctzdi2; - @export(__ctzdi2, .{ .name = "__ctzdi2", .linkage = linkage }); - const __ctzti2 = @import("compiler_rt/count0bits.zig").__ctzti2; - @export(__ctzti2, .{ .name = "__ctzti2", .linkage = linkage }); - const __ffssi2 = @import("compiler_rt/count0bits.zig").__ffssi2; - @export(__ffssi2, .{ .name = "__ffssi2", .linkage = linkage }); - const __ffsdi2 = @import("compiler_rt/count0bits.zig").__ffsdi2; - @export(__ffsdi2, .{ .name = "__ffsdi2", .linkage = linkage }); - const __ffsti2 = @import("compiler_rt/count0bits.zig").__ffsti2; - @export(__ffsti2, .{ .name = "__ffsti2", .linkage = linkage }); - - const __paritysi2 = @import("compiler_rt/parity.zig").__paritysi2; - @export(__paritysi2, .{ .name = "__paritysi2", .linkage = linkage }); - const __paritydi2 = @import("compiler_rt/parity.zig").__paritydi2; - @export(__paritydi2, .{ .name = "__paritydi2", .linkage = linkage }); - const __parityti2 = @import("compiler_rt/parity.zig").__parityti2; - @export(__parityti2, .{ .name = "__parityti2", .linkage = linkage }); - const __popcountsi2 = @import("compiler_rt/popcount.zig").__popcountsi2; - @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = linkage }); - const __popcountdi2 = @import("compiler_rt/popcount.zig").__popcountdi2; - @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); - const __popcountti2 = @import("compiler_rt/popcount.zig").__popcountti2; - @export(__popcountti2, .{ .name = "__popcountti2", .linkage = linkage }); - const __bswapsi2 = @import("compiler_rt/bswap.zig").__bswapsi2; - @export(__bswapsi2, .{ .name = "__bswapsi2", .linkage = linkage }); - const __bswapdi2 = @import("compiler_rt/bswap.zig").__bswapdi2; - @export(__bswapdi2, .{ .name = "__bswapdi2", .linkage = linkage }); - const __bswapti2 = @import("compiler_rt/bswap.zig").__bswapti2; - @export(__bswapti2, .{ .name = "__bswapti2", .linkage = linkage }); - - // Integral / floating point conversion (part 1/2) - const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf; - @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); - const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf; - @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); - const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf; - @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); - const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf; - @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); - - const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf; - @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); + const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2; + @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); + const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2; + @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); + const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2; + @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); + + const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3; + @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); + const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3; + @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); + const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; + @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); + const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; + @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); + const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; + @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); + const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; + @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); + + const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; + @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); + const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3; + @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); + const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3; + @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); + + const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3; + @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); + const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3; + @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); + const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3; + @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); + + // Integral bit manipulation + const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3; + @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); + const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3; + @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); + const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3; + @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); + const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3; + @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); + const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3; + @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); + const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3; + @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); + + const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2; + @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); + const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2; + @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); + const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2; + @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); + const __ctzsi2 = @import("compiler_rt/count0bits.zig").__ctzsi2; + @export(__ctzsi2, .{ .name = "__ctzsi2", .linkage = linkage }); + const __ctzdi2 = @import("compiler_rt/count0bits.zig").__ctzdi2; + @export(__ctzdi2, .{ .name = "__ctzdi2", .linkage = linkage }); + const __ctzti2 = @import("compiler_rt/count0bits.zig").__ctzti2; + @export(__ctzti2, .{ .name = "__ctzti2", .linkage = linkage }); + const __ffssi2 = @import("compiler_rt/count0bits.zig").__ffssi2; + @export(__ffssi2, .{ .name = "__ffssi2", .linkage = linkage }); + const __ffsdi2 = @import("compiler_rt/count0bits.zig").__ffsdi2; + @export(__ffsdi2, .{ .name = "__ffsdi2", .linkage = linkage }); + const __ffsti2 = @import("compiler_rt/count0bits.zig").__ffsti2; + @export(__ffsti2, .{ .name = "__ffsti2", .linkage = linkage }); + + const __paritysi2 = @import("compiler_rt/parity.zig").__paritysi2; + @export(__paritysi2, .{ .name = "__paritysi2", .linkage = linkage }); + const __paritydi2 = @import("compiler_rt/parity.zig").__paritydi2; + @export(__paritydi2, .{ .name = "__paritydi2", .linkage = linkage }); + const __parityti2 = @import("compiler_rt/parity.zig").__parityti2; + @export(__parityti2, .{ .name = "__parityti2", .linkage = linkage }); + + const __popcountsi2 = @import("compiler_rt/popcount.zig").__popcountsi2; + @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = linkage }); + const __popcountdi2 = @import("compiler_rt/popcount.zig").__popcountdi2; + @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); + const __popcountti2 = @import("compiler_rt/popcount.zig").__popcountti2; + @export(__popcountti2, .{ .name = "__popcountti2", .linkage = linkage }); + + const __bswapsi2 = @import("compiler_rt/bswap.zig").__bswapsi2; + @export(__bswapsi2, .{ .name = "__bswapsi2", .linkage = linkage }); + const __bswapdi2 = @import("compiler_rt/bswap.zig").__bswapdi2; + @export(__bswapdi2, .{ .name = "__bswapdi2", .linkage = linkage }); + const __bswapti2 = @import("compiler_rt/bswap.zig").__bswapti2; + @export(__bswapti2, .{ .name = "__bswapti2", .linkage = linkage }); + + // Integral / floating point conversion (part 1/2) + const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf; + @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); + const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf; + @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); + const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf; + @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); + const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf; + @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); + + const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf; + @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); + if (!builtin.zig_is_stage2) { const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf; @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); - const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf; - @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); - const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf; - @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); - - const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf; - @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); - const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf; - @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); - const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf; - @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); - const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf; - @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); - const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf; - @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); - - const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf; - @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); - const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf; - @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); - - const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf; - @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); - const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf; - @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); - const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf; - @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); - - const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; - @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); - if (!is_test) { - @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); - } - const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; - @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); - - // Integral / floating point conversion (part 2/2) - const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi; - @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); - const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi; - @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); - const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti; - @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); - - const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi; - @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); - const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi; - @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); - const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti; - @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); - - const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi; - @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); - const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi; - @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); - const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti; - @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); - - const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi; - @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); - const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi; - @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); - const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti; - @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); - const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi; - @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); - const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi; - @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); - const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti; - @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); - const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi; - @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); - const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi; - @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); - const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti; - @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); - - const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; - @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); - - if (is_darwin) { - const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; - @export(__isPlatformVersionAtLeast, .{ .name = "__isPlatformVersionAtLeast", .linkage = linkage }); - } + } + const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf; + @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); + const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf; + @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); + + const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf; + @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); + const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf; + @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); + const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf; + @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); + const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf; + @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); + const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf; + @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); + + const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf; + @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); + const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf; + @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); + + const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf; + @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); + const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf; + @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); + const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf; + @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); + + const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; + @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); + if (!is_test) { + @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); + } + const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; + @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); + + // Integral / floating point conversion (part 2/2) + const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi; + @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); + const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi; + @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); + const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti; + @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); + + const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi; + @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); + const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi; + @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); + const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti; + @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); + + const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi; + @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); + const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi; + @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); + const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti; + @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); + + const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi; + @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); + const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi; + @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); + const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti; + @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); + const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi; + @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); + const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi; + @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); + const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti; + @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); + const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi; + @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); + const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi; + @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); + const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti; + @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); + + const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; + @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); + + if (is_darwin) { + const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; + @export(__isPlatformVersionAtLeast, .{ .name = "__isPlatformVersionAtLeast", .linkage = linkage }); + } - // Integral arithmetic - const __negsi2 = @import("compiler_rt/negXi2.zig").__negsi2; - @export(__negsi2, .{ .name = "__negsi2", .linkage = linkage }); - const __negdi2 = @import("compiler_rt/negXi2.zig").__negdi2; - @export(__negdi2, .{ .name = "__negdi2", .linkage = linkage }); - const __negti2 = @import("compiler_rt/negXi2.zig").__negti2; - @export(__negti2, .{ .name = "__negti2", .linkage = linkage }); - const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3; - @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); - const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3; - @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); - const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4; - @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); - const __divsi3 = @import("compiler_rt/int.zig").__divsi3; - @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); - const __divdi3 = @import("compiler_rt/int.zig").__divdi3; - @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); - const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3; - @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); - const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3; - @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); - const __modsi3 = @import("compiler_rt/int.zig").__modsi3; - @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); - const __moddi3 = @import("compiler_rt/int.zig").__moddi3; - @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); - const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3; - @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); - const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3; - @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); - const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4; - @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); - const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4; - @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); - - // Integral arithmetic with trapping overflow - const __absvsi2 = @import("compiler_rt/absv.zig").__absvsi2; - @export(__absvsi2, .{ .name = "__absvsi2", .linkage = linkage }); - const __absvdi2 = @import("compiler_rt/absv.zig").__absvdi2; - @export(__absvdi2, .{ .name = "__absvdi2", .linkage = linkage }); - const __absvti2 = @import("compiler_rt/absv.zig").__absvti2; - @export(__absvti2, .{ .name = "__absvti2", .linkage = linkage }); - const __negvsi2 = @import("compiler_rt/negv.zig").__negvsi2; - @export(__negvsi2, .{ .name = "__negvsi2", .linkage = linkage }); - const __negvdi2 = @import("compiler_rt/negv.zig").__negvdi2; - @export(__negvdi2, .{ .name = "__negvdi2", .linkage = linkage }); - const __negvti2 = @import("compiler_rt/negv.zig").__negvti2; - @export(__negvti2, .{ .name = "__negvti2", .linkage = linkage }); - - // missing: Integral arithmetic which returns if overflow - - // Integral comparison - // (a < b) => 0 - // (a == b) => 1 - // (a > b) => 2 - const __cmpsi2 = @import("compiler_rt/cmp.zig").__cmpsi2; - @export(__cmpsi2, .{ .name = "__cmpsi2", .linkage = linkage }); - const __cmpdi2 = @import("compiler_rt/cmp.zig").__cmpdi2; - @export(__cmpdi2, .{ .name = "__cmpdi2", .linkage = linkage }); - const __cmpti2 = @import("compiler_rt/cmp.zig").__cmpti2; - @export(__cmpti2, .{ .name = "__cmpti2", .linkage = linkage }); - const __ucmpsi2 = @import("compiler_rt/cmp.zig").__ucmpsi2; - @export(__ucmpsi2, .{ .name = "__ucmpsi2", .linkage = linkage }); - const __ucmpdi2 = @import("compiler_rt/cmp.zig").__ucmpdi2; - @export(__ucmpdi2, .{ .name = "__ucmpdi2", .linkage = linkage }); - const __ucmpti2 = @import("compiler_rt/cmp.zig").__ucmpti2; - @export(__ucmpti2, .{ .name = "__ucmpti2", .linkage = linkage }); - - // missing: Floating point raised to integer power - - // missing: Complex arithmetic - // (a + ib) * (c + id) - // (a + ib) / (c + id) - - const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2; - @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); - const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2; - @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); - - if (builtin.link_libc and os_tag == .openbsd) { - const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address; - @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); - } + // Integral arithmetic + const __negsi2 = @import("compiler_rt/negXi2.zig").__negsi2; + @export(__negsi2, .{ .name = "__negsi2", .linkage = linkage }); + const __negdi2 = @import("compiler_rt/negXi2.zig").__negdi2; + @export(__negdi2, .{ .name = "__negdi2", .linkage = linkage }); + const __negti2 = @import("compiler_rt/negXi2.zig").__negti2; + @export(__negti2, .{ .name = "__negti2", .linkage = linkage }); + const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3; + @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); + const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3; + @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); + const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4; + @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); + const __divsi3 = @import("compiler_rt/int.zig").__divsi3; + @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); + const __divdi3 = @import("compiler_rt/int.zig").__divdi3; + @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); + const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3; + @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); + const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3; + @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); + const __modsi3 = @import("compiler_rt/int.zig").__modsi3; + @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); + const __moddi3 = @import("compiler_rt/int.zig").__moddi3; + @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); + const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3; + @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); + const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3; + @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); + const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4; + @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); + const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4; + @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); + + // Integral arithmetic with trapping overflow + const __absvsi2 = @import("compiler_rt/absv.zig").__absvsi2; + @export(__absvsi2, .{ .name = "__absvsi2", .linkage = linkage }); + const __absvdi2 = @import("compiler_rt/absv.zig").__absvdi2; + @export(__absvdi2, .{ .name = "__absvdi2", .linkage = linkage }); + const __absvti2 = @import("compiler_rt/absv.zig").__absvti2; + @export(__absvti2, .{ .name = "__absvti2", .linkage = linkage }); + const __negvsi2 = @import("compiler_rt/negv.zig").__negvsi2; + @export(__negvsi2, .{ .name = "__negvsi2", .linkage = linkage }); + const __negvdi2 = @import("compiler_rt/negv.zig").__negvdi2; + @export(__negvdi2, .{ .name = "__negvdi2", .linkage = linkage }); + const __negvti2 = @import("compiler_rt/negv.zig").__negvti2; + @export(__negvti2, .{ .name = "__negvti2", .linkage = linkage }); + + // missing: Integral arithmetic which returns if overflow + + // Integral comparison + // (a < b) => 0 + // (a == b) => 1 + // (a > b) => 2 + const __cmpsi2 = @import("compiler_rt/cmp.zig").__cmpsi2; + @export(__cmpsi2, .{ .name = "__cmpsi2", .linkage = linkage }); + const __cmpdi2 = @import("compiler_rt/cmp.zig").__cmpdi2; + @export(__cmpdi2, .{ .name = "__cmpdi2", .linkage = linkage }); + const __cmpti2 = @import("compiler_rt/cmp.zig").__cmpti2; + @export(__cmpti2, .{ .name = "__cmpti2", .linkage = linkage }); + const __ucmpsi2 = @import("compiler_rt/cmp.zig").__ucmpsi2; + @export(__ucmpsi2, .{ .name = "__ucmpsi2", .linkage = linkage }); + const __ucmpdi2 = @import("compiler_rt/cmp.zig").__ucmpdi2; + @export(__ucmpdi2, .{ .name = "__ucmpdi2", .linkage = linkage }); + const __ucmpti2 = @import("compiler_rt/cmp.zig").__ucmpti2; + @export(__ucmpti2, .{ .name = "__ucmpti2", .linkage = linkage }); + + // missing: Floating point raised to integer power + + // missing: Complex arithmetic + // (a + ib) * (c + id) + // (a + ib) / (c + id) + + const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2; + @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); + const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2; + @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); + + if (builtin.link_libc and os_tag == .openbsd) { + const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address; + @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); + } - if ((arch.isARM() or arch.isThumb()) and !is_test) { - const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0; - @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); - const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1; - @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); - const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2; - @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); - - @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage }); - - const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod; - @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); - const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod; - @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); - - @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage }); - const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod; - @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); - @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage }); - const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod; - @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); - - const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy; - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); - - const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove; - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage }); - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage }); - - const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset; - @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); - @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage }); - @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage }); - - const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr; - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); - - if (os_tag == .linux) { - const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp; - @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); - } - - const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d; - @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); - const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d; - @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); - const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d; - @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); - const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f; - @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); - const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d; - @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); - const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d; - @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); - const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f; - @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); - const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f; - @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); - - const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; - @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); - const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg; - @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); - - const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul; - @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); - const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul; - @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); - - const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h; - @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); - - const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz; - @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); - const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz; - @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); - - const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz; - @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); - const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz; - @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); - - const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz; - @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); - - const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; - @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); - const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h; - @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); - - const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f; - @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); - const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f; - @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); - - const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd; - @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); - const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd; - @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); - const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub; - @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); - const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub; - @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); - - const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz; - @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); - - const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz; - @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); - const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz; - @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); - - const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; - @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); - const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv; - @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); - - const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl; - @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); - const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr; - @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); - const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr; - @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); - - const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq; - @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); - const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt; - @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); - const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple; - @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); - const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge; - @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); - const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt; - @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); - const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun; - @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); - - const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq; - @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); - const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt; - @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); - const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple; - @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); - const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge; - @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); - const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt; - @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); - const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun; - @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); + if ((arch.isARM() or arch.isThumb()) and !is_test) { + const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0; + @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); + const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1; + @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); + const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2; + @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); + + @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage }); + + const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod; + @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); + const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod; + @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); + + @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage }); + const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod; + @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); + @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage }); + const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod; + @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); + + const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy; + @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); + @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); + @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); + + const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove; + @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); + @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage }); + @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage }); + + const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset; + @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); + @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage }); + @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage }); + + const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr; + @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); + @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); + @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); + + if (os_tag == .linux) { + const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp; + @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); } - if (arch == .i386 and abi == .msvc) { - // Don't let LLVM apply the stdcall name mangling on those MSVC builtins - const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv; - @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); - const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv; - @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); - const _allrem = @import("compiler_rt/aullrem.zig")._allrem; - @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); - const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem; - @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); - } + const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d; + @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); + const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d; + @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); + const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d; + @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); + const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f; + @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); + const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d; + @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); + const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d; + @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); + const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f; + @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); + const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f; + @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); + + const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; + @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); + const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg; + @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); + + const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul; + @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); + const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul; + @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); + + const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h; + @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); + + const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz; + @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); + const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz; + @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); + + const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz; + @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); + const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz; + @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); + + const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz; + @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); + + const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; + @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); + const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h; + @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); + + const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f; + @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); + const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f; + @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); + + const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd; + @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); + const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd; + @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); + const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub; + @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); + const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub; + @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); + + const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz; + @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); + + const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz; + @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); + const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz; + @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); + + const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; + @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); + const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv; + @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); + + const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl; + @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); + const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr; + @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); + const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr; + @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); + + const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq; + @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); + const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt; + @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); + const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple; + @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); + const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge; + @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); + const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt; + @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); + const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun; + @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); + + const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq; + @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); + const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt; + @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); + const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple; + @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); + const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge; + @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); + const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt; + @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); + const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun; + @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); + } - if (arch.isSPARC()) { - // SPARC systems use a different naming scheme - const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add; - @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); - const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div; - @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); - const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul; - @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); - const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub; - @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); - - const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp; - @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); - const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq; - @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); - const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne; - @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); - const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt; - @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); - const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle; - @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); - const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt; - @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); - const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge; - @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); - - const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq; - @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); - const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq; - @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); - const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq; - @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); - const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq; - @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); - const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq; - @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); - const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq; - @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); - const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi; - @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); - const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui; - @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); - const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox; - @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); - const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux; - @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); - const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos; - @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); - const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod; - @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); - } + if (arch == .i386 and abi == .msvc) { + // Don't let LLVM apply the stdcall name mangling on those MSVC builtins + const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv; + @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); + const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv; + @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); + const _allrem = @import("compiler_rt/aullrem.zig")._allrem; + @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); + const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem; + @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); + } - if ((arch == .powerpc or arch.isPPC64()) and !is_test) { - @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage }); - @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage }); - @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage }); - @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage }); - @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage }); - @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage }); - @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); - @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); - @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage }); - @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage }); - @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); - @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); - @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage }); - @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage }); - @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage }); - @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage }); - - @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__nekf2", .linkage = linkage }); - @export(__getf2, .{ .name = "__gekf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__lekf2", .linkage = linkage }); - @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage }); - @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); - } + if (arch.isSPARC()) { + // SPARC systems use a different naming scheme + const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add; + @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); + const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div; + @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); + const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul; + @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); + const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub; + @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); + + const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp; + @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); + const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq; + @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); + const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne; + @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); + const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt; + @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); + const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle; + @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); + const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt; + @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); + const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge; + @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); + + const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq; + @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); + const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq; + @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); + const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq; + @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); + const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq; + @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); + const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq; + @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); + const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq; + @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); + const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi; + @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); + const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui; + @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); + const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox; + @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); + const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux; + @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); + const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos; + @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); + const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod; + @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); + } - _ = @import("compiler_rt/atomics.zig"); + if ((arch == .powerpc or arch.isPPC64()) and !is_test) { + @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage }); + @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage }); + @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage }); + @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage }); + @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage }); + @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage }); + @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); + @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); + @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage }); + @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage }); + @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); + @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); + @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage }); + @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage }); + @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage }); + @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage }); + + @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage }); + @export(__letf2, .{ .name = "__nekf2", .linkage = linkage }); + @export(__getf2, .{ .name = "__gekf2", .linkage = linkage }); + @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage }); + @export(__letf2, .{ .name = "__lekf2", .linkage = linkage }); + @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage }); + @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); + } + + @export(floorf, .{ .name = "floorf", .linkage = linkage }); + @export(floor, .{ .name = "floor", .linkage = linkage }); + @export(floorl, .{ .name = "floorl", .linkage = linkage }); + if (!builtin.zig_is_stage2) { @export(fmaq, .{ .name = "fmaq", .linkage = linkage }); - @export(floorf, .{ .name = "floorf", .linkage = linkage }); - @export(floor, .{ .name = "floor", .linkage = linkage }); - @export(floorl, .{ .name = "floorl", .linkage = linkage }); } } diff --git a/lib/std/special/compiler_rt/absv.zig b/lib/std/special/compiler_rt/absv.zig index 5c46d028a9c9..f14497daf204 100644 --- a/lib/std/special/compiler_rt/absv.zig +++ b/lib/std/special/compiler_rt/absv.zig @@ -2,31 +2,36 @@ // * @panic, if value can not be represented // - absvXi4_generic for unoptimized version -fn absvXi_generic(comptime ST: type) fn (a: ST) callconv(.C) ST { - return struct { - fn f(a: ST) callconv(.C) ST { - const UT = switch (ST) { - i32 => u32, - i64 => u64, - i128 => u128, - else => unreachable, - }; - // taken from Bit Twiddling Hacks - // compute the integer absolute value (abs) without branching - var x: ST = a; - const N: UT = @bitSizeOf(ST); - const sign: ST = a >> N - 1; - x +%= sign; - x ^= sign; - if (x < 0) - @panic("compiler_rt absv: overflow"); - return x; - } - }.f; +inline fn absvXi(comptime ST: type, a: ST) ST { + const UT = switch (ST) { + i32 => u32, + i64 => u64, + i128 => u128, + else => unreachable, + }; + // taken from Bit Twiddling Hacks + // compute the integer absolute value (abs) without branching + var x: ST = a; + const N: UT = @bitSizeOf(ST); + const sign: ST = a >> N - 1; + x +%= sign; + x ^= sign; + if (x < 0) + @panic("compiler_rt absv: overflow"); + return x; +} + +pub fn __absvsi2(a: i32) callconv(.C) i32 { + return absvXi(i32, a); +} + +pub fn __absvdi2(a: i64) callconv(.C) i64 { + return absvXi(i64, a); +} + +pub fn __absvti2(a: i128) callconv(.C) i128 { + return absvXi(i128, a); } -pub const __absvsi2 = absvXi_generic(i32); -pub const __absvdi2 = absvXi_generic(i64); -pub const __absvti2 = absvXi_generic(i128); test { _ = @import("absvsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig index 3d93dc33b1c4..7727d7af3d9d 100644 --- a/lib/std/special/compiler_rt/atomics.zig +++ b/lib/std/special/compiler_rt/atomics.zig @@ -119,225 +119,311 @@ fn __atomic_compare_exchange( return 0; } -comptime { - if (supports_atomic_ops) { - @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage }); - @export(__atomic_store, .{ .name = "__atomic_store", .linkage = linkage }); - @export(__atomic_exchange, .{ .name = "__atomic_exchange", .linkage = linkage }); - @export(__atomic_compare_exchange, .{ .name = "__atomic_compare_exchange", .linkage = linkage }); - } -} - // Specialized versions of the GCC atomic builtin functions. // LLVM emits those iff the object size is known and the pointers are correctly // aligned. +inline fn atomic_load_N(comptime T: type, src: *T, model: i32) T { + _ = model; + if (@sizeOf(T) > largest_atomic_size) { + var sl = spinlocks.get(@ptrToInt(src)); + defer sl.release(); + return src.*; + } else { + return @atomicLoad(T, src, .SeqCst); + } +} -fn atomicLoadFn(comptime T: type) fn (*T, i32) callconv(.C) T { - return struct { - fn atomic_load_N(src: *T, model: i32) callconv(.C) T { - _ = model; - if (@sizeOf(T) > largest_atomic_size) { - var sl = spinlocks.get(@ptrToInt(src)); - defer sl.release(); - return src.*; - } else { - return @atomicLoad(T, src, .SeqCst); - } - } - }.atomic_load_N; +fn __atomic_load_1(src: *u8, model: i32) callconv(.C) u8 { + return atomic_load_N(u8, src, model); } -comptime { - if (supports_atomic_ops) { - const atomicLoad_u8 = atomicLoadFn(u8); - const atomicLoad_u16 = atomicLoadFn(u16); - const atomicLoad_u32 = atomicLoadFn(u32); - const atomicLoad_u64 = atomicLoadFn(u64); - @export(atomicLoad_u8, .{ .name = "__atomic_load_1", .linkage = linkage }); - @export(atomicLoad_u16, .{ .name = "__atomic_load_2", .linkage = linkage }); - @export(atomicLoad_u32, .{ .name = "__atomic_load_4", .linkage = linkage }); - @export(atomicLoad_u64, .{ .name = "__atomic_load_8", .linkage = linkage }); - } +fn __atomic_load_2(src: *u16, model: i32) callconv(.C) u16 { + return atomic_load_N(u16, src, model); } -fn atomicStoreFn(comptime T: type) fn (*T, T, i32) callconv(.C) void { - return struct { - fn atomic_store_N(dst: *T, value: T, model: i32) callconv(.C) void { - _ = model; - if (@sizeOf(T) > largest_atomic_size) { - var sl = spinlocks.get(@ptrToInt(dst)); - defer sl.release(); - dst.* = value; - } else { - @atomicStore(T, dst, value, .SeqCst); - } - } - }.atomic_store_N; +fn __atomic_load_4(src: *u32, model: i32) callconv(.C) u32 { + return atomic_load_N(u32, src, model); } -comptime { - if (supports_atomic_ops) { - const atomicStore_u8 = atomicStoreFn(u8); - const atomicStore_u16 = atomicStoreFn(u16); - const atomicStore_u32 = atomicStoreFn(u32); - const atomicStore_u64 = atomicStoreFn(u64); - @export(atomicStore_u8, .{ .name = "__atomic_store_1", .linkage = linkage }); - @export(atomicStore_u16, .{ .name = "__atomic_store_2", .linkage = linkage }); - @export(atomicStore_u32, .{ .name = "__atomic_store_4", .linkage = linkage }); - @export(atomicStore_u64, .{ .name = "__atomic_store_8", .linkage = linkage }); +fn __atomic_load_8(src: *u64, model: i32) callconv(.C) u64 { + return atomic_load_N(u64, src, model); +} + +inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void { + _ = model; + if (@sizeOf(T) > largest_atomic_size) { + var sl = spinlocks.get(@ptrToInt(dst)); + defer sl.release(); + dst.* = value; + } else { + @atomicStore(T, dst, value, .SeqCst); } } -fn atomicExchangeFn(comptime T: type) fn (*T, T, i32) callconv(.C) T { - return struct { - fn atomic_exchange_N(ptr: *T, val: T, model: i32) callconv(.C) T { - _ = model; - if (@sizeOf(T) > largest_atomic_size) { - var sl = spinlocks.get(@ptrToInt(ptr)); - defer sl.release(); - const value = ptr.*; - ptr.* = val; - return value; - } else { - return @atomicRmw(T, ptr, .Xchg, val, .SeqCst); - } - } - }.atomic_exchange_N; +fn __atomic_store_1(dst: *u8, value: u8, model: i32) callconv(.C) void { + return atomic_store_N(u8, dst, value, model); } -comptime { - if (supports_atomic_ops) { - const atomicExchange_u8 = atomicExchangeFn(u8); - const atomicExchange_u16 = atomicExchangeFn(u16); - const atomicExchange_u32 = atomicExchangeFn(u32); - const atomicExchange_u64 = atomicExchangeFn(u64); - @export(atomicExchange_u8, .{ .name = "__atomic_exchange_1", .linkage = linkage }); - @export(atomicExchange_u16, .{ .name = "__atomic_exchange_2", .linkage = linkage }); - @export(atomicExchange_u32, .{ .name = "__atomic_exchange_4", .linkage = linkage }); - @export(atomicExchange_u64, .{ .name = "__atomic_exchange_8", .linkage = linkage }); +fn __atomic_store_2(dst: *u16, value: u16, model: i32) callconv(.C) void { + return atomic_store_N(u16, dst, value, model); +} + +fn __atomic_store_4(dst: *u32, value: u32, model: i32) callconv(.C) void { + return atomic_store_N(u32, dst, value, model); +} + +fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void { + return atomic_store_N(u64, dst, value, model); +} + +inline fn atomic_exchange_N(comptime T: type, ptr: *T, val: T, model: i32) T { + _ = model; + if (@sizeOf(T) > largest_atomic_size) { + var sl = spinlocks.get(@ptrToInt(ptr)); + defer sl.release(); + const value = ptr.*; + ptr.* = val; + return value; + } else { + return @atomicRmw(T, ptr, .Xchg, val, .SeqCst); } } -fn atomicCompareExchangeFn(comptime T: type) fn (*T, *T, T, i32, i32) callconv(.C) i32 { - return struct { - fn atomic_compare_exchange_N(ptr: *T, expected: *T, desired: T, success: i32, failure: i32) callconv(.C) i32 { - _ = success; - _ = failure; - if (@sizeOf(T) > largest_atomic_size) { - var sl = spinlocks.get(@ptrToInt(ptr)); - defer sl.release(); - const value = ptr.*; - if (value == expected.*) { - ptr.* = desired; - return 1; - } - expected.* = value; - return 0; - } else { - if (@cmpxchgStrong(T, ptr, expected.*, desired, .SeqCst, .SeqCst)) |old_value| { - expected.* = old_value; - return 0; - } - return 1; - } +fn __atomic_exchange_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return atomic_exchange_N(u8, ptr, val, model); +} + +fn __atomic_exchange_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return atomic_exchange_N(u16, ptr, val, model); +} + +fn __atomic_exchange_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return atomic_exchange_N(u32, ptr, val, model); +} + +fn __atomic_exchange_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return atomic_exchange_N(u64, ptr, val, model); +} + +inline fn atomic_compare_exchange_N( + comptime T: type, + ptr: *T, + expected: *T, + desired: T, + success: i32, + failure: i32, +) i32 { + _ = success; + _ = failure; + if (@sizeOf(T) > largest_atomic_size) { + var sl = spinlocks.get(@ptrToInt(ptr)); + defer sl.release(); + const value = ptr.*; + if (value == expected.*) { + ptr.* = desired; + return 1; } - }.atomic_compare_exchange_N; + expected.* = value; + return 0; + } else { + if (@cmpxchgStrong(T, ptr, expected.*, desired, .SeqCst, .SeqCst)) |old_value| { + expected.* = old_value; + return 0; + } + return 1; + } } -comptime { - if (supports_atomic_ops) { - const atomicCompareExchange_u8 = atomicCompareExchangeFn(u8); - const atomicCompareExchange_u16 = atomicCompareExchangeFn(u16); - const atomicCompareExchange_u32 = atomicCompareExchangeFn(u32); - const atomicCompareExchange_u64 = atomicCompareExchangeFn(u64); - @export(atomicCompareExchange_u8, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage }); - @export(atomicCompareExchange_u16, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage }); - @export(atomicCompareExchange_u32, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage }); - @export(atomicCompareExchange_u64, .{ .name = "__atomic_compare_exchange_8", .linkage = linkage }); +fn __atomic_compare_exchange_1(ptr: *u8, expected: *u8, desired: u8, success: i32, failure: i32) callconv(.C) i32 { + return atomic_compare_exchange_N(u8, ptr, expected, desired, success, failure); +} + +fn __atomic_compare_exchange_2(ptr: *u16, expected: *u16, desired: u16, success: i32, failure: i32) callconv(.C) i32 { + return atomic_compare_exchange_N(u16, ptr, expected, desired, success, failure); +} + +fn __atomic_compare_exchange_4(ptr: *u32, expected: *u32, desired: u32, success: i32, failure: i32) callconv(.C) i32 { + return atomic_compare_exchange_N(u32, ptr, expected, desired, success, failure); +} + +fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success: i32, failure: i32) callconv(.C) i32 { + return atomic_compare_exchange_N(u64, ptr, expected, desired, success, failure); +} + +inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T { + _ = model; + if (@sizeOf(T) > largest_atomic_size) { + var sl = spinlocks.get(@ptrToInt(ptr)); + defer sl.release(); + + const value = ptr.*; + ptr.* = switch (op) { + .Add => value +% val, + .Sub => value -% val, + .And => value & val, + .Nand => ~(value & val), + .Or => value | val, + .Xor => value ^ val, + else => @compileError("unsupported atomic op"), + }; + + return value; } + + return @atomicRmw(T, ptr, op, val, .SeqCst); } -fn fetchFn(comptime T: type, comptime op: std.builtin.AtomicRmwOp) fn (*T, T, i32) callconv(.C) T { - return struct { - pub fn fetch_op_N(ptr: *T, val: T, model: i32) callconv(.C) T { - _ = model; - if (@sizeOf(T) > largest_atomic_size) { - var sl = spinlocks.get(@ptrToInt(ptr)); - defer sl.release(); - - const value = ptr.*; - ptr.* = switch (op) { - .Add => value +% val, - .Sub => value -% val, - .And => value & val, - .Nand => ~(value & val), - .Or => value | val, - .Xor => value ^ val, - else => @compileError("unsupported atomic op"), - }; - - return value; - } +fn __atomic_fetch_add_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return fetch_op_N(u8, .Add, ptr, val, model); +} - return @atomicRmw(T, ptr, op, val, .SeqCst); - } - }.fetch_op_N; +fn __atomic_fetch_add_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return fetch_op_N(u16, .Add, ptr, val, model); +} + +fn __atomic_fetch_add_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return fetch_op_N(u32, .Add, ptr, val, model); +} + +fn __atomic_fetch_add_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return fetch_op_N(u64, .Add, ptr, val, model); +} + +fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return fetch_op_N(u8, .Sub, ptr, val, model); +} + +fn __atomic_fetch_sub_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return fetch_op_N(u16, .Sub, ptr, val, model); +} + +fn __atomic_fetch_sub_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return fetch_op_N(u32, .Sub, ptr, val, model); +} + +fn __atomic_fetch_sub_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return fetch_op_N(u64, .Sub, ptr, val, model); +} + +fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return fetch_op_N(u8, .And, ptr, val, model); +} + +fn __atomic_fetch_and_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return fetch_op_N(u16, .And, ptr, val, model); +} + +fn __atomic_fetch_and_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return fetch_op_N(u32, .And, ptr, val, model); +} + +fn __atomic_fetch_and_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return fetch_op_N(u64, .And, ptr, val, model); +} + +fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return fetch_op_N(u8, .Or, ptr, val, model); +} + +fn __atomic_fetch_or_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return fetch_op_N(u16, .Or, ptr, val, model); +} + +fn __atomic_fetch_or_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return fetch_op_N(u32, .Or, ptr, val, model); +} + +fn __atomic_fetch_or_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return fetch_op_N(u64, .Or, ptr, val, model); +} + +fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return fetch_op_N(u8, .Xor, ptr, val, model); +} + +fn __atomic_fetch_xor_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return fetch_op_N(u16, .Xor, ptr, val, model); +} + +fn __atomic_fetch_xor_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return fetch_op_N(u32, .Xor, ptr, val, model); +} + +fn __atomic_fetch_xor_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return fetch_op_N(u64, .Xor, ptr, val, model); +} + +fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { + return fetch_op_N(u8, .Nand, ptr, val, model); +} + +fn __atomic_fetch_nand_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { + return fetch_op_N(u16, .Nand, ptr, val, model); +} + +fn __atomic_fetch_nand_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { + return fetch_op_N(u32, .Nand, ptr, val, model); +} + +fn __atomic_fetch_nand_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { + return fetch_op_N(u64, .Nand, ptr, val, model); } comptime { if (supports_atomic_ops) { - const fetch_add_u8 = fetchFn(u8, .Add); - const fetch_add_u16 = fetchFn(u16, .Add); - const fetch_add_u32 = fetchFn(u32, .Add); - const fetch_add_u64 = fetchFn(u64, .Add); - @export(fetch_add_u8, .{ .name = "__atomic_fetch_add_1", .linkage = linkage }); - @export(fetch_add_u16, .{ .name = "__atomic_fetch_add_2", .linkage = linkage }); - @export(fetch_add_u32, .{ .name = "__atomic_fetch_add_4", .linkage = linkage }); - @export(fetch_add_u64, .{ .name = "__atomic_fetch_add_8", .linkage = linkage }); - - const fetch_sub_u8 = fetchFn(u8, .Sub); - const fetch_sub_u16 = fetchFn(u16, .Sub); - const fetch_sub_u32 = fetchFn(u32, .Sub); - const fetch_sub_u64 = fetchFn(u64, .Sub); - @export(fetch_sub_u8, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage }); - @export(fetch_sub_u16, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage }); - @export(fetch_sub_u32, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage }); - @export(fetch_sub_u64, .{ .name = "__atomic_fetch_sub_8", .linkage = linkage }); - - const fetch_and_u8 = fetchFn(u8, .And); - const fetch_and_u16 = fetchFn(u16, .And); - const fetch_and_u32 = fetchFn(u32, .And); - const fetch_and_u64 = fetchFn(u64, .And); - @export(fetch_and_u8, .{ .name = "__atomic_fetch_and_1", .linkage = linkage }); - @export(fetch_and_u16, .{ .name = "__atomic_fetch_and_2", .linkage = linkage }); - @export(fetch_and_u32, .{ .name = "__atomic_fetch_and_4", .linkage = linkage }); - @export(fetch_and_u64, .{ .name = "__atomic_fetch_and_8", .linkage = linkage }); - - const fetch_or_u8 = fetchFn(u8, .Or); - const fetch_or_u16 = fetchFn(u16, .Or); - const fetch_or_u32 = fetchFn(u32, .Or); - const fetch_or_u64 = fetchFn(u64, .Or); - @export(fetch_or_u8, .{ .name = "__atomic_fetch_or_1", .linkage = linkage }); - @export(fetch_or_u16, .{ .name = "__atomic_fetch_or_2", .linkage = linkage }); - @export(fetch_or_u32, .{ .name = "__atomic_fetch_or_4", .linkage = linkage }); - @export(fetch_or_u64, .{ .name = "__atomic_fetch_or_8", .linkage = linkage }); - - const fetch_xor_u8 = fetchFn(u8, .Xor); - const fetch_xor_u16 = fetchFn(u16, .Xor); - const fetch_xor_u32 = fetchFn(u32, .Xor); - const fetch_xor_u64 = fetchFn(u64, .Xor); - @export(fetch_xor_u8, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage }); - @export(fetch_xor_u16, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage }); - @export(fetch_xor_u32, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage }); - @export(fetch_xor_u64, .{ .name = "__atomic_fetch_xor_8", .linkage = linkage }); - - const fetch_nand_u8 = fetchFn(u8, .Nand); - const fetch_nand_u16 = fetchFn(u16, .Nand); - const fetch_nand_u32 = fetchFn(u32, .Nand); - const fetch_nand_u64 = fetchFn(u64, .Nand); - @export(fetch_nand_u8, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage }); - @export(fetch_nand_u16, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage }); - @export(fetch_nand_u32, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage }); - @export(fetch_nand_u64, .{ .name = "__atomic_fetch_nand_8", .linkage = linkage }); + @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage }); + @export(__atomic_store, .{ .name = "__atomic_store", .linkage = linkage }); + @export(__atomic_exchange, .{ .name = "__atomic_exchange", .linkage = linkage }); + @export(__atomic_compare_exchange, .{ .name = "__atomic_compare_exchange", .linkage = linkage }); + + @export(__atomic_fetch_add_1, .{ .name = "__atomic_fetch_add_1", .linkage = linkage }); + @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage }); + @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage }); + @export(__atomic_fetch_add_8, .{ .name = "__atomic_fetch_add_8", .linkage = linkage }); + + @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage }); + @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage }); + @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage }); + @export(__atomic_fetch_sub_8, .{ .name = "__atomic_fetch_sub_8", .linkage = linkage }); + + @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage }); + @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage }); + @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage }); + @export(__atomic_fetch_and_8, .{ .name = "__atomic_fetch_and_8", .linkage = linkage }); + + @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage }); + @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage }); + @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage }); + @export(__atomic_fetch_or_8, .{ .name = "__atomic_fetch_or_8", .linkage = linkage }); + + @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage }); + @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage }); + @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage }); + @export(__atomic_fetch_xor_8, .{ .name = "__atomic_fetch_xor_8", .linkage = linkage }); + + @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage }); + @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage }); + @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage }); + @export(__atomic_fetch_nand_8, .{ .name = "__atomic_fetch_nand_8", .linkage = linkage }); + + @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage }); + @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage }); + @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage }); + @export(__atomic_load_8, .{ .name = "__atomic_load_8", .linkage = linkage }); + + @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage }); + @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage }); + @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage }); + @export(__atomic_store_8, .{ .name = "__atomic_store_8", .linkage = linkage }); + + @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage }); + @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage }); + @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage }); + @export(__atomic_exchange_8, .{ .name = "__atomic_exchange_8", .linkage = linkage }); + + @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage }); + @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage }); + @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage }); + @export(__atomic_compare_exchange_8, .{ .name = "__atomic_compare_exchange_8", .linkage = linkage }); } } diff --git a/lib/std/special/compiler_rt/bswap.zig b/lib/std/special/compiler_rt/bswap.zig index 0646f68c1a3c..f1d2138811e0 100644 --- a/lib/std/special/compiler_rt/bswap.zig +++ b/lib/std/special/compiler_rt/bswap.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); // bswap - byteswap -// - bswapXi2_generic for unoptimized big and little endian +// - bswapXi2 for unoptimized big and little endian // ie for u32 // DE AD BE EF <- little|big endian // FE BE AD DE <- big|little endian @@ -11,64 +11,64 @@ const builtin = @import("builtin"); // 00 00 ff 00 << 1*8 (2n right byte) // 00 00 00 ff << 3*8 (rightmost byte) -fn bswapXi2_generic(comptime T: type) fn (a: T) callconv(.C) T { - return struct { - fn f(a: T) callconv(.C) T { - @setRuntimeSafety(builtin.is_test); - switch (@bitSizeOf(T)) { - 32 => { - // zig fmt: off - return (((a & 0xff000000) >> 24) - | ((a & 0x00ff0000) >> 8 ) - | ((a & 0x0000ff00) << 8 ) - | ((a & 0x000000ff) << 24)); - // zig fmt: on - }, - 64 => { - // zig fmt: off - return (((a & 0xff00000000000000) >> 56) - | ((a & 0x00ff000000000000) >> 40 ) - | ((a & 0x0000ff0000000000) >> 24 ) - | ((a & 0x000000ff00000000) >> 8 ) - | ((a & 0x00000000ff000000) << 8 ) - | ((a & 0x0000000000ff0000) << 24 ) - | ((a & 0x000000000000ff00) << 40 ) - | ((a & 0x00000000000000ff) << 56)); - // zig fmt: on - }, - 128 => { - // zig fmt: off - return (((a & 0xff000000000000000000000000000000) >> 120) - | ((a & 0x00ff0000000000000000000000000000) >> 104) - | ((a & 0x0000ff00000000000000000000000000) >> 88 ) - | ((a & 0x000000ff000000000000000000000000) >> 72 ) - | ((a & 0x00000000ff0000000000000000000000) >> 56 ) - | ((a & 0x0000000000ff00000000000000000000) >> 40 ) - | ((a & 0x000000000000ff000000000000000000) >> 24 ) - | ((a & 0x00000000000000ff0000000000000000) >> 8 ) - | ((a & 0x0000000000000000ff00000000000000) << 8 ) - | ((a & 0x000000000000000000ff000000000000) << 24 ) - | ((a & 0x00000000000000000000ff0000000000) << 40 ) - | ((a & 0x0000000000000000000000ff00000000) << 56 ) - | ((a & 0x000000000000000000000000ff000000) << 72 ) - | ((a & 0x00000000000000000000000000ff0000) << 88 ) - | ((a & 0x0000000000000000000000000000ff00) << 104) - | ((a & 0x000000000000000000000000000000ff) << 120)); - // zig fmt: on - }, - else => { - unreachable; - }, - } - } - }.f; +inline fn bswapXi2(comptime T: type, a: T) T { + @setRuntimeSafety(builtin.is_test); + switch (@bitSizeOf(T)) { + 32 => { + // zig fmt: off + return (((a & 0xff000000) >> 24) + | ((a & 0x00ff0000) >> 8 ) + | ((a & 0x0000ff00) << 8 ) + | ((a & 0x000000ff) << 24)); + // zig fmt: on + }, + 64 => { + // zig fmt: off + return (((a & 0xff00000000000000) >> 56) + | ((a & 0x00ff000000000000) >> 40 ) + | ((a & 0x0000ff0000000000) >> 24 ) + | ((a & 0x000000ff00000000) >> 8 ) + | ((a & 0x00000000ff000000) << 8 ) + | ((a & 0x0000000000ff0000) << 24 ) + | ((a & 0x000000000000ff00) << 40 ) + | ((a & 0x00000000000000ff) << 56)); + // zig fmt: on + }, + 128 => { + // zig fmt: off + return (((a & 0xff000000000000000000000000000000) >> 120) + | ((a & 0x00ff0000000000000000000000000000) >> 104) + | ((a & 0x0000ff00000000000000000000000000) >> 88 ) + | ((a & 0x000000ff000000000000000000000000) >> 72 ) + | ((a & 0x00000000ff0000000000000000000000) >> 56 ) + | ((a & 0x0000000000ff00000000000000000000) >> 40 ) + | ((a & 0x000000000000ff000000000000000000) >> 24 ) + | ((a & 0x00000000000000ff0000000000000000) >> 8 ) + | ((a & 0x0000000000000000ff00000000000000) << 8 ) + | ((a & 0x000000000000000000ff000000000000) << 24 ) + | ((a & 0x00000000000000000000ff0000000000) << 40 ) + | ((a & 0x0000000000000000000000ff00000000) << 56 ) + | ((a & 0x000000000000000000000000ff000000) << 72 ) + | ((a & 0x00000000000000000000000000ff0000) << 88 ) + | ((a & 0x0000000000000000000000000000ff00) << 104) + | ((a & 0x000000000000000000000000000000ff) << 120)); + // zig fmt: on + }, + else => unreachable, + } } -pub const __bswapsi2 = bswapXi2_generic(u32); +pub fn __bswapsi2(a: u32) callconv(.C) u32 { + return bswapXi2(u32, a); +} -pub const __bswapdi2 = bswapXi2_generic(u64); +pub fn __bswapdi2(a: u64) callconv(.C) u64 { + return bswapXi2(u64, a); +} -pub const __bswapti2 = bswapXi2_generic(u128); +pub fn __bswapti2(a: u128) callconv(.C) u128 { + return bswapXi2(u128, a); +} test { _ = @import("bswapsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/cmp.zig b/lib/std/special/compiler_rt/cmp.zig index 630948ba2832..9eb4227527f2 100644 --- a/lib/std/special/compiler_rt/cmp.zig +++ b/lib/std/special/compiler_rt/cmp.zig @@ -11,28 +11,40 @@ const builtin = @import("builtin"); // a == b => 1 // a > b => 2 -fn XcmpXi2_generic(comptime T: type) fn (a: T, b: T) callconv(.C) i32 { - return struct { - fn f(a: T, b: T) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - var cmp1: i32 = 0; - var cmp2: i32 = 0; - if (a > b) - cmp1 = 1; - if (a < b) - cmp2 = 1; - return cmp1 - cmp2 + 1; - } - }.f; -} - -pub const __cmpsi2 = XcmpXi2_generic(i32); -pub const __cmpdi2 = XcmpXi2_generic(i64); -pub const __cmpti2 = XcmpXi2_generic(i128); - -pub const __ucmpsi2 = XcmpXi2_generic(u32); -pub const __ucmpdi2 = XcmpXi2_generic(u64); -pub const __ucmpti2 = XcmpXi2_generic(u128); +inline fn XcmpXi2(comptime T: type, a: T, b: T) i32 { + @setRuntimeSafety(builtin.is_test); + var cmp1: i32 = 0; + var cmp2: i32 = 0; + if (a > b) + cmp1 = 1; + if (a < b) + cmp2 = 1; + return cmp1 - cmp2 + 1; +} + +pub fn __cmpsi2(a: i32, b: i32) callconv(.C) i32 { + return XcmpXi2(i32, a, b); +} + +pub fn __cmpdi2(a: i64, b: i64) callconv(.C) i32 { + return XcmpXi2(i64, a, b); +} + +pub fn __cmpti2(a: i128, b: i128) callconv(.C) i32 { + return XcmpXi2(i128, a, b); +} + +pub fn __ucmpsi2(a: u32, b: u32) callconv(.C) i32 { + return XcmpXi2(u32, a, b); +} + +pub fn __ucmpdi2(a: u64, b: u64) callconv(.C) i32 { + return XcmpXi2(u64, a, b); +} + +pub fn __ucmpti2(a: u128, b: u128) callconv(.C) i32 { + return XcmpXi2(u128, a, b); +} test { _ = @import("cmpsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/count0bits.zig b/lib/std/special/compiler_rt/count0bits.zig index 29ba800b43bc..1f6d28ae0b06 100644 --- a/lib/std/special/compiler_rt/count0bits.zig +++ b/lib/std/special/compiler_rt/count0bits.zig @@ -2,44 +2,40 @@ const std = @import("std"); const builtin = @import("builtin"); // clz - count leading zeroes -// - clzXi2_generic for unoptimized little and big endian +// - clzXi2 for unoptimized little and big endian // - __clzsi2_thumb1: assume a != 0 // - __clzsi2_arm32: assume a != 0 // ctz - count trailing zeroes -// - ctzXi2_generic for unoptimized little and big endian +// - ctzXi2 for unoptimized little and big endian // ffs - find first set // * ffs = (a == 0) => 0, (a != 0) => ctz + 1 // * dont pay for `if (x == 0) return shift;` inside ctz -// - ffsXi2_generic for unoptimized little and big endian - -fn clzXi2_generic(comptime T: type) fn (a: T) callconv(.C) i32 { - return struct { - fn f(a: T) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - - var x = switch (@bitSizeOf(T)) { - 32 => @bitCast(u32, a), - 64 => @bitCast(u64, a), - 128 => @bitCast(u128, a), - else => unreachable, - }; - var n: T = @bitSizeOf(T); - // Count first bit set using binary search, from Hacker's Delight - var y: @TypeOf(x) = 0; - comptime var shift: u8 = @bitSizeOf(T); - inline while (shift > 0) { - shift = shift >> 1; - y = x >> shift; - if (y != 0) { - n = n - shift; - x = y; - } - } - return @intCast(i32, n - @bitCast(T, x)); +// - ffsXi2 for unoptimized little and big endian + +inline fn clzXi2(comptime T: type, a: T) i32 { + @setRuntimeSafety(builtin.is_test); + + var x = switch (@bitSizeOf(T)) { + 32 => @bitCast(u32, a), + 64 => @bitCast(u64, a), + 128 => @bitCast(u128, a), + else => unreachable, + }; + var n: T = @bitSizeOf(T); + // Count first bit set using binary search, from Hacker's Delight + var y: @TypeOf(x) = 0; + comptime var shift: u8 = @bitSizeOf(T); + inline while (shift > 0) { + shift = shift >> 1; + y = x >> shift; + if (y != 0) { + n = n - shift; + x = y; } - }.f; + } + return @intCast(i32, n - @bitCast(T, x)); } fn __clzsi2_thumb1() callconv(.Naked) void { @@ -125,103 +121,113 @@ fn __clzsi2_arm32() callconv(.Naked) void { unreachable; } -pub const __clzsi2 = impl: { - switch (builtin.cpu.arch) { - .arm, .armeb, .thumb, .thumbeb => { - const use_thumb1 = - (builtin.cpu.arch.isThumb() or - std.Target.arm.featureSetHas(builtin.cpu.features, .noarm)) and - !std.Target.arm.featureSetHas(builtin.cpu.features, .thumb2); - - if (use_thumb1) { - break :impl __clzsi2_thumb1; - } - // From here on we're either targeting Thumb2 or ARM. - else if (!builtin.cpu.arch.isThumb()) { - break :impl __clzsi2_arm32; - } - // Use the generic implementation otherwise. - else break :impl clzXi2_generic(i32); - }, - else => break :impl clzXi2_generic(i32), - } +fn clzsi2_generic(a: i32) callconv(.C) i32 { + return clzXi2(i32, a); +} + +pub const __clzsi2 = switch (builtin.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => impl: { + const use_thumb1 = + (builtin.cpu.arch.isThumb() or + std.Target.arm.featureSetHas(builtin.cpu.features, .noarm)) and + !std.Target.arm.featureSetHas(builtin.cpu.features, .thumb2); + + if (use_thumb1) { + break :impl __clzsi2_thumb1; + } + // From here on we're either targeting Thumb2 or ARM. + else if (!builtin.cpu.arch.isThumb()) { + break :impl __clzsi2_arm32; + } + // Use the generic implementation otherwise. + else break :impl clzsi2_generic; + }, + else => clzsi2_generic, }; -pub const __clzdi2 = clzXi2_generic(i64); - -pub const __clzti2 = clzXi2_generic(i128); - -fn ctzXi2_generic(comptime T: type) fn (a: T) callconv(.C) i32 { - return struct { - fn f(a: T) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - - var x = switch (@bitSizeOf(T)) { - 32 => @bitCast(u32, a), - 64 => @bitCast(u64, a), - 128 => @bitCast(u128, a), - else => unreachable, - }; - var n: T = 1; - // Number of trailing zeroes as binary search, from Hacker's Delight - var mask: @TypeOf(x) = std.math.maxInt(@TypeOf(x)); - comptime var shift = @bitSizeOf(T); - if (x == 0) return shift; - inline while (shift > 1) { - shift = shift >> 1; - mask = mask >> shift; - if ((x & mask) == 0) { - n = n + shift; - x = x >> shift; - } - } - return @intCast(i32, n - @bitCast(T, (x & 1))); +pub fn __clzdi2(a: i64) callconv(.C) i32 { + return clzXi2(i64, a); +} + +pub fn __clzti2(a: i128) callconv(.C) i32 { + return clzXi2(i128, a); +} + +inline fn ctzXi2(comptime T: type, a: T) i32 { + @setRuntimeSafety(builtin.is_test); + + var x = switch (@bitSizeOf(T)) { + 32 => @bitCast(u32, a), + 64 => @bitCast(u64, a), + 128 => @bitCast(u128, a), + else => unreachable, + }; + var n: T = 1; + // Number of trailing zeroes as binary search, from Hacker's Delight + var mask: @TypeOf(x) = std.math.maxInt(@TypeOf(x)); + comptime var shift = @bitSizeOf(T); + if (x == 0) return shift; + inline while (shift > 1) { + shift = shift >> 1; + mask = mask >> shift; + if ((x & mask) == 0) { + n = n + shift; + x = x >> shift; } - }.f; + } + return @intCast(i32, n - @bitCast(T, (x & 1))); +} + +pub fn __ctzsi2(a: i32) callconv(.C) i32 { + return ctzXi2(i32, a); } -pub const __ctzsi2 = ctzXi2_generic(i32); - -pub const __ctzdi2 = ctzXi2_generic(i64); - -pub const __ctzti2 = ctzXi2_generic(i128); - -fn ffsXi2_generic(comptime T: type) fn (a: T) callconv(.C) i32 { - return struct { - fn f(a: T) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - - var x = switch (@bitSizeOf(T)) { - 32 => @bitCast(u32, a), - 64 => @bitCast(u64, a), - 128 => @bitCast(u128, a), - else => unreachable, - }; - var n: T = 1; - // adapted from Number of trailing zeroes (see ctzXi2_generic) - var mask: @TypeOf(x) = std.math.maxInt(@TypeOf(x)); - comptime var shift = @bitSizeOf(T); - // In contrast to ctz return 0 - if (x == 0) return 0; - inline while (shift > 1) { - shift = shift >> 1; - mask = mask >> shift; - if ((x & mask) == 0) { - n = n + shift; - x = x >> shift; - } - } - // return ctz + 1 - return @intCast(i32, n - @bitCast(T, (x & 1))) + @as(i32, 1); +pub fn __ctzdi2(a: i64) callconv(.C) i32 { + return ctzXi2(i64, a); +} + +pub fn __ctzti2(a: i128) callconv(.C) i32 { + return ctzXi2(i128, a); +} + +inline fn ffsXi2(comptime T: type, a: T) i32 { + @setRuntimeSafety(builtin.is_test); + + var x = switch (@bitSizeOf(T)) { + 32 => @bitCast(u32, a), + 64 => @bitCast(u64, a), + 128 => @bitCast(u128, a), + else => unreachable, + }; + var n: T = 1; + // adapted from Number of trailing zeroes (see ctzXi2) + var mask: @TypeOf(x) = std.math.maxInt(@TypeOf(x)); + comptime var shift = @bitSizeOf(T); + // In contrast to ctz return 0 + if (x == 0) return 0; + inline while (shift > 1) { + shift = shift >> 1; + mask = mask >> shift; + if ((x & mask) == 0) { + n = n + shift; + x = x >> shift; } - }.f; + } + // return ctz + 1 + return @intCast(i32, n - @bitCast(T, (x & 1))) + @as(i32, 1); } -pub const __ffssi2 = ffsXi2_generic(i32); +pub fn __ffssi2(a: i32) callconv(.C) i32 { + return ffsXi2(i32, a); +} -pub const __ffsdi2 = ffsXi2_generic(i64); +pub fn __ffsdi2(a: i64) callconv(.C) i32 { + return ffsXi2(i64, a); +} -pub const __ffsti2 = ffsXi2_generic(i128); +pub fn __ffsti2(a: i128) callconv(.C) i32 { + return ffsXi2(i128, a); +} test { _ = @import("clzsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/divdf3.zig b/lib/std/special/compiler_rt/divdf3.zig index ebb139f32406..2148902de25e 100644 --- a/lib/std/special/compiler_rt/divdf3.zig +++ b/lib/std/special/compiler_rt/divdf3.zig @@ -35,7 +35,7 @@ pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 { var scale: i32 = 0; // Detect if a or b is zero, denormal, infinity, or NaN. - if (aExponent -% 1 >= maxExponent -% 1 or bExponent -% 1 >= maxExponent -% 1) { + if (aExponent -% 1 >= maxExponent - 1 or bExponent -% 1 >= maxExponent - 1) { const aAbs: Z = @bitCast(Z, a) & absMask; const bAbs: Z = @bitCast(Z, b) & absMask; diff --git a/lib/std/special/compiler_rt/divsf3.zig b/lib/std/special/compiler_rt/divsf3.zig index af4cfaaa868e..5e7dc7bb44c8 100644 --- a/lib/std/special/compiler_rt/divsf3.zig +++ b/lib/std/special/compiler_rt/divsf3.zig @@ -34,7 +34,7 @@ pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 { var scale: i32 = 0; // Detect if a or b is zero, denormal, infinity, or NaN. - if (aExponent -% 1 >= maxExponent -% 1 or bExponent -% 1 >= maxExponent -% 1) { + if (aExponent -% 1 >= maxExponent - 1 or bExponent -% 1 >= maxExponent - 1) { const aAbs: Z = @bitCast(Z, a) & absMask; const bAbs: Z = @bitCast(Z, b) & absMask; diff --git a/lib/std/special/compiler_rt/divtf3.zig b/lib/std/special/compiler_rt/divtf3.zig index 4dce86087dcf..fc26c6026657 100644 --- a/lib/std/special/compiler_rt/divtf3.zig +++ b/lib/std/special/compiler_rt/divtf3.zig @@ -33,7 +33,7 @@ pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 { var scale: i32 = 0; // Detect if a or b is zero, denormal, infinity, or NaN. - if (aExponent -% 1 >= maxExponent -% 1 or bExponent -% 1 >= maxExponent -% 1) { + if (aExponent -% 1 >= maxExponent - 1 or bExponent -% 1 >= maxExponent - 1) { const aAbs: Z = @bitCast(Z, a) & absMask; const bAbs: Z = @bitCast(Z, b) & absMask; diff --git a/lib/std/special/compiler_rt/fixuint.zig b/lib/std/special/compiler_rt/fixuint.zig index c51b80fbdb0b..6bfbcf6d654c 100644 --- a/lib/std/special/compiler_rt/fixuint.zig +++ b/lib/std/special/compiler_rt/fixuint.zig @@ -1,7 +1,7 @@ const is_test = @import("builtin").is_test; const Log2Int = @import("std").math.Log2Int; -pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t { +pub inline fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t { @setRuntimeSafety(is_test); const rep_t = switch (fp_t) { diff --git a/lib/std/special/compiler_rt/floatXisf.zig b/lib/std/special/compiler_rt/floatXisf.zig index 9fc4c711001b..de3f4495cb07 100644 --- a/lib/std/special/compiler_rt/floatXisf.zig +++ b/lib/std/special/compiler_rt/floatXisf.zig @@ -4,7 +4,7 @@ const maxInt = std.math.maxInt; const FLT_MANT_DIG = 24; -fn __floatXisf(comptime T: type, arg: T) f32 { +inline fn floatXisf(comptime T: type, arg: T) f32 { @setRuntimeSafety(builtin.is_test); const bits = @typeInfo(T).Int.bits; @@ -71,18 +71,15 @@ fn __floatXisf(comptime T: type, arg: T) f32 { } pub fn __floatdisf(arg: i64) callconv(.C) f32 { - @setRuntimeSafety(builtin.is_test); - return @call(.{ .modifier = .always_inline }, __floatXisf, .{ i64, arg }); + return floatXisf(i64, arg); } pub fn __floattisf(arg: i128) callconv(.C) f32 { - @setRuntimeSafety(builtin.is_test); - return @call(.{ .modifier = .always_inline }, __floatXisf, .{ i128, arg }); + return floatXisf(i128, arg); } pub fn __aeabi_l2f(arg: i64) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatdisf, .{arg}); + return floatXisf(i64, arg); } test { diff --git a/lib/std/special/compiler_rt/floatsiXf.zig b/lib/std/special/compiler_rt/floatsiXf.zig index 23d5cb1e3c15..ef551d1911d4 100644 --- a/lib/std/special/compiler_rt/floatsiXf.zig +++ b/lib/std/special/compiler_rt/floatsiXf.zig @@ -2,7 +2,7 @@ const builtin = @import("builtin"); const std = @import("std"); const maxInt = std.math.maxInt; -fn floatsiXf(comptime T: type, a: i32) T { +inline fn floatsiXf(comptime T: type, a: i32) T { @setRuntimeSafety(builtin.is_test); const bits = @typeInfo(T).Float.bits; @@ -56,27 +56,27 @@ fn floatsiXf(comptime T: type, a: i32) T { pub fn __floatsisf(arg: i32) callconv(.C) f32 { @setRuntimeSafety(builtin.is_test); - return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f32, arg }); + return floatsiXf(f32, arg); } pub fn __floatsidf(arg: i32) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); - return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f64, arg }); + return floatsiXf(f64, arg); } pub fn __floatsitf(arg: i32) callconv(.C) f128 { @setRuntimeSafety(builtin.is_test); - return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f128, arg }); + return floatsiXf(f128, arg); } pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatsidf, .{arg}); + return floatsiXf(f64, arg); } pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatsisf, .{arg}); + return floatsiXf(f32, arg); } fn test_one_floatsitf(a: i32, expected: u128) !void { diff --git a/lib/std/special/compiler_rt/floatundisf.zig b/lib/std/special/compiler_rt/floatundisf.zig index aca30ee30992..ffbe3ef252b6 100644 --- a/lib/std/special/compiler_rt/floatundisf.zig +++ b/lib/std/special/compiler_rt/floatundisf.zig @@ -4,7 +4,7 @@ const maxInt = std.math.maxInt; const FLT_MANT_DIG = 24; -pub fn __floatundisf(arg: u64) callconv(.C) f32 { +inline fn floatundisf(arg: u64) f32 { @setRuntimeSafety(builtin.is_test); if (arg == 0) return 0; @@ -56,9 +56,12 @@ pub fn __floatundisf(arg: u64) callconv(.C) f32 { return @bitCast(f32, result); } +pub fn __floatundisf(arg: u64) callconv(.C) f32 { + return floatundisf(arg); +} + pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatundisf, .{arg}); + return floatundisf(arg); } fn test__floatundisf(a: u64, expected: f32) !void { diff --git a/lib/std/special/compiler_rt/floatunsidf.zig b/lib/std/special/compiler_rt/floatunsidf.zig index 555d4f5657a3..f474c1de8f73 100644 --- a/lib/std/special/compiler_rt/floatunsidf.zig +++ b/lib/std/special/compiler_rt/floatunsidf.zig @@ -4,7 +4,7 @@ const maxInt = std.math.maxInt; const implicitBit = @as(u64, 1) << 52; -pub fn __floatunsidf(arg: u32) callconv(.C) f64 { +inline fn floatunsidf(arg: u32) f64 { @setRuntimeSafety(builtin.is_test); if (arg == 0) return 0.0; @@ -18,9 +18,12 @@ pub fn __floatunsidf(arg: u32) callconv(.C) f64 { return @bitCast(f64, mant | (exp + 1023) << 52); } +pub fn __floatunsidf(arg: u32) callconv(.C) f64 { + return floatunsidf(arg); +} + pub fn __aeabi_ui2d(arg: u32) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatunsidf, .{arg}); + return floatunsidf(arg); } fn test_one_floatunsidf(a: u32, expected: u64) !void { diff --git a/lib/std/special/compiler_rt/floatunsisf.zig b/lib/std/special/compiler_rt/floatunsisf.zig index c8a654ff7a7d..d267baee0152 100644 --- a/lib/std/special/compiler_rt/floatunsisf.zig +++ b/lib/std/special/compiler_rt/floatunsisf.zig @@ -6,7 +6,7 @@ const significandBits = 23; const exponentBias = 127; const implicitBit = @as(u32, 1) << significandBits; -pub fn __floatunsisf(arg: u32) callconv(.C) f32 { +inline fn floatunsisf(arg: u32) f32 { @setRuntimeSafety(builtin.is_test); if (arg == 0) return 0.0; @@ -38,9 +38,12 @@ pub fn __floatunsisf(arg: u32) callconv(.C) f32 { return @bitCast(f32, result); } +pub fn __floatunsisf(arg: u32) callconv(.C) f32 { + return floatunsisf(arg); +} + pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatunsisf, .{arg}); + return floatunsisf(arg); } fn test_one_floatunsisf(a: u32, expected: u32) !void { diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/std/special/compiler_rt/mulXf3.zig index 1e9171c8cb79..48c31f47b100 100644 --- a/lib/std/special/compiler_rt/mulXf3.zig +++ b/lib/std/special/compiler_rt/mulXf3.zig @@ -56,7 +56,7 @@ fn mulXf3(comptime T: type, a: T, b: T) T { var scale: i32 = 0; // Detect if a or b is zero, denormal, infinity, or NaN. - if (aExponent -% 1 >= maxExponent -% 1 or bExponent -% 1 >= maxExponent -% 1) { + if (aExponent -% 1 >= maxExponent - 1 or bExponent -% 1 >= maxExponent - 1) { const aAbs: Z = @bitCast(Z, a) & absMask; const bAbs: Z = @bitCast(Z, b) & absMask; diff --git a/lib/std/special/compiler_rt/negXi2.zig b/lib/std/special/compiler_rt/negXi2.zig index de89c8deb7aa..15102b5df7b1 100644 --- a/lib/std/special/compiler_rt/negXi2.zig +++ b/lib/std/special/compiler_rt/negXi2.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); // neg - negate (the number) -// - negXi2_generic for unoptimized little and big endian +// - negXi2 for unoptimized little and big endian // sfffffff = 2^31-1 // two's complement inverting bits and add 1 would result in -INT_MIN == 0 @@ -11,20 +11,22 @@ const builtin = @import("builtin"); // * size optimized builds // * machines that dont support carry operations -fn negXi2_generic(comptime T: type) fn (a: T) callconv(.C) T { - return struct { - fn f(a: T) callconv(.C) T { - @setRuntimeSafety(builtin.is_test); - return -a; - } - }.f; +inline fn negXi2(comptime T: type, a: T) T { + @setRuntimeSafety(builtin.is_test); + return -a; } -pub const __negsi2 = negXi2_generic(i32); +pub fn __negsi2(a: i32) callconv(.C) i32 { + return negXi2(i32, a); +} -pub const __negdi2 = negXi2_generic(i64); +pub fn __negdi2(a: i64) callconv(.C) i64 { + return negXi2(i64, a); +} -pub const __negti2 = negXi2_generic(i128); +pub fn __negti2(a: i128) callconv(.C) i128 { + return negXi2(i128, a); +} test { _ = @import("negsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/negv.zig b/lib/std/special/compiler_rt/negv.zig index 99525a6e5be1..09abb040d5ec 100644 --- a/lib/std/special/compiler_rt/negv.zig +++ b/lib/std/special/compiler_rt/negv.zig @@ -3,26 +3,31 @@ // - negvXi4_generic for unoptimized version // assume -0 == 0 is gracefully handled by the hardware -fn negvXi_generic(comptime ST: type) fn (a: ST) callconv(.C) ST { - return struct { - fn f(a: ST) callconv(.C) ST { - const UT = switch (ST) { - i32 => u32, - i64 => u64, - i128 => u128, - else => unreachable, - }; - const N: UT = @bitSizeOf(ST); - const min: ST = @bitCast(ST, (@as(UT, 1) << (N - 1))); - if (a == min) - @panic("compiler_rt negv: overflow"); - return -a; - } - }.f; +inline fn negvXi(comptime ST: type, a: ST) ST { + const UT = switch (ST) { + i32 => u32, + i64 => u64, + i128 => u128, + else => unreachable, + }; + const N: UT = @bitSizeOf(ST); + const min: ST = @bitCast(ST, (@as(UT, 1) << (N - 1))); + if (a == min) + @panic("compiler_rt negv: overflow"); + return -a; +} + +pub fn __negvsi2(a: i32) callconv(.C) i32 { + return negvXi(i32, a); +} + +pub fn __negvdi2(a: i64) callconv(.C) i64 { + return negvXi(i64, a); +} + +pub fn __negvti2(a: i128) callconv(.C) i128 { + return negvXi(i128, a); } -pub const __negvsi2 = negvXi_generic(i32); -pub const __negvdi2 = negvXi_generic(i64); -pub const __negvti2 = negvXi_generic(i128); test { _ = @import("negvsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/parity.zig b/lib/std/special/compiler_rt/parity.zig index 1c47aa3c73f0..ae634b079036 100644 --- a/lib/std/special/compiler_rt/parity.zig +++ b/lib/std/special/compiler_rt/parity.zig @@ -4,34 +4,36 @@ const builtin = @import("builtin"); // parity - if number of bits set is even => 0, else => 1 // - pariytXi2_generic for big and little endian -fn parityXi2_generic(comptime T: type) fn (a: T) callconv(.C) i32 { - return struct { - fn f(a: T) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); +inline fn parityXi2(comptime T: type, a: T) i32 { + @setRuntimeSafety(builtin.is_test); - var x = switch (@bitSizeOf(T)) { - 32 => @bitCast(u32, a), - 64 => @bitCast(u64, a), - 128 => @bitCast(u128, a), - else => unreachable, - }; - // Bit Twiddling Hacks: Compute parity in parallel - comptime var shift: u8 = @bitSizeOf(T) / 2; - inline while (shift > 2) { - x ^= x >> shift; - shift = shift >> 1; - } - x &= 0xf; - return (@intCast(u16, 0x6996) >> @intCast(u4, x)) & 1; // optimization for >>2 and >>1 - } - }.f; + var x = switch (@bitSizeOf(T)) { + 32 => @bitCast(u32, a), + 64 => @bitCast(u64, a), + 128 => @bitCast(u128, a), + else => unreachable, + }; + // Bit Twiddling Hacks: Compute parity in parallel + comptime var shift: u8 = @bitSizeOf(T) / 2; + inline while (shift > 2) { + x ^= x >> shift; + shift = shift >> 1; + } + x &= 0xf; + return (@intCast(u16, 0x6996) >> @intCast(u4, x)) & 1; // optimization for >>2 and >>1 } -pub const __paritysi2 = parityXi2_generic(i32); +pub fn __paritysi2(a: i32) callconv(.C) i32 { + return parityXi2(i32, a); +} -pub const __paritydi2 = parityXi2_generic(i64); +pub fn __paritydi2(a: i64) callconv(.C) i32 { + return parityXi2(i64, a); +} -pub const __parityti2 = parityXi2_generic(i128); +pub fn __parityti2(a: i128) callconv(.C) i32 { + return parityXi2(i128, a); +} test { _ = @import("paritysi2_test.zig"); diff --git a/lib/std/special/compiler_rt/popcount.zig b/lib/std/special/compiler_rt/popcount.zig index 72513895db03..362b232fb854 100644 --- a/lib/std/special/compiler_rt/popcount.zig +++ b/lib/std/special/compiler_rt/popcount.zig @@ -10,35 +10,37 @@ const std = @import("std"); // TAOCP: Combinational Algorithms, Bitwise Tricks And Techniques, // subsubsection "Working with the rightmost bits" and "Sideways addition". -fn popcountXi2_generic(comptime ST: type) fn (a: ST) callconv(.C) i32 { - return struct { - fn f(a: ST) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const UT = switch (ST) { - i32 => u32, - i64 => u64, - i128 => u128, - else => unreachable, - }; - var x = @bitCast(UT, a); - x -= (x >> 1) & (~@as(UT, 0) / 3); // 0x55...55, aggregate duos - x = ((x >> 2) & (~@as(UT, 0) / 5)) // 0x33...33, aggregate nibbles - + (x & (~@as(UT, 0) / 5)); - x += x >> 4; - x &= ~@as(UT, 0) / 17; // 0x0F...0F, aggregate bytes - // 8 most significant bits of x + (x<<8) + (x<<16) + .. - x *%= ~@as(UT, 0) / 255; // 0x01...01 - x >>= (@bitSizeOf(ST) - 8); - return @intCast(i32, x); - } - }.f; +inline fn popcountXi2(comptime ST: type, a: ST) i32 { + @setRuntimeSafety(builtin.is_test); + const UT = switch (ST) { + i32 => u32, + i64 => u64, + i128 => u128, + else => unreachable, + }; + var x = @bitCast(UT, a); + x -= (x >> 1) & (~@as(UT, 0) / 3); // 0x55...55, aggregate duos + x = ((x >> 2) & (~@as(UT, 0) / 5)) // 0x33...33, aggregate nibbles + + (x & (~@as(UT, 0) / 5)); + x += x >> 4; + x &= ~@as(UT, 0) / 17; // 0x0F...0F, aggregate bytes + // 8 most significant bits of x + (x<<8) + (x<<16) + .. + x *%= ~@as(UT, 0) / 255; // 0x01...01 + x >>= (@bitSizeOf(ST) - 8); + return @intCast(i32, x); } -pub const __popcountsi2 = popcountXi2_generic(i32); +pub fn __popcountsi2(a: i32) callconv(.C) i32 { + return popcountXi2(i32, a); +} -pub const __popcountdi2 = popcountXi2_generic(i64); +pub fn __popcountdi2(a: i64) callconv(.C) i32 { + return popcountXi2(i64, a); +} -pub const __popcountti2 = popcountXi2_generic(i128); +pub fn __popcountti2(a: i128) callconv(.C) i32 { + return popcountXi2(i128, a); +} test { _ = @import("popcountsi2_test.zig"); diff --git a/lib/std/special/compiler_rt/shift.zig b/lib/std/special/compiler_rt/shift.zig index b20516e46c14..edcf246daf75 100644 --- a/lib/std/special/compiler_rt/shift.zig +++ b/lib/std/special/compiler_rt/shift.zig @@ -19,7 +19,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type { // Arithmetic shift left // Precondition: 0 <= b < bits_in_dword -pub fn ashlXi3(comptime T: type, a: T, b: i32) T { +pub inline fn ashlXi3(comptime T: type, a: T, b: i32) T { const dwords = Dwords(T, false); const S = Log2Int(dwords.HalfT); @@ -42,7 +42,7 @@ pub fn ashlXi3(comptime T: type, a: T, b: i32) T { // Arithmetic shift right // Precondition: 0 <= b < T.bit_count -pub fn ashrXi3(comptime T: type, a: T, b: i32) T { +pub inline fn ashrXi3(comptime T: type, a: T, b: i32) T { const dwords = Dwords(T, true); const S = Log2Int(dwords.HalfT); @@ -69,7 +69,7 @@ pub fn ashrXi3(comptime T: type, a: T, b: i32) T { // Logical shift right // Precondition: 0 <= b < T.bit_count -pub fn lshrXi3(comptime T: type, a: T, b: i32) T { +pub inline fn lshrXi3(comptime T: type, a: T, b: i32) T { const dwords = Dwords(T, false); const S = Log2Int(dwords.HalfT); @@ -91,32 +91,32 @@ pub fn lshrXi3(comptime T: type, a: T, b: i32) T { } pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 { - return @call(.{ .modifier = .always_inline }, ashlXi3, .{ i64, a, b }); + return ashlXi3(i64, a, b); } pub fn __ashlti3(a: i128, b: i32) callconv(.C) i128 { - return @call(.{ .modifier = .always_inline }, ashlXi3, .{ i128, a, b }); + return ashlXi3(i128, a, b); } pub fn __ashrdi3(a: i64, b: i32) callconv(.C) i64 { - return @call(.{ .modifier = .always_inline }, ashrXi3, .{ i64, a, b }); + return ashrXi3(i64, a, b); } pub fn __ashrti3(a: i128, b: i32) callconv(.C) i128 { - return @call(.{ .modifier = .always_inline }, ashrXi3, .{ i128, a, b }); + return ashrXi3(i128, a, b); } pub fn __lshrdi3(a: i64, b: i32) callconv(.C) i64 { - return @call(.{ .modifier = .always_inline }, lshrXi3, .{ i64, a, b }); + return lshrXi3(i64, a, b); } pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { - return @call(.{ .modifier = .always_inline }, lshrXi3, .{ i128, a, b }); + return lshrXi3(i128, a, b); } pub fn __aeabi_llsl(a: i64, b: i32) callconv(.AAPCS) i64 { - return __ashldi3(a, b); + return ashlXi3(i64, a, b); } pub fn __aeabi_lasr(a: i64, b: i32) callconv(.AAPCS) i64 { - return __ashrdi3(a, b); + return ashrXi3(i64, a, b); } pub fn __aeabi_llsr(a: i64, b: i32) callconv(.AAPCS) i64 { - return __lshrdi3(a, b); + return lshrXi3(i64, a, b); } test { diff --git a/src/Cache.zig b/src/Cache.zig index 94ad947f6992..a5995f64ea42 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -47,10 +47,16 @@ pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); pub const File = struct { path: ?[]const u8, max_file_size: ?usize, - stat: fs.File.Stat, + stat: Stat, bin_digest: BinDigest, contents: ?[]const u8, + pub const Stat = struct { + inode: fs.File.INode, + size: u64, + mtime: i128, + }; + pub fn deinit(self: *File, allocator: Allocator) void { if (self.path) |owned_slice| { allocator.free(owned_slice); @@ -424,7 +430,11 @@ pub const Manifest = struct { if (!size_match or !mtime_match or !inode_match) { self.manifest_dirty = true; - cache_hash_file.stat = actual_stat; + cache_hash_file.stat = .{ + .size = actual_stat.size, + .mtime = actual_stat.mtime, + .inode = actual_stat.inode, + }; if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) { // The actual file has an unreliable timestamp, force it to be hashed @@ -530,7 +540,12 @@ pub const Manifest = struct { const file = try fs.cwd().openFile(ch_file.path.?, .{}); defer file.close(); - ch_file.stat = try file.stat(); + const actual_stat = try file.stat(); + ch_file.stat = .{ + .size = actual_stat.size, + .mtime = actual_stat.mtime, + .inode = actual_stat.inode, + }; if (self.isProblematicTimestamp(ch_file.stat.mtime)) { // The actual file has an unreliable timestamp, force it to be hashed @@ -615,6 +630,42 @@ pub const Manifest = struct { try self.populateFileHash(new_ch_file); } + /// Like `addFilePost` but when the file contents have already been loaded from disk. + /// On success, cache takes ownership of `resolved_path`. + pub fn addFilePostContents( + self: *Manifest, + resolved_path: []const u8, + bytes: []const u8, + stat: File.Stat, + ) error{OutOfMemory}!void { + assert(self.manifest_file != null); + + const ch_file = try self.files.addOne(self.cache.gpa); + errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); + + ch_file.* = .{ + .path = resolved_path, + .max_file_size = null, + .stat = stat, + .bin_digest = undefined, + .contents = null, + }; + + if (self.isProblematicTimestamp(ch_file.stat.mtime)) { + // The actual file has an unreliable timestamp, force it to be hashed + ch_file.stat.mtime = 0; + ch_file.stat.inode = 0; + } + + { + var hasher = hasher_init; + hasher.update(bytes); + hasher.final(&ch_file.bin_digest); + } + + self.hash.hasher.update(&ch_file.bin_digest); + } + pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void { assert(self.manifest_file != null); diff --git a/src/Compilation.zig b/src/Compilation.zig index cd4794554e4e..cbb44c1e183a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -41,8 +41,8 @@ gpa: Allocator, arena_state: std.heap.ArenaAllocator.State, bin_file: *link.File, c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, -stage1_lock: ?Cache.Lock = null, -stage1_cache_manifest: *Cache.Manifest = undefined, +/// This is a pointer to a local variable inside `update()`. +whole_cache_manifest: ?*Cache.Manifest = null, link_error_flags: link.File.ErrorFlags = .{}, @@ -98,6 +98,13 @@ clang_argv: []const []const u8, cache_parent: *Cache, /// Path to own executable for invoking `zig clang`. self_exe_path: ?[]const u8, +/// null means -fno-emit-bin. +/// This is mutable memory allocated into the Compilation-lifetime arena (`arena_state`) +/// of exactly the correct size for "o/[digest]/[basename]". +/// The basename is of the outputted binary file in case we don't know the directory yet. +whole_bin_sub_path: ?[]u8, +/// Same as `whole_bin_sub_path` but for implibs. +whole_implib_sub_path: ?[]u8, zig_lib_directory: Directory, local_cache_directory: Directory, global_cache_directory: Directory, @@ -418,7 +425,7 @@ pub const AllErrors = struct { const module_note = module_err_msg.notes[i]; const source = try module_note.src_loc.file_scope.getSource(module.gpa); const byte_offset = try module_note.src_loc.byteOffset(module.gpa); - const loc = std.zig.findLineColumn(source, byte_offset); + const loc = std.zig.findLineColumn(source.bytes, byte_offset); const file_path = try module_note.src_loc.file_scope.fullPath(allocator); note.* = .{ .src = .{ @@ -441,7 +448,7 @@ pub const AllErrors = struct { } const source = try module_err_msg.src_loc.file_scope.getSource(module.gpa); const byte_offset = try module_err_msg.src_loc.byteOffset(module.gpa); - const loc = std.zig.findLineColumn(source, byte_offset); + const loc = std.zig.findLineColumn(source.bytes, byte_offset); const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator); try errors.append(.{ .src = .{ @@ -612,6 +619,15 @@ pub const Directory = struct { return std.fs.path.joinZ(allocator, paths); } } + + /// Whether or not the handle should be closed, or the path should be freed + /// is determined by usage, however this function is provided for convenience + /// if it happens to be what the caller needs. + pub fn closeAndFree(self: *Directory, gpa: Allocator) void { + self.handle.close(); + if (self.path) |p| gpa.free(p); + self.* = undefined; + } }; pub const EmitLoc = struct { @@ -631,6 +647,7 @@ pub const ClangPreprocessorMode = enum { }; pub const SystemLib = link.SystemLib; +pub const CacheMode = link.CacheMode; pub const InitOptions = struct { zig_lib_directory: Directory, @@ -668,6 +685,7 @@ pub const InitOptions = struct { /// is externally modified - essentially anything other than zig-cache - then /// this flag would be set to disable this machinery to avoid false positives. disable_lld_caching: bool = false, + cache_mode: CacheMode = .incremental, object_format: ?std.Target.ObjectFormat = null, optimize_mode: std.builtin.Mode = .Debug, keep_source_files_loaded: bool = false, @@ -885,6 +903,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { break :blk build_options.is_stage1; }; + const cache_mode = if (use_stage1 and !options.disable_lld_caching) + CacheMode.whole + else + options.cache_mode; + // Make a decision on whether to use LLVM or our own backend. const use_llvm = build_options.have_llvm and blk: { if (options.use_llvm) |explicit| @@ -1219,39 +1242,75 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // modified between incremental updates. var hash = cache.hash; - // Here we put the root source file path name, but *not* with addFile. We want the - // hash to be the same regardless of the contents of the source file, because - // incremental compilation will handle it, but we do want to namespace different - // source file names because they are likely different compilations and therefore this - // would be likely to cause cache hits. - hash.addBytes(main_pkg.root_src_path); - hash.addOptionalBytes(main_pkg.root_src_directory.path); - { - var local_arena = std.heap.ArenaAllocator.init(gpa); - defer local_arena.deinit(); - var seen_table = std.AutoHashMap(*Package, void).init(local_arena.allocator()); - try addPackageTableToCacheHash(&hash, &local_arena, main_pkg.table, &seen_table, .path_bytes); + switch (cache_mode) { + .incremental => { + // Here we put the root source file path name, but *not* with addFile. + // We want the hash to be the same regardless of the contents of the + // source file, because incremental compilation will handle it, but we + // do want to namespace different source file names because they are + // likely different compilations and therefore this would be likely to + // cause cache hits. + hash.addBytes(main_pkg.root_src_path); + hash.addOptionalBytes(main_pkg.root_src_directory.path); + { + var seen_table = std.AutoHashMap(*Package, void).init(arena); + try addPackageTableToCacheHash(&hash, &arena_allocator, main_pkg.table, &seen_table, .path_bytes); + } + }, + .whole => { + // In this case, we postpone adding the input source file until + // we create the cache manifest, in update(), because we want to + // track it and packages as files. + }, } + + // Synchronize with other matching comments: ZigOnlyHashStuff hash.add(valgrind); hash.add(single_threaded); hash.add(use_stage1); hash.add(use_llvm); hash.add(dll_export_fns); hash.add(options.is_test); + hash.add(options.test_evented_io); + hash.addOptionalBytes(options.test_filter); + hash.addOptionalBytes(options.test_name_prefix); hash.add(options.skip_linker_dependencies); hash.add(options.parent_compilation_link_libc); + // In the case of incremental cache mode, this `zig_cache_artifact_directory` + // is computed based on a hash of non-linker inputs, and it is where all + // build artifacts are stored (even while in-progress). + // + // For whole cache mode, it is still used for builtin.zig so that the file + // path to builtin.zig can remain consistent during a debugging session at + // runtime. However, we don't know where to put outputs from the linker + // or stage1 backend object files until the final cache hash, which is available + // after the compilation is complete. + // + // Therefore, in whole cache mode, we additionally create a temporary cache + // directory for these two kinds of build artifacts, and then rename it + // into place after the final hash is known. However, we don't want + // to create the temporary directory here, because in the case of a cache hit, + // this would have been wasted syscalls to make the directory and then not + // use it (or delete it). + // + // In summary, for whole cache mode, we simulate `-fno-emit-bin` in this + // function, and `zig_cache_artifact_directory` is *wrong* except for builtin.zig, + // and then at the beginning of `update()` when we find out whether we need + // a temporary directory, we patch up all the places that the incorrect + // `zig_cache_artifact_directory` was passed to various components of the compiler. + const digest = hash.final(); const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); errdefer artifact_dir.close(); const zig_cache_artifact_directory: Directory = .{ .handle = artifact_dir, - .path = if (options.local_cache_directory.path) |p| - try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir }) - else - artifact_sub_dir, + .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), }; + log.debug("zig_cache_artifact_directory='{s}' use_stage1={}", .{ + zig_cache_artifact_directory.path, use_stage1, + }); const builtin_pkg = try Package.createWithDir( gpa, @@ -1374,6 +1433,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }; } + switch (cache_mode) { + .whole => break :blk null, + .incremental => {}, + } + if (module) |zm| { break :blk link.Emit{ .directory = zm.zig_cache_artifact_directory, @@ -1417,6 +1481,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }; } + // This is here for the same reason as in `bin_file_emit` above. + switch (cache_mode) { + .whole => break :blk null, + .incremental => {}, + } + // Use the same directory as the bin. The CLI already emits an // error if -fno-emit-bin is combined with -femit-implib. break :blk link.Emit{ @@ -1425,6 +1495,16 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }; }; + // This is so that when doing `CacheMode.whole`, the mechanism in update() + // can use it for communicating the result directory via `bin_file.emit`. + // This is used to distinguish between -fno-emit-bin and -femit-bin + // for `CacheMode.whole`. + // This memory will be overwritten with the real digest in update() but + // the basename will be preserved. + const whole_bin_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_bin); + // Same thing but for implibs. + const whole_implib_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_implib); + var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{}; errdefer system_libs.deinit(gpa); try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len); @@ -1512,7 +1592,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .skip_linker_dependencies = options.skip_linker_dependencies, .parent_compilation_link_libc = options.parent_compilation_link_libc, .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, - .disable_lld_caching = options.disable_lld_caching, + .cache_mode = cache_mode, + .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, .subsystem = options.subsystem, .is_test = options.is_test, .wasi_exec_model = wasi_exec_model, @@ -1529,6 +1610,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .local_cache_directory = options.local_cache_directory, .global_cache_directory = options.global_cache_directory, .bin_file = bin_file, + .whole_bin_sub_path = whole_bin_sub_path, + .whole_implib_sub_path = whole_implib_sub_path, .emit_asm = options.emit_asm, .emit_llvm_ir = options.emit_llvm_ir, .emit_llvm_bc = options.emit_llvm_bc, @@ -1593,7 +1676,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); } - if (comp.bin_file.options.emit != null and !comp.bin_file.options.skip_linker_dependencies) { + const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null; + + if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies) { // If we need to build glibc for the target, add work items for it. // We go through the work queue so that building can be done in parallel. if (comp.wantBuildGLibCFromSource()) { @@ -1698,8 +1783,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { if (is_exe_or_dyn_lib) { + log.debug("queuing a job to build compiler_rt_lib", .{}); try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); } else if (options.output_mode != .Obj) { + log.debug("queuing a job to build compiler_rt_obj", .{}); // If build-obj with -fcompiler-rt is requested, that is handled specially // elsewhere. In this case we are making a static library, so we ask // for a compiler-rt object to put in it. @@ -1725,20 +1812,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { return comp; } -fn releaseStage1Lock(comp: *Compilation) void { - if (comp.stage1_lock) |*lock| { - lock.release(); - comp.stage1_lock = null; - } -} - pub fn destroy(self: *Compilation) void { const optional_module = self.bin_file.options.module; self.bin_file.destroy(); if (optional_module) |module| module.deinit(); - self.releaseStage1Lock(); - const gpa = self.gpa; self.work_queue.deinit(); self.anon_work_queue.deinit(); @@ -1815,22 +1893,126 @@ pub fn getTarget(self: Compilation) Target { return self.bin_file.options.target; } +fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void { + if (directory.path) |p| comp.gpa.free(p); + + // Restore the Module's previous zig_cache_artifact_directory + // This is only for cleanup purposes; Module.deinit calls close + // on the handle of zig_cache_artifact_directory. + if (comp.bin_file.options.module) |module| { + const builtin_pkg = module.main_pkg.table.get("builtin").?; + module.zig_cache_artifact_directory = builtin_pkg.root_src_directory; + } +} + +fn cleanupTmpArtifactDirectory( + comp: *Compilation, + tmp_artifact_directory: *?Directory, + tmp_dir_sub_path: []const u8, +) void { + comp.gpa.free(tmp_dir_sub_path); + if (tmp_artifact_directory.*) |*directory| { + directory.handle.close(); + restorePrevZigCacheArtifactDirectory(comp, directory); + } +} + /// Detect changes to source files, perform semantic analysis, and update the output files. -pub fn update(self: *Compilation) !void { +pub fn update(comp: *Compilation) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); - self.clearMiscFailures(); + comp.clearMiscFailures(); + + var man: Cache.Manifest = undefined; + defer if (comp.whole_cache_manifest != null) man.deinit(); + + var tmp_dir_sub_path: []const u8 = &.{}; + var tmp_artifact_directory: ?Directory = null; + defer cleanupTmpArtifactDirectory(comp, &tmp_artifact_directory, tmp_dir_sub_path); + + // If using the whole caching strategy, we check for *everything* up front, including + // C source files. + if (comp.bin_file.options.cache_mode == .whole) { + // We are about to obtain this lock, so here we give other processes a chance first. + comp.bin_file.releaseLock(); + + comp.whole_cache_manifest = &man; + man = comp.cache_parent.obtain(); + try comp.addNonIncrementalStuffToCacheManifest(&man); + + const is_hit = man.hit() catch |err| { + // TODO properly bubble these up instead of emitting a warning + const i = man.failed_file_index orelse return err; + const file_path = man.files.items[i].path orelse return err; + std.log.warn("{s}: {s}", .{ @errorName(err), file_path }); + return err; + }; + if (is_hit) { + log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); + const digest = man.final(); + + comp.wholeCacheModeSetBinFilePath(&digest); + + assert(comp.bin_file.lock == null); + comp.bin_file.lock = man.toOwnedLock(); + return; + } + log.debug("CacheMode.whole cache miss for {s}", .{comp.bin_file.options.root_name}); + + // Initialize `bin_file.emit` with a temporary Directory so that compilation can + // continue on the same path as incremental, using the temporary Directory. + tmp_artifact_directory = d: { + const s = std.fs.path.sep_str; + const rand_int = std.crypto.random.int(u64); + + tmp_dir_sub_path = try std.fmt.allocPrint(comp.gpa, "tmp" ++ s ++ "{x}", .{rand_int}); + + const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path}); + errdefer comp.gpa.free(path); + + const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); + errdefer handle.close(); + + break :d .{ + .path = path, + .handle = handle, + }; + }; + + // This updates the output directory for stage1 backend and linker outputs. + if (comp.bin_file.options.module) |module| { + module.zig_cache_artifact_directory = tmp_artifact_directory.?; + } + + // This resets the link.File to operate as if we called openPath() in create() + // instead of simulating -fno-emit-bin. + var options = comp.bin_file.options.move(); + if (comp.whole_bin_sub_path) |sub_path| { + options.emit = .{ + .directory = tmp_artifact_directory.?, + .sub_path = std.fs.path.basename(sub_path), + }; + } + if (comp.whole_implib_sub_path) |sub_path| { + options.implib_emit = .{ + .directory = tmp_artifact_directory.?, + .sub_path = std.fs.path.basename(sub_path), + }; + } + comp.bin_file.destroy(); + comp.bin_file = try link.File.openPath(comp.gpa, options); + } // For compiling C objects, we rely on the cache hash system to avoid duplicating work. // Add a Job for each C object. - try self.c_object_work_queue.ensureUnusedCapacity(self.c_object_table.count()); - for (self.c_object_table.keys()) |key| { - self.c_object_work_queue.writeItemAssumeCapacity(key); + try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count()); + for (comp.c_object_table.keys()) |key| { + comp.c_object_work_queue.writeItemAssumeCapacity(key); } - const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_stage1; - if (self.bin_file.options.module) |module| { + const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; + if (comp.bin_file.options.module) |module| { module.compile_log_text.shrinkAndFree(module.gpa, 0); module.generation += 1; @@ -1845,7 +2027,7 @@ pub fn update(self: *Compilation) !void { // import_table here. // Likewise, in the case of `zig test`, the test runner is the root source file, // and so there is nothing to import the main file. - if (use_stage1 or self.bin_file.options.is_test) { + if (use_stage1 or comp.bin_file.options.is_test) { _ = try module.importPkg(module.main_pkg); } @@ -1854,34 +2036,34 @@ pub fn update(self: *Compilation) !void { // to update it. // We still want AstGen work items for stage1 so that we expose compile errors // that are implemented in stage2 but not stage1. - try self.astgen_work_queue.ensureUnusedCapacity(module.import_table.count()); + try comp.astgen_work_queue.ensureUnusedCapacity(module.import_table.count()); for (module.import_table.values()) |value| { - self.astgen_work_queue.writeItemAssumeCapacity(value); + comp.astgen_work_queue.writeItemAssumeCapacity(value); } if (!use_stage1) { // Put a work item in for checking if any files used with `@embedFile` changed. { - try self.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count()); + try comp.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count()); var it = module.embed_table.iterator(); while (it.next()) |entry| { const embed_file = entry.value_ptr.*; - self.embed_file_work_queue.writeItemAssumeCapacity(embed_file); + comp.embed_file_work_queue.writeItemAssumeCapacity(embed_file); } } - try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); - if (self.bin_file.options.is_test) { - try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); + try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); + if (comp.bin_file.options.is_test) { + try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); } } } - try self.performAllTheWork(); + try comp.performAllTheWork(); if (!use_stage1) { - if (self.bin_file.options.module) |module| { - if (self.bin_file.options.is_test and self.totalErrorCount() == 0) { + if (comp.bin_file.options.module) |module| { + if (comp.bin_file.options.is_test and comp.totalErrorCount() == 0) { // The `test_functions` decl has been intentionally postponed until now, // at which point we must populate it with the list of test functions that // have been discovered and not filtered out. @@ -1910,41 +2092,241 @@ pub fn update(self: *Compilation) !void { } } - if (self.totalErrorCount() != 0) { - // Skip flushing. - self.link_error_flags = .{}; + if (comp.totalErrorCount() != 0) { + // Skip flushing and keep source files loaded for error reporting. + comp.link_error_flags = .{}; return; } - // This is needed before reading the error flags. - try self.bin_file.flush(self); - self.link_error_flags = self.bin_file.errorFlags(); + // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and + // -femit-asm to handle, in the case of C objects. + comp.emitOthers(); + + if (comp.whole_cache_manifest != null) { + const digest = man.final(); - if (!use_stage1) { - if (self.bin_file.options.module) |module| { - try link.File.C.flushEmitH(module); + // Rename the temporary directory into place. + var directory = tmp_artifact_directory.?; + tmp_artifact_directory = null; + + directory.handle.close(); + defer restorePrevZigCacheArtifactDirectory(comp, &directory); + + const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest }); + defer comp.gpa.free(o_sub_path); + + try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); + comp.wholeCacheModeSetBinFilePath(&digest); + + // This is intentionally sandwiched between renameTmpIntoCache() and writeManifest(). + if (comp.bin_file.options.module) |module| { + // We need to set the zig_cache_artifact_directory for -femit-asm, -femit-llvm-ir, + // etc to know where to output to. + var artifact_dir = try comp.local_cache_directory.handle.openDir(o_sub_path, .{}); + defer artifact_dir.close(); + + var dir_path = try comp.local_cache_directory.join(comp.gpa, &.{o_sub_path}); + defer comp.gpa.free(dir_path); + + module.zig_cache_artifact_directory = .{ + .handle = artifact_dir, + .path = dir_path, + }; + + try comp.flush(); + } else { + try comp.flush(); } - } - // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and - // -femit-asm to handle, in the case of C objects. - self.emitOthers(); + // Failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); + }; + + assert(comp.bin_file.lock == null); + comp.bin_file.lock = man.toOwnedLock(); + } else { + try comp.flush(); + } - // If there are any errors, we anticipate the source files being loaded - // to report error messages. Otherwise we unload all source files to save memory. + // Unload all source files to save memory. // The ZIR needs to stay loaded in memory because (1) Decl objects contain references // to it, and (2) generic instantiations, comptime calls, inline calls will need // to reference the ZIR. - if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { - if (self.bin_file.options.module) |module| { + if (!comp.keep_source_files_loaded) { + if (comp.bin_file.options.module) |module| { for (module.import_table.values()) |file| { - file.unloadTree(self.gpa); - file.unloadSource(self.gpa); + file.unloadTree(comp.gpa); + file.unloadSource(comp.gpa); } } } } +fn flush(comp: *Compilation) !void { + try comp.bin_file.flush(comp); // This is needed before reading the error flags. + comp.link_error_flags = comp.bin_file.errorFlags(); + + const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; + if (!use_stage1) { + if (comp.bin_file.options.module) |module| { + try link.File.C.flushEmitH(module); + } + } +} + +/// Communicate the output binary location to parent Compilations. +fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_digest_len]u8) void { + const digest_start = 2; // "o/[digest]/[basename]" + + if (comp.whole_bin_sub_path) |sub_path| { + mem.copy(u8, sub_path[digest_start..], digest); + + comp.bin_file.options.emit = .{ + .directory = comp.local_cache_directory, + .sub_path = sub_path, + }; + } + + if (comp.whole_implib_sub_path) |sub_path| { + mem.copy(u8, sub_path[digest_start..], digest); + + comp.bin_file.options.implib_emit = .{ + .directory = comp.local_cache_directory, + .sub_path = sub_path, + }; + } +} + +fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemory}!?[]u8 { + const emit = opt_emit orelse return null; + if (emit.directory != null) return null; + const s = std.fs.path.sep_str; + const format = "o" ++ s ++ ("x" ** Cache.hex_digest_len) ++ s ++ "{s}"; + return try std.fmt.allocPrint(arena, format, .{emit.basename}); +} + +/// This is only observed at compile-time and used to emit a compile error +/// to remind the programmer to update multiple related pieces of code that +/// are in different locations. Bump this number when adding or deleting +/// anything from the link cache manifest. +pub const link_hash_implementation_version = 1; + +fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { + const gpa = comp.gpa; + const target = comp.getTarget(); + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + comptime assert(link_hash_implementation_version == 1); + + if (comp.bin_file.options.module) |mod| { + const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ + mod.main_pkg.root_src_path, + }); + _ = try man.addFile(main_zig_file, null); + { + var seen_table = std.AutoHashMap(*Package, void).init(arena); + + // Skip builtin.zig; it is useless as an input, and we don't want to have to + // write it before checking for a cache hit. + const builtin_pkg = mod.main_pkg.table.get("builtin").?; + try seen_table.put(builtin_pkg, {}); + + try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = man }); + } + + // Synchronize with other matching comments: ZigOnlyHashStuff + man.hash.add(comp.bin_file.options.valgrind); + man.hash.add(comp.bin_file.options.single_threaded); + man.hash.add(comp.bin_file.options.use_stage1); + man.hash.add(comp.bin_file.options.use_llvm); + man.hash.add(comp.bin_file.options.dll_export_fns); + man.hash.add(comp.bin_file.options.is_test); + man.hash.add(comp.test_evented_io); + man.hash.addOptionalBytes(comp.test_filter); + man.hash.addOptionalBytes(comp.test_name_prefix); + man.hash.add(comp.bin_file.options.skip_linker_dependencies); + man.hash.add(comp.bin_file.options.parent_compilation_link_libc); + man.hash.add(mod.emit_h != null); + } + + try man.addOptionalFile(comp.bin_file.options.linker_script); + try man.addOptionalFile(comp.bin_file.options.version_script); + try man.addListOfFiles(comp.bin_file.options.objects); + + for (comp.c_object_table.keys()) |key| { + _ = try man.addFile(key.src.src_path, null); + man.hash.addListOfBytes(key.src.extra_flags); + } + + man.hash.addOptionalEmitLoc(comp.emit_asm); + man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); + man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); + man.hash.addOptionalEmitLoc(comp.emit_analysis); + man.hash.addOptionalEmitLoc(comp.emit_docs); + + man.hash.addListOfBytes(comp.clang_argv); + + man.hash.addOptional(comp.bin_file.options.stack_size_override); + man.hash.addOptional(comp.bin_file.options.image_base_override); + man.hash.addOptional(comp.bin_file.options.gc_sections); + man.hash.add(comp.bin_file.options.eh_frame_hdr); + man.hash.add(comp.bin_file.options.emit_relocs); + man.hash.add(comp.bin_file.options.rdynamic); + man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); + man.hash.addListOfBytes(comp.bin_file.options.rpath_list); + man.hash.add(comp.bin_file.options.each_lib_rpath); + man.hash.add(comp.bin_file.options.skip_linker_dependencies); + man.hash.add(comp.bin_file.options.z_nodelete); + man.hash.add(comp.bin_file.options.z_notext); + man.hash.add(comp.bin_file.options.z_defs); + man.hash.add(comp.bin_file.options.z_origin); + man.hash.add(comp.bin_file.options.z_noexecstack); + man.hash.add(comp.bin_file.options.z_now); + man.hash.add(comp.bin_file.options.z_relro); + man.hash.add(comp.bin_file.options.include_compiler_rt); + if (comp.bin_file.options.link_libc) { + man.hash.add(comp.bin_file.options.libc_installation != null); + if (comp.bin_file.options.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + if (target.abi == .msvc) { + man.hash.addBytes(libc_installation.msvc_lib_dir.?); + man.hash.addBytes(libc_installation.kernel32_lib_dir.?); + } + } + man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker); + } + man.hash.addOptionalBytes(comp.bin_file.options.soname); + man.hash.addOptional(comp.bin_file.options.version); + link.hashAddSystemLibs(&man.hash, comp.bin_file.options.system_libs); + man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined); + man.hash.add(comp.bin_file.options.bind_global_refs_locally); + man.hash.add(comp.bin_file.options.tsan); + man.hash.addOptionalBytes(comp.bin_file.options.sysroot); + man.hash.add(comp.bin_file.options.linker_optimization); + + // WASM specific stuff + man.hash.add(comp.bin_file.options.import_memory); + man.hash.addOptional(comp.bin_file.options.initial_memory); + man.hash.addOptional(comp.bin_file.options.max_memory); + man.hash.addOptional(comp.bin_file.options.global_base); + + // Mach-O specific stuff + man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); + man.hash.addListOfBytes(comp.bin_file.options.frameworks); + + // COFF specific stuff + man.hash.addOptional(comp.bin_file.options.subsystem); + man.hash.add(comp.bin_file.options.tsaware); + man.hash.add(comp.bin_file.options.nxcompat); + man.hash.add(comp.bin_file.options.dynamicbase); + man.hash.addOptional(comp.bin_file.options.major_subsystem_version); + man.hash.addOptional(comp.bin_file.options.minor_subsystem_version); +} + fn emitOthers(comp: *Compilation) void { if (comp.bin_file.options.output_mode != .Obj or comp.bin_file.options.module != null or comp.c_object_table.count() == 0) @@ -2988,7 +3370,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const dep_basename = std.fs.path.basename(out_dep_path); try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (build_options.is_stage1 and comp.bin_file.options.use_stage1) try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + if (comp.whole_cache_manifest) |whole_cache_manifest| { + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + } const digest = man.final(); const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); @@ -3351,13 +3735,13 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P }; } -pub fn tmpFilePath(comp: *Compilation, arena: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { +pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { const s = std.fs.path.sep_str; const rand_int = std.crypto.random.int(u64); if (comp.local_cache_directory.path) |p| { - return std.fmt.allocPrint(arena, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); + return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); } else { - return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); + return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); } } @@ -4424,6 +4808,7 @@ fn buildOutputFromZig( .global_cache_directory = comp.global_cache_directory, .local_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = target, .root_name = root_name, .main_pkg = &main_pkg, @@ -4501,10 +4886,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node mod.main_pkg.root_src_path, }); const zig_lib_dir = comp.zig_lib_directory.path.?; - const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"}); const target = comp.getTarget(); - const id_symlink_basename = "stage1.id"; - const libs_txt_basename = "libs.txt"; // The include_compiler_rt stored in the bin file options here means that we need // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend @@ -4516,115 +4898,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.include_compiler_rt; - // We are about to obtain this lock, so here we give other processes a chance first. - comp.releaseStage1Lock(); - - // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation, - // so we input all the zig source files into the cache hash system. We're going to keep - // the artifact directory the same, however, so we take the same strategy as linking - // does where we have a file which specifies the hash of the output directory so that we can - // skip the expensive compilation step if the hash matches. - var man = comp.cache_parent.obtain(); - defer man.deinit(); - - _ = try man.addFile(main_zig_file, null); - { - var seen_table = std.AutoHashMap(*Package, void).init(arena_allocator.allocator()); - try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = &man }); - } - man.hash.add(comp.bin_file.options.valgrind); - man.hash.add(comp.bin_file.options.single_threaded); - man.hash.add(target.os.getVersionRange()); - man.hash.add(comp.bin_file.options.dll_export_fns); - man.hash.add(comp.bin_file.options.function_sections); - man.hash.add(include_compiler_rt); - man.hash.add(comp.bin_file.options.is_test); - man.hash.add(comp.bin_file.options.emit != null); - man.hash.add(mod.emit_h != null); - if (mod.emit_h) |emit_h| { - man.hash.addEmitLoc(emit_h.loc); - } - man.hash.addOptionalEmitLoc(comp.emit_asm); - man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); - man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); - man.hash.addOptionalEmitLoc(comp.emit_analysis); - man.hash.addOptionalEmitLoc(comp.emit_docs); - man.hash.add(comp.test_evented_io); - man.hash.addOptionalBytes(comp.test_filter); - man.hash.addOptionalBytes(comp.test_name_prefix); - man.hash.addListOfBytes(comp.clang_argv); - - // Capture the state in case we come back from this branch where the hash doesn't match. - const prev_hash_state = man.hash.peekBin(); - const input_file_count = man.files.items.len; - - const hit = man.hit() catch |err| { - const i = man.failed_file_index orelse return err; - const file_path = man.files.items[i].path orelse return err; - fatal("unable to build stage1 zig object: {s}: {s}", .{ @errorName(err), file_path }); - }; - if (hit) { - const digest = man.final(); - - // We use an extra hex-encoded byte here to store some flags. - var prev_digest_buf: [digest.len + 2]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("stage1 {s} new_digest={s} error: {s}", .{ - mod.main_pkg.root_src_path, - std.fmt.fmtSliceHexLower(&digest), - @errorName(err), - }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - if (prev_digest.len >= digest.len + 2) hit: { - if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) - break :hit; - - log.debug("stage1 {s} digest={s} match - skipping invocation", .{ - mod.main_pkg.root_src_path, - std.fmt.fmtSliceHexLower(&digest), - }); - var flags_bytes: [1]u8 = undefined; - _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { - log.warn("bad cache stage1 digest: '{s}'", .{std.fmt.fmtSliceHexLower(prev_digest)}); - break :hit; - }; - - if (directory.handle.readFileAlloc(comp.gpa, libs_txt_basename, 10 * 1024 * 1024)) |libs_txt| { - var it = mem.tokenize(u8, libs_txt, "\n"); - while (it.next()) |lib_name| { - try comp.stage1AddLinkLib(lib_name); - } - } else |err| switch (err) { - error.FileNotFound => {}, // That's OK, it just means 0 libs. - else => { - log.warn("unable to read cached list of link libs: {s}", .{@errorName(err)}); - break :hit; - }, - } - comp.stage1_lock = man.toOwnedLock(); - mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); - return; - } - log.debug("stage1 {s} prev_digest={s} new_digest={s}", .{ - mod.main_pkg.root_src_path, - std.fmt.fmtSliceHexLower(prev_digest), - std.fmt.fmtSliceHexLower(&digest), - }); - man.unhit(prev_hash_state, input_file_count); - } - - // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - const stage2_target = try arena.create(stage1.Stage2Target); stage2_target.* = .{ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch @@ -4637,9 +4910,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node .llvm_target_abi = if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, }; - comp.stage1_cache_manifest = &man; - const main_pkg_path = mod.main_pkg.root_src_directory.path orelse ""; + const builtin_pkg = mod.main_pkg.table.get("builtin").?; + const builtin_zig_path = try builtin_pkg.root_src_directory.join(arena, &.{builtin_pkg.root_src_path}); const stage1_module = stage1.create( @enumToInt(comp.bin_file.options.optimize_mode), @@ -4740,19 +5013,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node .have_dllmain_crt_startup = false, }; - const inferred_lib_start_index = comp.bin_file.options.system_libs.count(); stage1_module.build_object(); - if (comp.bin_file.options.system_libs.count() > inferred_lib_start_index) { - // We need to save the inferred link libs to the cache, otherwise if we get a cache hit - // next time we will be missing these libs. - var libs_txt = std.ArrayList(u8).init(arena); - for (comp.bin_file.options.system_libs.keys()[inferred_lib_start_index..]) |key| { - try libs_txt.writer().print("{s}\n", .{key}); - } - try directory.handle.writeFile(libs_txt_basename, libs_txt.items); - } - mod.stage1_flags = .{ .have_c_main = stage1_module.have_c_main, .have_winmain = stage1_module.have_winmain, @@ -4763,34 +5025,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node }; stage1_module.destroy(); - - const digest = man.final(); - - // Update the small file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); - log.debug("stage1 {s} final digest={s} flags={x}", .{ - mod.main_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte, - }); - var digest_plus_flags: [digest.len + 2]u8 = undefined; - digest_plus_flags[0..digest.len].* = digest; - assert(std.fmt.formatIntBuf(digest_plus_flags[digest.len..], stage1_flags_byte, 16, .lower, .{ - .width = 2, - .fill = '0', - }) == 2); - log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{ - digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup, - }); - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| { - log.warn("failed to save stage1 hash digest file: {s}", .{@errorName(err)}); - }; - // Failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - comp.stage1_lock = man.toOwnedLock(); } fn stage1LocPath(arena: Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 { @@ -4862,6 +5096,7 @@ pub fn build_crt_file( .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = target, .root_name = root_name, .main_pkg = null, diff --git a/src/Module.zig b/src/Module.zig index ef01aa2c549d..0cbf75c735b0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -33,7 +33,7 @@ const build_options = @import("build_options"); gpa: Allocator, comp: *Compilation, -/// Where our incremental compilation metadata serialization will go. +/// Where build artifacts and incremental compilation metadata serialization go. zig_cache_artifact_directory: Compilation.Directory, /// Pointer to externally managed resource. root_pkg: *Package, @@ -1463,11 +1463,7 @@ pub const File = struct { /// Whether this is populated depends on `source_loaded`. source: [:0]const u8, /// Whether this is populated depends on `status`. - stat_size: u64, - /// Whether this is populated depends on `status`. - stat_inode: std.fs.File.INode, - /// Whether this is populated depends on `status`. - stat_mtime: i128, + stat: Cache.File.Stat, /// Whether this is populated or not depends on `tree_loaded`. tree: Ast, /// Whether this is populated or not depends on `zir_loaded`. @@ -1535,8 +1531,16 @@ pub const File = struct { file.* = undefined; } - pub fn getSource(file: *File, gpa: Allocator) ![:0]const u8 { - if (file.source_loaded) return file.source; + pub const Source = struct { + bytes: [:0]const u8, + stat: Cache.File.Stat, + }; + + pub fn getSource(file: *File, gpa: Allocator) !Source { + if (file.source_loaded) return Source{ + .bytes = file.source, + .stat = file.stat, + }; const root_dir_path = file.pkg.root_src_directory.path orelse "."; log.debug("File.getSource, not cached. pkgdir={s} sub_file_path={s}", .{ @@ -1565,14 +1569,21 @@ pub const File = struct { file.source = source; file.source_loaded = true; - return source; + return Source{ + .bytes = source, + .stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }, + }; } pub fn getTree(file: *File, gpa: Allocator) !*const Ast { if (file.tree_loaded) return &file.tree; const source = try file.getSource(gpa); - file.tree = try std.zig.parse(gpa, source); + file.tree = try std.zig.parse(gpa, source.bytes); file.tree_loaded = true; return &file.tree; } @@ -1631,9 +1642,7 @@ pub const EmbedFile = struct { /// Memory is stored in gpa, owned by EmbedFile. sub_file_path: []const u8, bytes: [:0]const u8, - stat_size: u64, - stat_inode: std.fs.File.INode, - stat_mtime: i128, + stat: Cache.File.Stat, /// Package that this file is a part of, managed externally. pkg: *Package, /// The Decl that was created from the `@embedFile` to own this resource. @@ -2704,9 +2713,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void { keep_zir = true; file.zir = zir; file.zir_loaded = true; - file.stat_size = header.stat_size; - file.stat_inode = header.stat_inode; - file.stat_mtime = header.stat_mtime; + file.stat = .{ + .size = header.stat_size, + .inode = header.stat_inode, + .mtime = header.stat_mtime, + }; file.status = .success_zir; log.debug("AstGen cached success: {s}", .{file.sub_file_path}); @@ -2724,9 +2735,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void { }, .parse_failure, .astgen_failure, .success_zir => { const unchanged_metadata = - stat.size == file.stat_size and - stat.mtime == file.stat_mtime and - stat.inode == file.stat_inode; + stat.size == file.stat.size and + stat.mtime == file.stat.mtime and + stat.inode == file.stat.inode; if (unchanged_metadata) { log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); @@ -2787,9 +2798,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void { if (amt != stat.size) return error.UnexpectedEndOfFile; - file.stat_size = stat.size; - file.stat_inode = stat.inode; - file.stat_mtime = stat.mtime; + file.stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }; file.source = source; file.source_loaded = true; @@ -3069,9 +3082,11 @@ pub fn populateBuiltinFile(mod: *Module) !void { try writeBuiltinFile(file, builtin_pkg); } else { - file.stat_size = stat.size; - file.stat_inode = stat.inode; - file.stat_mtime = stat.mtime; + file.stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }; } } else |err| switch (err) { error.BadPathName => unreachable, // it's always "builtin.zig" @@ -3099,9 +3114,11 @@ pub fn writeBuiltinFile(file: *File, builtin_pkg: *Package) !void { try af.file.writeAll(file.source); try af.finish(); - file.stat_size = file.source.len; - file.stat_inode = 0; // dummy value - file.stat_mtime = 0; // dummy value + file.stat = .{ + .size = file.source.len, + .inode = 0, // dummy value + .mtime = 0, // dummy value + }; } pub fn mapOldZirToNew( @@ -3380,6 +3397,19 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, } + + if (mod.comp.whole_cache_manifest) |man| { + const source = file.getSource(gpa) catch |err| { + try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)}); + return error.AnalysisFail; + }; + const resolved_path = try file.pkg.root_src_directory.join(gpa, &.{ + file.sub_file_path, + }); + errdefer gpa.free(resolved_path); + + try man.addFilePostContents(resolved_path, source.bytes, source.stat); + } } else { new_decl.analysis = .file_failure; } @@ -3710,9 +3740,7 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { .source_loaded = false, .tree_loaded = false, .zir_loaded = false, - .stat_size = undefined, - .stat_inode = undefined, - .stat_mtime = undefined, + .stat = undefined, .tree = undefined, .zir = undefined, .status = .never_loaded, @@ -3780,9 +3808,7 @@ pub fn importFile( .source_loaded = false, .tree_loaded = false, .zir_loaded = false, - .stat_size = undefined, - .stat_inode = undefined, - .stat_mtime = undefined, + .stat = undefined, .tree = undefined, .zir = undefined, .status = .never_loaded, @@ -3827,8 +3853,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{}); defer file.close(); - const stat = try file.stat(); - const size_usize = try std.math.cast(usize, stat.size); + const actual_stat = try file.stat(); + const stat: Cache.File.Stat = .{ + .size = actual_stat.size, + .inode = actual_stat.inode, + .mtime = actual_stat.mtime, + }; + const size_usize = try std.math.cast(usize, actual_stat.size); const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0); errdefer gpa.free(bytes); @@ -3836,14 +3867,18 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb resolved_root_path, resolved_path, sub_file_path, rel_file_path, }); + if (mod.comp.whole_cache_manifest) |man| { + const copied_resolved_path = try gpa.dupe(u8, resolved_path); + errdefer gpa.free(copied_resolved_path); + try man.addFilePostContents(copied_resolved_path, bytes, stat); + } + keep_resolved_path = true; // It's now owned by embed_table. gop.value_ptr.* = new_file; new_file.* = .{ .sub_file_path = sub_file_path, .bytes = bytes, - .stat_size = stat.size, - .stat_inode = stat.inode, - .stat_mtime = stat.mtime, + .stat = stat, .pkg = cur_file.pkg, .owner_decl = undefined, // Set by Sema immediately after this function returns. }; @@ -3857,9 +3892,9 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void { const stat = try file.stat(); const unchanged_metadata = - stat.size == embed_file.stat_size and - stat.mtime == embed_file.stat_mtime and - stat.inode == embed_file.stat_inode; + stat.size == embed_file.stat.size and + stat.mtime == embed_file.stat.mtime and + stat.inode == embed_file.stat.inode; if (unchanged_metadata) return; @@ -3868,9 +3903,11 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void { const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0); gpa.free(embed_file.bytes); embed_file.bytes = bytes; - embed_file.stat_size = stat.size; - embed_file.stat_mtime = stat.mtime; - embed_file.stat_inode = stat.inode; + embed_file.stat = .{ + .size = stat.size, + .mtime = stat.mtime, + .inode = stat.inode, + }; mod.comp.mutex.lock(); defer mod.comp.mutex.unlock(); @@ -5001,3 +5038,35 @@ pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void { }, }; } + +fn reportRetryableFileError( + mod: *Module, + file: *File, + comptime format: []const u8, + args: anytype, +) error{OutOfMemory}!void { + file.status = .retryable_failure; + + const err_msg = try ErrorMsg.create( + mod.gpa, + .{ + .file_scope = file, + .parent_decl_node = 0, + .lazy = .entire_file, + }, + format, + args, + ); + errdefer err_msg.destroy(mod.gpa); + + mod.comp.mutex.lock(); + defer mod.comp.mutex.unlock(); + + const gop = try mod.failed_files.getOrPut(mod.gpa, file); + if (gop.found_existing) { + if (gop.value_ptr.*) |old_err_msg| { + old_err_msg.destroy(mod.gpa); + } + } + gop.value_ptr.* = err_msg; +} diff --git a/src/Sema.zig b/src/Sema.zig index b65e88360f15..42bf795bd90d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14634,11 +14634,11 @@ fn resolvePeerTypes( instructions: []Air.Inst.Ref, candidate_srcs: Module.PeerTypeCandidateSrc, ) !Type { - if (instructions.len == 0) - return Type.initTag(.noreturn); - - if (instructions.len == 1) - return sema.typeOf(instructions[0]); + switch (instructions.len) { + 0 => return Type.initTag(.noreturn), + 1 => return sema.typeOf(instructions[0]), + else => {}, + } const target = sema.mod.getTarget(); @@ -14668,13 +14668,14 @@ fn resolvePeerTypes( continue; }, .Int => { - if (chosen_ty.isSignedInt() == candidate_ty.isSignedInt()) { - if (chosen_ty.intInfo(target).bits < candidate_ty.intInfo(target).bits) { - chosen = candidate; - chosen_i = candidate_i + 1; - } - continue; + const chosen_info = chosen_ty.intInfo(target); + const candidate_info = candidate_ty.intInfo(target); + + if (chosen_info.bits < candidate_info.bits) { + chosen = candidate; + chosen_i = candidate_i + 1; } + continue; }, .Pointer => if (chosen_ty.ptrSize() == .C) continue, else => {}, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a248fb17188e..e29116476d06 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -324,10 +324,12 @@ pub const Object = struct { const mod = comp.bin_file.options.module.?; const cache_dir = mod.zig_cache_artifact_directory; - const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| - try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path}) - else - null; + const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| blk: { + const full_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); + break :blk try std.fs.path.joinZ(arena, &.{ + std.fs.path.dirname(full_out_path).?, self.sub_path, + }); + } else null; const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir); const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir); diff --git a/src/glibc.zig b/src/glibc.zig index e9c0651fdcd9..4424cb9b9022 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -1062,6 +1062,7 @@ fn buildSharedLib( .local_cache_directory = zig_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = comp.getTarget(), .root_name = lib.name, .main_pkg = null, diff --git a/src/libcxx.zig b/src/libcxx.zig index fe96207c483a..2da680a40d29 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -177,6 +177,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = target, .root_name = root_name, .main_pkg = null, @@ -218,10 +219,9 @@ pub fn buildLibCXX(comp: *Compilation) !void { assert(comp.libcxx_static_lib == null); comp.libcxx_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( - comp.gpa, - &[_][]const u8{basename}, - ), + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), .lock = sub_compilation.bin_file.toOwnedLock(), }; } @@ -309,6 +309,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = target, .root_name = root_name, .main_pkg = null, @@ -350,10 +351,9 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { assert(comp.libcxxabi_static_lib == null); comp.libcxxabi_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( - comp.gpa, - &[_][]const u8{basename}, - ), + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), .lock = sub_compilation.bin_file.toOwnedLock(), }; } diff --git a/src/libtsan.zig b/src/libtsan.zig index 0f05957387ff..cbbcd11a8a08 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -199,6 +199,7 @@ pub fn buildTsan(comp: *Compilation) !void { .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = target, .root_name = root_name, .main_pkg = null, @@ -237,10 +238,9 @@ pub fn buildTsan(comp: *Compilation) !void { assert(comp.tsan_static_lib == null); comp.tsan_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( - comp.gpa, - &[_][]const u8{basename}, - ), + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), .lock = sub_compilation.bin_file.toOwnedLock(), }; } diff --git a/src/libunwind.zig b/src/libunwind.zig index 95c58936fa6d..0d030be32945 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -101,6 +101,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, .target = target, .root_name = root_name, .main_pkg = null, @@ -141,11 +142,11 @@ pub fn buildStaticLib(comp: *Compilation) !void { try sub_compilation.updateSubCompilation(); assert(comp.libunwind_static_lib == null); + comp.libunwind_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( - comp.gpa, - &[_][]const u8{basename}, - ), + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), .lock = sub_compilation.bin_file.toOwnedLock(), }; } diff --git a/src/link.zig b/src/link.zig index 464d96ddaca8..5c33ce3b7008 100644 --- a/src/link.zig +++ b/src/link.zig @@ -22,6 +22,8 @@ pub const SystemLib = struct { needed: bool = false, }; +pub const CacheMode = enum { incremental, whole }; + pub fn hashAddSystemLibs( hh: *Cache.HashHelper, hm: std.StringArrayHashMapUnmanaged(SystemLib), @@ -44,10 +46,9 @@ pub const Emit = struct { }; pub const Options = struct { - /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called, - /// it will have already been null-checked. + /// This is `null` when `-fno-emit-bin` is used. emit: ?Emit, - /// This is `null` not building a Windows DLL, or when -fno-emit-implib is used. + /// This is `null` not building a Windows DLL, or when `-fno-emit-implib` is used. implib_emit: ?Emit, target: std.Target, output_mode: std.builtin.OutputMode, @@ -70,6 +71,7 @@ pub const Options = struct { entry_addr: ?u64 = null, stack_size_override: ?u64, image_base_override: ?u64, + cache_mode: CacheMode, include_compiler_rt: bool, /// Set to `true` to omit debug info. strip: bool, @@ -165,6 +167,12 @@ pub const Options = struct { pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } + + pub fn move(self: *Options) Options { + const copied_state = self.*; + self.system_libs = .{}; + return copied_state; + } }; pub const File = struct { @@ -628,6 +636,36 @@ pub const File = struct { } } + /// This function is called by the frontend before flush(). It communicates that + /// `options.bin_file.emit` directory needs to be renamed from + /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`. + /// The frontend would like to simply perform a file system rename, however, + /// some linker backends care about the file paths of the objects they are linking. + /// So this function call tells linker backends to rename the paths of object files + /// to observe the new directory path. + /// Linker backends which do not have this requirement can fall back to the simple + /// implementation at the bottom of this function. + /// This function is only called when CacheMode is `whole`. + pub fn renameTmpIntoCache( + base: *File, + cache_directory: Compilation.Directory, + tmp_dir_sub_path: []const u8, + o_sub_path: []const u8, + ) !void { + // So far, none of the linker backends need to respond to this event, however, + // it makes sense that they might want to. So we leave this mechanism here + // for now. Once the linker backends get more mature, if it turns out this + // is not needed we can refactor this into having the frontend do the rename + // directly, and remove this function from link.zig. + _ = base; + try std.fs.rename( + cache_directory.handle, + tmp_dir_sub_path, + cache_directory.handle, + o_sub_path, + ); + } + pub fn linkAsArchive(base: *File, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); @@ -637,9 +675,11 @@ pub const File = struct { const arena = arena_allocator.allocator(); const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path}); + const full_out_path_z = try arena.dupeZ(u8, full_out_path); - // If there is no Zig code to compile, then we should skip flushing the output file because it - // will not be part of the linker line anyway. + // If there is no Zig code to compile, then we should skip flushing the output file + // because it will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: { const use_stage1 = build_options.is_stage1 and base.options.use_stage1; if (use_stage1) { @@ -648,20 +688,28 @@ pub const File = struct { .target = base.options.target, .output_mode = .Obj, }); - const o_directory = module.zig_cache_artifact_directory; - const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + switch (base.options.cache_mode) { + .incremental => break :blk try module.zig_cache_artifact_directory.join( + arena, + &[_][]const u8{obj_basename}, + ), + .whole => break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path_z).?, obj_basename, + }), + } } if (base.options.object_format == .macho) { try base.cast(MachO).?.flushObject(comp); } else { try base.flushModule(comp); } - const obj_basename = base.intermediary_basename.?; - const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path_z).?, base.intermediary_basename.?, + }); } else null; + log.debug("module_obj_path={s}", .{if (module_obj_path) |s| s else "(null)"}); + const compiler_rt_path: ?[]const u8 = if (base.options.include_compiler_rt) comp.compiler_rt_obj.?.full_object_path else @@ -734,9 +782,6 @@ pub const File = struct { object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); } - const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path}); - const full_out_path_z = try arena.dupeZ(u8, full_out_path); - if (base.options.verbose_link) { std.debug.print("ar rcs {s}", .{full_out_path_z}); for (object_files.items) |arg| { diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 2445b11caf0b..ec06c28b448a 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -880,6 +880,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { const arena = arena_allocator.allocator(); const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. @@ -891,15 +892,22 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { .target = self.base.options.target, .output_mode = .Obj, }); - const o_directory = module.zig_cache_artifact_directory; - const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + switch (self.base.options.cache_mode) { + .incremental => break :blk try module.zig_cache_artifact_directory.join( + arena, + &[_][]const u8{obj_basename}, + ), + .whole => break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, obj_basename, + }), + } } try self.flushModule(comp); - const obj_basename = self.base.intermediary_basename.?; - const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + + break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?, + }); } else null; const is_lib = self.base.options.output_mode == .Lib; @@ -920,6 +928,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { man = comp.cache_parent.obtain(); self.base.releaseLock(); + comptime assert(Compilation.link_hash_implementation_version == 1); + try man.addListOfFiles(self.base.options.objects); for (comp.c_object_table.keys()) |key| { _ = try man.addFile(key.status.success.object_path, null); @@ -976,7 +986,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { }; } - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); if (self.base.options.output_mode == .Obj) { // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 992332be36e9..105b012fbfb4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -297,6 +297,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { else => return error.UnsupportedELFArchitecture, }; const self = try gpa.create(Elf); + errdefer gpa.destroy(self); self.* = .{ .base = .{ .tag = .elf, @@ -306,6 +307,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { }, .ptr_width = ptr_width, }; + // TODO get rid of the sub_path parameter to LlvmObject.create + // and create the llvm_object here. Also openPath needs to + // not override this field or there will be a memory leak. return self; } @@ -1298,6 +1302,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { const arena = arena_allocator.allocator(); const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. @@ -1309,15 +1314,22 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { .target = self.base.options.target, .output_mode = .Obj, }); - const o_directory = module.zig_cache_artifact_directory; - const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + switch (self.base.options.cache_mode) { + .incremental => break :blk try module.zig_cache_artifact_directory.join( + arena, + &[_][]const u8{obj_basename}, + ), + .whole => break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, obj_basename, + }), + } } try self.flushModule(comp); - const obj_basename = self.base.intermediary_basename.?; - const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + + break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?, + }); } else null; const is_obj = self.base.options.output_mode == .Obj; @@ -1357,6 +1369,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); + comptime assert(Compilation.link_hash_implementation_version == 1); + try man.addOptionalFile(self.base.options.linker_script); try man.addOptionalFile(self.base.options.version_script); try man.addListOfFiles(self.base.options.objects); @@ -1432,8 +1446,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { }; } - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); - // Due to a deficiency in LLD, we need to special-case BPF to a simple file copy when generating // relocatables. Normally, we would expect `lld -r` to work. However, because LLD wants to resolve // BPF relocations which it shouldn't, it fails before even generating the relocatable. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7dc6c52372dc..ed720080af02 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -423,6 +423,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { const arena = arena_allocator.allocator(); const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. @@ -433,15 +434,24 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { .target = self.base.options.target, .output_mode = .Obj, }); - const o_directory = module.zig_cache_artifact_directory; - const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + switch (self.base.options.cache_mode) { + .incremental => break :blk try module.zig_cache_artifact_directory.join( + arena, + &[_][]const u8{obj_basename}, + ), + .whole => break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, obj_basename, + }), + } } const obj_basename = self.base.intermediary_basename orelse break :blk null; + try self.flushObject(comp); - const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + + break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, obj_basename, + }); } else null; const is_lib = self.base.options.output_mode == .Lib; @@ -466,6 +476,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); + comptime assert(Compilation.link_hash_implementation_version == 1); + try man.addListOfFiles(self.base.options.objects); for (comp.c_object_table.keys()) |key| { _ = try man.addFile(key.status.success.object_path, null); @@ -532,7 +544,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { else => |e| return e, }; } - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); if (self.base.options.output_mode == .Obj) { // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy @@ -1267,7 +1278,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const for (files) |file_name| { const full_path = full_path: { var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; - const path = try std.fs.realpath(file_name, &buffer); + const path = try fs.realpath(file_name, &buffer); break :full_path try self.base.allocator.dupe(u8, path); }; defer self.base.allocator.free(full_path); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 1ae50e8e3353..220ab2f53c68 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1050,6 +1050,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { const arena = arena_allocator.allocator(); const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. @@ -1061,15 +1062,22 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { .target = self.base.options.target, .output_mode = .Obj, }); - const o_directory = module.zig_cache_artifact_directory; - const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + switch (self.base.options.cache_mode) { + .incremental => break :blk try module.zig_cache_artifact_directory.join( + arena, + &[_][]const u8{obj_basename}, + ), + .whole => break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, obj_basename, + }), + } } try self.flushModule(comp); - const obj_basename = self.base.intermediary_basename.?; - const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); - break :blk full_obj_path; + + break :blk try fs.path.join(arena, &.{ + fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?, + }); } else null; const is_obj = self.base.options.output_mode == .Obj; @@ -1094,6 +1102,8 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); + comptime assert(Compilation.link_hash_implementation_version == 1); + try man.addListOfFiles(self.base.options.objects); for (comp.c_object_table.keys()) |key| { _ = try man.addFile(key.status.success.object_path, null); @@ -1141,8 +1151,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { }; } - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); - if (self.base.options.output_mode == .Obj) { // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing diff --git a/src/main.zig b/src/main.zig index 3f2dbd0cc797..4747772b8a88 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2564,9 +2564,7 @@ fn buildOutputType( switch (emit_bin) { .no => break :blk .none, - .yes_default_path => break :blk .{ - .print = comp.bin_file.options.emit.?.directory.path orelse ".", - }, + .yes_default_path => break :blk .print_emit_bin_dir_path, .yes => |full_path| break :blk .{ .update = full_path }, .yes_a_out => break :blk .{ .update = a_out_basename }, } @@ -2578,10 +2576,6 @@ fn buildOutputType( }; try comp.makeBinFileExecutable(); - if (build_options.is_stage1 and comp.stage1_lock != null and watch) { - warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{}); - } - if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: { // Default to using `zig run` to execute the produced .c code from `zig test`. const c_code_loc = emit_bin_loc orelse break :default_exec_args; @@ -2602,7 +2596,6 @@ fn buildOutputType( comp, gpa, arena, - emit_bin_loc, test_exec_args.items, self_exe_path, arg_mode, @@ -2675,7 +2668,6 @@ fn buildOutputType( comp, gpa, arena, - emit_bin_loc, test_exec_args.items, self_exe_path, arg_mode, @@ -2701,7 +2693,6 @@ fn buildOutputType( comp, gpa, arena, - emit_bin_loc, test_exec_args.items, self_exe_path, arg_mode, @@ -2766,7 +2757,6 @@ fn runOrTest( comp: *Compilation, gpa: Allocator, arena: Allocator, - emit_bin_loc: ?Compilation.EmitLoc, test_exec_args: []const ?[]const u8, self_exe_path: []const u8, arg_mode: ArgMode, @@ -2777,10 +2767,11 @@ fn runOrTest( runtime_args_start: ?usize, link_libc: bool, ) !void { - const exe_loc = emit_bin_loc orelse return; - const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory; + const exe_emit = comp.bin_file.options.emit orelse return; + // A naive `directory.join` here will indeed get the correct path to the binary, + // however, in the case of cwd, we actually want `./foo` so that the path can be executed. const exe_path = try fs.path.join(arena, &[_][]const u8{ - exe_directory.path orelse ".", exe_loc.basename, + exe_emit.directory.path orelse ".", exe_emit.sub_path, }); var argv = std.ArrayList([]const u8).init(gpa); @@ -2884,7 +2875,7 @@ fn runOrTest( const AfterUpdateHook = union(enum) { none, - print: []const u8, + print_emit_bin_dir_path, update: []const u8, }; @@ -2910,7 +2901,13 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void return error.SemanticAnalyzeFail; } else switch (hook) { .none => {}, - .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}), + .print_emit_bin_dir_path => { + const emit = comp.bin_file.options.emit.?; + const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); + defer gpa.free(full_path); + const dir_path = fs.path.dirname(full_path).?; + try io.getStdOut().writer().print("{s}\n", .{dir_path}); + }, .update => |full_path| { const bin_sub_path = comp.bin_file.options.emit.?.sub_path; const cwd = fs.cwd(); @@ -3473,9 +3470,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi }; try comp.makeBinFileExecutable(); - child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join( + const emit = comp.bin_file.options.emit.?; + child_argv.items[argv_index_exe] = try emit.directory.join( arena, - &[_][]const u8{exe_basename}, + &[_][]const u8{emit.sub_path}, ); break :argv child_argv.items; @@ -3666,9 +3664,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void .zir_loaded = false, .sub_file_path = "", .source = source_code, - .stat_size = undefined, - .stat_inode = undefined, - .stat_mtime = undefined, + .stat = undefined, .tree = tree, .tree_loaded = true, .zir = undefined, @@ -3862,9 +3858,11 @@ fn fmtPathFile( .zir_loaded = false, .sub_file_path = file_path, .source = source_code, - .stat_size = stat.size, - .stat_inode = stat.inode, - .stat_mtime = stat.mtime, + .stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }, .tree = tree, .tree_loaded = true, .zir = undefined, @@ -4460,9 +4458,7 @@ pub fn cmdAstCheck( .zir_loaded = false, .sub_file_path = undefined, .source = undefined, - .stat_size = undefined, - .stat_inode = undefined, - .stat_mtime = undefined, + .stat = undefined, .tree = undefined, .zir = undefined, .pkg = undefined, @@ -4487,9 +4483,11 @@ pub fn cmdAstCheck( file.sub_file_path = file_name; file.source = source; file.source_loaded = true; - file.stat_size = stat.size; - file.stat_inode = stat.inode; - file.stat_mtime = stat.mtime; + file.stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }; } else { const stdin = io.getStdIn(); const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| { @@ -4498,7 +4496,7 @@ pub fn cmdAstCheck( file.sub_file_path = ""; file.source = source; file.source_loaded = true; - file.stat_size = source.len; + file.stat.size = source.len; } file.pkg = try Package.create(gpa, null, file.sub_file_path); @@ -4611,9 +4609,11 @@ pub fn cmdChangelist( .zir_loaded = false, .sub_file_path = old_source_file, .source = undefined, - .stat_size = stat.size, - .stat_inode = stat.inode, - .stat_mtime = stat.mtime, + .stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }, .tree = undefined, .zir = undefined, .pkg = undefined, diff --git a/src/musl.zig b/src/musl.zig index d15d691a9c62..c576dbec8429 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -203,6 +203,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { const sub_compilation = try Compilation.create(comp.gpa, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, + .cache_mode = .whole, .zig_lib_directory = comp.zig_lib_directory, .target = target, .root_name = "c", diff --git a/src/stage1.zig b/src/stage1.zig index 8263427fbe6d..384988dc075b 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -458,7 +458,10 @@ export fn stage2_fetch_file( const comp = @intToPtr(*Compilation, stage1.userdata); const file_path = path_ptr[0..path_len]; const max_file_size = std.math.maxInt(u32); - const contents = comp.stage1_cache_manifest.addFilePostFetch(file_path, max_file_size) catch return null; + const contents = if (comp.whole_cache_manifest) |man| + man.addFilePostFetch(file_path, max_file_size) catch return null + else + std.fs.cwd().readFileAlloc(comp.gpa, file_path, max_file_size) catch return null; result_len.* = contents.len; // TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1); diff --git a/src/type.zig b/src/type.zig index 5bcf310fc40e..b94809399468 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3106,9 +3106,9 @@ pub const Type = extern union { .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, - .enum_numbered => ty = self.castTag(.enum_numbered).?.data.tag_ty, + .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty, .enum_simple => { - const enum_obj = self.castTag(.enum_simple).?.data; + const enum_obj = ty.castTag(.enum_simple).?.data; const field_count = enum_obj.fields.count(); if (field_count == 0) return .{ .signedness = .unsigned, .bits = 0 }; return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) }; @@ -4603,7 +4603,18 @@ pub const CType = enum { .longlong, .ulonglong, => return 64, - .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), + .longdouble => switch (target.cpu.arch) { + .riscv64, + .aarch64, + .aarch64_be, + .aarch64_32, + .s390x, + .mips64, + .mips64el, + => return 128, + + else => return 80, + }, }, .windows, .uefi => switch (self) { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index c4ebbc7643e3..d1edefeb2d2c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -295,3 +295,12 @@ test "cast from ?[*]T to ??[*]T" { const a: ??[*]u8 = @as(?[*]u8, null); try expect(a != null and a.? == null); } + +test "peer type unsigned int to signed" { + var w: u31 = 5; + var x: u8 = 7; + var y: i32 = -5; + var a = w + y + x; + comptime try expect(@TypeOf(a) == i32); + try expect(a == 7); +} diff --git a/test/behavior/cast_stage1.zig b/test/behavior/cast_stage1.zig index a4854b30b280..ff4c657ed7c4 100644 --- a/test/behavior/cast_stage1.zig +++ b/test/behavior/cast_stage1.zig @@ -383,15 +383,6 @@ test "peer type resolve string lit with sentinel-terminated mutable slice" { comptime try expect(@TypeOf("hi", slice) == [:0]const u8); } -test "peer type unsigned int to signed" { - var w: u31 = 5; - var x: u8 = 7; - var y: i32 = -5; - var a = w + y + x; - comptime try expect(@TypeOf(a) == i32); - try expect(a == 7); -} - test "peer type resolve array pointers, one of them const" { var array1: [4]u8 = undefined; const array2: [5]u8 = undefined;