diff options
195 files changed, 7585 insertions, 4711 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d12843be..81b96d67ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,9 +444,9 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/math.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/big.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/big/int.zig" + "${CMAKE_SOURCE_DIR}/lib/std/math/float.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/floor.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/frexp.zig" - "${CMAKE_SOURCE_DIR}/lib/std/math/inf.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/isinf.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/isnan.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/ln.zig" @@ -493,41 +493,8 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extendXfYf2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixdfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixdfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixdfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixint.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixsfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixsfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixsfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixtfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixtfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixtfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixuint.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunsdfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunsdfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunsdfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunssfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunssfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunssfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunstfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunstfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunstfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatdidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatditf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatsiXf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floattidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floattitf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatundidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatundisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunditf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunsidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunsisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunsitf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatuntidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatuntisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatuntitf.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXiYf.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/int.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig" diff --git a/deps/SoftFloat-3e-prebuilt/platform.h b/deps/SoftFloat-3e-prebuilt/platform.h index ef99e21b97..588c548c60 100644 --- a/deps/SoftFloat-3e-prebuilt/platform.h +++ b/deps/SoftFloat-3e-prebuilt/platform.h @@ -17,8 +17,6 @@ #define BIGENDIAN 1 #elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define BIGENDIAN 1 -#elif defined(_BIG_ENDIAN) -#define BIGENDIAN 1 #elif defined(__sparc) #define BIGENDIAN 1 #elif defined(__sparc__) @@ -37,7 +35,9 @@ #define BIGENDIAN 1 #elif defined(__s390__) #define BIGENDIAN 1 -#elif defined(__LITTLE_ENDIAN__) +#endif + +#if defined(__LITTLE_ENDIAN__) #define LITTLEENDIAN 1 #elif defined(__ARMEL__) #define LITTLEENDIAN 1 @@ -53,8 +53,6 @@ #define LITTLEENDIAN 1 #elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define LITTLEENDIAN 1 -#elif defined(_LITTLE_ENDIAN) -#define LITTLEENDIAN 1 #elif defined(__i386__) #define LITTLEENDIAN 1 #elif defined(__alpha__) @@ -83,7 +81,11 @@ #define LITTLEENDIAN 1 #elif defined(__bfin__) #define LITTLEENDIAN 1 -#else +#endif + +#if defined(LITTLEENDIAN) && defined(BIGENDIAN) +#error unable to detect endianness +#elif !defined(LITTLEENDIAN) && !defined(BIGENDIAN) #error unable to detect endianness #endif diff --git a/doc/langref.html.in b/doc/langref.html.in index 578f0297ae..0f90a88822 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7833,7 +7833,7 @@ fn readFile(allocator: Allocator, filename: []const u8) ![]u8 { for the current target to match the C ABI. When the child type of a pointer has this alignment, the alignment can be omitted from the type. </p> - <pre>{#syntax#}const expect = @import("std").debug.assert; + <pre>{#syntax#}const assert = @import("std").debug.assert; comptime { assert(*u32 == *align(@alignOf(u32)) u32); }{#endsyntax#}</pre> @@ -11957,17 +11957,6 @@ fn readU32Be() u32 {} </tr> <tr> <th scope="row"> - <pre>{#syntax#}false{#endsyntax#}</pre> - </th> - <td> - The boolean value {#syntax#}false{#endsyntax#}. - <ul> - <li>See also {#link|Primitive Values#}</li> - </ul> - </td> - </tr> - <tr> - <th scope="row"> <pre>{#syntax#}fn{#endsyntax#}</pre> </th> <td> @@ -12043,17 +12032,6 @@ fn readU32Be() u32 {} </tr> <tr> <th scope="row"> - <pre>{#syntax#}null{#endsyntax#}</pre> - </th> - <td> - The optional value {#syntax#}null{#endsyntax#}. - <ul> - <li>See also {#link|null#}</li> - </ul> - </td> - </tr> - <tr> - <th scope="row"> <pre>{#syntax#}or{#endsyntax#}</pre> </th> <td> @@ -12192,17 +12170,6 @@ fn readU32Be() u32 {} </tr> <tr> <th scope="row"> - <pre>{#syntax#}true{#endsyntax#}</pre> - </th> - <td> - The boolean value {#syntax#}true{#endsyntax#}. - <ul> - <li>See also {#link|Primitive Values#}</li> - </ul> - </td> - </tr> - <tr> - <th scope="row"> <pre>{#syntax#}try{#endsyntax#}</pre> </th> <td> @@ -12216,17 +12183,6 @@ fn readU32Be() u32 {} </tr> <tr> <th scope="row"> - <pre>{#syntax#}undefined{#endsyntax#}</pre> - </th> - <td> - {#syntax#}undefined{#endsyntax#} can be used to leave a value uninitialized. - <ul> - <li>See also {#link|undefined#}</li> - </ul> - </td> - </tr> - <tr> - <th scope="row"> <pre>{#syntax#}union{#endsyntax#}</pre> </th> <td> diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index 7a479e5540..fb48db8e53 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -17,6 +17,10 @@ pub fn wait(cond: *Condition, mutex: *Mutex) void { cond.impl.wait(mutex); } +pub fn timedWait(cond: *Condition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + try cond.impl.timedWait(mutex, timeout_ns); +} + pub fn signal(cond: *Condition) void { cond.impl.signal(); } @@ -41,6 +45,14 @@ pub const SingleThreadedCondition = struct { unreachable; // deadlock detected } + pub fn timedWait(cond: *SingleThreadedCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + _ = cond; + _ = mutex; + _ = timeout_ns; + std.time.sleep(timeout_ns); + return error.TimedOut; + } + pub fn signal(cond: *SingleThreadedCondition) void { _ = cond; } @@ -63,6 +75,25 @@ pub const WindowsCondition = struct { assert(rc != windows.FALSE); } + pub fn timedWait(cond: *WindowsCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + var timeout_checked = std.math.cast(windows.DWORD, timeout_ns / std.time.ns_per_ms) catch overflow: { + break :overflow std.math.maxInt(windows.DWORD); + }; + + // Handle the case where timeout is INFINITE, otherwise SleepConditionVariableSRW's time-out never elapses + const timeout_overflowed = timeout_checked == windows.INFINITE; + timeout_checked -= @boolToInt(timeout_overflowed); + + const rc = windows.kernel32.SleepConditionVariableSRW( + &cond.cond, + &mutex.impl.srwlock, + timeout_checked, + @as(windows.ULONG, 0), + ); + if (rc == windows.FALSE and windows.kernel32.GetLastError() == windows.Win32Error.TIMEOUT) return error.TimedOut; + assert(rc != windows.FALSE); + } + pub fn signal(cond: *WindowsCondition) void { windows.kernel32.WakeConditionVariable(&cond.cond); } @@ -80,6 +111,24 @@ pub const PthreadCondition = struct { assert(rc == .SUCCESS); } + pub fn timedWait(cond: *PthreadCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + var ts: std.os.timespec = undefined; + std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts) catch unreachable; + ts.tv_sec += @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + ts.tv_nsec += @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); + if (ts.tv_nsec >= std.time.ns_per_s) { + ts.tv_sec += 1; + ts.tv_nsec -= std.time.ns_per_s; + } + + const rc = std.c.pthread_cond_timedwait(&cond.cond, &mutex.impl.pthread_mutex, &ts); + return switch (rc) { + .SUCCESS => {}, + .TIMEDOUT => error.TimedOut, + else => unreachable, + }; + } + pub fn signal(cond: *PthreadCondition) void { const rc = std.c.pthread_cond_signal(&cond.cond); assert(rc == .SUCCESS); @@ -100,6 +149,7 @@ pub const AtomicCondition = struct { pub const QueueItem = struct { futex: i32 = 0, + dequeued: bool = false, fn wait(cond: *@This()) void { while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) { @@ -122,6 +172,39 @@ pub const AtomicCondition = struct { } } + pub fn timedWait(cond: *@This(), timeout_ns: u64) error{TimedOut}!void { + const start_time = std.time.nanoTimestamp(); + while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) { + switch (builtin.os.tag) { + .linux => { + var ts: std.os.timespec = undefined; + ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); + switch (linux.getErrno(linux.futex_wait( + &cond.futex, + linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT, + 0, + &ts, + ))) { + .SUCCESS => {}, + .INTR => {}, + .AGAIN => {}, + .TIMEDOUT => return error.TimedOut, + .INVAL => {}, // possibly timeout overflow + .FAULT => unreachable, + else => unreachable, + } + }, + else => { + if (std.time.nanoTimestamp() - start_time >= timeout_ns) { + return error.TimedOut; + } + std.atomic.spinLoopHint(); + }, + } + } + } + fn notify(cond: *@This()) void { @atomicStore(i32, &cond.futex, 1, .Release); @@ -158,6 +241,41 @@ pub const AtomicCondition = struct { mutex.lock(); } + pub fn timedWait(cond: *AtomicCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + var waiter = QueueList.Node{ .data = .{} }; + + { + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); + + cond.queue_list.prepend(&waiter); + @atomicStore(bool, &cond.pending, true, .SeqCst); + } + + var timed_out = false; + mutex.unlock(); + defer mutex.lock(); + waiter.data.timedWait(timeout_ns) catch |err| switch (err) { + error.TimedOut => { + defer if (!timed_out) { + waiter.data.wait(); + }; + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); + + if (!waiter.data.dequeued) { + timed_out = true; + cond.queue_list.remove(&waiter); + } + }, + else => unreachable, + }; + + if (timed_out) { + return error.TimedOut; + } + } + pub fn signal(cond: *AtomicCondition) void { if (@atomicLoad(bool, &cond.pending, .SeqCst) == false) return; @@ -167,12 +285,16 @@ pub const AtomicCondition = struct { defer cond.queue_mutex.unlock(); const maybe_waiter = cond.queue_list.popFirst(); + if (maybe_waiter) |waiter| { + waiter.data.dequeued = true; + } @atomicStore(bool, &cond.pending, cond.queue_list.first != null, .SeqCst); break :blk maybe_waiter; }; - if (maybe_waiter) |waiter| + if (maybe_waiter) |waiter| { waiter.data.notify(); + } } pub fn broadcast(cond: *AtomicCondition) void { @@ -186,12 +308,19 @@ pub const AtomicCondition = struct { defer cond.queue_mutex.unlock(); const waiters = cond.queue_list; + + var it = waiters.first; + while (it) |node| : (it = node.next) { + node.data.dequeued = true; + } + cond.queue_list = .{}; break :blk waiters; }; - while (waiters.popFirst()) |waiter| + while (waiters.popFirst()) |waiter| { waiter.data.notify(); + } } }; @@ -238,3 +367,45 @@ test "Thread.Condition" { for (threads) |t| t.join(); } + +test "Thread.Condition.timedWait" { + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + var cond = Condition{}; + var mut = Mutex{}; + + // Expect a timeout, as the condition variable is never signaled + { + mut.lock(); + defer mut.unlock(); + try testing.expectError(error.TimedOut, cond.timedWait(&mut, 10 * std.time.ns_per_ms)); + } + + // Expect a signal before timeout + { + const TestContext = struct { + cond: *Condition, + mutex: *Mutex, + n: *u32, + fn worker(ctx: *@This()) void { + ctx.mutex.lock(); + defer ctx.mutex.unlock(); + ctx.n.* = 1; + ctx.cond.signal(); + } + }; + + var n: u32 = 0; + + var ctx = TestContext{ .cond = &cond, .mutex = &mut, .n = &n }; + mut.lock(); + var thread = try std.Thread.spawn(.{}, TestContext.worker, .{&ctx}); + // Looped check to handle spurious wakeups + while (n != 1) try cond.timedWait(&mut, 500 * std.time.ns_per_ms); + mut.unlock(); + try testing.expect(n == 1); + thread.join(); + } +} diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index c9a19b4cd9..d256fb5a43 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -1,7 +1,7 @@ //! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints. //! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value. //! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`. -//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals. +//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals. const std = @import("../std.zig"); const builtin = @import("builtin"); @@ -20,7 +20,7 @@ const spinLoopHint = std.atomic.spinLoopHint; /// - The value at `ptr` is no longer equal to `expect`. /// - The caller is unblocked by a matching `wake()`. /// - The caller is unblocked spuriously by an arbitrary internal signal. -/// +/// /// If `timeout` is provided, and the caller is blocked for longer than `timeout` nanoseconds`, `error.TimedOut` is returned. /// /// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index 7c562b7c84..0b0efc55e4 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -239,6 +239,24 @@ pub fn BoundedArray(comptime T: type, comptime capacity: usize) type { assert(self.len <= capacity); mem.set(T, self.slice()[old_len..self.len], value); } + + pub const Writer = if (T != u8) + @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ + "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") + else + std.io.Writer(*Self, error{Overflow}, appendWrite); + + /// Initializes a writer which will write into the array. + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + /// Same as `appendSlice` except it returns the number of bytes written, which is always the same + /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API. + fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { + try self.appendSlice(m); + return m.len; + } }; } @@ -336,4 +354,10 @@ test "BoundedArray" { const swapped = a.swapRemove(0); try testing.expectEqual(swapped, 0xdd); try testing.expectEqual(a.get(0), 0xee); + + while (a.popOrNull()) |_| {} + const w = a.writer(); + const s = "hello, this is a test string"; + try w.writeAll(s); + try testing.expectEqualStrings(s, a.constSlice()); } diff --git a/lib/std/build.zig b/lib/std/build.zig index f1287c7be5..ff0f36b4a8 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1600,12 +1600,26 @@ pub const LibExeObjStep = struct { pub const LinkObject = union(enum) { static_path: FileSource, other_step: *LibExeObjStep, - system_lib: []const u8, + system_lib: SystemLib, assembly_file: FileSource, c_source_file: *CSourceFile, c_source_files: *CSourceFiles, }; + pub const SystemLib = struct { + name: []const u8, + use_pkg_config: enum { + /// Don't use pkg-config, just pass -lfoo where foo is name. + no, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, fall back to passing -lfoo where foo is name. + yes, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, error out. + force, + }, + }; + pub const IncludeDir = union(enum) { raw_path: []const u8, raw_path_system: []const u8, @@ -1854,7 +1868,7 @@ pub const LibExeObjStep = struct { } for (self.link_objects.items) |link_object| { switch (link_object) { - .system_lib => |n| if (mem.eql(u8, n, name)) return true, + .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, else => continue, } } @@ -1879,14 +1893,24 @@ pub const LibExeObjStep = struct { pub fn linkLibC(self: *LibExeObjStep) void { if (!self.is_linking_libc) { self.is_linking_libc = true; - self.link_objects.append(.{ .system_lib = "c" }) catch unreachable; + self.link_objects.append(.{ + .system_lib = .{ + .name = "c", + .use_pkg_config = .no, + }, + }) catch unreachable; } } pub fn linkLibCpp(self: *LibExeObjStep) void { if (!self.is_linking_libcpp) { self.is_linking_libcpp = true; - self.link_objects.append(.{ .system_lib = "c++" }) catch unreachable; + self.link_objects.append(.{ + .system_lib = .{ + .name = "c++", + .use_pkg_config = .no, + }, + }) catch unreachable; } } @@ -1905,12 +1929,28 @@ pub const LibExeObjStep = struct { /// This one has no integration with anything, it just puts -lname on the command line. /// Prefer to use `linkSystemLibrary` instead. pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { - self.link_objects.append(.{ .system_lib = self.builder.dupe(name) }) catch unreachable; + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .use_pkg_config = .no, + }, + }) catch unreachable; } /// This links against a system library, exclusively using pkg-config to find the library. /// Prefer to use `linkSystemLibrary` instead. - pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) !void { + pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(lib_name), + .use_pkg_config = .force, + }, + }) catch unreachable; + } + + /// Run pkg-config for the given library name and parse the output, returning the arguments + /// that should be passed to zig to link the given library. + fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 { const pkg_name = match: { // First we have to map the library name to pkg config name. Unfortunately, // there are several examples where this is not straightforward: @@ -1970,34 +2010,38 @@ pub const LibExeObjStep = struct { error.ChildExecFailed => return error.PkgConfigFailed, else => return err, }; + + var zig_args = std.ArrayList([]const u8).init(self.builder.allocator); + defer zig_args.deinit(); + var it = mem.tokenize(u8, stdout, " \r\n\t"); while (it.next()) |tok| { if (mem.eql(u8, tok, "-I")) { const dir = it.next() orelse return error.PkgConfigInvalidOutput; - self.addIncludePath(dir); + try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); } else if (mem.startsWith(u8, tok, "-I")) { - self.addIncludePath(tok["-I".len..]); + try zig_args.append(tok); } else if (mem.eql(u8, tok, "-L")) { const dir = it.next() orelse return error.PkgConfigInvalidOutput; - self.addLibraryPath(dir); + try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); } else if (mem.startsWith(u8, tok, "-L")) { - self.addLibraryPath(tok["-L".len..]); + try zig_args.append(tok); } else if (mem.eql(u8, tok, "-l")) { const lib = it.next() orelse return error.PkgConfigInvalidOutput; - self.linkSystemLibraryName(lib); + try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); } else if (mem.startsWith(u8, tok, "-l")) { - self.linkSystemLibraryName(tok["-l".len..]); + try zig_args.append(tok); } else if (mem.eql(u8, tok, "-D")) { const macro = it.next() orelse return error.PkgConfigInvalidOutput; - self.defineCMacroRaw(macro); + try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); } else if (mem.startsWith(u8, tok, "-D")) { - self.defineCMacroRaw(tok["-D".len..]); - } else if (mem.eql(u8, tok, "-pthread")) { - self.linkLibC(); + try zig_args.append(tok); } else if (self.builder.verbose) { warn("Ignoring pkg-config flag '{s}'\n", .{tok}); } } + + return zig_args.toOwnedSlice(); } pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { @@ -2009,21 +2053,13 @@ pub const LibExeObjStep = struct { self.linkLibCpp(); return; } - if (self.linkSystemLibraryPkgConfigOnly(name)) |_| { - // pkg-config worked, so nothing further needed to do. - return; - } else |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => {}, - - else => unreachable, - } - self.linkSystemLibraryName(name); + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .use_pkg_config = .yes, + }, + }) catch unreachable; } pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { @@ -2317,27 +2353,34 @@ pub const LibExeObjStep = struct { var prev_has_extra_flags = false; // Resolve transitive dependencies - for (self.link_objects.items) |link_object| { - switch (link_object) { - .other_step => |other| { - // Inherit dependency on system libraries - for (other.link_objects.items) |other_link_object| { - switch (other_link_object) { - .system_lib => |name| self.linkSystemLibrary(name), - else => continue, + { + var transitive_dependencies = std.ArrayList(LinkObject).init(builder.allocator); + defer transitive_dependencies.deinit(); + + for (self.link_objects.items) |link_object| { + switch (link_object) { + .other_step => |other| { + // Inherit dependency on system libraries + for (other.link_objects.items) |other_link_object| { + switch (other_link_object) { + .system_lib => try transitive_dependencies.append(other_link_object), + else => continue, + } } - } - // Inherit dependencies on darwin frameworks - if (!other.isDynamicLibrary()) { - var it = other.frameworks.iterator(); - while (it.next()) |framework| { - self.frameworks.insert(framework.*) catch unreachable; + // Inherit dependencies on darwin frameworks + if (!other.isDynamicLibrary()) { + var it = other.frameworks.iterator(); + while (it.next()) |framework| { + self.frameworks.insert(framework.*) catch unreachable; + } } - } - }, - else => continue, + }, + else => continue, + } } + + try self.link_objects.appendSlice(transitive_dependencies.items); } for (self.link_objects.items) |link_object| { @@ -2363,8 +2406,35 @@ pub const LibExeObjStep = struct { } }, }, - .system_lib => |name| { - try zig_args.append(builder.fmt("-l{s}", .{name})); + + .system_lib => |system_lib| { + switch (system_lib.use_pkg_config) { + .no => try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})), + .yes, .force => { + if (self.runPkgConfig(system_lib.name)) |args| { + try zig_args.appendSlice(args); + } else |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so fall back to linking the library + // by name directly. + try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})); + }, + .force => { + panic("pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, + + else => |e| return e, + } + }, + } }, .assembly_file => |asm_file| { diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 7b66998dc1..f38fc4e155 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -716,6 +716,9 @@ pub const CompilerBackend = enum(u64) { /// The reference implementation self-hosted compiler of Zig, using the /// riscv64 backend. stage2_riscv64 = 9, + /// The reference implementation self-hosted compiler of Zig, using the + /// sparcv9 backend. + stage2_sparcv9 = 10, _, }; @@ -761,7 +764,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_x86 or - builtin.zig_backend == .stage2_riscv64) + builtin.zig_backend == .stage2_riscv64 or + builtin.zig_backend == .stage2_sparcv9) { while (true) { @breakpoint(); diff --git a/lib/std/compress/deflate/compressor.zig b/lib/std/compress/deflate/compressor.zig index 8ced8b5faf..58b1955838 100644 --- a/lib/std/compress/deflate/compressor.zig +++ b/lib/std/compress/deflate/compressor.zig @@ -219,7 +219,7 @@ const CompressorOptions = struct { /// /// `dictionary` is optional and initializes the new `Compressor` with a preset dictionary. /// The returned Compressor behaves as if the dictionary had been written to it without producing -/// any compressed output. The compressed data written to hm_bw can only be decompressed by a +/// any compressed output. The compressed data written to hm_bw can only be decompressed by a /// Decompressor initialized with the same dictionary. /// /// The compressed data will be passed to the provided `writer`, see `writer()` and `write()`. diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index 20b0dccaa0..f5938dd218 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -39,8 +39,11 @@ pub const Curve25519 = struct { } } - /// Multiply a point by the cofactor - pub const clearCofactor = @compileError("TODO what was this function supposed to do? it didn't compile successfully"); + /// Multiply a point by the cofactor, returning WeakPublicKey if the element is in a small-order group. + pub fn clearCofactor(p: Curve25519) WeakPublicKeyError!Curve25519 { + const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; + return ladder(p, cofactor, 4) catch return error.WeakPublicKey; + } fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 { var x1 = p.x; @@ -94,8 +97,7 @@ pub const Curve25519 = struct { /// the identity element or error.WeakPublicKey if the public /// key is a low-order point. pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 { - const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; - _ = ladder(p, cofactor, 4) catch return error.WeakPublicKey; + _ = try p.clearCofactor(); return try ladder(p, s, 256); } @@ -148,6 +150,7 @@ test "curve25519 small order check" { }, }; for (small_order_ss) |small_order_s| { + try std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).clearCofactor()); try std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s)); var extra = small_order_s; extra[31] ^= 0x80; diff --git a/lib/std/crypto/argon2.zig b/lib/std/crypto/argon2.zig index 493f36ca94..7269470d5f 100644 --- a/lib/std/crypto/argon2.zig +++ b/lib/std/crypto/argon2.zig @@ -34,18 +34,18 @@ const max_hash_len = 64; /// Argon2 type pub const Mode = enum { - /// Argon2d is faster and uses data-depending memory access, which makes it highly resistant - /// against GPU cracking attacks and suitable for applications with no threats from side-channel + /// Argon2d is faster and uses data-depending memory access, which makes it highly resistant + /// against GPU cracking attacks and suitable for applications with no threats from side-channel /// timing attacks (eg. cryptocurrencies). argon2d, - /// Argon2i instead uses data-independent memory access, which is preferred for password - /// hashing and password-based key derivation, but it is slower as it makes more passes over + /// Argon2i instead uses data-independent memory access, which is preferred for password + /// hashing and password-based key derivation, but it is slower as it makes more passes over /// the memory to protect from tradeoff attacks. argon2i, - /// Argon2id is a hybrid of Argon2i and Argon2d, using a combination of data-depending and - /// data-independent memory accesses, which gives some of Argon2i's resistance to side-channel + /// Argon2id is a hybrid of Argon2i and Argon2d, using a combination of data-depending and + /// data-independent memory accesses, which gives some of Argon2i's resistance to side-channel /// cache timing attacks and much of Argon2d's resistance to GPU cracking attacks. argon2id, }; @@ -54,7 +54,7 @@ pub const Mode = enum { pub const Params = struct { const Self = @This(); - /// A [t]ime cost, which defines the amount of computation realized and therefore the execution + /// A [t]ime cost, which defines the amount of computation realized and therefore the execution /// time, given in number of iterations. t: u32, @@ -64,16 +64,16 @@ pub const Params = struct { /// A [p]arallelism degree, which defines the number of parallel threads. p: u24, - /// The [secret] parameter, which is used for keyed hashing. This allows a secret key to be input - /// at hashing time (from some external location) and be folded into the value of the hash. This - /// means that even if your salts and hashes are compromised, an attacker cannot brute-force to + /// The [secret] parameter, which is used for keyed hashing. This allows a secret key to be input + /// at hashing time (from some external location) and be folded into the value of the hash. This + /// means that even if your salts and hashes are compromised, an attacker cannot brute-force to /// find the password without the key. secret: ?[]const u8 = null, - /// The [ad] parameter, which is used to fold any additional data into the hash value. Functionally, - /// this behaves almost exactly like the secret or salt parameters; the ad parameter is folding - /// into the value of the hash. However, this parameter is used for different data. The salt - /// should be a random string stored alongside your password. The secret should be a random key + /// The [ad] parameter, which is used to fold any additional data into the hash value. Functionally, + /// this behaves almost exactly like the secret or salt parameters; the ad parameter is folding + /// into the value of the hash. However, this parameter is used for different data. The salt + /// should be a random string stored alongside your password. The secret should be a random key /// only usable at hashing time. The ad is for any other data. ad: ?[]const u8 = null, diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index e464cca28e..a9506d3d23 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -131,7 +131,7 @@ pub const Params = struct { r: u30, /// The [p]arallelization parameter. - /// A large value of [p] can be used to increase the computational cost of scrypt without + /// A large value of [p] can be used to increase the computational cost of scrypt without /// increasing the memory usage. p: u30, @@ -326,7 +326,7 @@ const crypt_format = struct { try out.writeAll(hash_str); } - /// Custom codec that maps 6 bits into 8 like regular Base64, but uses its own alphabet, + /// Custom codec that maps 6 bits into 8 like regular Base64, but uses its own alphabet, /// encodes bits in little-endian, and can also encode integers. fn CustomB64Codec(comptime map: [64]u8) type { return struct { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d2173e114a..e00c0a21a2 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -842,7 +842,7 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn nosuspend { const mapped_mem = try mapWholeFile(elf_file); const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]); - if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + if (!mem.eql(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; const endian: std.builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) { diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index d9ebb0d1d5..40a84fc76f 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -133,7 +133,7 @@ pub const ElfDynLib = struct { defer os.munmap(file_bytes); const eh = @ptrCast(*elf.Ehdr, file_bytes.ptr); - if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; + if (!mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.NotElfFile; if (eh.e_type != elf.ET.DYN) return error.NotDynamicLibrary; const elf_addr = @ptrToInt(file_bytes.ptr); diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 84001ec1c9..846bb8d11a 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -305,6 +305,8 @@ pub const STT_ARM_16BIT = STT_HIPROC; pub const VER_FLG_BASE = 0x1; pub const VER_FLG_WEAK = 0x2; +pub const MAGIC = "\x7fELF"; + /// File types pub const ET = enum(u16) { /// No file type @@ -367,7 +369,7 @@ pub const Header = struct { pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header { const hdr32 = @ptrCast(*const Elf32_Ehdr, hdr_buf); const hdr64 = @ptrCast(*const Elf64_Ehdr, hdr_buf); - if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + if (!mem.eql(u8, hdr32.e_ident[0..4], MAGIC)) return error.InvalidElfMagic; if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion; const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { diff --git a/lib/std/event/wait_group.zig b/lib/std/event/wait_group.zig index d26986f384..4cc82cf98e 100644 --- a/lib/std/event/wait_group.zig +++ b/lib/std/event/wait_group.zig @@ -35,8 +35,8 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { const Self = @This(); pub fn begin(self: *Self, count: CounterType) error{Overflow}!void { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); const new_counter = try std.math.add(CounterType, self.counter, count); if (new_counter > self.max_counter) return error.Overflow; @@ -45,8 +45,8 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { pub fn finish(self: *Self, count: CounterType) void { var waiters = blk: { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); self.counter = std.math.sub(CounterType, self.counter, count) catch unreachable; if (self.counter == 0) { const temp = self.waiters; @@ -65,10 +65,10 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { } pub fn wait(self: *Self) void { - const held = self.mutex.acquire(); + self.mutex.lock(); if (self.counter == 0) { - held.release(); + self.mutex.unlock(); return; } @@ -83,7 +83,7 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { self_waiter.next = null; } suspend { - held.release(); + self.mutex.unlock(); } } }; diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 395c502d61..80a2e87c5c 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -766,12 +766,10 @@ fn formatFloatValue( } else if (comptime std.mem.eql(u8, fmt, "d")) { formatFloatDecimal(value, options, buf_stream.writer()) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; } else if (comptime std.mem.eql(u8, fmt, "x")) { formatFloatHexadecimal(value, options, buf_stream.writer()) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; } else { @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); @@ -968,10 +966,10 @@ pub fn formatUnicodeCodepoint( writer: anytype, ) !void { var buf: [4]u8 = undefined; - const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { + const len = unicode.utf8Encode(c, &buf) catch |err| switch (err) { error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => { - // In case of error output the replacement char U+FFFD - return formatBuf(&[_]u8{ 0xef, 0xbf, 0xbd }, options, writer); + const len = unicode.utf8Encode(unicode.replacement_character, &buf) catch unreachable; + return formatBuf(buf[0..len], options, writer); }, }; return formatBuf(buf[0..len], options, writer); @@ -2228,8 +2226,8 @@ test "float.special" { if (builtin.target.cpu.arch != .arm) { try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); } - try expectFmt("f64: inf", "f64: {}", .{math.inf_f64}); - try expectFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); + try expectFmt("f64: inf", "f64: {}", .{math.inf(f64)}); + try expectFmt("f64: -inf", "f64: {}", .{-math.inf(f64)}); } test "float.hexadecimal.special" { @@ -2239,8 +2237,8 @@ test "float.hexadecimal.special" { if (builtin.target.cpu.arch != .arm) { try expectFmt("f64: -nan", "f64: {x}", .{-math.nan_f64}); } - try expectFmt("f64: inf", "f64: {x}", .{math.inf_f64}); - try expectFmt("f64: -inf", "f64: {x}", .{-math.inf_f64}); + try expectFmt("f64: inf", "f64: {x}", .{math.inf(f64)}); + try expectFmt("f64: -inf", "f64: {x}", .{-math.inf(f64)}); try expectFmt("f64: 0x0.0p0", "f64: {x}", .{@as(f64, 0)}); try expectFmt("f64: -0x0.0p0", "f64: {x}", .{-@as(f64, 0)}); @@ -2252,20 +2250,20 @@ test "float.hexadecimal" { try expectFmt("f64: 0x1.5555555555555p-2", "f64: {x}", .{@as(f64, 1.0 / 3.0)}); try expectFmt("f128: 0x1.5555555555555555555555555555p-2", "f128: {x}", .{@as(f128, 1.0 / 3.0)}); - try expectFmt("f16: 0x1p-14", "f16: {x}", .{@as(f16, math.f16_min)}); - try expectFmt("f32: 0x1p-126", "f32: {x}", .{@as(f32, math.f32_min)}); - try expectFmt("f64: 0x1p-1022", "f64: {x}", .{@as(f64, math.f64_min)}); - try expectFmt("f128: 0x1p-16382", "f128: {x}", .{@as(f128, math.f128_min)}); + try expectFmt("f16: 0x1p-14", "f16: {x}", .{math.floatMin(f16)}); + try expectFmt("f32: 0x1p-126", "f32: {x}", .{math.floatMin(f32)}); + try expectFmt("f64: 0x1p-1022", "f64: {x}", .{math.floatMin(f64)}); + try expectFmt("f128: 0x1p-16382", "f128: {x}", .{math.floatMin(f128)}); - try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{@as(f16, math.f16_true_min)}); - try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{@as(f32, math.f32_true_min)}); - try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{@as(f64, math.f64_true_min)}); - try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{@as(f128, math.f128_true_min)}); + try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{math.floatTrueMin(f16)}); + try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{math.floatTrueMin(f32)}); + try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{math.floatTrueMin(f64)}); + try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{math.floatTrueMin(f128)}); - try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{@as(f16, math.f16_max)}); - try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{@as(f32, math.f32_max)}); - try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{@as(f64, math.f64_max)}); - try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{@as(f128, math.f128_max)}); + try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{math.floatMax(f16)}); + try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{math.floatMax(f32)}); + try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{math.floatMax(f64)}); + try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{math.floatMax(f128)}); } test "float.hexadecimal.precision" { @@ -2299,6 +2297,8 @@ test "float.decimal" { try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)}); + try expectFmt("f64: 10000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e37)}); + try expectFmt("f64: 100000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e38)}); } test "float.libc.sanity" { diff --git a/lib/std/fmt/errol.zig b/lib/std/fmt/errol.zig index e98c23f6ec..29dd2b7a63 100644 --- a/lib/std/fmt/errol.zig +++ b/lib/std/fmt/errol.zig @@ -106,7 +106,10 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { } else if (val >= 16.0 and val < 9.007199254740992e15) { return errolFixed(val, buffer); } + return errolSlow(val, buffer); +} +fn errolSlow(val: f64, buffer: []u8) FloatDecimal { // normalize the midpoint const e = math.frexp(val).exponent; @@ -336,7 +339,9 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var buf_index = u64toa(m64, buffer) - 1; if (mi != 0) { - buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); + const round_up = buffer[buf_index] >= '5'; + if (buf_index == 0 or (round_up and buffer[buf_index - 1] == '9')) return errolSlow(val, buffer); + buffer[buf_index - 1] += @boolToInt(round_up); } else { buf_index += 1; } diff --git a/lib/std/fmt/parse_hex_float.zig b/lib/std/fmt/parse_hex_float.zig index 83c798ab96..3e8bc5c5d9 100644 --- a/lib/std/fmt/parse_hex_float.zig +++ b/lib/std/fmt/parse_hex_float.zig @@ -12,17 +12,16 @@ const assert = std.debug.assert; pub fn parseHexFloat(comptime T: type, s: []const u8) !T { assert(@typeInfo(T) == .Float); - const IntT = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const mantissa_bits = math.floatMantissaBits(T); const exponent_bits = math.floatExponentBits(T); + const exponent_min = math.floatExponentMin(T); + const exponent_max = math.floatExponentMax(T); + const exponent_bias = exponent_max; const sign_shift = mantissa_bits + exponent_bits; - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const exponent_min = 1 - exponent_bias; - const exponent_max = exponent_bias; - if (s.len == 0) return error.InvalidCharacter; @@ -233,10 +232,10 @@ pub fn parseHexFloat(comptime T: type, s: []const u8) !T { // Remove the implicit bit. mantissa &= @as(u128, (1 << mantissa_bits) - 1); - const raw: IntT = - (if (negative) @as(IntT, 1) << sign_shift else 0) | - @as(IntT, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits | - @truncate(IntT, mantissa); + const raw: TBits = + (if (negative) @as(TBits, 1) << sign_shift else 0) | + @as(TBits, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits | + @truncate(TBits, mantissa); return @bitCast(T, raw); } @@ -263,14 +262,14 @@ test "f16" { .{ .s = "0x10p+10", .v = 16384.0 }, .{ .s = "0x10p-10", .v = 0.015625 }, // Max normalized value. - .{ .s = "0x1.ffcp+15", .v = math.f16_max }, - .{ .s = "-0x1.ffcp+15", .v = -math.f16_max }, + .{ .s = "0x1.ffcp+15", .v = math.floatMax(f16) }, + .{ .s = "-0x1.ffcp+15", .v = -math.floatMax(f16) }, // Min normalized value. - .{ .s = "0x1p-14", .v = math.f16_min }, - .{ .s = "-0x1p-14", .v = -math.f16_min }, + .{ .s = "0x1p-14", .v = math.floatMin(f16) }, + .{ .s = "-0x1p-14", .v = -math.floatMin(f16) }, // Min denormal value. - .{ .s = "0x1p-24", .v = math.f16_true_min }, - .{ .s = "-0x1p-24", .v = -math.f16_true_min }, + .{ .s = "0x1p-24", .v = math.floatTrueMin(f16) }, + .{ .s = "-0x1p-24", .v = -math.floatTrueMin(f16) }, }; for (cases) |case| { @@ -287,14 +286,14 @@ test "f32" { .{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 }, .{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 }, // Max normalized value. - .{ .s = "0x1.fffffeP+127", .v = math.f32_max }, - .{ .s = "-0x1.fffffeP+127", .v = -math.f32_max }, + .{ .s = "0x1.fffffeP+127", .v = math.floatMax(f32) }, + .{ .s = "-0x1.fffffeP+127", .v = -math.floatMax(f32) }, // Min normalized value. - .{ .s = "0x1p-126", .v = math.f32_min }, - .{ .s = "-0x1p-126", .v = -math.f32_min }, + .{ .s = "0x1p-126", .v = math.floatMin(f32) }, + .{ .s = "-0x1p-126", .v = -math.floatMin(f32) }, // Min denormal value. - .{ .s = "0x1P-149", .v = math.f32_true_min }, - .{ .s = "-0x1P-149", .v = -math.f32_true_min }, + .{ .s = "0x1P-149", .v = math.floatTrueMin(f32) }, + .{ .s = "-0x1P-149", .v = -math.floatTrueMin(f32) }, }; for (cases) |case| { @@ -309,14 +308,14 @@ test "f64" { .{ .s = "0x10p+10", .v = 16384.0 }, .{ .s = "0x10p-10", .v = 0.015625 }, // Max normalized value. - .{ .s = "0x1.fffffffffffffp+1023", .v = math.f64_max }, - .{ .s = "-0x1.fffffffffffffp1023", .v = -math.f64_max }, + .{ .s = "0x1.fffffffffffffp+1023", .v = math.floatMax(f64) }, + .{ .s = "-0x1.fffffffffffffp1023", .v = -math.floatMax(f64) }, // Min normalized value. - .{ .s = "0x1p-1022", .v = math.f64_min }, - .{ .s = "-0x1p-1022", .v = -math.f64_min }, + .{ .s = "0x1p-1022", .v = math.floatMin(f64) }, + .{ .s = "-0x1p-1022", .v = -math.floatMin(f64) }, // Min denormalized value. - .{ .s = "0x1p-1074", .v = math.f64_true_min }, - .{ .s = "-0x1p-1074", .v = -math.f64_true_min }, + .{ .s = "0x1p-1074", .v = math.floatTrueMin(f64) }, + .{ .s = "-0x1p-1074", .v = -math.floatTrueMin(f64) }, }; for (cases) |case| { @@ -331,14 +330,14 @@ test "f128" { .{ .s = "0x10p+10", .v = 16384.0 }, .{ .s = "0x10p-10", .v = 0.015625 }, // Max normalized value. - .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.f128_max }, - .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.f128_max }, + .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.floatMax(f128) }, + .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.floatMax(f128) }, // Min normalized value. - .{ .s = "0x1p-16382", .v = math.f128_min }, - .{ .s = "-0x1p-16382", .v = -math.f128_min }, + .{ .s = "0x1p-16382", .v = math.floatMin(f128) }, + .{ .s = "-0x1p-16382", .v = -math.floatMin(f128) }, // // Min denormalized value. - .{ .s = "0x1p-16494", .v = math.f128_true_min }, - .{ .s = "-0x1p-16494", .v = -math.f128_true_min }, + .{ .s = "0x1p-16494", .v = math.floatTrueMin(f128) }, + .{ .s = "-0x1p-16494", .v = -math.floatTrueMin(f128) }, .{ .s = "0x1.edcb34a235253948765432134674fp-1", .v = 0x1.edcb34a235253948765432134674fp-1 }, }; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 73efccbbfc..052343599e 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1308,9 +1308,9 @@ pub const Dir = struct { if (end_index == sub_path.len) return; }, error.FileNotFound => { - if (end_index == 0) return err; // march end_index backward until next path component while (true) { + if (end_index == 0) return err; end_index -= 1; if (path.isSep(sub_path[end_index])) break; } diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 568c34e0ac..3ea8dd0285 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -200,6 +200,17 @@ pub const File = struct { } } + pub const SyncError = os.SyncError; + + /// Blocks until all pending file contents and metadata modifications + /// for the file have been synchronized with the underlying filesystem. + /// + /// Note that this does not ensure that metadata for the + /// directory containing the file has also reached disk. + pub fn sync(self: File) SyncError!void { + return os.fsync(self.handle); + } + /// Test whether the file refers to a terminal. /// See also `supportsAnsiEscapeCodes`. pub fn isTty(self: File) bool { diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 69fbe5449f..82005152e5 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -434,9 +434,6 @@ test "Dir.rename files" { } test "Dir.rename directories" { - // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364 - if (builtin.os.tag == .windows) return error.SkipZigTest; - var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); @@ -461,18 +458,44 @@ test "Dir.rename directories" { file = try dir.openFile("test_file", .{}); file.close(); dir.close(); +} + +test "Dir.rename directory onto empty dir" { + // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364 + if (builtin.os.tag == .windows) return error.SkipZigTest; + + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + try tmp_dir.dir.makeDir("test_dir"); + try tmp_dir.dir.makeDir("target_dir"); + try tmp_dir.dir.rename("test_dir", "target_dir"); + + // Ensure the directory was renamed + try testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir", .{})); + var dir = try tmp_dir.dir.openDir("target_dir", .{}); + dir.close(); +} - // Try to rename to a non-empty directory now - var target_dir = try tmp_dir.dir.makeOpenPath("non_empty_target_dir", .{}); - file = try target_dir.createFile("filler", .{ .read = true }); +test "Dir.rename directory onto non-empty dir" { + // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364 + if (builtin.os.tag == .windows) return error.SkipZigTest; + + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + try tmp_dir.dir.makeDir("test_dir"); + + var target_dir = try tmp_dir.dir.makeOpenPath("target_dir", .{}); + var file = try target_dir.createFile("test_file", .{ .read = true }); file.close(); + target_dir.close(); - try testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir_renamed_again", "non_empty_target_dir")); + // Rename should fail with PathAlreadyExists if target_dir is non-empty + try testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir", "target_dir")); // Ensure the directory was not renamed - dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{}); - file = try dir.openFile("test_file", .{}); - file.close(); + var dir = try tmp_dir.dir.openDir("test_dir", .{}); dir.close(); } @@ -587,6 +610,16 @@ test "makePath, put some files in it, deleteTree" { } } +test "makePath in a directory that no longer exists" { + if (builtin.os.tag == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + try tmp.parent_dir.deleteTree(&tmp.sub_path); + + try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path")); +} + test "writev, readv" { var tmp = tmpDir(.{}); defer tmp.cleanup(); diff --git a/lib/std/io.zig b/lib/std/io.zig index fba1c289f7..50d134b856 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -9,7 +9,6 @@ const os = std.os; const fs = std.fs; const mem = std.mem; const meta = std.meta; -const trait = meta.trait; const File = std.fs.File; pub const Mode = enum { diff --git a/lib/std/math.zig b/lib/std/math.zig index 318d70c726..be49ba8030 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -36,38 +36,54 @@ pub const sqrt2 = 1.414213562373095048801688724209698079; /// 1/sqrt(2) pub const sqrt1_2 = 0.707106781186547524400844362104849039; -pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)); -pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); -pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); -pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); -pub const f128_toint = 1.0 / f128_epsilon; - -// float.h details -pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 }); -pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }); -pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }); -pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }); -pub const f80_toint = 1.0 / f80_epsilon; - -pub const f64_true_min = 4.94065645841246544177e-324; -pub const f64_min = 2.2250738585072014e-308; -pub const f64_max = 1.79769313486231570815e+308; -pub const f64_epsilon = 2.22044604925031308085e-16; -pub const f64_toint = 1.0 / f64_epsilon; - -pub const f32_true_min = 1.40129846432481707092e-45; -pub const f32_min = 1.17549435082228750797e-38; -pub const f32_max = 3.40282346638528859812e+38; -pub const f32_epsilon = 1.1920928955078125e-07; -pub const f32_toint = 1.0 / f32_epsilon; - -pub const f16_true_min = 0.000000059604644775390625; // 2**-24 -pub const f16_min = 0.00006103515625; // 2**-14 -pub const f16_max = 65504; -pub const f16_epsilon = 0.0009765625; // 2**-10 -pub const f16_toint = 1.0 / f16_epsilon; - -pub const epsilon = @import("math/epsilon.zig").epsilon; +pub const floatExponentBits = @import("math/float.zig").floatExponentBits; +pub const floatMantissaBits = @import("math/float.zig").floatMantissaBits; +pub const floatFractionalBits = @import("math/float.zig").floatFractionalBits; +pub const floatExponentMin = @import("math/float.zig").floatExponentMin; +pub const floatExponentMax = @import("math/float.zig").floatExponentMax; +pub const floatTrueMin = @import("math/float.zig").floatTrueMin; +pub const floatMin = @import("math/float.zig").floatMin; +pub const floatMax = @import("math/float.zig").floatMax; +pub const floatEps = @import("math/float.zig").floatEps; +pub const inf = @import("math/float.zig").inf; + +// TODO Replace with @compileError("deprecated for foobar") after 0.10.0 is released. +pub const f16_true_min: comptime_float = floatTrueMin(f16); // prev: 0.000000059604644775390625 +pub const f32_true_min: comptime_float = floatTrueMin(f32); // prev: 1.40129846432481707092e-45 +pub const f64_true_min: comptime_float = floatTrueMin(f64); // prev: 4.94065645841246544177e-324 +pub const f80_true_min = floatTrueMin(f80); // prev: make_f80(.{ .fraction = 1, .exp = 0 }) +pub const f128_true_min = floatTrueMin(f128); // prev: @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)) +pub const f16_min: comptime_float = floatMin(f16); // prev: 0.00006103515625 +pub const f32_min: comptime_float = floatMin(f32); // prev: 1.17549435082228750797e-38 +pub const f64_min: comptime_float = floatMin(f64); // prev: 2.2250738585072014e-308 +pub const f80_min = floatMin(f80); // prev: make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }) +pub const f128_min = floatMin(f128); // prev: @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)) +pub const f16_max: comptime_float = floatMax(f16); // prev: 65504 +pub const f32_max: comptime_float = floatMax(f32); // prev: 3.40282346638528859812e+38 +pub const f64_max: comptime_float = floatMax(f64); // prev: 1.79769313486231570815e+308 +pub const f80_max = floatMax(f80); // prev: make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }) +pub const f128_max = floatMax(f128); // prev: @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +pub const f16_epsilon: comptime_float = floatEps(f16); // prev: 0.0009765625 +pub const f32_epsilon: comptime_float = floatEps(f32); // prev: 1.1920928955078125e-07 +pub const f64_epsilon: comptime_float = floatEps(f64); // prev: 2.22044604925031308085e-16 +pub const f80_epsilon = floatEps(f80); // prev: make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }) +pub const f128_epsilon = floatEps(f128); // prev: @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)) +pub const f16_toint: comptime_float = 1.0 / f16_epsilon; // same as before +pub const f32_toint: comptime_float = 1.0 / f32_epsilon; // same as before +pub const f64_toint: comptime_float = 1.0 / f64_epsilon; // same as before +pub const f80_toint = 1.0 / f80_epsilon; // same as before +pub const f128_toint = 1.0 / f128_epsilon; // same as before +pub const inf_u16 = @bitCast(u16, inf_f16); // prev: @as(u16, 0x7C00) +pub const inf_f16 = inf(f16); // prev: @bitCast(f16, inf_u16) +pub const inf_u32 = @bitCast(u32, inf_f32); // prev: @as(u32, 0x7F800000) +pub const inf_f32 = inf(f32); // prev: @bitCast(f32, inf_u32) +pub const inf_u64 = @bitCast(u64, inf_f64); // prev: @as(u64, 0x7FF << 52) +pub const inf_f64 = inf(f64); // prev: @bitCast(f64, inf_u64) +pub const inf_f80 = inf(f80); // prev: make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }) +pub const inf_u128 = @bitCast(u128, inf_f128); // prev: @as(u128, 0x7fff0000000000000000000000000000) +pub const inf_f128 = inf(f128); // prev: @bitCast(f128, inf_u128) +pub const epsilon = floatEps; +// End of "soft deprecated" section pub const nan_u16 = @as(u16, 0x7C01); pub const nan_f16 = @bitCast(f16, nan_u16); @@ -75,28 +91,18 @@ pub const nan_f16 = @bitCast(f16, nan_u16); pub const qnan_u16 = @as(u16, 0x7E00); pub const qnan_f16 = @bitCast(f16, qnan_u16); -pub const inf_u16 = @as(u16, 0x7C00); -pub const inf_f16 = @bitCast(f16, inf_u16); - pub const nan_u32 = @as(u32, 0x7F800001); pub const nan_f32 = @bitCast(f32, nan_u32); pub const qnan_u32 = @as(u32, 0x7FC00000); pub const qnan_f32 = @bitCast(f32, qnan_u32); -pub const inf_u32 = @as(u32, 0x7F800000); -pub const inf_f32 = @bitCast(f32, inf_u32); - pub const nan_u64 = @as(u64, 0x7FF << 52) | 1; pub const nan_f64 = @bitCast(f64, nan_u64); pub const qnan_u64 = @as(u64, 0x7ff8000000000000); pub const qnan_f64 = @bitCast(f64, qnan_u64); -pub const inf_u64 = @as(u64, 0x7FF << 52); -pub const inf_f64 = @bitCast(f64, inf_u64); - -pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }); pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); @@ -106,12 +112,8 @@ pub const nan_f128 = @bitCast(f128, nan_u128); pub const qnan_u128 = @as(u128, 0x7fff8000000000000000000000000000); pub const qnan_f128 = @bitCast(f128, qnan_u128); -pub const inf_u128 = @as(u128, 0x7fff0000000000000000000000000000); -pub const inf_f128 = @bitCast(f128, inf_u128); - pub const nan = @import("math/nan.zig").nan; pub const snan = @import("math/nan.zig").snan; -pub const inf = @import("math/inf.zig").inf; /// Performs an approximate comparison of two floating point values `x` and `y`. /// Returns true if the absolute difference between them is less or equal than @@ -119,7 +121,7 @@ pub const inf = @import("math/inf.zig").inf; /// /// The `tolerance` parameter is the absolute tolerance used when determining if /// the two numbers are close enough; a good value for this parameter is a small -/// multiple of `epsilon(T)`. +/// multiple of `floatEps(T)`. /// /// Note that this function is recommended for comparing small numbers /// around zero; using `approxEqRel` is suggested otherwise. @@ -146,7 +148,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool { /// /// The `tolerance` parameter is the relative tolerance used when determining if /// the two numbers are close enough; a good value for this parameter is usually -/// `sqrt(epsilon(T))`, meaning that the two numbers are considered equal if at +/// `sqrt(floatEps(T))`, meaning that the two numbers are considered equal if at /// least half of the digits are equal. /// /// Note that for comparisons of small numbers around zero this function won't @@ -177,25 +179,19 @@ pub fn approxEq(comptime T: type, x: T, y: T, tolerance: T) bool { test "approxEqAbs and approxEqRel" { inline for ([_]type{ f16, f32, f64, f128 }) |T| { - const eps_value = comptime epsilon(T); + const eps_value = comptime floatEps(T); const sqrt_eps_value = comptime sqrt(eps_value); const nan_value = comptime nan(T); const inf_value = comptime inf(T); - const min_value: T = switch (T) { - f16 => f16_min, - f32 => f32_min, - f64 => f64_min, - f128 => f128_min, - else => unreachable, - }; + const min_value = comptime floatMin(T); try testing.expect(approxEqAbs(T, 0.0, 0.0, eps_value)); try testing.expect(approxEqAbs(T, -0.0, -0.0, eps_value)); try testing.expect(approxEqAbs(T, 0.0, -0.0, eps_value)); try testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value)); try testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value)); - try testing.expect(!approxEqAbs(T, 1.0 + 2 * epsilon(T), 1.0, eps_value)); - try testing.expect(approxEqAbs(T, 1.0 + 1 * epsilon(T), 1.0, eps_value)); + try testing.expect(!approxEqAbs(T, 1.0 + 2 * eps_value, 1.0, eps_value)); + try testing.expect(approxEqAbs(T, 1.0 + 1 * eps_value, 1.0, eps_value)); try testing.expect(!approxEqRel(T, 1.0, nan_value, sqrt_eps_value)); try testing.expect(!approxEqRel(T, nan_value, nan_value, sqrt_eps_value)); try testing.expect(approxEqRel(T, inf_value, inf_value, sqrt_eps_value)); @@ -294,36 +290,6 @@ test { std.testing.refAllDecls(@This()); } -/// Returns the number of bits in the mantissa of floating point type -/// T. -pub fn floatMantissaBits(comptime T: type) comptime_int { - assert(@typeInfo(T) == .Float); - - return switch (@typeInfo(T).Float.bits) { - 16 => 10, - 32 => 23, - 64 => 52, - 80 => 64, - 128 => 112, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; -} - -/// Returns the number of bits in the exponent of floating point type -/// T. -pub fn floatExponentBits(comptime T: type) comptime_int { - assert(@typeInfo(T) == .Float); - - return switch (@typeInfo(T).Float.bits) { - 16 => 5, - 32 => 8, - 64 => 11, - 80 => 15, - 128 => 15, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; -} - /// Given two types, returns the smallest one which is capable of holding the /// full range of the minimum value. pub fn Min(comptime A: type, comptime B: type) type { @@ -437,7 +403,7 @@ test "max3" { try testing.expect(max3(@as(i32, 2), @as(i32, 1), @as(i32, 0)) == 2); } -/// Limit val to the inclusive range [lower, upper]. +/// Limit val to the inclusive range [lower, upper]. pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) { assert(lower <= upper); return max(lower, min(val, upper)); @@ -1221,12 +1187,6 @@ test "lossyCast" { try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32)); } -test "f64_min" { - const f64_min_u64 = 0x0010000000000000; - const fmin: f64 = f64_min; - try testing.expect(@bitCast(u64, fmin) == f64_min_u64); -} - /// Returns the maximum value of integer type T. pub fn maxInt(comptime T: type) comptime_int { const info = @typeInfo(T); diff --git a/lib/std/math/__rem_pio2.zig b/lib/std/math/__rem_pio2.zig index c8cb8fb644..f01d8fe94a 100644 --- a/lib/std/math/__rem_pio2.zig +++ b/lib/std/math/__rem_pio2.zig @@ -7,7 +7,7 @@ const std = @import("../std.zig"); const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large; const math = std.math; -const toint = 1.5 / math.epsilon(f64); +const toint = 1.5 / math.floatEps(f64); // pi/4 const pio4 = 0x1.921fb54442d18p-1; // invpio2: 53 bits of 2/pi diff --git a/lib/std/math/__rem_pio2f.zig b/lib/std/math/__rem_pio2f.zig index 9f78e18d36..5867fb30d9 100644 --- a/lib/std/math/__rem_pio2f.zig +++ b/lib/std/math/__rem_pio2f.zig @@ -7,7 +7,7 @@ const std = @import("../std.zig"); const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large; const math = std.math; -const toint = 1.5 / math.epsilon(f64); +const toint = 1.5 / math.floatEps(f64); // pi/4 const pio4 = 0x1.921fb6p-1; // invpio2: 53 bits of 2/pi diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 0303aa12f7..7ca9b9ccb7 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1750,8 +1750,8 @@ pub const Mutable = struct { /// Read the value of `x` from `buffer` /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value. /// - /// The contents of `buffer` are interpreted as if they were the contents of - /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian` + /// The contents of `buffer` are interpreted as if they were the contents of + /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian` /// and any required padding bits are expected on the MSB end. pub fn readTwosComplement( x: *Mutable, diff --git a/lib/std/math/ceil.zig b/lib/std/math/ceil.zig index cf3adcf5b5..686be8e58d 100644 --- a/lib/std/math/ceil.zig +++ b/lib/std/math/ceil.zig @@ -62,6 +62,8 @@ fn ceil32(x: f32) f32 { } fn ceil64(x: f64) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + const u = @bitCast(u64, x); const e = (u >> 52) & 0x7FF; var y: f64 = undefined; @@ -71,9 +73,9 @@ fn ceil64(x: f64) f64 { } if (u >> 63 != 0) { - y = x - math.f64_toint + math.f64_toint - x; + y = x - f64_toint + f64_toint - x; } else { - y = x + math.f64_toint - math.f64_toint - x; + y = x + f64_toint - f64_toint - x; } if (e <= 0x3FF - 1) { @@ -91,6 +93,8 @@ fn ceil64(x: f64) f64 { } fn ceil128(x: f128) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + const u = @bitCast(u128, x); const e = (u >> 112) & 0x7FFF; var y: f128 = undefined; @@ -98,9 +102,9 @@ fn ceil128(x: f128) f128 { if (e >= 0x3FFF + 112 or x == 0) return x; if (u >> 127 != 0) { - y = x - math.f128_toint + math.f128_toint - x; + y = x - f128_toint + f128_toint - x; } else { - y = x + math.f128_toint - math.f128_toint - x; + y = x + f128_toint - f128_toint - x; } if (e <= 0x3FFF - 1) { diff --git a/lib/std/math/complex/exp.zig b/lib/std/math/complex/exp.zig index f2ae28d3fd..ce25025ded 100644 --- a/lib/std/math/complex/exp.zig +++ b/lib/std/math/complex/exp.zig @@ -120,7 +120,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { } test "complex.cexp32" { - const tolerance_f32 = math.sqrt(math.epsilon(f32)); + const tolerance_f32 = math.sqrt(math.floatEps(f32)); { const a = Complex(f32).init(5, 3); @@ -140,7 +140,7 @@ test "complex.cexp32" { } test "complex.cexp64" { - const tolerance_f64 = math.sqrt(math.epsilon(f64)); + const tolerance_f64 = math.sqrt(math.floatEps(f64)); { const a = Complex(f64).init(5, 3); diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index ed344999ee..851af3e62e 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -79,7 +79,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) { if (iy >= 0x7f800000) { return Complex(f32).init(x * x, x * (y - y)); } - return Complex(f32).init(x * math.cos(y), math.inf_f32 * math.sin(y)); + return Complex(f32).init(x * math.cos(y), math.inf(f32) * math.sin(y)); } return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y)); @@ -146,7 +146,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) { if (iy >= 0x7ff00000) { return Complex(f64).init(x * x, x * (y - y)); } - return Complex(f64).init(x * math.cos(y), math.inf_f64 * math.sin(y)); + return Complex(f64).init(x * math.cos(y), math.inf(f64) * math.sin(y)); } return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y)); diff --git a/lib/std/math/epsilon.zig b/lib/std/math/epsilon.zig deleted file mode 100644 index 7f78be1aab..0000000000 --- a/lib/std/math/epsilon.zig +++ /dev/null @@ -1,15 +0,0 @@ -const math = @import("../math.zig"); - -/// Returns the machine epsilon for type T. -/// This is the smallest value of type T that satisfies the inequality 1.0 + -/// epsilon != 1.0. -pub fn epsilon(comptime T: type) T { - return switch (T) { - f16 => math.f16_epsilon, - f32 => math.f32_epsilon, - f64 => math.f64_epsilon, - f80 => math.f80_epsilon, - f128 => math.f128_epsilon, - else => @compileError("epsilon not implemented for " ++ @typeName(T)), - }; -} diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig index f098d531cf..f1bb4be7e7 100644 --- a/lib/std/math/fabs.zig +++ b/lib/std/math/fabs.zig @@ -1,13 +1,6 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/fabsf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/fabs.c - const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns the absolute value of x. /// @@ -16,86 +9,37 @@ const maxInt = std.math.maxInt; /// - fabs(nan) = nan pub fn fabs(x: anytype) @TypeOf(x) { const T = @TypeOf(x); - return switch (T) { - f16 => fabs16(x), - f32 => fabs32(x), - f64 => fabs64(x), - f128 => fabs128(x), - else => @compileError("fabs not implemented for " ++ @typeName(T)), - }; -} - -fn fabs16(x: f16) f16 { - var u = @bitCast(u16, x); - u &= maxInt(u16) >> 1; - return @bitCast(f16, u); -} - -fn fabs32(x: f32) f32 { - var u = @bitCast(u32, x); - u &= maxInt(u32) >> 1; - return @bitCast(f32, u); -} + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("fabs not implemented for " ++ @typeName(T)); + } -fn fabs64(x: f64) f64 { - var u = @bitCast(u64, x); - u &= maxInt(u64) >> 1; - return @bitCast(f64, u); -} + const float_bits = @bitCast(TBits, x); + const remove_sign = ~@as(TBits, 0) >> 1; -fn fabs128(x: f128) f128 { - var u = @bitCast(u128, x); - u &= maxInt(u128) >> 1; - return @bitCast(f128, u); + return @bitCast(T, float_bits & remove_sign); } test "math.fabs" { - try expect(fabs(@as(f16, 1.0)) == fabs16(1.0)); - try expect(fabs(@as(f32, 1.0)) == fabs32(1.0)); - try expect(fabs(@as(f64, 1.0)) == fabs64(1.0)); - try expect(fabs(@as(f128, 1.0)) == fabs128(1.0)); -} - -test "math.fabs16" { - try expect(fabs16(1.0) == 1.0); - try expect(fabs16(-1.0) == 1.0); -} - -test "math.fabs32" { - try expect(fabs32(1.0) == 1.0); - try expect(fabs32(-1.0) == 1.0); -} - -test "math.fabs64" { - try expect(fabs64(1.0) == 1.0); - try expect(fabs64(-1.0) == 1.0); -} - -test "math.fabs128" { - try expect(fabs128(1.0) == 1.0); - try expect(fabs128(-1.0) == 1.0); -} - -test "math.fabs16.special" { - try expect(math.isPositiveInf(fabs(math.inf(f16)))); - try expect(math.isPositiveInf(fabs(-math.inf(f16)))); - try expect(math.isNan(fabs(math.nan(f16)))); -} - -test "math.fabs32.special" { - try expect(math.isPositiveInf(fabs(math.inf(f32)))); - try expect(math.isPositiveInf(fabs(-math.inf(f32)))); - try expect(math.isNan(fabs(math.nan(f32)))); -} - -test "math.fabs64.special" { - try expect(math.isPositiveInf(fabs(math.inf(f64)))); - try expect(math.isPositiveInf(fabs(-math.inf(f64)))); - try expect(math.isNan(fabs(math.nan(f64)))); -} - -test "math.fabs128.special" { - try expect(math.isPositiveInf(fabs(math.inf(f128)))); - try expect(math.isPositiveInf(fabs(-math.inf(f128)))); - try expect(math.isNan(fabs(math.nan(f128)))); + // TODO add support for f80 & c_longdouble here + inline for ([_]type{ f16, f32, f64, f128 }) |T| { + // normals + try expect(fabs(@as(T, 1.0)) == 1.0); + try expect(fabs(@as(T, -1.0)) == 1.0); + try expect(fabs(math.floatMin(T)) == math.floatMin(T)); + try expect(fabs(-math.floatMin(T)) == math.floatMin(T)); + try expect(fabs(math.floatMax(T)) == math.floatMax(T)); + try expect(fabs(-math.floatMax(T)) == math.floatMax(T)); + + // subnormals + try expect(fabs(@as(T, 0.0)) == 0.0); + try expect(fabs(@as(T, -0.0)) == 0.0); + try expect(fabs(math.floatTrueMin(T)) == math.floatTrueMin(T)); + try expect(fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); + + // non-finite numbers + try expect(math.isPositiveInf(fabs(math.inf(T)))); + try expect(math.isPositiveInf(fabs(-math.inf(T)))); + try expect(math.isNan(fabs(math.nan(T)))); + } } diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig new file mode 100644 index 0000000000..72c7f086ac --- /dev/null +++ b/lib/std/math/float.zig @@ -0,0 +1,110 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const expect = std.testing.expect; + +/// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. +fn mantissaOne(comptime T: type) comptime_int { + return if (@typeInfo(T).Float.bits == 80) 1 << floatFractionalBits(T) else 0; +} + +/// Creates floating point type T from an unbiased exponent and raw mantissa. +fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T { + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + const biased_exponent = @as(TBits, exponent + floatExponentMax(T)); + return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa)); +} + +/// Returns the number of bits in the exponent of floating point type T. +pub fn floatExponentBits(comptime T: type) comptime_int { + assert(@typeInfo(T) == .Float); + + return switch (@typeInfo(T).Float.bits) { + 16 => 5, + 32 => 8, + 64 => 11, + 80 => 15, + 128 => 15, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +/// Returns the number of bits in the mantissa of floating point type T. +pub fn floatMantissaBits(comptime T: type) comptime_int { + assert(@typeInfo(T) == .Float); + + return switch (@typeInfo(T).Float.bits) { + 16 => 10, + 32 => 23, + 64 => 52, + 80 => 64, + 128 => 112, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +/// Returns the number of fractional bits in the mantissa of floating point type T. +pub fn floatFractionalBits(comptime T: type) comptime_int { + assert(@typeInfo(T) == .Float); + + // standard IEEE floats have an implicit 0.m or 1.m integer part + // f80 is special and has an explicitly stored bit in the MSB + // this function corresponds to `MANT_DIG - 1' from C + return switch (@typeInfo(T).Float.bits) { + 16 => 10, + 32 => 23, + 64 => 52, + 80 => 63, + 128 => 112, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +/// Returns the minimum exponent that can represent +/// a normalised value in floating point type T. +pub fn floatExponentMin(comptime T: type) comptime_int { + return -floatExponentMax(T) + 1; +} + +/// Returns the maximum exponent that can represent +/// a normalised value in floating point type T. +pub fn floatExponentMax(comptime T: type) comptime_int { + return (1 << (floatExponentBits(T) - 1)) - 1; +} + +/// Returns the smallest subnormal number representable in floating point type T. +pub fn floatTrueMin(comptime T: type) T { + return reconstructFloat(T, floatExponentMin(T) - 1, 1); +} + +/// Returns the smallest normal number representable in floating point type T. +pub fn floatMin(comptime T: type) T { + return reconstructFloat(T, floatExponentMin(T), mantissaOne(T)); +} + +/// Returns the largest normal number representable in floating point type T. +pub fn floatMax(comptime T: type) T { + const all1s_mantissa = (1 << floatMantissaBits(T)) - 1; + return reconstructFloat(T, floatExponentMax(T), all1s_mantissa); +} + +/// Returns the machine epsilon of floating point type T. +pub fn floatEps(comptime T: type) T { + return reconstructFloat(T, -floatFractionalBits(T), mantissaOne(T)); +} + +/// Returns the value inf for floating point type T. +pub fn inf(comptime T: type) T { + return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T)); +} + +test "std.math.float" { + inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { + // (1 +) for the sign bit, since it is separate from the other bits + const size = 1 + floatExponentBits(T) + floatMantissaBits(T); + try expect(@bitSizeOf(T) == size); + + // for machine epsilon, assert expmin <= -prec <= expmax + try expect(floatExponentMin(T) <= -floatFractionalBits(T)); + try expect(-floatFractionalBits(T) <= floatExponentMax(T)); + } +} diff --git a/lib/std/math/floor.zig b/lib/std/math/floor.zig index d6761ba77e..ab5ca3583b 100644 --- a/lib/std/math/floor.zig +++ b/lib/std/math/floor.zig @@ -98,6 +98,8 @@ fn floor32(x: f32) f32 { } fn floor64(x: f64) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + const u = @bitCast(u64, x); const e = (u >> 52) & 0x7FF; var y: f64 = undefined; @@ -107,9 +109,9 @@ fn floor64(x: f64) f64 { } if (u >> 63 != 0) { - y = x - math.f64_toint + math.f64_toint - x; + y = x - f64_toint + f64_toint - x; } else { - y = x + math.f64_toint - math.f64_toint - x; + y = x + f64_toint - f64_toint - x; } if (e <= 0x3FF - 1) { @@ -127,6 +129,8 @@ fn floor64(x: f64) f64 { } fn floor128(x: f128) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + const u = @bitCast(u128, x); const e = (u >> 112) & 0x7FFF; var y: f128 = undefined; @@ -134,9 +138,9 @@ fn floor128(x: f128) f128 { if (e >= 0x3FFF + 112 or x == 0) return x; if (u >> 127 != 0) { - y = x - math.f128_toint + math.f128_toint - x; + y = x - f128_toint + f128_toint - x; } else { - y = x + math.f128_toint - math.f128_toint - x; + y = x + f128_toint - f128_toint - x; } if (e <= 0x3FFF - 1) { diff --git a/lib/std/math/fma.zig b/lib/std/math/fma.zig index 7ef734cf4e..7afc6e557e 100644 --- a/lib/std/math/fma.zig +++ b/lib/std/math/fma.zig @@ -68,7 +68,7 @@ fn fma64(x: f64, y: f64, z: f64) f64 { if (spread <= 53 * 2) { zs = math.scalbn(zs, -spread); } else { - zs = math.copysign(f64, math.f64_min, zs); + zs = math.copysign(f64, math.floatMin(f64), zs); } const xy = dd_mul(xs, ys); @@ -277,7 +277,7 @@ fn fma128(x: f128, y: f128, z: f128) f128 { if (spread <= 113 * 2) { zs = math.scalbn(zs, -spread); } else { - zs = math.copysign(f128, math.f128_min, zs); + zs = math.copysign(f128, math.floatMin(f128), zs); } const xy = dd_mul128(xs, ys); diff --git a/lib/std/math/inf.zig b/lib/std/math/inf.zig deleted file mode 100644 index fd7d7c4380..0000000000 --- a/lib/std/math/inf.zig +++ /dev/null @@ -1,14 +0,0 @@ -const std = @import("../std.zig"); -const math = std.math; - -/// Returns value inf for the type T. -pub fn inf(comptime T: type) T { - return switch (T) { - f16 => math.inf_f16, - f32 => math.inf_f32, - f64 => math.inf_f64, - f80 => math.inf_f80, - f128 => math.inf_f128, - else => @compileError("inf not implemented for " ++ @typeName(T)), - }; -} diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig index 762fb39991..e9314213ce 100644 --- a/lib/std/math/isfinite.zig +++ b/lib/std/math/isfinite.zig @@ -1,59 +1,40 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns whether x is a finite value. pub fn isFinite(x: anytype) bool { const T = @TypeOf(x); - switch (T) { - f16 => { - const bits = @bitCast(u16, x); - return bits & 0x7FFF < 0x7C00; - }, - f32 => { - const bits = @bitCast(u32, x); - return bits & 0x7FFFFFFF < 0x7F800000; - }, - f64 => { - const bits = @bitCast(u64, x); - return bits & (maxInt(u64) >> 1) < (0x7FF << 52); - }, - f128 => { - const bits = @bitCast(u128, x); - return bits & (maxInt(u128) >> 1) < (0x7FFF << 112); - }, - else => { - @compileError("isFinite not implemented for " ++ @typeName(T)); - }, + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("isFinite not implemented for " ++ @typeName(T)); } + const remove_sign = ~@as(TBits, 0) >> 1; + return @bitCast(TBits, x) & remove_sign < @bitCast(TBits, math.inf(T)); } test "math.isFinite" { - try expect(isFinite(@as(f16, 0.0))); - try expect(isFinite(@as(f16, -0.0))); - try expect(isFinite(@as(f32, 0.0))); - try expect(isFinite(@as(f32, -0.0))); - try expect(isFinite(@as(f64, 0.0))); - try expect(isFinite(@as(f64, -0.0))); - try expect(isFinite(@as(f128, 0.0))); - try expect(isFinite(@as(f128, -0.0))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - try expect(!isFinite(math.inf(f16))); - try expect(!isFinite(-math.inf(f16))); - try expect(!isFinite(math.inf(f32))); - try expect(!isFinite(-math.inf(f32))); - try expect(!isFinite(math.inf(f64))); - try expect(!isFinite(-math.inf(f64))); - try expect(!isFinite(math.inf(f128))); - try expect(!isFinite(-math.inf(f128))); + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + // normals + try expect(isFinite(@as(T, 1.0))); + try expect(isFinite(-@as(T, 1.0))); - try expect(!isFinite(math.nan(f16))); - try expect(!isFinite(-math.nan(f16))); - try expect(!isFinite(math.nan(f32))); - try expect(!isFinite(-math.nan(f32))); - try expect(!isFinite(math.nan(f64))); - try expect(!isFinite(-math.nan(f64))); - try expect(!isFinite(math.nan(f128))); - try expect(!isFinite(-math.nan(f128))); + // zero & subnormals + try expect(isFinite(@as(T, 0.0))); + try expect(isFinite(@as(T, -0.0))); + try expect(isFinite(math.floatTrueMin(T))); + + // other float limits + try expect(isFinite(math.floatMin(T))); + try expect(isFinite(math.floatMax(T))); + + // inf & nan + try expect(!isFinite(math.inf(T))); + try expect(!isFinite(-math.inf(T))); + try expect(!isFinite(math.nan(T))); + try expect(!isFinite(-math.nan(T))); + } } diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index cadaa8d937..e88b9810b6 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -1,131 +1,66 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns whether x is an infinity, ignoring sign. pub fn isInf(x: anytype) bool { const T = @TypeOf(x); - switch (T) { - f16 => { - const bits = @bitCast(u16, x); - return bits & 0x7FFF == 0x7C00; - }, - f32 => { - const bits = @bitCast(u32, x); - return bits & 0x7FFFFFFF == 0x7F800000; - }, - f64 => { - const bits = @bitCast(u64, x); - return bits & (maxInt(u64) >> 1) == (0x7FF << 52); - }, - f128 => { - const bits = @bitCast(u128, x); - return bits & (maxInt(u128) >> 1) == (0x7FFF << 112); - }, - else => { - @compileError("isInf not implemented for " ++ @typeName(T)); - }, + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("isInf not implemented for " ++ @typeName(T)); } + const remove_sign = ~@as(TBits, 0) >> 1; + return @bitCast(TBits, x) & remove_sign == @bitCast(TBits, math.inf(T)); } /// Returns whether x is an infinity with a positive sign. pub fn isPositiveInf(x: anytype) bool { - const T = @TypeOf(x); - switch (T) { - f16 => { - return @bitCast(u16, x) == 0x7C00; - }, - f32 => { - return @bitCast(u32, x) == 0x7F800000; - }, - f64 => { - return @bitCast(u64, x) == 0x7FF << 52; - }, - f128 => { - return @bitCast(u128, x) == 0x7FFF << 112; - }, - else => { - @compileError("isPositiveInf not implemented for " ++ @typeName(T)); - }, - } + return x == math.inf(@TypeOf(x)); } /// Returns whether x is an infinity with a negative sign. pub fn isNegativeInf(x: anytype) bool { - const T = @TypeOf(x); - switch (T) { - f16 => { - return @bitCast(u16, x) == 0xFC00; - }, - f32 => { - return @bitCast(u32, x) == 0xFF800000; - }, - f64 => { - return @bitCast(u64, x) == 0xFFF << 52; - }, - f128 => { - return @bitCast(u128, x) == 0xFFFF << 112; - }, - else => { - @compileError("isNegativeInf not implemented for " ++ @typeName(T)); - }, - } + return x == -math.inf(@TypeOf(x)); } test "math.isInf" { - try expect(!isInf(@as(f16, 0.0))); - try expect(!isInf(@as(f16, -0.0))); - try expect(!isInf(@as(f32, 0.0))); - try expect(!isInf(@as(f32, -0.0))); - try expect(!isInf(@as(f64, 0.0))); - try expect(!isInf(@as(f64, -0.0))); - try expect(!isInf(@as(f128, 0.0))); - try expect(!isInf(@as(f128, -0.0))); - try expect(isInf(math.inf(f16))); - try expect(isInf(-math.inf(f16))); - try expect(isInf(math.inf(f32))); - try expect(isInf(-math.inf(f32))); - try expect(isInf(math.inf(f64))); - try expect(isInf(-math.inf(f64))); - try expect(isInf(math.inf(f128))); - try expect(isInf(-math.inf(f128))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!isInf(@as(T, 0.0))); + try expect(!isInf(@as(T, -0.0))); + try expect(isInf(math.inf(T))); + try expect(isInf(-math.inf(T))); + try expect(!isInf(math.nan(T))); + try expect(!isInf(-math.nan(T))); + } } test "math.isPositiveInf" { - try expect(!isPositiveInf(@as(f16, 0.0))); - try expect(!isPositiveInf(@as(f16, -0.0))); - try expect(!isPositiveInf(@as(f32, 0.0))); - try expect(!isPositiveInf(@as(f32, -0.0))); - try expect(!isPositiveInf(@as(f64, 0.0))); - try expect(!isPositiveInf(@as(f64, -0.0))); - try expect(!isPositiveInf(@as(f128, 0.0))); - try expect(!isPositiveInf(@as(f128, -0.0))); - try expect(isPositiveInf(math.inf(f16))); - try expect(!isPositiveInf(-math.inf(f16))); - try expect(isPositiveInf(math.inf(f32))); - try expect(!isPositiveInf(-math.inf(f32))); - try expect(isPositiveInf(math.inf(f64))); - try expect(!isPositiveInf(-math.inf(f64))); - try expect(isPositiveInf(math.inf(f128))); - try expect(!isPositiveInf(-math.inf(f128))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!isPositiveInf(@as(T, 0.0))); + try expect(!isPositiveInf(@as(T, -0.0))); + try expect(isPositiveInf(math.inf(T))); + try expect(!isPositiveInf(-math.inf(T))); + try expect(!isInf(math.nan(T))); + try expect(!isInf(-math.nan(T))); + } } test "math.isNegativeInf" { - try expect(!isNegativeInf(@as(f16, 0.0))); - try expect(!isNegativeInf(@as(f16, -0.0))); - try expect(!isNegativeInf(@as(f32, 0.0))); - try expect(!isNegativeInf(@as(f32, -0.0))); - try expect(!isNegativeInf(@as(f64, 0.0))); - try expect(!isNegativeInf(@as(f64, -0.0))); - try expect(!isNegativeInf(@as(f128, 0.0))); - try expect(!isNegativeInf(@as(f128, -0.0))); - try expect(!isNegativeInf(math.inf(f16))); - try expect(isNegativeInf(-math.inf(f16))); - try expect(!isNegativeInf(math.inf(f32))); - try expect(isNegativeInf(-math.inf(f32))); - try expect(!isNegativeInf(math.inf(f64))); - try expect(isNegativeInf(-math.inf(f64))); - try expect(!isNegativeInf(math.inf(f128))); - try expect(isNegativeInf(-math.inf(f128))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!isNegativeInf(@as(T, 0.0))); + try expect(!isNegativeInf(@as(T, -0.0))); + try expect(!isNegativeInf(math.inf(T))); + try expect(isNegativeInf(-math.inf(T))); + try expect(!isInf(math.nan(T))); + try expect(!isInf(-math.nan(T))); + } } diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index 88e186a3c9..42b2e1c188 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -1,57 +1,54 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; -// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1). +/// Returns whether x is neither zero, subnormal, infinity, or NaN. pub fn isNormal(x: anytype) bool { const T = @TypeOf(x); - switch (T) { - f16 => { - const bits = @bitCast(u16, x); - return (bits +% (1 << 10)) & (maxInt(u16) >> 1) >= (1 << 11); - }, - f32 => { - const bits = @bitCast(u32, x); - return (bits +% (1 << 23)) & (maxInt(u32) >> 1) >= (1 << 24); - }, - f64 => { - const bits = @bitCast(u64, x); - return (bits +% (1 << 52)) & (maxInt(u64) >> 1) >= (1 << 53); - }, - f128 => { - const bits = @bitCast(u128, x); - return (bits +% (1 << 112)) & (maxInt(u128) >> 1) >= (1 << 113); - }, - else => { - @compileError("isNormal not implemented for " ++ @typeName(T)); - }, + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("isNormal not implemented for " ++ @typeName(T)); } + + const increment_exp = 1 << math.floatMantissaBits(T); + const remove_sign = ~@as(TBits, 0) >> 1; + + // We add 1 to the exponent, and if it overflows to 0 or becomes 1, + // then it was all zeroes (subnormal) or all ones (special, inf/nan). + // The sign bit is removed because all ones would overflow into it. + // For f80, even though it has an explicit integer part stored, + // the exponent effectively takes priority if mismatching. + const value = @bitCast(TBits, x) +% increment_exp; + return value & remove_sign >= (increment_exp << 1); } test "math.isNormal" { - try expect(!isNormal(math.nan(f16))); - try expect(!isNormal(math.nan(f32))); - try expect(!isNormal(math.nan(f64))); - try expect(!isNormal(math.nan(f128))); - try expect(!isNormal(-math.nan(f16))); - try expect(!isNormal(-math.nan(f32))); - try expect(!isNormal(-math.nan(f64))); - try expect(!isNormal(-math.nan(f128))); - try expect(!isNormal(math.inf(f16))); - try expect(!isNormal(math.inf(f32))); - try expect(!isNormal(math.inf(f64))); - try expect(!isNormal(math.inf(f128))); - try expect(!isNormal(-math.inf(f16))); - try expect(!isNormal(-math.inf(f32))); - try expect(!isNormal(-math.inf(f64))); - try expect(!isNormal(-math.inf(f128))); - try expect(!isNormal(@as(f16, 0))); - try expect(!isNormal(@as(f32, 0))); - try expect(!isNormal(@as(f64, 0))); - try expect(!isNormal(@as(f128, 0))); - try expect(isNormal(@as(f16, 1.0))); - try expect(isNormal(@as(f32, 1.0))); - try expect(isNormal(@as(f64, 1.0))); - try expect(isNormal(@as(f128, 1.0))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + // TODO add `c_longdouble' when math.inf(T) supports it + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + + // normals + try expect(isNormal(@as(T, 1.0))); + try expect(isNormal(math.floatMin(T))); + try expect(isNormal(math.floatMax(T))); + + // subnormals + try expect(!isNormal(@as(T, -0.0))); + try expect(!isNormal(@as(T, 0.0))); + try expect(!isNormal(@as(T, math.floatTrueMin(T)))); + + // largest subnormal + try expect(!isNormal(@bitCast(T, ~(~@as(TBits, 0) << math.floatFractionalBits(T))))); + + // non-finite numbers + try expect(!isNormal(-math.inf(T))); + try expect(!isNormal(math.inf(T))); + try expect(!isNormal(math.nan(T))); + + // overflow edge-case (described in implementation, also see #10133) + try expect(!isNormal(@bitCast(T, ~@as(TBits, 0)))); + } } diff --git a/lib/std/math/ldexp.zig b/lib/std/math/ldexp.zig index 228bd7dd39..0934244c65 100644 --- a/lib/std/math/ldexp.zig +++ b/lib/std/math/ldexp.zig @@ -15,22 +15,22 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) { var shift = n; const T = @TypeOf(base); - const IntT = std.meta.Int(.unsigned, @bitSizeOf(T)); + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); if (@typeInfo(T) != .Float) { @compileError("ldexp not implemented for " ++ @typeName(T)); } const mantissa_bits = math.floatMantissaBits(T); - const exponent_bits = math.floatExponentBits(T); - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const exponent_min = 1 - exponent_bias; - const exponent_max = exponent_bias; + const exponent_min = math.floatExponentMin(T); + const exponent_max = math.floatExponentMax(T); + + const exponent_bias = exponent_max; // fix double rounding errors in subnormal ranges // https://git.musl-libc.org/cgit/musl/commit/src/math/ldexp.c?id=8c44a060243f04283ca68dad199aab90336141db const scale_min_expo = exponent_min + mantissa_bits + 1; - const scale_min = @bitCast(T, @as(IntT, scale_min_expo + exponent_bias) << mantissa_bits); - const scale_max = @bitCast(T, @intCast(IntT, exponent_max + exponent_bias) << mantissa_bits); + const scale_min = @bitCast(T, @as(TBits, scale_min_expo + exponent_bias) << mantissa_bits); + const scale_max = @bitCast(T, @intCast(TBits, exponent_max + exponent_bias) << mantissa_bits); // scale `shift` within floating point limits, if possible // second pass is possible due to subnormal range @@ -53,10 +53,12 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) { } } - return base * @bitCast(T, @intCast(IntT, shift + exponent_bias) << mantissa_bits); + return base * @bitCast(T, @intCast(TBits, shift + exponent_bias) << mantissa_bits); } test "math.ldexp" { + // TODO derive the various constants here with new maths API + // basic usage try expect(ldexp(@as(f16, 1.5), 4) == 24.0); try expect(ldexp(@as(f32, 1.5), 4) == 24.0); @@ -73,20 +75,20 @@ test "math.ldexp" { try expect(math.isNormal(ldexp(@as(f128, 1.0), -16382))); try expect(!math.isNormal(ldexp(@as(f128, 1.0), -16383))); // unreliable due to lack of native f16 support, see talk on PR #8733 - // try expect(ldexp(@as(f16, 0x1.1FFp-1), -14 - 9) == math.f16_true_min); - try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.f32_true_min); - try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.f64_true_min); - try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.f128_true_min); + // try expect(ldexp(@as(f16, 0x1.1FFp-1), -14 - 9) == math.floatTrueMin(f16)); + try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32)); + try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64)); + try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128)); // float limits - try expect(ldexp(@as(f32, math.f32_max), -128 - 149) > 0.0); - try expect(ldexp(@as(f32, math.f32_max), -128 - 149 - 1) == 0.0); - try expect(!math.isPositiveInf(ldexp(@as(f16, math.f16_true_min), 15 + 24))); - try expect(math.isPositiveInf(ldexp(@as(f16, math.f16_true_min), 15 + 24 + 1))); - try expect(!math.isPositiveInf(ldexp(@as(f32, math.f32_true_min), 127 + 149))); - try expect(math.isPositiveInf(ldexp(@as(f32, math.f32_true_min), 127 + 149 + 1))); - try expect(!math.isPositiveInf(ldexp(@as(f64, math.f64_true_min), 1023 + 1074))); - try expect(math.isPositiveInf(ldexp(@as(f64, math.f64_true_min), 1023 + 1074 + 1))); - try expect(!math.isPositiveInf(ldexp(@as(f128, math.f128_true_min), 16383 + 16494))); - try expect(math.isPositiveInf(ldexp(@as(f128, math.f128_true_min), 16383 + 16494 + 1))); + try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0); + try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24 + 1))); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149 + 1))); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074 + 1))); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494 + 1))); } diff --git a/lib/std/math/round.zig b/lib/std/math/round.zig index c948431a35..be33a9cfbd 100644 --- a/lib/std/math/round.zig +++ b/lib/std/math/round.zig @@ -29,6 +29,8 @@ pub fn round(x: anytype) @TypeOf(x) { } fn round32(x_: f32) f32 { + const f32_toint = 1.0 / math.floatEps(f32); + var x = x_; const u = @bitCast(u32, x); const e = (u >> 23) & 0xFF; @@ -41,11 +43,11 @@ fn round32(x_: f32) f32 { x = -x; } if (e < 0x7F - 1) { - math.doNotOptimizeAway(x + math.f32_toint); + math.doNotOptimizeAway(x + f32_toint); return 0 * @bitCast(f32, u); } - y = x + math.f32_toint - math.f32_toint - x; + y = x + f32_toint - f32_toint - x; if (y > 0.5) { y = y + x - 1; } else if (y <= -0.5) { @@ -62,6 +64,8 @@ fn round32(x_: f32) f32 { } fn round64(x_: f64) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + var x = x_; const u = @bitCast(u64, x); const e = (u >> 52) & 0x7FF; @@ -74,11 +78,11 @@ fn round64(x_: f64) f64 { x = -x; } if (e < 0x3ff - 1) { - math.doNotOptimizeAway(x + math.f64_toint); + math.doNotOptimizeAway(x + f64_toint); return 0 * @bitCast(f64, u); } - y = x + math.f64_toint - math.f64_toint - x; + y = x + f64_toint - f64_toint - x; if (y > 0.5) { y = y + x - 1; } else if (y <= -0.5) { @@ -95,6 +99,8 @@ fn round64(x_: f64) f64 { } fn round128(x_: f128) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + var x = x_; const u = @bitCast(u128, x); const e = (u >> 112) & 0x7FFF; @@ -107,11 +113,11 @@ fn round128(x_: f128) f128 { x = -x; } if (e < 0x3FFF - 1) { - math.doNotOptimizeAway(x + math.f64_toint); + math.doNotOptimizeAway(x + f128_toint); return 0 * @bitCast(f128, u); } - y = x + math.f128_toint - math.f128_toint - x; + y = x + f128_toint - f128_toint - x; if (y > 0.5) { y = y + x - 1; } else if (y <= -0.5) { diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 59e93480ba..2c6d53849d 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -50,7 +50,7 @@ pub const VTable = struct { else => *const resizeProto, }, - /// Free and invalidate a buffer. `buf.len` must equal the most recent length returned by `alloc` or `resize`. + /// Free and invalidate a buffer. `buf.len` must equal the most recent length returned by `alloc` or `resize`. /// `buf_align` must equal the same value that was passed as the `ptr_align` parameter to the original `alloc` call. /// /// `ret_addr` is optionally provided as the first return address of the allocation call stack. @@ -603,7 +603,7 @@ test "allocBytes non-zero len_align" { /// allocation could not be granted this function returns `error.OutOfMemory`. /// When the size/alignment is less than or equal to the previous allocation, /// this function returns `error.OutOfMemory` when the allocator decides the client -/// would be better off keeping the extra alignment/size. +/// would be better off keeping the extra alignment/size. /// Clients will call `resizeFn` when they require the allocator to track a new alignment/size, /// and so this function should only return success when the allocator considers /// the reallocation desirable from the allocator's perspective. diff --git a/lib/std/os.zig b/lib/std/os.zig index 87fe6c7f7b..df42d14c3d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1441,7 +1441,7 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { /// Note that `cwd_init` corresponds to a Preopen directory, not necessarily /// a POSIX path. For example, "." matches a Preopen provided with `--dir=.` /// -/// This must be called before using any relative or absolute paths with `std.os` +/// This must be called before using any relative or absolute paths with `std.os` /// functions, if you are on WASI without linking libc. /// /// `alloc` must not be a temporary or leak-detecting allocator, since `std.os` @@ -1475,7 +1475,7 @@ pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { /// Resolve a relative or absolute path to an handle (`fd_t`) and a relative subpath. /// -/// For absolute paths, this automatically searches among available Preopens to find +/// For absolute paths, this automatically searches among available Preopens to find /// a match. For relative paths, it uses the "emulated" CWD. pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { // Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi @@ -5325,7 +5325,7 @@ pub fn dl_iterate_phdr( const elf_base = std.process.getBaseAddress(); const ehdr = @intToPtr(*elf.Ehdr, elf_base); // Make sure the base address points to an ELF image. - assert(mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")); + assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC)); const n_phdr = ehdr.e_phnum; const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr]; diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 1ca83dada6..8598c7b3be 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -447,7 +447,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void { // Find the ELF header const elf_header = @intToPtr(*elf.Ehdr, reloc_addr - phdr.p_offset); // Validate the magic - if (!mem.eql(u8, elf_header.e_ident[0..4], "\x7fELF")) return error.BadElfMagic; + if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic; // Consistency check if (elf_header.e_phnum != info.dlpi_phnum) return error.FailedConsistencyCheck; diff --git a/lib/std/os/uefi/pool_allocator.zig b/lib/std/os/uefi/pool_allocator.zig index 3294621a18..544179aad3 100644 --- a/lib/std/os/uefi/pool_allocator.zig +++ b/lib/std/os/uefi/pool_allocator.zig @@ -90,7 +90,7 @@ const pool_allocator_vtable = Allocator.VTable{ .free = UefiPoolAllocator.free, }; -/// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`. +/// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`. pub const raw_pool_allocator = Allocator{ .ptr = undefined, .vtable = &raw_pool_allocator_table, diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index ee1e72f094..4774375b3c 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -67,7 +67,7 @@ pub const BootServices = extern struct { /// Reinstalls a protocol interface on a device handle reinstallProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, old_interface: *anyopaque, new_interface: *anyopaque) callconv(.C) Status, - /// Removes a protocol interface from a device handle. Usage of + /// Removes a protocol interface from a device handle. Usage of /// uninstallMultipleProtocolInterfaces is recommended over this. uninstallProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, interface: *anyopaque) callconv(.C) Status, diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig index 599f02ca89..a07fc13af1 100644 --- a/lib/std/packed_int_array.zig +++ b/lib/std/packed_int_array.zig @@ -182,7 +182,7 @@ pub fn PackedIntIo(comptime Int: type, comptime endian: Endian) type { /// Creates a bit-packed array of `Int`. Non-byte-multiple integers /// will take up less memory in PackedIntArray than in a normal array. -/// Elements are packed using native endianess and without storing any +/// Elements are packed using native endianess and without storing any /// meta data. PackedArray(i3, 8) will occupy exactly 3 bytes /// of memory. pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type { diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 9f82a15d28..52d9f59951 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -100,7 +100,7 @@ pub fn PriorityQueue(comptime T: type, comptime Context: type, comptime compareF const item = self.items[index]; self.items[index] = last; self.len -= 1; - siftDown(self, 0); + siftDown(self, index); return item; } @@ -460,22 +460,25 @@ test "std.PriorityQueue: remove at index" { var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); - try queue.add(3); - try queue.add(2); - try queue.add(1); + const items = [_]u32{ 2, 1, 8, 9, 3, 4, 5 }; + for (items) |e| { + _ = try queue.add(e); + } var it = queue.iterator(); - var elem = it.next(); var idx: usize = 0; - const two_idx = while (elem != null) : (elem = it.next()) { - if (elem.? == 2) + const two_idx = while (it.next()) |elem| { + if (elem == 2) break idx; idx += 1; } else unreachable; - + var sorted_items = [_]u32{ 1, 3, 4, 5, 8, 9 }; try expectEqual(queue.removeIndex(two_idx), 2); - try expectEqual(queue.remove(), 1); - try expectEqual(queue.remove(), 3); + + var i: usize = 0; + while (queue.removeOrNull()) |n| : (i += 1) { + try expectEqual(n, sorted_items[i]); + } try expectEqual(queue.removeOrNull(), null); } diff --git a/lib/std/rand.zig b/lib/std/rand.zig index cfac15a1fb..0c3a0fd2ba 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -66,9 +66,7 @@ pub const Random = struct { /// Returns a random value from an enum, evenly distributed. pub fn enumValue(r: Random, comptime EnumType: type) EnumType { - if (comptime !std.meta.trait.is(.Enum)(EnumType)) { - @compileError("Random.enumValue requires an enum type, not a " ++ @typeName(EnumType)); - } + comptime assert(@typeInfo(EnumType) == .Enum); // We won't use int -> enum casting because enum elements can have // arbitrary values. Instead we'll randomly pick one of the type's values. diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index 59b7e53395..5c18d0023b 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -28,7 +28,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { } else { // Generate a value in the range [1, 2) and scale into (0, 1) const repr = (0x3ff << 52) | (bits >> 12); - break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0); + break :blk @bitCast(f64, repr) - (1.0 - math.floatEps(f64) / 2.0); } }; diff --git a/lib/std/simd.zig b/lib/std/simd.zig index 02e08b0dad..a30622aef6 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -23,7 +23,9 @@ pub fn suggestVectorSizeForCpu(comptime T: type, cpu: std.Target.Cpu) ?usize { const element_bit_size = std.math.max(8, std.math.ceilPowerOfTwo(T, @bitSizeOf(T))); return @divExact(vector_bit_size, element_bit_size); }, - else => @compileError("No vector sizes for this CPU architecture have yet been recommended"), + else => { + return null; + }, } } @@ -296,7 +298,7 @@ test "vector searching" { pub fn prefixScanWithFunc( comptime hop: isize, vec: anytype, - /// The error type that `func` might return. Set this to `void` if `func` doesn't return an error union. + /// The error type that `func` might return. Set this to `void` if `func` doesn't return an error union. comptime ErrorType: type, comptime func: fn (@TypeOf(vec), @TypeOf(vec)) if (ErrorType == void) @TypeOf(vec) else ErrorType!@TypeOf(vec), /// When one operand of the operation performed by `func` is this value, the result must equal the other operand. diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index a017266304..b54a1e980e 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -291,100 +291,149 @@ comptime { 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 }); + // Integral -> Float Conversion - const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf; + // Conversion to f32 + const __floatsisf = @import("compiler_rt/floatXiYf.zig").__floatsisf; + @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); + const __floatunsisf = @import("compiler_rt/floatXiYf.zig").__floatunsisf; @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); - const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf; + + const __floatundisf = @import("compiler_rt/floatXiYf.zig").__floatundisf; @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); - const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf; + const __floatdisf = @import("compiler_rt/floatXiYf.zig").__floatdisf; + @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); + + const __floattisf = @import("compiler_rt/floatXiYf.zig").__floattisf; + @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); + const __floatuntisf = @import("compiler_rt/floatXiYf.zig").__floatuntisf; + @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); + + // Conversion to f64 + const __floatsidf = @import("compiler_rt/floatXiYf.zig").__floatsidf; + @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); + const __floatunsidf = @import("compiler_rt/floatXiYf.zig").__floatunsidf; @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); - const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf; + + const __floatdidf = @import("compiler_rt/floatXiYf.zig").__floatdidf; + @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); + const __floatundidf = @import("compiler_rt/floatXiYf.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; + const __floattidf = @import("compiler_rt/floatXiYf.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 __floatuntidf = @import("compiler_rt/floatXiYf.zig").__floatuntidf; + @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); - const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf; - @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); - const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf; + // Conversion to f80 + const __floatsixf = @import("compiler_rt/floatXiYf.zig").__floatsixf; + @export(__floatsixf, .{ .name = "__floatsixf", .linkage = linkage }); + const __floatunsixf = @import("compiler_rt/floatXiYf.zig").__floatunsixf; + @export(__floatunsixf, .{ .name = "__floatunsixf", .linkage = linkage }); + + const __floatdixf = @import("compiler_rt/floatXiYf.zig").__floatdixf; + @export(__floatdixf, .{ .name = "__floatdixf", .linkage = linkage }); + const __floatundixf = @import("compiler_rt/floatXiYf.zig").__floatundixf; + @export(__floatundixf, .{ .name = "__floatundixf", .linkage = linkage }); + + const __floattixf = @import("compiler_rt/floatXiYf.zig").__floattixf; + @export(__floattixf, .{ .name = "__floattixf", .linkage = linkage }); + const __floatuntixf = @import("compiler_rt/floatXiYf.zig").__floatuntixf; + @export(__floatuntixf, .{ .name = "__floatuntixf", .linkage = linkage }); + + // Conversion to f128 + const __floatsitf = @import("compiler_rt/floatXiYf.zig").__floatsitf; + @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); + const __floatunsitf = @import("compiler_rt/floatXiYf.zig").__floatunsitf; @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); - const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf; + const __floatditf = @import("compiler_rt/floatXiYf.zig").__floatditf; + @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); + const __floatunditf = @import("compiler_rt/floatXiYf.zig").__floatunditf; + @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); + + const __floattitf = @import("compiler_rt/floatXiYf.zig").__floattitf; + @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); + const __floatuntitf = @import("compiler_rt/floatXiYf.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 }); + // Float -> Integral Conversion - // Integral / floating point conversion (part 2/2) - const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi; + // Conversion from f32 + const __fixsfsi = @import("compiler_rt/fixXfYi.zig").__fixsfsi; + @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); + const __fixunssfsi = @import("compiler_rt/fixXfYi.zig").__fixunssfsi; @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); - const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi; + + const __fixsfdi = @import("compiler_rt/fixXfYi.zig").__fixsfdi; + @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); + const __fixunssfdi = @import("compiler_rt/fixXfYi.zig").__fixunssfdi; @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); - const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti; + + const __fixsfti = @import("compiler_rt/fixXfYi.zig").__fixsfti; + @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); + const __fixunssfti = @import("compiler_rt/fixXfYi.zig").__fixunssfti; @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); - const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi; + // Conversion from f64 + const __fixdfsi = @import("compiler_rt/fixXfYi.zig").__fixdfsi; + @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); + const __fixunsdfsi = @import("compiler_rt/fixXfYi.zig").__fixunsdfsi; @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); - const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi; + + const __fixdfdi = @import("compiler_rt/fixXfYi.zig").__fixdfdi; + @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); + const __fixunsdfdi = @import("compiler_rt/fixXfYi.zig").__fixunsdfdi; @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); - const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti; + + const __fixdfti = @import("compiler_rt/fixXfYi.zig").__fixdfti; + @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); + const __fixunsdfti = @import("compiler_rt/fixXfYi.zig").__fixunsdfti; @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); - const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi; + // Conversion from f80 + const __fixxfsi = @import("compiler_rt/fixXfYi.zig").__fixxfsi; + @export(__fixxfsi, .{ .name = "__fixxfsi", .linkage = linkage }); + const __fixunsxfsi = @import("compiler_rt/fixXfYi.zig").__fixunsxfsi; + @export(__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = linkage }); + + const __fixxfdi = @import("compiler_rt/fixXfYi.zig").__fixxfdi; + @export(__fixxfdi, .{ .name = "__fixxfdi", .linkage = linkage }); + const __fixunsxfdi = @import("compiler_rt/fixXfYi.zig").__fixunsxfdi; + @export(__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = linkage }); + + const __fixxfti = @import("compiler_rt/fixXfYi.zig").__fixxfti; + @export(__fixxfti, .{ .name = "__fixxfti", .linkage = linkage }); + const __fixunsxfti = @import("compiler_rt/fixXfYi.zig").__fixunsxfti; + @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = linkage }); + + // Conversion from f128 + const __fixtfsi = @import("compiler_rt/fixXfYi.zig").__fixtfsi; + @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); + const __fixunstfsi = @import("compiler_rt/fixXfYi.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; + const __fixtfdi = @import("compiler_rt/fixXfYi.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; + const __fixunstfdi = @import("compiler_rt/fixXfYi.zig").__fixunstfdi; + @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); + + const __fixtfti = @import("compiler_rt/fixXfYi.zig").__fixtfti; @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); + const __fixunstfti = @import("compiler_rt/fixXfYi.zig").__fixunstfti; + @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; @export(__udivmoddi4, .{ .name = "__udivmoddi4", .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 }); + if (is_darwin) { const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; @export(__isPlatformVersionAtLeast, .{ .name = "__isPlatformVersionAtLeast", .linkage = linkage }); @@ -553,19 +602,19 @@ comptime { 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; + const __aeabi_i2d = @import("compiler_rt/floatXiYf.zig").__aeabi_i2d; @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); - const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d; + const __aeabi_l2d = @import("compiler_rt/floatXiYf.zig").__aeabi_l2d; @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); - const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f; + const __aeabi_l2f = @import("compiler_rt/floatXiYf.zig").__aeabi_l2f; @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); - const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d; + const __aeabi_ui2d = @import("compiler_rt/floatXiYf.zig").__aeabi_ui2d; @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); - const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d; + const __aeabi_ul2d = @import("compiler_rt/floatXiYf.zig").__aeabi_ul2d; @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); - const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f; + const __aeabi_ui2f = @import("compiler_rt/floatXiYf.zig").__aeabi_ui2f; @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); - const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f; + const __aeabi_ul2f = @import("compiler_rt/floatXiYf.zig").__aeabi_ul2f; @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; @@ -581,17 +630,17 @@ comptime { 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; + const __aeabi_f2ulz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2ulz; @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); - const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz; + const __aeabi_d2ulz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2ulz; @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); - const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz; + const __aeabi_f2lz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2lz; @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); - const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz; + const __aeabi_d2lz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2lz; @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); - const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz; + const __aeabi_d2uiz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2uiz; @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; @@ -599,7 +648,7 @@ comptime { 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; + const __aeabi_i2f = @import("compiler_rt/floatXiYf.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 }); @@ -613,12 +662,12 @@ comptime { 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; + const __aeabi_f2uiz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2uiz; @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); - const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz; + const __aeabi_f2iz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2iz; @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); - const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz; + const __aeabi_d2iz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2iz; @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; @@ -672,9 +721,13 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - const fmodl = @import("compiler_rt/floatfmodl.zig").fmodl; if (!is_test) { @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); + if (long_double_is_f128) { + @export(fmodl, .{ .name = "fmodq", .linkage = linkage }); + } else { + @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); + } @export(floorf, .{ .name = "floorf", .linkage = linkage }); @export(floor, .{ .name = "floor", .linkage = linkage }); @@ -828,6 +881,14 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble { return math.ceil(x); } +const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq; +fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { + if (!long_double_is_f128) { + @panic("TODO implement this"); + } + return @floatCast(c_longdouble, fmodq(x, y)); +} + // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { diff --git a/lib/std/special/compiler_rt/divtf3_test.zig b/lib/std/special/compiler_rt/divtf3_test.zig index f426f827e8..62204057d4 100644 --- a/lib/std/special/compiler_rt/divtf3_test.zig +++ b/lib/std/special/compiler_rt/divtf3_test.zig @@ -34,8 +34,12 @@ test "divtf3" { try test__divtf3(math.qnan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); // NaN / any = NaN try test__divtf3(math.nan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); - // inf / any = inf - try test__divtf3(math.inf_f128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); + // inf / any(except inf and nan) = inf + try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); + // inf / inf = nan + try test__divtf3(math.inf(f128), math.inf(f128), 0x7fff800000000000, 0); + // inf / nan = nan + try test__divtf3(math.inf(f128), math.nan(f128), 0x7fff800000000000, 0); try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2); try test__divtf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9, 0x3fd5b2af3f828c9b, 0x40e51f64cde8b1f2); diff --git a/lib/std/special/compiler_rt/fixXfYi.zig b/lib/std/special/compiler_rt/fixXfYi.zig new file mode 100644 index 0000000000..01832ec56f --- /dev/null +++ b/lib/std/special/compiler_rt/fixXfYi.zig @@ -0,0 +1,224 @@ +const std = @import("std"); +const math = std.math; +const Log2Int = math.Log2Int; +const is_test = @import("builtin").is_test; + +pub inline fn fixXfYi(comptime I: type, a: anytype) I { + @setRuntimeSafety(is_test); + + const F = @TypeOf(a); + const float_bits = @typeInfo(F).Float.bits; + const int_bits = @typeInfo(I).Int.bits; + const rep_t = std.meta.Int(.unsigned, float_bits); + const sig_bits = math.floatMantissaBits(F); + const exp_bits = math.floatExponentBits(F); + const fractional_bits = math.floatFractionalBits(F); + + const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0; + const max_exp = (1 << (exp_bits - 1)); + const exp_bias = max_exp - 1; + const sig_mask = (@as(rep_t, 1) << sig_bits) - 1; + + // Break a into sign, exponent, significand + const a_rep: rep_t = @bitCast(rep_t, a); + const negative = (a_rep >> (float_bits - 1)) != 0; + const exponent = @intCast(i32, (a_rep << 1) >> (sig_bits + 1)) - exp_bias; + const significand: rep_t = (a_rep & sig_mask) | implicit_bit; + + // If the exponent is negative, the result rounds to zero. + if (exponent < 0) return 0; + + // If the value is too large for the integer type, saturate. + switch (@typeInfo(I).Int.signedness) { + .unsigned => { + if (negative) return 0; + if (@intCast(c_uint, exponent) >= @minimum(int_bits, max_exp)) return math.maxInt(I); + }, + .signed => if (@intCast(c_uint, exponent) >= @minimum(int_bits - 1, max_exp)) { + return if (negative) math.minInt(I) else math.maxInt(I); + }, + } + + // If 0 <= exponent < sig_bits, right shift to get the result. + // Otherwise, shift left. + var result: I = undefined; + if (exponent < fractional_bits) { + result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_bits - exponent)); + } else { + result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_bits); + } + + if ((@typeInfo(I).Int.signedness == .signed) and negative) + return ~result +% 1; + return result; +} + +// Conversion from f16 + +pub fn __fixhfsi(a: f16) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunshfsi(a: f16) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixhfdi(a: f16) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunshfdi(a: f16) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixhfti(a: f16) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunshfti(a: f16) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f32 + +pub fn __fixsfsi(a: f32) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunssfsi(a: f32) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixsfdi(a: f32) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunssfdi(a: f32) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixsfti(a: f32) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunssfti(a: f32) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f64 + +pub fn __fixdfsi(a: f64) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixdfdi(a: f64) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixdfti(a: f64) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunsdfti(a: f64) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f80 + +pub fn __fixxfsi(a: f80) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunsxfsi(a: f80) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixxfdi(a: f80) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunsxfdi(a: f80) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixxfti(a: f80) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunsxfti(a: f80) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f128 + +pub fn __fixtfsi(a: f128) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunstfsi(a: f128) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixtfdi(a: f128) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunstfdi(a: f128) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixtfti(a: f128) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunstfti(a: f128) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f32 + +pub fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { + return fixXfYi(i32, a); +} + +pub fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { + return fixXfYi(u32, a); +} + +pub fn __aeabi_f2lz(a: f32) callconv(.AAPCS) i64 { + return fixXfYi(i64, a); +} + +pub fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { + return fixXfYi(u64, a); +} + +// Conversion from f64 + +pub fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { + return fixXfYi(i32, a); +} + +pub fn __aeabi_d2uiz(a: f64) callconv(.AAPCS) u32 { + return fixXfYi(u32, a); +} + +pub fn __aeabi_d2lz(a: f64) callconv(.AAPCS) i64 { + return fixXfYi(i64, a); +} + +pub fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { + return fixXfYi(u64, a); +} + +test { + _ = @import("fixXfYi_test.zig"); +} diff --git a/lib/std/special/compiler_rt/fixXfYi_test.zig b/lib/std/special/compiler_rt/fixXfYi_test.zig new file mode 100644 index 0000000000..00ed455609 --- /dev/null +++ b/lib/std/special/compiler_rt/fixXfYi_test.zig @@ -0,0 +1,948 @@ +const std = @import("std"); +const testing = std.testing; +const math = std.math; +const fixXfYi = @import("fixXfYi.zig").fixXfYi; + +// Conversion from f32 +const __fixsfsi = @import("fixXfYi.zig").__fixsfsi; +const __fixunssfsi = @import("fixXfYi.zig").__fixunssfsi; +const __fixsfdi = @import("fixXfYi.zig").__fixsfdi; +const __fixunssfdi = @import("fixXfYi.zig").__fixunssfdi; +const __fixsfti = @import("fixXfYi.zig").__fixsfti; +const __fixunssfti = @import("fixXfYi.zig").__fixunssfti; + +// Conversion from f64 +const __fixdfsi = @import("fixXfYi.zig").__fixdfsi; +const __fixunsdfsi = @import("fixXfYi.zig").__fixunsdfsi; +const __fixdfdi = @import("fixXfYi.zig").__fixdfdi; +const __fixunsdfdi = @import("fixXfYi.zig").__fixunsdfdi; +const __fixdfti = @import("fixXfYi.zig").__fixdfti; +const __fixunsdfti = @import("fixXfYi.zig").__fixunsdfti; + +// Conversion from f128 +const __fixtfsi = @import("fixXfYi.zig").__fixtfsi; +const __fixunstfsi = @import("fixXfYi.zig").__fixunstfsi; +const __fixtfdi = @import("fixXfYi.zig").__fixtfdi; +const __fixunstfdi = @import("fixXfYi.zig").__fixunstfdi; +const __fixtfti = @import("fixXfYi.zig").__fixtfti; +const __fixunstfti = @import("fixXfYi.zig").__fixunstfti; + +fn test__fixsfsi(a: f32, expected: i32) !void { + const x = __fixsfsi(a); + try testing.expect(x == expected); +} + +fn test__fixunssfsi(a: f32, expected: u32) !void { + const x = __fixunssfsi(a); + try testing.expect(x == expected); +} + +test "fixsfsi" { + try test__fixsfsi(-math.floatMax(f32), math.minInt(i32)); + + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + try test__fixsfsi(-0x1.0000000000000p+127, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + try test__fixsfsi(-0x1.0000000000001p+63, -0x80000000); + try test__fixsfsi(-0x1.0000000000000p+63, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + try test__fixsfsi(-0x1.FFFFFEp+62, -0x80000000); + try test__fixsfsi(-0x1.FFFFFCp+62, -0x80000000); + + try test__fixsfsi(-0x1.000000p+31, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFp+30, -0x80000000); + try test__fixsfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixsfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixsfsi(-2.01, -2); + try test__fixsfsi(-2.0, -2); + try test__fixsfsi(-1.99, -1); + try test__fixsfsi(-1.0, -1); + try test__fixsfsi(-0.99, 0); + try test__fixsfsi(-0.5, 0); + try test__fixsfsi(-math.floatMin(f32), 0); + try test__fixsfsi(0.0, 0); + try test__fixsfsi(math.floatMin(f32), 0); + try test__fixsfsi(0.5, 0); + try test__fixsfsi(0.99, 0); + try test__fixsfsi(1.0, 1); + try test__fixsfsi(1.5, 1); + try test__fixsfsi(1.99, 1); + try test__fixsfsi(2.0, 2); + try test__fixsfsi(2.01, 2); + + try test__fixsfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixsfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixsfsi(0x1.FFFFFFp+30, 0x7FFFFFFF); + try test__fixsfsi(0x1.000000p+31, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + try test__fixsfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + try test__fixsfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + try test__fixsfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + try test__fixsfsi(math.floatMax(f32), math.maxInt(i32)); +} + +test "fixunssfsi" { + try test__fixunssfsi(0.0, 0); + + try test__fixunssfsi(0.5, 0); + try test__fixunssfsi(0.99, 0); + try test__fixunssfsi(1.0, 1); + try test__fixunssfsi(1.5, 1); + try test__fixunssfsi(1.99, 1); + try test__fixunssfsi(2.0, 2); + try test__fixunssfsi(2.01, 2); + try test__fixunssfsi(-0.5, 0); + try test__fixunssfsi(-0.99, 0); + + try test__fixunssfsi(-1.0, 0); + try test__fixunssfsi(-1.5, 0); + try test__fixunssfsi(-1.99, 0); + try test__fixunssfsi(-2.0, 0); + try test__fixunssfsi(-2.01, 0); + + try test__fixunssfsi(0x1.000000p+31, 0x80000000); + try test__fixunssfsi(0x1.000000p+32, 0xFFFFFFFF); + try test__fixunssfsi(0x1.FFFFFEp+31, 0xFFFFFF00); + try test__fixunssfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixunssfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + + try test__fixunssfsi(-0x1.FFFFFEp+30, 0); + try test__fixunssfsi(-0x1.FFFFFCp+30, 0); +} + +fn test__fixsfdi(a: f32, expected: i64) !void { + const x = __fixsfdi(a); + try testing.expect(x == expected); +} + +fn test__fixunssfdi(a: f32, expected: u64) !void { + const x = __fixunssfdi(a); + try testing.expect(x == expected); +} + +test "fixsfdi" { + try test__fixsfdi(-math.floatMax(f32), math.minInt(i64)); + + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + try test__fixsfdi(-0x1.0000000000000p+127, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + try test__fixsfdi(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixsfdi(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); + + try test__fixsfdi(-0x1.FFFFFFp+62, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixsfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixsfdi(-2.01, -2); + try test__fixsfdi(-2.0, -2); + try test__fixsfdi(-1.99, -1); + try test__fixsfdi(-1.0, -1); + try test__fixsfdi(-0.99, 0); + try test__fixsfdi(-0.5, 0); + try test__fixsfdi(-math.floatMin(f32), 0); + try test__fixsfdi(0.0, 0); + try test__fixsfdi(math.floatMin(f32), 0); + try test__fixsfdi(0.5, 0); + try test__fixsfdi(0.99, 0); + try test__fixsfdi(1.0, 1); + try test__fixsfdi(1.5, 1); + try test__fixsfdi(1.99, 1); + try test__fixsfdi(2.0, 2); + try test__fixsfdi(2.01, 2); + + try test__fixsfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixsfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixsfdi(0x1.FFFFFFp+62, 0x7FFFFFFFFFFFFFFF); + + try test__fixsfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + try test__fixsfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + try test__fixsfdi(math.floatMax(f32), math.maxInt(i64)); +} + +test "fixunssfdi" { + try test__fixunssfdi(0.0, 0); + + try test__fixunssfdi(0.5, 0); + try test__fixunssfdi(0.99, 0); + try test__fixunssfdi(1.0, 1); + try test__fixunssfdi(1.5, 1); + try test__fixunssfdi(1.99, 1); + try test__fixunssfdi(2.0, 2); + try test__fixunssfdi(2.01, 2); + try test__fixunssfdi(-0.5, 0); + try test__fixunssfdi(-0.99, 0); + + try test__fixunssfdi(-1.0, 0); + try test__fixunssfdi(-1.5, 0); + try test__fixunssfdi(-1.99, 0); + try test__fixunssfdi(-2.0, 0); + try test__fixunssfdi(-2.01, 0); + + try test__fixunssfdi(0x1.FFFFFEp+63, 0xFFFFFF0000000000); + try test__fixunssfdi(0x1.000000p+63, 0x8000000000000000); + try test__fixunssfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunssfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunssfdi(-0x1.FFFFFEp+62, 0x0000000000000000); + try test__fixunssfdi(-0x1.FFFFFCp+62, 0x0000000000000000); +} + +fn test__fixsfti(a: f32, expected: i128) !void { + const x = __fixsfti(a); + try testing.expect(x == expected); +} + +fn test__fixunssfti(a: f32, expected: u128) !void { + const x = __fixunssfti(a); + try testing.expect(x == expected); +} + +test "fixsfti" { + try test__fixsfti(-math.floatMax(f32), math.minInt(i128)); + + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + try test__fixsfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+126, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFEp+126, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFF0000000p+126, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFE0000000p+126, -0x7FFFFF80000000000000000000000000); + try test__fixsfti(-0x1.FFFFFC0000000p+126, -0x7FFFFF00000000000000000000000000); + + try test__fixsfti(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixsfti(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); + + try test__fixsfti(-0x1.FFFFFFp+62, -0x8000000000000000); + try test__fixsfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixsfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixsfti(-0x1.000000p+31, -0x80000000); + try test__fixsfti(-0x1.FFFFFFp+30, -0x80000000); + try test__fixsfti(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixsfti(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixsfti(-2.01, -2); + try test__fixsfti(-2.0, -2); + try test__fixsfti(-1.99, -1); + try test__fixsfti(-1.0, -1); + try test__fixsfti(-0.99, 0); + try test__fixsfti(-0.5, 0); + try test__fixsfti(-math.floatMin(f32), 0); + try test__fixsfti(0.0, 0); + try test__fixsfti(math.floatMin(f32), 0); + try test__fixsfti(0.5, 0); + try test__fixsfti(0.99, 0); + try test__fixsfti(1.0, 1); + try test__fixsfti(1.5, 1); + try test__fixsfti(1.99, 1); + try test__fixsfti(2.0, 2); + try test__fixsfti(2.01, 2); + + try test__fixsfti(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixsfti(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixsfti(0x1.FFFFFFp+30, 0x80000000); + try test__fixsfti(0x1.000000p+31, 0x80000000); + + try test__fixsfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixsfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixsfti(0x1.FFFFFFp+62, 0x8000000000000000); + + try test__fixsfti(0x1.FFFFFFFFFFFFEp+62, 0x8000000000000000); + try test__fixsfti(0x1.FFFFFFFFFFFFFp+62, 0x8000000000000000); + try test__fixsfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixsfti(0x1.0000000000001p+63, 0x8000000000000000); + + try test__fixsfti(0x1.FFFFFC0000000p+126, 0x7FFFFF00000000000000000000000000); + try test__fixsfti(0x1.FFFFFE0000000p+126, 0x7FFFFF80000000000000000000000000); + try test__fixsfti(0x1.FFFFFF0000000p+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + try test__fixsfti(math.floatMax(f32), math.maxInt(i128)); +} + +test "fixunssfti" { + try test__fixunssfti(0.0, 0); + + try test__fixunssfti(0.5, 0); + try test__fixunssfti(0.99, 0); + try test__fixunssfti(1.0, 1); + try test__fixunssfti(1.5, 1); + try test__fixunssfti(1.99, 1); + try test__fixunssfti(2.0, 2); + try test__fixunssfti(2.01, 2); + try test__fixunssfti(-0.5, 0); + try test__fixunssfti(-0.99, 0); + + try test__fixunssfti(-1.0, 0); + try test__fixunssfti(-1.5, 0); + try test__fixunssfti(-1.99, 0); + try test__fixunssfti(-2.0, 0); + try test__fixunssfti(-2.01, 0); + + try test__fixunssfti(0x1.FFFFFEp+63, 0xFFFFFF0000000000); + try test__fixunssfti(0x1.000000p+63, 0x8000000000000000); + try test__fixunssfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunssfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixunssfti(0x1.FFFFFEp+127, 0xFFFFFF00000000000000000000000000); + try test__fixunssfti(0x1.000000p+127, 0x80000000000000000000000000000000); + try test__fixunssfti(0x1.FFFFFEp+126, 0x7FFFFF80000000000000000000000000); + try test__fixunssfti(0x1.FFFFFCp+126, 0x7FFFFF00000000000000000000000000); + + try test__fixunssfti(-0x1.FFFFFEp+62, 0x0000000000000000); + try test__fixunssfti(-0x1.FFFFFCp+62, 0x0000000000000000); + try test__fixunssfti(-0x1.FFFFFEp+126, 0x0000000000000000); + try test__fixunssfti(-0x1.FFFFFCp+126, 0x0000000000000000); + try test__fixunssfti(math.floatMax(f32), 0xffffff00000000000000000000000000); + try test__fixunssfti(math.inf(f32), math.maxInt(u128)); +} + +fn test__fixdfsi(a: f64, expected: i32) !void { + const x = __fixdfsi(a); + try testing.expect(x == expected); +} + +fn test__fixunsdfsi(a: f64, expected: u32) !void { + const x = __fixunsdfsi(a); + try testing.expect(x == expected); +} + +test "fixdfsi" { + try test__fixdfsi(-math.floatMax(f64), math.minInt(i32)); + + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + try test__fixdfsi(-0x1.0000000000000p+127, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + try test__fixdfsi(-0x1.0000000000001p+63, -0x80000000); + try test__fixdfsi(-0x1.0000000000000p+63, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + try test__fixdfsi(-0x1.FFFFFEp+62, -0x80000000); + try test__fixdfsi(-0x1.FFFFFCp+62, -0x80000000); + + try test__fixdfsi(-0x1.000000p+31, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + try test__fixdfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + + try test__fixdfsi(-2.01, -2); + try test__fixdfsi(-2.0, -2); + try test__fixdfsi(-1.99, -1); + try test__fixdfsi(-1.0, -1); + try test__fixdfsi(-0.99, 0); + try test__fixdfsi(-0.5, 0); + try test__fixdfsi(-math.floatMin(f64), 0); + try test__fixdfsi(0.0, 0); + try test__fixdfsi(math.floatMin(f64), 0); + try test__fixdfsi(0.5, 0); + try test__fixdfsi(0.99, 0); + try test__fixdfsi(1.0, 1); + try test__fixdfsi(1.5, 1); + try test__fixdfsi(1.99, 1); + try test__fixdfsi(2.0, 2); + try test__fixdfsi(2.01, 2); + + try test__fixdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixdfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); + try test__fixdfsi(0x1.000000p+31, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + try test__fixdfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + try test__fixdfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + try test__fixdfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + try test__fixdfsi(math.floatMax(f64), math.maxInt(i32)); +} + +test "fixunsdfsi" { + try test__fixunsdfsi(0.0, 0); + + try test__fixunsdfsi(0.5, 0); + try test__fixunsdfsi(0.99, 0); + try test__fixunsdfsi(1.0, 1); + try test__fixunsdfsi(1.5, 1); + try test__fixunsdfsi(1.99, 1); + try test__fixunsdfsi(2.0, 2); + try test__fixunsdfsi(2.01, 2); + try test__fixunsdfsi(-0.5, 0); + try test__fixunsdfsi(-0.99, 0); + try test__fixunsdfsi(-1.0, 0); + try test__fixunsdfsi(-1.5, 0); + try test__fixunsdfsi(-1.99, 0); + try test__fixunsdfsi(-2.0, 0); + try test__fixunsdfsi(-2.01, 0); + + try test__fixunsdfsi(0x1.000000p+31, 0x80000000); + try test__fixunsdfsi(0x1.000000p+32, 0xFFFFFFFF); + try test__fixunsdfsi(0x1.FFFFFEp+31, 0xFFFFFF00); + try test__fixunsdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixunsdfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + + try test__fixunsdfsi(-0x1.FFFFFEp+30, 0); + try test__fixunsdfsi(-0x1.FFFFFCp+30, 0); + + try test__fixunsdfsi(0x1.FFFFFFFEp+31, 0xFFFFFFFF); + try test__fixunsdfsi(0x1.FFFFFFFC00000p+30, 0x7FFFFFFF); + try test__fixunsdfsi(0x1.FFFFFFF800000p+30, 0x7FFFFFFE); +} + +fn test__fixdfdi(a: f64, expected: i64) !void { + const x = __fixdfdi(a); + try testing.expect(x == expected); +} + +fn test__fixunsdfdi(a: f64, expected: u64) !void { + const x = __fixunsdfdi(a); + try testing.expect(x == expected); +} + +test "fixdfdi" { + try test__fixdfdi(-math.floatMax(f64), math.minInt(i64)); + + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + try test__fixdfdi(-0x1.0000000000000p+127, -0x8000000000000000); + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + try test__fixdfdi(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixdfdi(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixdfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixdfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixdfdi(-2.01, -2); + try test__fixdfdi(-2.0, -2); + try test__fixdfdi(-1.99, -1); + try test__fixdfdi(-1.0, -1); + try test__fixdfdi(-0.99, 0); + try test__fixdfdi(-0.5, 0); + try test__fixdfdi(-math.floatMin(f64), 0); + try test__fixdfdi(0.0, 0); + try test__fixdfdi(math.floatMin(f64), 0); + try test__fixdfdi(0.5, 0); + try test__fixdfdi(0.99, 0); + try test__fixdfdi(1.0, 1); + try test__fixdfdi(1.5, 1); + try test__fixdfdi(1.99, 1); + try test__fixdfdi(2.0, 2); + try test__fixdfdi(2.01, 2); + + try test__fixdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixdfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + try test__fixdfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + try test__fixdfdi(math.floatMax(f64), math.maxInt(i64)); +} + +test "fixunsdfdi" { + try test__fixunsdfdi(0.0, 0); + try test__fixunsdfdi(0.5, 0); + try test__fixunsdfdi(0.99, 0); + try test__fixunsdfdi(1.0, 1); + try test__fixunsdfdi(1.5, 1); + try test__fixunsdfdi(1.99, 1); + try test__fixunsdfdi(2.0, 2); + try test__fixunsdfdi(2.01, 2); + try test__fixunsdfdi(-0.5, 0); + try test__fixunsdfdi(-0.99, 0); + try test__fixunsdfdi(-1.0, 0); + try test__fixunsdfdi(-1.5, 0); + try test__fixunsdfdi(-1.99, 0); + try test__fixunsdfdi(-2.0, 0); + try test__fixunsdfdi(-2.01, 0); + + try test__fixunsdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunsdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunsdfdi(-0x1.FFFFFEp+62, 0); + try test__fixunsdfdi(-0x1.FFFFFCp+62, 0); + + try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); + try test__fixunsdfdi(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixunsdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + + try test__fixunsdfdi(-0x1.FFFFFFFFFFFFFp+62, 0); + try test__fixunsdfdi(-0x1.FFFFFFFFFFFFEp+62, 0); +} + +fn test__fixdfti(a: f64, expected: i128) !void { + const x = __fixdfti(a); + try testing.expect(x == expected); +} + +fn test__fixunsdfti(a: f64, expected: u128) !void { + const x = __fixunsdfti(a); + try testing.expect(x == expected); +} + +test "fixdfti" { + try test__fixdfti(-math.floatMax(f64), math.minInt(i128)); + + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + try test__fixdfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); + try test__fixdfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); + + try test__fixdfti(-0x1.0000000000001p+63, -0x8000000000000800); + try test__fixdfti(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixdfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixdfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixdfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixdfti(-2.01, -2); + try test__fixdfti(-2.0, -2); + try test__fixdfti(-1.99, -1); + try test__fixdfti(-1.0, -1); + try test__fixdfti(-0.99, 0); + try test__fixdfti(-0.5, 0); + try test__fixdfti(-math.floatMin(f64), 0); + try test__fixdfti(0.0, 0); + try test__fixdfti(math.floatMin(f64), 0); + try test__fixdfti(0.5, 0); + try test__fixdfti(0.99, 0); + try test__fixdfti(1.0, 1); + try test__fixdfti(1.5, 1); + try test__fixdfti(1.99, 1); + try test__fixdfti(2.0, 2); + try test__fixdfti(2.01, 2); + + try test__fixdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixdfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixdfti(0x1.0000000000001p+63, 0x8000000000000800); + + try test__fixdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + try test__fixdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + try test__fixdfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + try test__fixdfti(math.floatMax(f64), math.maxInt(i128)); +} + +test "fixunsdfti" { + try test__fixunsdfti(0.0, 0); + + try test__fixunsdfti(0.5, 0); + try test__fixunsdfti(0.99, 0); + try test__fixunsdfti(1.0, 1); + try test__fixunsdfti(1.5, 1); + try test__fixunsdfti(1.99, 1); + try test__fixunsdfti(2.0, 2); + try test__fixunsdfti(2.01, 2); + try test__fixunsdfti(-0.5, 0); + try test__fixunsdfti(-0.99, 0); + try test__fixunsdfti(-1.0, 0); + try test__fixunsdfti(-1.5, 0); + try test__fixunsdfti(-1.99, 0); + try test__fixunsdfti(-2.0, 0); + try test__fixunsdfti(-2.01, 0); + + try test__fixunsdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunsdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunsdfti(-0x1.FFFFFEp+62, 0); + try test__fixunsdfti(-0x1.FFFFFCp+62, 0); + + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); + try test__fixunsdfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+127, 0xFFFFFFFFFFFFF8000000000000000000); + try test__fixunsdfti(0x1.0000000000000p+127, 0x80000000000000000000000000000000); + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + try test__fixunsdfti(0x1.0000000000000p+128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixunsdfti(-0x1.FFFFFFFFFFFFFp+62, 0); + try test__fixunsdfti(-0x1.FFFFFFFFFFFFEp+62, 0); +} + +fn test__fixtfsi(a: f128, expected: i32) !void { + const x = __fixtfsi(a); + try testing.expect(x == expected); +} + +fn test__fixunstfsi(a: f128, expected: u32) !void { + const x = __fixunstfsi(a); + try testing.expect(x == expected); +} + +test "fixtfsi" { + try test__fixtfsi(-math.floatMax(f128), math.minInt(i32)); + + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + try test__fixtfsi(-0x1.0000000000000p+127, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + try test__fixtfsi(-0x1.0000000000001p+63, -0x80000000); + try test__fixtfsi(-0x1.0000000000000p+63, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + try test__fixtfsi(-0x1.FFFFFEp+62, -0x80000000); + try test__fixtfsi(-0x1.FFFFFCp+62, -0x80000000); + + try test__fixtfsi(-0x1.000000p+31, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + try test__fixtfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixtfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixtfsi(-2.01, -2); + try test__fixtfsi(-2.0, -2); + try test__fixtfsi(-1.99, -1); + try test__fixtfsi(-1.0, -1); + try test__fixtfsi(-0.99, 0); + try test__fixtfsi(-0.5, 0); + try test__fixtfsi(-math.floatMin(f32), 0); + try test__fixtfsi(0.0, 0); + try test__fixtfsi(math.floatMin(f32), 0); + try test__fixtfsi(0.5, 0); + try test__fixtfsi(0.99, 0); + try test__fixtfsi(1.0, 1); + try test__fixtfsi(1.5, 1); + try test__fixtfsi(1.99, 1); + try test__fixtfsi(2.0, 2); + try test__fixtfsi(2.01, 2); + + try test__fixtfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixtfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixtfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); + try test__fixtfsi(0x1.000000p+31, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + try test__fixtfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + try test__fixtfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + try test__fixtfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + try test__fixtfsi(math.floatMax(f128), math.maxInt(i32)); +} + +test "fixunstfsi" { + try test__fixunstfsi(math.inf(f128), 0xffffffff); + try test__fixunstfsi(0, 0x0); + try test__fixunstfsi(0x1.23456789abcdefp+5, 0x24); + try test__fixunstfsi(0x1.23456789abcdefp-3, 0x0); + try test__fixunstfsi(0x1.23456789abcdefp+20, 0x123456); + try test__fixunstfsi(0x1.23456789abcdefp+40, 0xffffffff); + try test__fixunstfsi(0x1.23456789abcdefp+256, 0xffffffff); + try test__fixunstfsi(-0x1.23456789abcdefp+3, 0x0); + + try test__fixunstfsi(0x1p+32, 0xFFFFFFFF); +} + +fn test__fixtfdi(a: f128, expected: i64) !void { + const x = __fixtfdi(a); + try testing.expect(x == expected); +} + +fn test__fixunstfdi(a: f128, expected: u64) !void { + const x = __fixunstfdi(a); + try testing.expect(x == expected); +} + +test "fixtfdi" { + try test__fixtfdi(-math.floatMax(f128), math.minInt(i64)); + + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + try test__fixtfdi(-0x1.0000000000000p+127, -0x8000000000000000); + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + try test__fixtfdi(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixtfdi(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixtfdi(-0x1.FFFFFEp+62, -0x7FFFFF8000000000); + try test__fixtfdi(-0x1.FFFFFCp+62, -0x7FFFFF0000000000); + + try test__fixtfdi(-0x1.000000p+31, -0x80000000); + try test__fixtfdi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + try test__fixtfdi(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixtfdi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixtfdi(-2.01, -2); + try test__fixtfdi(-2.0, -2); + try test__fixtfdi(-1.99, -1); + try test__fixtfdi(-1.0, -1); + try test__fixtfdi(-0.99, 0); + try test__fixtfdi(-0.5, 0); + try test__fixtfdi(-math.floatMin(f64), 0); + try test__fixtfdi(0.0, 0); + try test__fixtfdi(math.floatMin(f64), 0); + try test__fixtfdi(0.5, 0); + try test__fixtfdi(0.99, 0); + try test__fixtfdi(1.0, 1); + try test__fixtfdi(1.5, 1); + try test__fixtfdi(1.99, 1); + try test__fixtfdi(2.0, 2); + try test__fixtfdi(2.01, 2); + + try test__fixtfdi(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixtfdi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixtfdi(0x1.FFFFFFp+30, 0x7FFFFFC0); + try test__fixtfdi(0x1.000000p+31, 0x80000000); + + try test__fixtfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixtfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixtfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixtfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + try test__fixtfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + try test__fixtfdi(math.floatMax(f128), math.maxInt(i64)); +} + +test "fixunstfdi" { + try test__fixunstfdi(0.0, 0); + + try test__fixunstfdi(0.5, 0); + try test__fixunstfdi(0.99, 0); + try test__fixunstfdi(1.0, 1); + try test__fixunstfdi(1.5, 1); + try test__fixunstfdi(1.99, 1); + try test__fixunstfdi(2.0, 2); + try test__fixunstfdi(2.01, 2); + try test__fixunstfdi(-0.5, 0); + try test__fixunstfdi(-0.99, 0); + try test__fixunstfdi(-1.0, 0); + try test__fixunstfdi(-1.5, 0); + try test__fixunstfdi(-1.99, 0); + try test__fixunstfdi(-2.0, 0); + try test__fixunstfdi(-2.01, 0); + + try test__fixunstfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunstfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunstfdi(-0x1.FFFFFEp+62, 0); + try test__fixunstfdi(-0x1.FFFFFCp+62, 0); + + try test__fixunstfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixunstfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + + try test__fixunstfdi(-0x1.FFFFFFFFFFFFFp+62, 0); + try test__fixunstfdi(-0x1.FFFFFFFFFFFFEp+62, 0); + + try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFEp+63, 0xFFFFFFFFFFFFFFFF); + try test__fixunstfdi(0x1.0000000000000002p+63, 0x8000000000000001); + try test__fixunstfdi(0x1.0000000000000000p+63, 0x8000000000000000); + try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFCp+62, 0x7FFFFFFFFFFFFFFF); + try test__fixunstfdi(0x1.FFFFFFFFFFFFFFF8p+62, 0x7FFFFFFFFFFFFFFE); + try test__fixunstfdi(0x1p+64, 0xFFFFFFFFFFFFFFFF); + + try test__fixunstfdi(-0x1.0000000000000000p+63, 0); + try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFFCp+62, 0); + try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFF8p+62, 0); +} + +fn test__fixtfti(a: f128, expected: i128) !void { + const x = __fixtfti(a); + try testing.expect(x == expected); +} + +fn test__fixunstfti(a: f128, expected: u128) !void { + const x = __fixunstfti(a); + try testing.expect(x == expected); +} + +test "fixtfti" { + try test__fixtfti(-math.floatMax(f128), math.minInt(i128)); + + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + try test__fixtfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); + try test__fixtfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); + + try test__fixtfti(-0x1.0000000000001p+63, -0x8000000000000800); + try test__fixtfti(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixtfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixtfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixtfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixtfti(-2.01, -2); + try test__fixtfti(-2.0, -2); + try test__fixtfti(-1.99, -1); + try test__fixtfti(-1.0, -1); + try test__fixtfti(-0.99, 0); + try test__fixtfti(-0.5, 0); + try test__fixtfti(-math.floatMin(f128), 0); + try test__fixtfti(0.0, 0); + try test__fixtfti(math.floatMin(f128), 0); + try test__fixtfti(0.5, 0); + try test__fixtfti(0.99, 0); + try test__fixtfti(1.0, 1); + try test__fixtfti(1.5, 1); + try test__fixtfti(1.99, 1); + try test__fixtfti(2.0, 2); + try test__fixtfti(2.01, 2); + + try test__fixtfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixtfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixtfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixtfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixtfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixtfti(0x1.0000000000001p+63, 0x8000000000000800); + + try test__fixtfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + try test__fixtfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + try test__fixtfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + try test__fixtfti(math.floatMax(f128), math.maxInt(i128)); +} + +test "fixunstfti" { + try test__fixunstfti(math.inf(f128), 0xffffffffffffffffffffffffffffffff); + + try test__fixunstfti(0.0, 0); + + try test__fixunstfti(0.5, 0); + try test__fixunstfti(0.99, 0); + try test__fixunstfti(1.0, 1); + try test__fixunstfti(1.5, 1); + try test__fixunstfti(1.99, 1); + try test__fixunstfti(2.0, 2); + try test__fixunstfti(2.01, 2); + try test__fixunstfti(-0.01, 0); + try test__fixunstfti(-0.99, 0); + + try test__fixunstfti(0x1p+128, 0xffffffffffffffffffffffffffffffff); + + try test__fixunstfti(0x1.FFFFFEp+126, 0x7fffff80000000000000000000000000); + try test__fixunstfti(0x1.FFFFFEp+127, 0xffffff00000000000000000000000000); + try test__fixunstfti(0x1.FFFFFEp+128, 0xffffffffffffffffffffffffffffffff); + try test__fixunstfti(0x1.FFFFFEp+129, 0xffffffffffffffffffffffffffffffff); +} + +fn test__fixunshfti(a: f16, expected: u128) !void { + const x = fixXfYi(u128, a); + try testing.expect(x == expected); +} + +test "fixXfYi for f16" { + try test__fixunshfti(math.inf(f16), math.maxInt(u128)); + try test__fixunshfti(math.floatMax(f16), 65504); +} + +fn test__fixunsxfti(a: f80, expected: u128) !void { + const x = fixXfYi(u128, a); + try testing.expect(x == expected); +} + +test "fixXfYi for f80" { + try test__fixunsxfti(math.inf(f80), math.maxInt(u128)); + try test__fixunsxfti(math.floatMax(f80), math.maxInt(u128)); + try test__fixunsxfti(math.maxInt(u64), math.maxInt(u64)); +} diff --git a/lib/std/special/compiler_rt/fixdfdi.zig b/lib/std/special/compiler_rt/fixdfdi.zig deleted file mode 100644 index 6c69f10e09..0000000000 --- a/lib/std/special/compiler_rt/fixdfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixdfdi(a: f64) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - return fixint(f64, i64, a); -} - -pub fn __aeabi_d2lz(arg: f64) callconv(.AAPCS) i64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixdfdi, .{arg}); -} - -test { - _ = @import("fixdfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixdfdi_test.zig b/lib/std/special/compiler_rt/fixdfdi_test.zig deleted file mode 100644 index ac2fdbe7ef..0000000000 --- a/lib/std/special/compiler_rt/fixdfdi_test.zig +++ /dev/null @@ -1,62 +0,0 @@ -const __fixdfdi = @import("fixdfdi.zig").__fixdfdi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixdfdi(a: f64, expected: i64) !void { - const x = __fixdfdi(a); - try testing.expect(x == expected); -} - -test "fixdfdi" { - try test__fixdfdi(-math.f64_max, math.minInt(i64)); - - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); - - try test__fixdfdi(-0x1.0000000000000p+127, -0x8000000000000000); - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); - try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); - - try test__fixdfdi(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixdfdi(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixdfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixdfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixdfdi(-2.01, -2); - try test__fixdfdi(-2.0, -2); - try test__fixdfdi(-1.99, -1); - try test__fixdfdi(-1.0, -1); - try test__fixdfdi(-0.99, 0); - try test__fixdfdi(-0.5, 0); - try test__fixdfdi(-math.f64_min, 0); - try test__fixdfdi(0.0, 0); - try test__fixdfdi(math.f64_min, 0); - try test__fixdfdi(0.5, 0); - try test__fixdfdi(0.99, 0); - try test__fixdfdi(1.0, 1); - try test__fixdfdi(1.5, 1); - try test__fixdfdi(1.99, 1); - try test__fixdfdi(2.0, 2); - try test__fixdfdi(2.01, 2); - - try test__fixdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixdfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); - - try test__fixdfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); - - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - - try test__fixdfdi(math.f64_max, math.maxInt(i64)); -} diff --git a/lib/std/special/compiler_rt/fixdfsi.zig b/lib/std/special/compiler_rt/fixdfsi.zig deleted file mode 100644 index 5842d99cd3..0000000000 --- a/lib/std/special/compiler_rt/fixdfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixdfsi(a: f64) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return fixint(f64, i32, a); -} - -pub fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixdfsi, .{a}); -} - -test { - _ = @import("fixdfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixdfsi_test.zig b/lib/std/special/compiler_rt/fixdfsi_test.zig deleted file mode 100644 index 39d4f64369..0000000000 --- a/lib/std/special/compiler_rt/fixdfsi_test.zig +++ /dev/null @@ -1,70 +0,0 @@ -const __fixdfsi = @import("fixdfsi.zig").__fixdfsi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixdfsi(a: f64, expected: i32) !void { - const x = __fixdfsi(a); - try testing.expect(x == expected); -} - -test "fixdfsi" { - try test__fixdfsi(-math.f64_max, math.minInt(i32)); - - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); - - try test__fixdfsi(-0x1.0000000000000p+127, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); - - try test__fixdfsi(-0x1.0000000000001p+63, -0x80000000); - try test__fixdfsi(-0x1.0000000000000p+63, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); - - try test__fixdfsi(-0x1.FFFFFEp+62, -0x80000000); - try test__fixdfsi(-0x1.FFFFFCp+62, -0x80000000); - - try test__fixdfsi(-0x1.000000p+31, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); - try test__fixdfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); - - try test__fixdfsi(-2.01, -2); - try test__fixdfsi(-2.0, -2); - try test__fixdfsi(-1.99, -1); - try test__fixdfsi(-1.0, -1); - try test__fixdfsi(-0.99, 0); - try test__fixdfsi(-0.5, 0); - try test__fixdfsi(-math.f64_min, 0); - try test__fixdfsi(0.0, 0); - try test__fixdfsi(math.f64_min, 0); - try test__fixdfsi(0.5, 0); - try test__fixdfsi(0.99, 0); - try test__fixdfsi(1.0, 1); - try test__fixdfsi(1.5, 1); - try test__fixdfsi(1.99, 1); - try test__fixdfsi(2.0, 2); - try test__fixdfsi(2.01, 2); - - try test__fixdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixdfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); - try test__fixdfsi(0x1.000000p+31, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); - try test__fixdfsi(0x1.0000000000000p+63, 0x7FFFFFFF); - try test__fixdfsi(0x1.0000000000001p+63, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); - try test__fixdfsi(0x1.0000000000000p+127, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - - try test__fixdfsi(math.f64_max, math.maxInt(i32)); -} diff --git a/lib/std/special/compiler_rt/fixdfti.zig b/lib/std/special/compiler_rt/fixdfti.zig deleted file mode 100644 index e8af6a2daf..0000000000 --- a/lib/std/special/compiler_rt/fixdfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixdfti(a: f64) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - return fixint(f64, i128, a); -} - -test { - _ = @import("fixdfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixdfti_test.zig b/lib/std/special/compiler_rt/fixdfti_test.zig deleted file mode 100644 index eb8269b0ea..0000000000 --- a/lib/std/special/compiler_rt/fixdfti_test.zig +++ /dev/null @@ -1,62 +0,0 @@ -const __fixdfti = @import("fixdfti.zig").__fixdfti; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixdfti(a: f64, expected: i128) !void { - const x = __fixdfti(a); - try testing.expect(x == expected); -} - -test "fixdfti" { - try test__fixdfti(-math.f64_max, math.minInt(i128)); - - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); - - try test__fixdfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); - try test__fixdfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); - - try test__fixdfti(-0x1.0000000000001p+63, -0x8000000000000800); - try test__fixdfti(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixdfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixdfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixdfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixdfti(-2.01, -2); - try test__fixdfti(-2.0, -2); - try test__fixdfti(-1.99, -1); - try test__fixdfti(-1.0, -1); - try test__fixdfti(-0.99, 0); - try test__fixdfti(-0.5, 0); - try test__fixdfti(-math.f64_min, 0); - try test__fixdfti(0.0, 0); - try test__fixdfti(math.f64_min, 0); - try test__fixdfti(0.5, 0); - try test__fixdfti(0.99, 0); - try test__fixdfti(1.0, 1); - try test__fixdfti(1.5, 1); - try test__fixdfti(1.99, 1); - try test__fixdfti(2.0, 2); - try test__fixdfti(2.01, 2); - - try test__fixdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixdfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixdfti(0x1.0000000000001p+63, 0x8000000000000800); - - try test__fixdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); - try test__fixdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); - try test__fixdfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - - try test__fixdfti(math.f64_max, math.maxInt(i128)); -} diff --git a/lib/std/special/compiler_rt/fixint.zig b/lib/std/special/compiler_rt/fixint.zig deleted file mode 100644 index ff0577c115..0000000000 --- a/lib/std/special/compiler_rt/fixint.zig +++ /dev/null @@ -1,75 +0,0 @@ -const is_test = @import("builtin").is_test; -const std = @import("std"); -const math = std.math; -const Log2Int = std.math.Log2Int; -const maxInt = std.math.maxInt; -const minInt = std.math.minInt; - -const DBG = false; - -pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { - @setRuntimeSafety(is_test); - - const rep_t = switch (fp_t) { - f32 => u32, - f64 => u64, - f128 => u128, - else => unreachable, - }; - const significandBits = switch (fp_t) { - f32 => 23, - f64 => 52, - f128 => 112, - else => unreachable, - }; - - const typeWidth = @typeInfo(rep_t).Int.bits; - const exponentBits = (typeWidth - significandBits - 1); - const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); - const maxExponent = ((1 << exponentBits) - 1); - const exponentBias = (maxExponent >> 1); - - const implicitBit = (@as(rep_t, 1) << significandBits); - const significandMask = (implicitBit - 1); - - // Break a into sign, exponent, significand - const aRep: rep_t = @bitCast(rep_t, a); - const absMask = signBit - 1; - const aAbs: rep_t = aRep & absMask; - - const negative = (aRep & signBit) != 0; - const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; - const significand: rep_t = (aAbs & significandMask) | implicitBit; - - // If exponent is negative, the uint_result is zero. - if (exponent < 0) return 0; - - // The unsigned result needs to be large enough to handle an fixint_t or rep_t - const fixint_bits = @typeInfo(fixint_t).Int.bits; - const fixuint_t = std.meta.Int(.unsigned, fixint_bits); - const UintResultType = if (fixint_bits > typeWidth) fixuint_t else rep_t; - var uint_result: UintResultType = undefined; - - // If the value is too large for the integer type, saturate. - if (@intCast(usize, exponent) >= fixint_bits) { - return if (negative) @as(fixint_t, minInt(fixint_t)) else @as(fixint_t, maxInt(fixint_t)); - } - - // If 0 <= exponent < significandBits, right shift else left shift - if (exponent < significandBits) { - uint_result = @intCast(UintResultType, significand) >> @intCast(Log2Int(UintResultType), significandBits - exponent); - } else { - uint_result = @intCast(UintResultType, significand) << @intCast(Log2Int(UintResultType), exponent - significandBits); - } - - // Cast to final signed result - if (negative) { - return if (uint_result >= -math.minInt(fixint_t)) math.minInt(fixint_t) else -@intCast(fixint_t, uint_result); - } else { - return if (uint_result >= math.maxInt(fixint_t)) math.maxInt(fixint_t) else @intCast(fixint_t, uint_result); - } -} - -test { - _ = @import("fixint_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixint_test.zig b/lib/std/special/compiler_rt/fixint_test.zig index 9c31444ac5..57b4093809 100644 --- a/lib/std/special/compiler_rt/fixint_test.zig +++ b/lib/std/special/compiler_rt/fixint_test.zig @@ -11,49 +11,49 @@ fn test__fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t, expected: } test "fixint.i1" { - try test__fixint(f32, i1, -math.inf_f32, -1); - try test__fixint(f32, i1, -math.f32_max, -1); + try test__fixint(f32, i1, -math.inf(f32), -1); + try test__fixint(f32, i1, -math.floatMax(f32), -1); try test__fixint(f32, i1, -2.0, -1); try test__fixint(f32, i1, -1.1, -1); try test__fixint(f32, i1, -1.0, -1); try test__fixint(f32, i1, -0.9, 0); try test__fixint(f32, i1, -0.1, 0); - try test__fixint(f32, i1, -math.f32_min, 0); + try test__fixint(f32, i1, -math.floatMin(f32), 0); try test__fixint(f32, i1, -0.0, 0); try test__fixint(f32, i1, 0.0, 0); - try test__fixint(f32, i1, math.f32_min, 0); + try test__fixint(f32, i1, math.floatMin(f32), 0); try test__fixint(f32, i1, 0.1, 0); try test__fixint(f32, i1, 0.9, 0); try test__fixint(f32, i1, 1.0, 0); try test__fixint(f32, i1, 2.0, 0); - try test__fixint(f32, i1, math.f32_max, 0); - try test__fixint(f32, i1, math.inf_f32, 0); + try test__fixint(f32, i1, math.floatMax(f32), 0); + try test__fixint(f32, i1, math.inf(f32), 0); } test "fixint.i2" { - try test__fixint(f32, i2, -math.inf_f32, -2); - try test__fixint(f32, i2, -math.f32_max, -2); + try test__fixint(f32, i2, -math.inf(f32), -2); + try test__fixint(f32, i2, -math.floatMax(f32), -2); try test__fixint(f32, i2, -2.0, -2); try test__fixint(f32, i2, -1.9, -1); try test__fixint(f32, i2, -1.1, -1); try test__fixint(f32, i2, -1.0, -1); try test__fixint(f32, i2, -0.9, 0); try test__fixint(f32, i2, -0.1, 0); - try test__fixint(f32, i2, -math.f32_min, 0); + try test__fixint(f32, i2, -math.floatMin(f32), 0); try test__fixint(f32, i2, -0.0, 0); try test__fixint(f32, i2, 0.0, 0); - try test__fixint(f32, i2, math.f32_min, 0); + try test__fixint(f32, i2, math.floatMin(f32), 0); try test__fixint(f32, i2, 0.1, 0); try test__fixint(f32, i2, 0.9, 0); try test__fixint(f32, i2, 1.0, 1); try test__fixint(f32, i2, 2.0, 1); - try test__fixint(f32, i2, math.f32_max, 1); - try test__fixint(f32, i2, math.inf_f32, 1); + try test__fixint(f32, i2, math.floatMax(f32), 1); + try test__fixint(f32, i2, math.inf(f32), 1); } test "fixint.i3" { - try test__fixint(f32, i3, -math.inf_f32, -4); - try test__fixint(f32, i3, -math.f32_max, -4); + try test__fixint(f32, i3, -math.inf(f32), -4); + try test__fixint(f32, i3, -math.floatMax(f32), -4); try test__fixint(f32, i3, -4.0, -4); try test__fixint(f32, i3, -3.0, -3); try test__fixint(f32, i3, -2.0, -2); @@ -62,23 +62,23 @@ test "fixint.i3" { try test__fixint(f32, i3, -1.0, -1); try test__fixint(f32, i3, -0.9, 0); try test__fixint(f32, i3, -0.1, 0); - try test__fixint(f32, i3, -math.f32_min, 0); + try test__fixint(f32, i3, -math.floatMin(f32), 0); try test__fixint(f32, i3, -0.0, 0); try test__fixint(f32, i3, 0.0, 0); - try test__fixint(f32, i3, math.f32_min, 0); + try test__fixint(f32, i3, math.floatMin(f32), 0); try test__fixint(f32, i3, 0.1, 0); try test__fixint(f32, i3, 0.9, 0); try test__fixint(f32, i3, 1.0, 1); try test__fixint(f32, i3, 2.0, 2); try test__fixint(f32, i3, 3.0, 3); try test__fixint(f32, i3, 4.0, 3); - try test__fixint(f32, i3, math.f32_max, 3); - try test__fixint(f32, i3, math.inf_f32, 3); + try test__fixint(f32, i3, math.floatMax(f32), 3); + try test__fixint(f32, i3, math.inf(f32), 3); } test "fixint.i32" { - try test__fixint(f64, i32, -math.inf_f64, math.minInt(i32)); - try test__fixint(f64, i32, -math.f64_max, math.minInt(i32)); + try test__fixint(f64, i32, -math.inf(f64), math.minInt(i32)); + try test__fixint(f64, i32, -math.floatMax(f64), math.minInt(i32)); try test__fixint(f64, i32, @as(f64, math.minInt(i32)), math.minInt(i32)); try test__fixint(f64, i32, @as(f64, math.minInt(i32)) + 1, math.minInt(i32) + 1); try test__fixint(f64, i32, -2.0, -2); @@ -87,22 +87,22 @@ test "fixint.i32" { try test__fixint(f64, i32, -1.0, -1); try test__fixint(f64, i32, -0.9, 0); try test__fixint(f64, i32, -0.1, 0); - try test__fixint(f64, i32, -math.f32_min, 0); + try test__fixint(f64, i32, -@as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i32, -0.0, 0); try test__fixint(f64, i32, 0.0, 0); - try test__fixint(f64, i32, math.f32_min, 0); + try test__fixint(f64, i32, @as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i32, 0.1, 0); try test__fixint(f64, i32, 0.9, 0); try test__fixint(f64, i32, 1.0, 1); try test__fixint(f64, i32, @as(f64, math.maxInt(i32)) - 1, math.maxInt(i32) - 1); try test__fixint(f64, i32, @as(f64, math.maxInt(i32)), math.maxInt(i32)); - try test__fixint(f64, i32, math.f64_max, math.maxInt(i32)); - try test__fixint(f64, i32, math.inf_f64, math.maxInt(i32)); + try test__fixint(f64, i32, math.floatMax(f64), math.maxInt(i32)); + try test__fixint(f64, i32, math.inf(f64), math.maxInt(i32)); } test "fixint.i64" { - try test__fixint(f64, i64, -math.inf_f64, math.minInt(i64)); - try test__fixint(f64, i64, -math.f64_max, math.minInt(i64)); + try test__fixint(f64, i64, -math.inf(f64), math.minInt(i64)); + try test__fixint(f64, i64, -math.floatMax(f64), math.minInt(i64)); try test__fixint(f64, i64, @as(f64, math.minInt(i64)), math.minInt(i64)); try test__fixint(f64, i64, @as(f64, math.minInt(i64)) + 1, math.minInt(i64)); try test__fixint(f64, i64, @as(f64, math.minInt(i64) / 2), math.minInt(i64) / 2); @@ -112,22 +112,22 @@ test "fixint.i64" { try test__fixint(f64, i64, -1.0, -1); try test__fixint(f64, i64, -0.9, 0); try test__fixint(f64, i64, -0.1, 0); - try test__fixint(f64, i64, -math.f32_min, 0); + try test__fixint(f64, i64, -@as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i64, -0.0, 0); try test__fixint(f64, i64, 0.0, 0); - try test__fixint(f64, i64, math.f32_min, 0); + try test__fixint(f64, i64, @as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i64, 0.1, 0); try test__fixint(f64, i64, 0.9, 0); try test__fixint(f64, i64, 1.0, 1); try test__fixint(f64, i64, @as(f64, math.maxInt(i64)) - 1, math.maxInt(i64)); try test__fixint(f64, i64, @as(f64, math.maxInt(i64)), math.maxInt(i64)); - try test__fixint(f64, i64, math.f64_max, math.maxInt(i64)); - try test__fixint(f64, i64, math.inf_f64, math.maxInt(i64)); + try test__fixint(f64, i64, math.floatMax(f64), math.maxInt(i64)); + try test__fixint(f64, i64, math.inf(f64), math.maxInt(i64)); } test "fixint.i128" { - try test__fixint(f64, i128, -math.inf_f64, math.minInt(i128)); - try test__fixint(f64, i128, -math.f64_max, math.minInt(i128)); + try test__fixint(f64, i128, -math.inf(f64), math.minInt(i128)); + try test__fixint(f64, i128, -math.floatMax(f64), math.minInt(i128)); try test__fixint(f64, i128, @as(f64, math.minInt(i128)), math.minInt(i128)); try test__fixint(f64, i128, @as(f64, math.minInt(i128)) + 1, math.minInt(i128)); try test__fixint(f64, i128, -2.0, -2); @@ -136,15 +136,15 @@ test "fixint.i128" { try test__fixint(f64, i128, -1.0, -1); try test__fixint(f64, i128, -0.9, 0); try test__fixint(f64, i128, -0.1, 0); - try test__fixint(f64, i128, -math.f32_min, 0); + try test__fixint(f64, i128, -@as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i128, -0.0, 0); try test__fixint(f64, i128, 0.0, 0); - try test__fixint(f64, i128, math.f32_min, 0); + try test__fixint(f64, i128, @as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i128, 0.1, 0); try test__fixint(f64, i128, 0.9, 0); try test__fixint(f64, i128, 1.0, 1); try test__fixint(f64, i128, @as(f64, math.maxInt(i128)) - 1, math.maxInt(i128)); try test__fixint(f64, i128, @as(f64, math.maxInt(i128)), math.maxInt(i128)); - try test__fixint(f64, i128, math.f64_max, math.maxInt(i128)); - try test__fixint(f64, i128, math.inf_f64, math.maxInt(i128)); + try test__fixint(f64, i128, math.floatMax(f64), math.maxInt(i128)); + try test__fixint(f64, i128, math.inf(f64), math.maxInt(i128)); } diff --git a/lib/std/special/compiler_rt/fixsfdi.zig b/lib/std/special/compiler_rt/fixsfdi.zig deleted file mode 100644 index 64961075d0..0000000000 --- a/lib/std/special/compiler_rt/fixsfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixsfdi(a: f32) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - return fixint(f32, i64, a); -} - -pub fn __aeabi_f2lz(arg: f32) callconv(.AAPCS) i64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixsfdi, .{arg}); -} - -test { - _ = @import("fixsfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfdi_test.zig b/lib/std/special/compiler_rt/fixsfdi_test.zig deleted file mode 100644 index 95f56bd29e..0000000000 --- a/lib/std/special/compiler_rt/fixsfdi_test.zig +++ /dev/null @@ -1,64 +0,0 @@ -const __fixsfdi = @import("fixsfdi.zig").__fixsfdi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixsfdi(a: f32, expected: i64) !void { - const x = __fixsfdi(a); - try testing.expect(x == expected); -} - -test "fixsfdi" { - try test__fixsfdi(-math.f32_max, math.minInt(i64)); - - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); - - try test__fixsfdi(-0x1.0000000000000p+127, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); - - try test__fixsfdi(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixsfdi(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); - - try test__fixsfdi(-0x1.FFFFFFp+62, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixsfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixsfdi(-2.01, -2); - try test__fixsfdi(-2.0, -2); - try test__fixsfdi(-1.99, -1); - try test__fixsfdi(-1.0, -1); - try test__fixsfdi(-0.99, 0); - try test__fixsfdi(-0.5, 0); - try test__fixsfdi(-math.f32_min, 0); - try test__fixsfdi(0.0, 0); - try test__fixsfdi(math.f32_min, 0); - try test__fixsfdi(0.5, 0); - try test__fixsfdi(0.99, 0); - try test__fixsfdi(1.0, 1); - try test__fixsfdi(1.5, 1); - try test__fixsfdi(1.99, 1); - try test__fixsfdi(2.0, 2); - try test__fixsfdi(2.01, 2); - - try test__fixsfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixsfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixsfdi(0x1.FFFFFFp+62, 0x7FFFFFFFFFFFFFFF); - - try test__fixsfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); - - try test__fixsfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); - - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - - try test__fixsfdi(math.f64_max, math.maxInt(i64)); -} diff --git a/lib/std/special/compiler_rt/fixsfsi.zig b/lib/std/special/compiler_rt/fixsfsi.zig deleted file mode 100644 index 932a6e1a4f..0000000000 --- a/lib/std/special/compiler_rt/fixsfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixsfsi(a: f32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return fixint(f32, i32, a); -} - -pub fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixsfsi, .{a}); -} - -test { - _ = @import("fixsfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfsi_test.zig b/lib/std/special/compiler_rt/fixsfsi_test.zig deleted file mode 100644 index 9ea1aafb3e..0000000000 --- a/lib/std/special/compiler_rt/fixsfsi_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __fixsfsi = @import("fixsfsi.zig").__fixsfsi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixsfsi(a: f32, expected: i32) !void { - const x = __fixsfsi(a); - try testing.expect(x == expected); -} - -test "fixsfsi" { - try test__fixsfsi(-math.f32_max, math.minInt(i32)); - - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); - - try test__fixsfsi(-0x1.0000000000000p+127, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); - - try test__fixsfsi(-0x1.0000000000001p+63, -0x80000000); - try test__fixsfsi(-0x1.0000000000000p+63, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); - - try test__fixsfsi(-0x1.FFFFFEp+62, -0x80000000); - try test__fixsfsi(-0x1.FFFFFCp+62, -0x80000000); - - try test__fixsfsi(-0x1.000000p+31, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFp+30, -0x80000000); - try test__fixsfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixsfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixsfsi(-2.01, -2); - try test__fixsfsi(-2.0, -2); - try test__fixsfsi(-1.99, -1); - try test__fixsfsi(-1.0, -1); - try test__fixsfsi(-0.99, 0); - try test__fixsfsi(-0.5, 0); - try test__fixsfsi(-math.f32_min, 0); - try test__fixsfsi(0.0, 0); - try test__fixsfsi(math.f32_min, 0); - try test__fixsfsi(0.5, 0); - try test__fixsfsi(0.99, 0); - try test__fixsfsi(1.0, 1); - try test__fixsfsi(1.5, 1); - try test__fixsfsi(1.99, 1); - try test__fixsfsi(2.0, 2); - try test__fixsfsi(2.01, 2); - - try test__fixsfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixsfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixsfsi(0x1.FFFFFFp+30, 0x7FFFFFFF); - try test__fixsfsi(0x1.000000p+31, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); - try test__fixsfsi(0x1.0000000000000p+63, 0x7FFFFFFF); - try test__fixsfsi(0x1.0000000000001p+63, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); - try test__fixsfsi(0x1.0000000000000p+127, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - - try test__fixsfsi(math.f32_max, math.maxInt(i32)); -} diff --git a/lib/std/special/compiler_rt/fixsfti.zig b/lib/std/special/compiler_rt/fixsfti.zig deleted file mode 100644 index e67bbabbd7..0000000000 --- a/lib/std/special/compiler_rt/fixsfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixsfti(a: f32) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - return fixint(f32, i128, a); -} - -test { - _ = @import("fixsfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfti_test.zig b/lib/std/special/compiler_rt/fixsfti_test.zig deleted file mode 100644 index 8f29d9ea06..0000000000 --- a/lib/std/special/compiler_rt/fixsfti_test.zig +++ /dev/null @@ -1,80 +0,0 @@ -const __fixsfti = @import("fixsfti.zig").__fixsfti; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixsfti(a: f32, expected: i128) !void { - const x = __fixsfti(a); - try testing.expect(x == expected); -} - -test "fixsfti" { - try test__fixsfti(-math.f32_max, math.minInt(i128)); - - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); - - try test__fixsfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+126, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFEp+126, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFF0000000p+126, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFE0000000p+126, -0x7FFFFF80000000000000000000000000); - try test__fixsfti(-0x1.FFFFFC0000000p+126, -0x7FFFFF00000000000000000000000000); - - try test__fixsfti(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixsfti(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); - - try test__fixsfti(-0x1.FFFFFFp+62, -0x8000000000000000); - try test__fixsfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixsfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixsfti(-0x1.000000p+31, -0x80000000); - try test__fixsfti(-0x1.FFFFFFp+30, -0x80000000); - try test__fixsfti(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixsfti(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixsfti(-2.01, -2); - try test__fixsfti(-2.0, -2); - try test__fixsfti(-1.99, -1); - try test__fixsfti(-1.0, -1); - try test__fixsfti(-0.99, 0); - try test__fixsfti(-0.5, 0); - try test__fixsfti(-math.f32_min, 0); - try test__fixsfti(0.0, 0); - try test__fixsfti(math.f32_min, 0); - try test__fixsfti(0.5, 0); - try test__fixsfti(0.99, 0); - try test__fixsfti(1.0, 1); - try test__fixsfti(1.5, 1); - try test__fixsfti(1.99, 1); - try test__fixsfti(2.0, 2); - try test__fixsfti(2.01, 2); - - try test__fixsfti(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixsfti(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixsfti(0x1.FFFFFFp+30, 0x80000000); - try test__fixsfti(0x1.000000p+31, 0x80000000); - - try test__fixsfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixsfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixsfti(0x1.FFFFFFp+62, 0x8000000000000000); - - try test__fixsfti(0x1.FFFFFFFFFFFFEp+62, 0x8000000000000000); - try test__fixsfti(0x1.FFFFFFFFFFFFFp+62, 0x8000000000000000); - try test__fixsfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixsfti(0x1.0000000000001p+63, 0x8000000000000000); - - try test__fixsfti(0x1.FFFFFC0000000p+126, 0x7FFFFF00000000000000000000000000); - try test__fixsfti(0x1.FFFFFE0000000p+126, 0x7FFFFF80000000000000000000000000); - try test__fixsfti(0x1.FFFFFF0000000p+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - - try test__fixsfti(math.f32_max, math.maxInt(i128)); -} diff --git a/lib/std/special/compiler_rt/fixtfdi.zig b/lib/std/special/compiler_rt/fixtfdi.zig deleted file mode 100644 index 6087d7e720..0000000000 --- a/lib/std/special/compiler_rt/fixtfdi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixtfdi(a: f128) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - return fixint(f128, i64, a); -} - -test { - _ = @import("fixtfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig deleted file mode 100644 index 5e43a85408..0000000000 --- a/lib/std/special/compiler_rt/fixtfdi_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __fixtfdi = @import("fixtfdi.zig").__fixtfdi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixtfdi(a: f128, expected: i64) !void { - const x = __fixtfdi(a); - try testing.expect(x == expected); -} - -test "fixtfdi" { - try test__fixtfdi(-math.f128_max, math.minInt(i64)); - - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); - - try test__fixtfdi(-0x1.0000000000000p+127, -0x8000000000000000); - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); - try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); - - try test__fixtfdi(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixtfdi(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixtfdi(-0x1.FFFFFEp+62, -0x7FFFFF8000000000); - try test__fixtfdi(-0x1.FFFFFCp+62, -0x7FFFFF0000000000); - - try test__fixtfdi(-0x1.000000p+31, -0x80000000); - try test__fixtfdi(-0x1.FFFFFFp+30, -0x7FFFFFC0); - try test__fixtfdi(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixtfdi(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixtfdi(-2.01, -2); - try test__fixtfdi(-2.0, -2); - try test__fixtfdi(-1.99, -1); - try test__fixtfdi(-1.0, -1); - try test__fixtfdi(-0.99, 0); - try test__fixtfdi(-0.5, 0); - try test__fixtfdi(-math.f64_min, 0); - try test__fixtfdi(0.0, 0); - try test__fixtfdi(math.f64_min, 0); - try test__fixtfdi(0.5, 0); - try test__fixtfdi(0.99, 0); - try test__fixtfdi(1.0, 1); - try test__fixtfdi(1.5, 1); - try test__fixtfdi(1.99, 1); - try test__fixtfdi(2.0, 2); - try test__fixtfdi(2.01, 2); - - try test__fixtfdi(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixtfdi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixtfdi(0x1.FFFFFFp+30, 0x7FFFFFC0); - try test__fixtfdi(0x1.000000p+31, 0x80000000); - - try test__fixtfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixtfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixtfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixtfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); - - try test__fixtfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); - - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - - try test__fixtfdi(math.f128_max, math.maxInt(i64)); -} diff --git a/lib/std/special/compiler_rt/fixtfsi.zig b/lib/std/special/compiler_rt/fixtfsi.zig deleted file mode 100644 index b5fea97c5c..0000000000 --- a/lib/std/special/compiler_rt/fixtfsi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixtfsi(a: f128) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return fixint(f128, i32, a); -} - -test { - _ = @import("fixtfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig deleted file mode 100644 index f00c4735d6..0000000000 --- a/lib/std/special/compiler_rt/fixtfsi_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __fixtfsi = @import("fixtfsi.zig").__fixtfsi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixtfsi(a: f128, expected: i32) !void { - const x = __fixtfsi(a); - try testing.expect(x == expected); -} - -test "fixtfsi" { - try test__fixtfsi(-math.f128_max, math.minInt(i32)); - - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); - - try test__fixtfsi(-0x1.0000000000000p+127, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); - - try test__fixtfsi(-0x1.0000000000001p+63, -0x80000000); - try test__fixtfsi(-0x1.0000000000000p+63, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); - - try test__fixtfsi(-0x1.FFFFFEp+62, -0x80000000); - try test__fixtfsi(-0x1.FFFFFCp+62, -0x80000000); - - try test__fixtfsi(-0x1.000000p+31, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); - try test__fixtfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixtfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixtfsi(-2.01, -2); - try test__fixtfsi(-2.0, -2); - try test__fixtfsi(-1.99, -1); - try test__fixtfsi(-1.0, -1); - try test__fixtfsi(-0.99, 0); - try test__fixtfsi(-0.5, 0); - try test__fixtfsi(-math.f32_min, 0); - try test__fixtfsi(0.0, 0); - try test__fixtfsi(math.f32_min, 0); - try test__fixtfsi(0.5, 0); - try test__fixtfsi(0.99, 0); - try test__fixtfsi(1.0, 1); - try test__fixtfsi(1.5, 1); - try test__fixtfsi(1.99, 1); - try test__fixtfsi(2.0, 2); - try test__fixtfsi(2.01, 2); - - try test__fixtfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixtfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixtfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); - try test__fixtfsi(0x1.000000p+31, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); - try test__fixtfsi(0x1.0000000000000p+63, 0x7FFFFFFF); - try test__fixtfsi(0x1.0000000000001p+63, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); - try test__fixtfsi(0x1.0000000000000p+127, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - - try test__fixtfsi(math.f128_max, math.maxInt(i32)); -} diff --git a/lib/std/special/compiler_rt/fixtfti.zig b/lib/std/special/compiler_rt/fixtfti.zig deleted file mode 100644 index 5a001d0b69..0000000000 --- a/lib/std/special/compiler_rt/fixtfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixtfti(a: f128) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - return fixint(f128, i128, a); -} - -test { - _ = @import("fixtfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig deleted file mode 100644 index 3bb113e46b..0000000000 --- a/lib/std/special/compiler_rt/fixtfti_test.zig +++ /dev/null @@ -1,62 +0,0 @@ -const __fixtfti = @import("fixtfti.zig").__fixtfti; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixtfti(a: f128, expected: i128) !void { - const x = __fixtfti(a); - try testing.expect(x == expected); -} - -test "fixtfti" { - try test__fixtfti(-math.f128_max, math.minInt(i128)); - - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); - - try test__fixtfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); - try test__fixtfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); - - try test__fixtfti(-0x1.0000000000001p+63, -0x8000000000000800); - try test__fixtfti(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixtfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixtfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixtfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixtfti(-2.01, -2); - try test__fixtfti(-2.0, -2); - try test__fixtfti(-1.99, -1); - try test__fixtfti(-1.0, -1); - try test__fixtfti(-0.99, 0); - try test__fixtfti(-0.5, 0); - try test__fixtfti(-math.f128_min, 0); - try test__fixtfti(0.0, 0); - try test__fixtfti(math.f128_min, 0); - try test__fixtfti(0.5, 0); - try test__fixtfti(0.99, 0); - try test__fixtfti(1.0, 1); - try test__fixtfti(1.5, 1); - try test__fixtfti(1.99, 1); - try test__fixtfti(2.0, 2); - try test__fixtfti(2.01, 2); - - try test__fixtfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixtfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixtfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixtfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixtfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixtfti(0x1.0000000000001p+63, 0x8000000000000800); - - try test__fixtfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); - try test__fixtfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); - try test__fixtfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - - try test__fixtfti(math.f128_max, math.maxInt(i128)); -} diff --git a/lib/std/special/compiler_rt/fixuint.zig b/lib/std/special/compiler_rt/fixuint.zig deleted file mode 100644 index 6bfbcf6d65..0000000000 --- a/lib/std/special/compiler_rt/fixuint.zig +++ /dev/null @@ -1,50 +0,0 @@ -const is_test = @import("builtin").is_test; -const Log2Int = @import("std").math.Log2Int; - -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) { - f32 => u32, - f64 => u64, - f128 => u128, - else => unreachable, - }; - const typeWidth = @typeInfo(rep_t).Int.bits; - const significandBits = switch (fp_t) { - f32 => 23, - f64 => 52, - f128 => 112, - else => unreachable, - }; - const exponentBits = (typeWidth - significandBits - 1); - const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); - const maxExponent = ((1 << exponentBits) - 1); - const exponentBias = (maxExponent >> 1); - - const implicitBit = (@as(rep_t, 1) << significandBits); - const significandMask = (implicitBit - 1); - - // Break a into sign, exponent, significand - const aRep: rep_t = @bitCast(rep_t, a); - const absMask = signBit - 1; - const aAbs: rep_t = aRep & absMask; - - const sign = if ((aRep & signBit) != 0) @as(i32, -1) else @as(i32, 1); - const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; - const significand: rep_t = (aAbs & significandMask) | implicitBit; - - // If either the value or the exponent is negative, the result is zero. - if (sign == -1 or exponent < 0) return 0; - - // If the value is too large for the integer type, saturate. - if (@intCast(c_uint, exponent) >= @typeInfo(fixuint_t).Int.bits) return ~@as(fixuint_t, 0); - - // If 0 <= exponent < significandBits, right shift to get the result. - // Otherwise, shift left. - if (exponent < significandBits) { - return @intCast(fixuint_t, significand >> @intCast(Log2Int(rep_t), significandBits - exponent)); - } else { - return @intCast(fixuint_t, significand) << @intCast(Log2Int(fixuint_t), exponent - significandBits); - } -} diff --git a/lib/std/special/compiler_rt/fixunsdfdi.zig b/lib/std/special/compiler_rt/fixunsdfdi.zig deleted file mode 100644 index c864f0f6c2..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f64, u64, a); -} - -pub fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunsdfdi, .{a}); -} - -test { - _ = @import("fixunsdfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunsdfdi_test.zig b/lib/std/special/compiler_rt/fixunsdfdi_test.zig deleted file mode 100644 index 59591cf181..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfdi_test.zig +++ /dev/null @@ -1,39 +0,0 @@ -const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi; -const testing = @import("std").testing; - -fn test__fixunsdfdi(a: f64, expected: u64) !void { - const x = __fixunsdfdi(a); - try testing.expect(x == expected); -} - -test "fixunsdfdi" { - //test__fixunsdfdi(0.0, 0); - //test__fixunsdfdi(0.5, 0); - //test__fixunsdfdi(0.99, 0); - try test__fixunsdfdi(1.0, 1); - try test__fixunsdfdi(1.5, 1); - try test__fixunsdfdi(1.99, 1); - try test__fixunsdfdi(2.0, 2); - try test__fixunsdfdi(2.01, 2); - try test__fixunsdfdi(-0.5, 0); - try test__fixunsdfdi(-0.99, 0); - try test__fixunsdfdi(-1.0, 0); - try test__fixunsdfdi(-1.5, 0); - try test__fixunsdfdi(-1.99, 0); - try test__fixunsdfdi(-2.0, 0); - try test__fixunsdfdi(-2.01, 0); - - try test__fixunsdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunsdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunsdfdi(-0x1.FFFFFEp+62, 0); - try test__fixunsdfdi(-0x1.FFFFFCp+62, 0); - - try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); - try test__fixunsdfdi(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixunsdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - - try test__fixunsdfdi(-0x1.FFFFFFFFFFFFFp+62, 0); - try test__fixunsdfdi(-0x1.FFFFFFFFFFFFEp+62, 0); -} diff --git a/lib/std/special/compiler_rt/fixunsdfsi.zig b/lib/std/special/compiler_rt/fixunsdfsi.zig deleted file mode 100644 index c1f5173661..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f64, u32, a); -} - -pub fn __aeabi_d2uiz(arg: f64) callconv(.AAPCS) u32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunsdfsi, .{arg}); -} - -test { - _ = @import("fixunsdfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunsdfsi_test.zig b/lib/std/special/compiler_rt/fixunsdfsi_test.zig deleted file mode 100644 index b4b7ec8840..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfsi_test.zig +++ /dev/null @@ -1,39 +0,0 @@ -const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi; -const testing = @import("std").testing; - -fn test__fixunsdfsi(a: f64, expected: u32) !void { - const x = __fixunsdfsi(a); - try testing.expect(x == expected); -} - -test "fixunsdfsi" { - try test__fixunsdfsi(0.0, 0); - - try test__fixunsdfsi(0.5, 0); - try test__fixunsdfsi(0.99, 0); - try test__fixunsdfsi(1.0, 1); - try test__fixunsdfsi(1.5, 1); - try test__fixunsdfsi(1.99, 1); - try test__fixunsdfsi(2.0, 2); - try test__fixunsdfsi(2.01, 2); - try test__fixunsdfsi(-0.5, 0); - try test__fixunsdfsi(-0.99, 0); - try test__fixunsdfsi(-1.0, 0); - try test__fixunsdfsi(-1.5, 0); - try test__fixunsdfsi(-1.99, 0); - try test__fixunsdfsi(-2.0, 0); - try test__fixunsdfsi(-2.01, 0); - - try test__fixunsdfsi(0x1.000000p+31, 0x80000000); - try test__fixunsdfsi(0x1.000000p+32, 0xFFFFFFFF); - try test__fixunsdfsi(0x1.FFFFFEp+31, 0xFFFFFF00); - try test__fixunsdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixunsdfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - - try test__fixunsdfsi(-0x1.FFFFFEp+30, 0); - try test__fixunsdfsi(-0x1.FFFFFCp+30, 0); - - try test__fixunsdfsi(0x1.FFFFFFFEp+31, 0xFFFFFFFF); - try test__fixunsdfsi(0x1.FFFFFFFC00000p+30, 0x7FFFFFFF); - try test__fixunsdfsi(0x1.FFFFFFF800000p+30, 0x7FFFFFFE); -} diff --git a/lib/std/special/compiler_rt/fixunsdfti.zig b/lib/std/special/compiler_rt/fixunsdfti.zig deleted file mode 100644 index 3c9871a4df..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunsdfti(a: f64) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f64, u128, a); -} - -test { - _ = @import("fixunsdfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunsdfti_test.zig b/lib/std/special/compiler_rt/fixunsdfti_test.zig deleted file mode 100644 index d9e1424836..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfti_test.zig +++ /dev/null @@ -1,46 +0,0 @@ -const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti; -const testing = @import("std").testing; - -fn test__fixunsdfti(a: f64, expected: u128) !void { - const x = __fixunsdfti(a); - try testing.expect(x == expected); -} - -test "fixunsdfti" { - try test__fixunsdfti(0.0, 0); - - try test__fixunsdfti(0.5, 0); - try test__fixunsdfti(0.99, 0); - try test__fixunsdfti(1.0, 1); - try test__fixunsdfti(1.5, 1); - try test__fixunsdfti(1.99, 1); - try test__fixunsdfti(2.0, 2); - try test__fixunsdfti(2.01, 2); - try test__fixunsdfti(-0.5, 0); - try test__fixunsdfti(-0.99, 0); - try test__fixunsdfti(-1.0, 0); - try test__fixunsdfti(-1.5, 0); - try test__fixunsdfti(-1.99, 0); - try test__fixunsdfti(-2.0, 0); - try test__fixunsdfti(-2.01, 0); - - try test__fixunsdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunsdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunsdfti(-0x1.FFFFFEp+62, 0); - try test__fixunsdfti(-0x1.FFFFFCp+62, 0); - - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); - try test__fixunsdfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+127, 0xFFFFFFFFFFFFF8000000000000000000); - try test__fixunsdfti(0x1.0000000000000p+127, 0x80000000000000000000000000000000); - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); - try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); - try test__fixunsdfti(0x1.0000000000000p+128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixunsdfti(-0x1.FFFFFFFFFFFFFp+62, 0); - try test__fixunsdfti(-0x1.FFFFFFFFFFFFEp+62, 0); -} diff --git a/lib/std/special/compiler_rt/fixunssfdi.zig b/lib/std/special/compiler_rt/fixunssfdi.zig deleted file mode 100644 index edad20e8bf..0000000000 --- a/lib/std/special/compiler_rt/fixunssfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunssfdi(a: f32) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f32, u64, a); -} - -pub fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunssfdi, .{a}); -} - -test { - _ = @import("fixunssfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunssfdi_test.zig b/lib/std/special/compiler_rt/fixunssfdi_test.zig deleted file mode 100644 index 3c46511b6d..0000000000 --- a/lib/std/special/compiler_rt/fixunssfdi_test.zig +++ /dev/null @@ -1,35 +0,0 @@ -const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi; -const testing = @import("std").testing; - -fn test__fixunssfdi(a: f32, expected: u64) !void { - const x = __fixunssfdi(a); - try testing.expect(x == expected); -} - -test "fixunssfdi" { - try test__fixunssfdi(0.0, 0); - - try test__fixunssfdi(0.5, 0); - try test__fixunssfdi(0.99, 0); - try test__fixunssfdi(1.0, 1); - try test__fixunssfdi(1.5, 1); - try test__fixunssfdi(1.99, 1); - try test__fixunssfdi(2.0, 2); - try test__fixunssfdi(2.01, 2); - try test__fixunssfdi(-0.5, 0); - try test__fixunssfdi(-0.99, 0); - - try test__fixunssfdi(-1.0, 0); - try test__fixunssfdi(-1.5, 0); - try test__fixunssfdi(-1.99, 0); - try test__fixunssfdi(-2.0, 0); - try test__fixunssfdi(-2.01, 0); - - try test__fixunssfdi(0x1.FFFFFEp+63, 0xFFFFFF0000000000); - try test__fixunssfdi(0x1.000000p+63, 0x8000000000000000); - try test__fixunssfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunssfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunssfdi(-0x1.FFFFFEp+62, 0x0000000000000000); - try test__fixunssfdi(-0x1.FFFFFCp+62, 0x0000000000000000); -} diff --git a/lib/std/special/compiler_rt/fixunssfsi.zig b/lib/std/special/compiler_rt/fixunssfsi.zig deleted file mode 100644 index 96722a2a36..0000000000 --- a/lib/std/special/compiler_rt/fixunssfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunssfsi(a: f32) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f32, u32, a); -} - -pub fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunssfsi, .{a}); -} - -test { - _ = @import("fixunssfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunssfsi_test.zig b/lib/std/special/compiler_rt/fixunssfsi_test.zig deleted file mode 100644 index 4bf90e4bff..0000000000 --- a/lib/std/special/compiler_rt/fixunssfsi_test.zig +++ /dev/null @@ -1,36 +0,0 @@ -const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi; -const testing = @import("std").testing; - -fn test__fixunssfsi(a: f32, expected: u32) !void { - const x = __fixunssfsi(a); - try testing.expect(x == expected); -} - -test "fixunssfsi" { - try test__fixunssfsi(0.0, 0); - - try test__fixunssfsi(0.5, 0); - try test__fixunssfsi(0.99, 0); - try test__fixunssfsi(1.0, 1); - try test__fixunssfsi(1.5, 1); - try test__fixunssfsi(1.99, 1); - try test__fixunssfsi(2.0, 2); - try test__fixunssfsi(2.01, 2); - try test__fixunssfsi(-0.5, 0); - try test__fixunssfsi(-0.99, 0); - - try test__fixunssfsi(-1.0, 0); - try test__fixunssfsi(-1.5, 0); - try test__fixunssfsi(-1.99, 0); - try test__fixunssfsi(-2.0, 0); - try test__fixunssfsi(-2.01, 0); - - try test__fixunssfsi(0x1.000000p+31, 0x80000000); - try test__fixunssfsi(0x1.000000p+32, 0xFFFFFFFF); - try test__fixunssfsi(0x1.FFFFFEp+31, 0xFFFFFF00); - try test__fixunssfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixunssfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - - try test__fixunssfsi(-0x1.FFFFFEp+30, 0); - try test__fixunssfsi(-0x1.FFFFFCp+30, 0); -} diff --git a/lib/std/special/compiler_rt/fixunssfti.zig b/lib/std/special/compiler_rt/fixunssfti.zig deleted file mode 100644 index 4967202e69..0000000000 --- a/lib/std/special/compiler_rt/fixunssfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunssfti(a: f32) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f32, u128, a); -} - -test { - _ = @import("fixunssfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunssfti_test.zig b/lib/std/special/compiler_rt/fixunssfti_test.zig deleted file mode 100644 index 3c1a314e91..0000000000 --- a/lib/std/special/compiler_rt/fixunssfti_test.zig +++ /dev/null @@ -1,41 +0,0 @@ -const __fixunssfti = @import("fixunssfti.zig").__fixunssfti; -const testing = @import("std").testing; - -fn test__fixunssfti(a: f32, expected: u128) !void { - const x = __fixunssfti(a); - try testing.expect(x == expected); -} - -test "fixunssfti" { - try test__fixunssfti(0.0, 0); - - try test__fixunssfti(0.5, 0); - try test__fixunssfti(0.99, 0); - try test__fixunssfti(1.0, 1); - try test__fixunssfti(1.5, 1); - try test__fixunssfti(1.99, 1); - try test__fixunssfti(2.0, 2); - try test__fixunssfti(2.01, 2); - try test__fixunssfti(-0.5, 0); - try test__fixunssfti(-0.99, 0); - - try test__fixunssfti(-1.0, 0); - try test__fixunssfti(-1.5, 0); - try test__fixunssfti(-1.99, 0); - try test__fixunssfti(-2.0, 0); - try test__fixunssfti(-2.01, 0); - - try test__fixunssfti(0x1.FFFFFEp+63, 0xFFFFFF0000000000); - try test__fixunssfti(0x1.000000p+63, 0x8000000000000000); - try test__fixunssfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunssfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixunssfti(0x1.FFFFFEp+127, 0xFFFFFF00000000000000000000000000); - try test__fixunssfti(0x1.000000p+127, 0x80000000000000000000000000000000); - try test__fixunssfti(0x1.FFFFFEp+126, 0x7FFFFF80000000000000000000000000); - try test__fixunssfti(0x1.FFFFFCp+126, 0x7FFFFF00000000000000000000000000); - - try test__fixunssfti(-0x1.FFFFFEp+62, 0x0000000000000000); - try test__fixunssfti(-0x1.FFFFFCp+62, 0x0000000000000000); - try test__fixunssfti(-0x1.FFFFFEp+126, 0x0000000000000000); - try test__fixunssfti(-0x1.FFFFFCp+126, 0x0000000000000000); -} diff --git a/lib/std/special/compiler_rt/fixunstfdi.zig b/lib/std/special/compiler_rt/fixunstfdi.zig deleted file mode 100644 index 0db4987a08..0000000000 --- a/lib/std/special/compiler_rt/fixunstfdi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunstfdi(a: f128) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f128, u64, a); -} - -test { - _ = @import("fixunstfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunstfdi_test.zig b/lib/std/special/compiler_rt/fixunstfdi_test.zig deleted file mode 100644 index aa746799b7..0000000000 --- a/lib/std/special/compiler_rt/fixunstfdi_test.zig +++ /dev/null @@ -1,49 +0,0 @@ -const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi; -const testing = @import("std").testing; - -fn test__fixunstfdi(a: f128, expected: u64) !void { - const x = __fixunstfdi(a); - try testing.expect(x == expected); -} - -test "fixunstfdi" { - try test__fixunstfdi(0.0, 0); - - try test__fixunstfdi(0.5, 0); - try test__fixunstfdi(0.99, 0); - try test__fixunstfdi(1.0, 1); - try test__fixunstfdi(1.5, 1); - try test__fixunstfdi(1.99, 1); - try test__fixunstfdi(2.0, 2); - try test__fixunstfdi(2.01, 2); - try test__fixunstfdi(-0.5, 0); - try test__fixunstfdi(-0.99, 0); - try test__fixunstfdi(-1.0, 0); - try test__fixunstfdi(-1.5, 0); - try test__fixunstfdi(-1.99, 0); - try test__fixunstfdi(-2.0, 0); - try test__fixunstfdi(-2.01, 0); - - try test__fixunstfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunstfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunstfdi(-0x1.FFFFFEp+62, 0); - try test__fixunstfdi(-0x1.FFFFFCp+62, 0); - - try test__fixunstfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixunstfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - - try test__fixunstfdi(-0x1.FFFFFFFFFFFFFp+62, 0); - try test__fixunstfdi(-0x1.FFFFFFFFFFFFEp+62, 0); - - try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFEp+63, 0xFFFFFFFFFFFFFFFF); - try test__fixunstfdi(0x1.0000000000000002p+63, 0x8000000000000001); - try test__fixunstfdi(0x1.0000000000000000p+63, 0x8000000000000000); - try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFCp+62, 0x7FFFFFFFFFFFFFFF); - try test__fixunstfdi(0x1.FFFFFFFFFFFFFFF8p+62, 0x7FFFFFFFFFFFFFFE); - try test__fixunstfdi(0x1p+64, 0xFFFFFFFFFFFFFFFF); - - try test__fixunstfdi(-0x1.0000000000000000p+63, 0); - try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFFCp+62, 0); - try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFF8p+62, 0); -} diff --git a/lib/std/special/compiler_rt/fixunstfsi.zig b/lib/std/special/compiler_rt/fixunstfsi.zig deleted file mode 100644 index 8cedc07a35..0000000000 --- a/lib/std/special/compiler_rt/fixunstfsi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunstfsi(a: f128) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f128, u32, a); -} - -test { - _ = @import("fixunstfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunstfsi_test.zig b/lib/std/special/compiler_rt/fixunstfsi_test.zig deleted file mode 100644 index 206161bef5..0000000000 --- a/lib/std/special/compiler_rt/fixunstfsi_test.zig +++ /dev/null @@ -1,22 +0,0 @@ -const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi; -const testing = @import("std").testing; - -fn test__fixunstfsi(a: f128, expected: u32) !void { - const x = __fixunstfsi(a); - try testing.expect(x == expected); -} - -const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); - -test "fixunstfsi" { - try test__fixunstfsi(inf128, 0xffffffff); - try test__fixunstfsi(0, 0x0); - try test__fixunstfsi(0x1.23456789abcdefp+5, 0x24); - try test__fixunstfsi(0x1.23456789abcdefp-3, 0x0); - try test__fixunstfsi(0x1.23456789abcdefp+20, 0x123456); - try test__fixunstfsi(0x1.23456789abcdefp+40, 0xffffffff); - try test__fixunstfsi(0x1.23456789abcdefp+256, 0xffffffff); - try test__fixunstfsi(-0x1.23456789abcdefp+3, 0x0); - - try test__fixunstfsi(0x1p+32, 0xFFFFFFFF); -} diff --git a/lib/std/special/compiler_rt/fixunstfti.zig b/lib/std/special/compiler_rt/fixunstfti.zig deleted file mode 100644 index 0359327027..0000000000 --- a/lib/std/special/compiler_rt/fixunstfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunstfti(a: f128) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f128, u128, a); -} - -test { - _ = @import("fixunstfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunstfti_test.zig b/lib/std/special/compiler_rt/fixunstfti_test.zig deleted file mode 100644 index e35e2a65be..0000000000 --- a/lib/std/special/compiler_rt/fixunstfti_test.zig +++ /dev/null @@ -1,32 +0,0 @@ -const __fixunstfti = @import("fixunstfti.zig").__fixunstfti; -const testing = @import("std").testing; - -fn test__fixunstfti(a: f128, expected: u128) !void { - const x = __fixunstfti(a); - try testing.expect(x == expected); -} - -const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); - -test "fixunstfti" { - try test__fixunstfti(inf128, 0xffffffffffffffffffffffffffffffff); - - try test__fixunstfti(0.0, 0); - - try test__fixunstfti(0.5, 0); - try test__fixunstfti(0.99, 0); - try test__fixunstfti(1.0, 1); - try test__fixunstfti(1.5, 1); - try test__fixunstfti(1.99, 1); - try test__fixunstfti(2.0, 2); - try test__fixunstfti(2.01, 2); - try test__fixunstfti(-0.01, 0); - try test__fixunstfti(-0.99, 0); - - try test__fixunstfti(0x1p+128, 0xffffffffffffffffffffffffffffffff); - - try test__fixunstfti(0x1.FFFFFEp+126, 0x7fffff80000000000000000000000000); - try test__fixunstfti(0x1.FFFFFEp+127, 0xffffff00000000000000000000000000); - try test__fixunstfti(0x1.FFFFFEp+128, 0xffffffffffffffffffffffffffffffff); - try test__fixunstfti(0x1.FFFFFEp+129, 0xffffffffffffffffffffffffffffffff); -} diff --git a/lib/std/special/compiler_rt/floatXiYf.zig b/lib/std/special/compiler_rt/floatXiYf.zig new file mode 100644 index 0000000000..068413f715 --- /dev/null +++ b/lib/std/special/compiler_rt/floatXiYf.zig @@ -0,0 +1,222 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const std = @import("std"); +const math = std.math; +const expect = std.testing.expect; + +pub fn floatXiYf(comptime T: type, x: anytype) T { + @setRuntimeSafety(is_test); + + if (x == 0) return 0; + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const Z = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(x))); + const uT = std.meta.Int(.unsigned, @bitSizeOf(T)); + const inf = math.inf(T); + const float_bits = @bitSizeOf(T); + const int_bits = @bitSizeOf(@TypeOf(x)); + const exp_bits = math.floatExponentBits(T); + const fractional_bits = math.floatFractionalBits(T); + const exp_bias = math.maxInt(std.meta.Int(.unsigned, exp_bits - 1)); + const implicit_bit = if (T != f80) @as(uT, 1) << fractional_bits else 0; + const max_exp = exp_bias; + + // Sign + var abs_val = math.absCast(x); + const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0; + var result: uT = sign_bit; + + // Compute significand + var exp = int_bits - @clz(Z, abs_val) - 1; + if (int_bits <= fractional_bits or exp <= fractional_bits) { + const shift_amt = fractional_bits - @intCast(math.Log2Int(uT), exp); + + // Shift up result to line up with the significand - no rounding required + result = (@intCast(uT, abs_val) << shift_amt); + result ^= implicit_bit; // Remove implicit integer bit + } else { + var shift_amt = @intCast(math.Log2Int(Z), exp - fractional_bits); + const exact_tie: bool = @ctz(Z, abs_val) == shift_amt - 1; + + // Shift down result and remove implicit integer bit + result = @intCast(uT, (abs_val >> (shift_amt - 1))) ^ (implicit_bit << 1); + + // Round result, including round-to-even for exact ties + result = ((result + 1) >> 1) & ~@as(uT, @boolToInt(exact_tie)); + } + + // Compute exponent + if ((int_bits > max_exp) and (exp > max_exp)) // If exponent too large, overflow to infinity + return @bitCast(T, sign_bit | @bitCast(uT, inf)); + + result += (@as(uT, exp) + exp_bias) << math.floatMantissaBits(T); + + // If the result included a carry, we need to restore the explicit integer bit + if (T == f80) result |= 1 << fractional_bits; + + return @bitCast(T, sign_bit | result); +} + +// Conversion to f16 +pub fn __floatsihf(a: i32) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatunsihf(a: u32) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatdihf(a: i64) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatundihf(a: u64) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floattihf(a: i128) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatuntihf(a: u128) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +// Conversion to f32 +pub fn __floatsisf(a: i32) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatunsisf(a: u32) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatdisf(a: i64) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatundisf(a: u64) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floattisf(a: i128) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatuntisf(a: u128) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +// Conversion to f64 +pub fn __floatsidf(a: i32) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatunsidf(a: u32) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatdidf(a: i64) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatundidf(a: u64) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floattidf(a: i128) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatuntidf(a: u128) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +// Conversion to f80 +pub fn __floatsixf(a: i32) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatunsixf(a: u32) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatdixf(a: i64) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatundixf(a: u64) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floattixf(a: i128) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatuntixf(a: u128) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +// Conversion to f128 +pub fn __floatsitf(a: i32) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatunsitf(a: u32) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatditf(a: i64) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatunditf(a: u64) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floattitf(a: i128) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatuntitf(a: u128) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +// Conversion to f32 +pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +pub fn __aeabi_l2f(arg: i64) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +// Conversion to f64 +pub fn __aeabi_ui2d(arg: u32) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +pub fn __aeabi_ul2d(arg: u64) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +pub fn __aeabi_l2d(arg: i64) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +test { + _ = @import("floatXiYf_test.zig"); +} diff --git a/lib/std/special/compiler_rt/floatXiYf_test.zig b/lib/std/special/compiler_rt/floatXiYf_test.zig new file mode 100644 index 0000000000..cffa2a5b42 --- /dev/null +++ b/lib/std/special/compiler_rt/floatXiYf_test.zig @@ -0,0 +1,835 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const testing = std.testing; +const math = std.math; +const floatXiYf = @import("floatXiYf.zig").floatXiYf; + +// Conversion to f32 +const __floatsisf = @import("floatXiYf.zig").__floatsisf; +const __floatunsisf = @import("floatXiYf.zig").__floatunsisf; +const __floatdisf = @import("floatXiYf.zig").__floatdisf; +const __floatundisf = @import("floatXiYf.zig").__floatundisf; +const __floattisf = @import("floatXiYf.zig").__floattisf; +const __floatuntisf = @import("floatXiYf.zig").__floatuntisf; + +// Conversion to f64 +const __floatsidf = @import("floatXiYf.zig").__floatsidf; +const __floatunsidf = @import("floatXiYf.zig").__floatunsidf; +const __floatdidf = @import("floatXiYf.zig").__floatdidf; +const __floatundidf = @import("floatXiYf.zig").__floatundidf; +const __floattidf = @import("floatXiYf.zig").__floattidf; +const __floatuntidf = @import("floatXiYf.zig").__floatuntidf; + +// Conversion to f128 +const __floatsitf = @import("floatXiYf.zig").__floatsitf; +const __floatunsitf = @import("floatXiYf.zig").__floatunsitf; +const __floatditf = @import("floatXiYf.zig").__floatditf; +const __floatunditf = @import("floatXiYf.zig").__floatunditf; +const __floattitf = @import("floatXiYf.zig").__floattitf; +const __floatuntitf = @import("floatXiYf.zig").__floatuntitf; + +fn test__floatsisf(a: i32, expected: u32) !void { + const r = __floatsisf(a); + try std.testing.expect(@bitCast(u32, r) == expected); +} + +fn test_one_floatunsisf(a: u32, expected: u32) !void { + const r = __floatunsisf(a); + try std.testing.expect(@bitCast(u32, r) == expected); +} + +test "floatsisf" { + try test__floatsisf(0, 0x00000000); + try test__floatsisf(1, 0x3f800000); + try test__floatsisf(-1, 0xbf800000); + try test__floatsisf(0x7FFFFFFF, 0x4f000000); + try test__floatsisf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xcf000000); +} + +test "floatunsisf" { + // Test the produced bit pattern + try test_one_floatunsisf(0, 0); + try test_one_floatunsisf(1, 0x3f800000); + try test_one_floatunsisf(0x7FFFFFFF, 0x4f000000); + try test_one_floatunsisf(0x80000000, 0x4f000000); + try test_one_floatunsisf(0xFFFFFFFF, 0x4f800000); +} + +fn test__floatdisf(a: i64, expected: f32) !void { + const x = __floatdisf(a); + try testing.expect(x == expected); +} + +fn test__floatundisf(a: u64, expected: f32) !void { + try std.testing.expectEqual(expected, __floatundisf(a)); +} + +test "floatdisf" { + try test__floatdisf(0, 0.0); + try test__floatdisf(1, 1.0); + try test__floatdisf(2, 2.0); + try test__floatdisf(-1, -1.0); + try test__floatdisf(-2, -2.0); + try test__floatdisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatdisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000008000000000)), -0x1.FFFFFEp+62); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000010000000000)), -0x1.FFFFFCp+62); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000000000000000)), -0x1.000000p+63); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000000000000001)), -0x1.000000p+63); + try test__floatdisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floatdisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); +} + +test "floatundisf" { + try test__floatundisf(0, 0.0); + try test__floatundisf(1, 1.0); + try test__floatundisf(2, 2.0); + try test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatundisf(0x8000008000000000, 0x1p+63); + try test__floatundisf(0x8000010000000000, 0x1.000002p+63); + try test__floatundisf(0x8000000000000000, 0x1p+63); + try test__floatundisf(0x8000000000000001, 0x1p+63); + try test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); + try test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); + try test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); +} + +fn test__floattisf(a: i128, expected: f32) !void { + const x = __floattisf(a); + try testing.expect(x == expected); +} + +fn test__floatuntisf(a: u128, expected: f32) !void { + const x = __floatuntisf(a); + try testing.expect(x == expected); +} + +test "floattisf" { + try test__floattisf(0, 0.0); + + try test__floattisf(1, 1.0); + try test__floattisf(2, 2.0); + try test__floattisf(-1, -1.0); + try test__floattisf(-2, -2.0); + + try test__floattisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floattisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000008000000000), -0x1.FFFFFEp+62); + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000010000000000), -0x1.FFFFFCp+62); + + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000000), -0x1.000000p+63); + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000001), -0x1.000000p+63); + + try test__floattisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floattisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floattisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + + try test__floattisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); + + try test__floattisf(make_ti(0x0007FB72E8000000, 0), 0x1.FEDCBAp+114); + + try test__floattisf(make_ti(0x0007FB72EA000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72EB000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72EBFFFFFF, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72EC000000, 0), 0x1.FEDCBCp+114); + try test__floattisf(make_ti(0x0007FB72E8000001, 0), 0x1.FEDCBAp+114); + + try test__floattisf(make_ti(0x0007FB72E6000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E7000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E7FFFFFF, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E4000001, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E4000000, 0), 0x1.FEDCB8p+114); +} + +test "floatuntisf" { + try test__floatuntisf(0, 0.0); + + try test__floatuntisf(1, 1.0); + try test__floatuntisf(2, 2.0); + try test__floatuntisf(20, 20.0); + + try test__floatuntisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatuntisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + + try test__floatuntisf(make_uti(0x8000008000000000, 0), 0x1.000001p+127); + try test__floatuntisf(make_uti(0x8000000000000800, 0), 0x1.0p+127); + try test__floatuntisf(make_uti(0x8000010000000000, 0), 0x1.000002p+127); + + try test__floatuntisf(make_uti(0x8000000000000000, 0), 0x1.000000p+127); + + try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + + try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + + try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floatuntisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); + try test__floatuntisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); + + try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floatuntisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + + try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); + + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCB90000000000001), 0x1.FEDCBAp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBA0000000000000), 0x1.FEDCBAp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBAFFFFFFFFFFFFF), 0x1.FEDCBAp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBB0000000000000), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBB0000000000001), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBBFFFFFFFFFFFFF), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBC0000000000000), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBC0000000000001), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBD0000000000000), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBD0000000000001), 0x1.FEDCBEp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBDFFFFFFFFFFFFF), 0x1.FEDCBEp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBE0000000000000), 0x1.FEDCBEp+76); + + // Test overflow to infinity + try test__floatuntisf(@as(u128, math.maxInt(u128)), @bitCast(f32, math.inf(f32))); +} + +fn test_one_floatsidf(a: i32, expected: u64) !void { + const r = __floatsidf(a); + try std.testing.expect(@bitCast(u64, r) == expected); +} + +fn test_one_floatunsidf(a: u32, expected: u64) !void { + const r = __floatunsidf(a); + try std.testing.expect(@bitCast(u64, r) == expected); +} + +test "floatsidf" { + try test_one_floatsidf(0, 0x0000000000000000); + try test_one_floatsidf(1, 0x3ff0000000000000); + try test_one_floatsidf(-1, 0xbff0000000000000); + try test_one_floatsidf(0x7FFFFFFF, 0x41dfffffffc00000); + try test_one_floatsidf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc1e0000000000000); +} + +test "floatunsidf" { + try test_one_floatunsidf(0, 0x0000000000000000); + try test_one_floatunsidf(1, 0x3ff0000000000000); + try test_one_floatunsidf(0x7FFFFFFF, 0x41dfffffffc00000); + try test_one_floatunsidf(@intCast(u32, 0x80000000), 0x41e0000000000000); + try test_one_floatunsidf(@intCast(u32, 0xFFFFFFFF), 0x41efffffffe00000); +} + +fn test__floatdidf(a: i64, expected: f64) !void { + const r = __floatdidf(a); + try testing.expect(r == expected); +} + +fn test__floatundidf(a: u64, expected: f64) !void { + const r = __floatundidf(a); + try testing.expect(r == expected); +} + +test "floatdidf" { + try test__floatdidf(0, 0.0); + try test__floatdidf(1, 1.0); + try test__floatdidf(2, 2.0); + try test__floatdidf(20, 20.0); + try test__floatdidf(-1, -1.0); + try test__floatdidf(-2, -2.0); + try test__floatdidf(-20, -20.0); + try test__floatdidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatdidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatdidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatdidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000008000000000)), -0x1.FFFFFEp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000800)), -0x1.FFFFFFFFFFFFEp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000010000000000)), -0x1.FFFFFCp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000001000)), -0x1.FFFFFFFFFFFFCp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000000)), -0x1.000000p+63); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000001)), -0x1.000000p+63); // 0x8000000000000001 + try test__floatdidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatdidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatdidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatdidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatdidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatdidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + try test__floatdidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatdidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatdidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatdidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatdidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + try test__floatdidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floatdidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floatdidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floatdidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floatdidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); +} + +test "floatundidf" { + try test__floatundidf(0, 0.0); + try test__floatundidf(1, 1.0); + try test__floatundidf(2, 2.0); + try test__floatundidf(20, 20.0); + try test__floatundidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatundidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatundidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatundidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + try test__floatundidf(0x8000008000000000, 0x1.000001p+63); + try test__floatundidf(0x8000000000000800, 0x1.0000000000001p+63); + try test__floatundidf(0x8000010000000000, 0x1.000002p+63); + try test__floatundidf(0x8000000000001000, 0x1.0000000000002p+63); + try test__floatundidf(0x8000000000000000, 0x1p+63); + try test__floatundidf(0x8000000000000001, 0x1p+63); + try test__floatundidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatundidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatundidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatundidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatundidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatundidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + try test__floatundidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatundidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatundidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatundidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatundidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + try test__floatundidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floatundidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floatundidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floatundidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floatundidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); +} + +fn test__floattidf(a: i128, expected: f64) !void { + const x = __floattidf(a); + try testing.expect(x == expected); +} + +fn test__floatuntidf(a: u128, expected: f64) !void { + const x = __floatuntidf(a); + try testing.expect(x == expected); +} + +test "floattidf" { + try test__floattidf(0, 0.0); + + try test__floattidf(1, 1.0); + try test__floattidf(2, 2.0); + try test__floattidf(20, 20.0); + try test__floattidf(-1, -1.0); + try test__floattidf(-2, -2.0); + try test__floattidf(-20, -20.0); + + try test__floattidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floattidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floattidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floattidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + try test__floattidf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); + try test__floattidf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); + try test__floattidf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); + try test__floattidf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); + + try test__floattidf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); + try test__floattidf(make_ti(0x8000000000000001, 0), -0x1.000000p+127); + + try test__floattidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floattidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floattidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floattidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floattidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floattidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floattidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floattidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floattidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floattidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floattidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floattidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floattidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floattidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floattidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floattidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floattidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + try test__floattidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + try test__floattidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + try test__floattidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + try test__floattidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +test "floatuntidf" { + try test__floatuntidf(0, 0.0); + + try test__floatuntidf(1, 1.0); + try test__floatuntidf(2, 2.0); + try test__floatuntidf(20, 20.0); + + try test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + try test__floatuntidf(make_uti(0x8000008000000000, 0), 0x1.000001p+127); + try test__floatuntidf(make_uti(0x8000000000000800, 0), 0x1.0000000000001p+127); + try test__floatuntidf(make_uti(0x8000010000000000, 0), 0x1.000002p+127); + try test__floatuntidf(make_uti(0x8000000000001000, 0), 0x1.0000000000002p+127); + + try test__floatuntidf(make_uti(0x8000000000000000, 0), 0x1.000000p+127); + try test__floatuntidf(make_uti(0x8000000000000001, 0), 0x1.0000000000000002p+127); + + try test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floatuntidf(make_uti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +fn test__floatsitf(a: i32, expected: u128) !void { + const r = __floatsitf(a); + try std.testing.expect(@bitCast(u128, r) == expected); +} + +test "floatsitf" { + try test__floatsitf(0, 0); + try test__floatsitf(0x7FFFFFFF, 0x401dfffffffc00000000000000000000); + try test__floatsitf(0x12345678, 0x401b2345678000000000000000000000); + try test__floatsitf(-0x12345678, 0xc01b2345678000000000000000000000); + try test__floatsitf(@bitCast(i32, @intCast(u32, 0xffffffff)), 0xbfff0000000000000000000000000000); + try test__floatsitf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc01e0000000000000000000000000000); +} + +fn test__floatunsitf(a: u32, expected_hi: u64, expected_lo: u64) !void { + const x = __floatunsitf(a); + + const x_repr = @bitCast(u128, x); + const x_hi = @intCast(u64, x_repr >> 64); + const x_lo = @truncate(u64, x_repr); + + if (x_hi == expected_hi and x_lo == expected_lo) { + return; + } + // nan repr + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { + return; + } + } + + @panic("__floatunsitf test failure"); +} + +test "floatunsitf" { + try test__floatunsitf(0x7fffffff, 0x401dfffffffc0000, 0x0); + try test__floatunsitf(0, 0x0, 0x0); + try test__floatunsitf(0xffffffff, 0x401efffffffe0000, 0x0); + try test__floatunsitf(0x12345678, 0x401b234567800000, 0x0); +} + +fn test__floatditf(a: i64, expected: f128) !void { + const x = __floatditf(a); + try testing.expect(x == expected); +} + +fn test__floatunditf(a: u64, expected_hi: u64, expected_lo: u64) !void { + const x = __floatunditf(a); + + const x_repr = @bitCast(u128, x); + const x_hi = @intCast(u64, x_repr >> 64); + const x_lo = @truncate(u64, x_repr); + + if (x_hi == expected_hi and x_lo == expected_lo) { + return; + } + // nan repr + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { + return; + } + } + + @panic("__floatunditf test failure"); +} + +test "floatditf" { + try test__floatditf(0x7fffffffffffffff, make_tf(0x403dffffffffffff, 0xfffc000000000000)); + try test__floatditf(0x123456789abcdef1, make_tf(0x403b23456789abcd, 0xef10000000000000)); + try test__floatditf(0x2, make_tf(0x4000000000000000, 0x0)); + try test__floatditf(0x1, make_tf(0x3fff000000000000, 0x0)); + try test__floatditf(0x0, make_tf(0x0, 0x0)); + try test__floatditf(@bitCast(i64, @as(u64, 0xffffffffffffffff)), make_tf(0xbfff000000000000, 0x0)); + try test__floatditf(@bitCast(i64, @as(u64, 0xfffffffffffffffe)), make_tf(0xc000000000000000, 0x0)); + try test__floatditf(-0x123456789abcdef1, make_tf(0xc03b23456789abcd, 0xef10000000000000)); + try test__floatditf(@bitCast(i64, @as(u64, 0x8000000000000000)), make_tf(0xc03e000000000000, 0x0)); +} + +test "floatunditf" { + try test__floatunditf(0xffffffffffffffff, 0x403effffffffffff, 0xfffe000000000000); + try test__floatunditf(0xfffffffffffffffe, 0x403effffffffffff, 0xfffc000000000000); + try test__floatunditf(0x8000000000000000, 0x403e000000000000, 0x0); + try test__floatunditf(0x7fffffffffffffff, 0x403dffffffffffff, 0xfffc000000000000); + try test__floatunditf(0x123456789abcdef1, 0x403b23456789abcd, 0xef10000000000000); + try test__floatunditf(0x2, 0x4000000000000000, 0x0); + try test__floatunditf(0x1, 0x3fff000000000000, 0x0); + try test__floatunditf(0x0, 0x0, 0x0); +} + +fn test__floattitf(a: i128, expected: f128) !void { + const x = __floattitf(a); + try testing.expect(x == expected); +} + +fn test__floatuntitf(a: u128, expected: f128) !void { + const x = __floatuntitf(a); + try testing.expect(x == expected); +} + +test "floattitf" { + try test__floattitf(0, 0.0); + + try test__floattitf(1, 1.0); + try test__floattitf(2, 2.0); + try test__floattitf(20, 20.0); + try test__floattitf(-1, -1.0); + try test__floattitf(-2, -2.0); + try test__floattitf(-20, -20.0); + + try test__floattitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floattitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floattitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floattitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + try test__floattitf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); + try test__floattitf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); + try test__floattitf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); + try test__floattitf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); + + try test__floattitf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); + try test__floattitf(make_ti(0x8000000000000001, 0), -0x1.FFFFFFFFFFFFFFFCp+126); + + try test__floattitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floattitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floattitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floattitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floattitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floattitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floattitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floattitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floattitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floattitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floattitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floattitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floattitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); + try test__floattitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); + try test__floattitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); + try test__floattitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); + try test__floattitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); + try test__floattitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); + try test__floattitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); + try test__floattitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); + try test__floattitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); + try test__floattitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); + try test__floattitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); + try test__floattitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); + try test__floattitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); + try test__floattitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floattitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floattitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); + try test__floattitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); + try test__floattitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); + try test__floattitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); + try test__floattitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); + try test__floattitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); + try test__floattitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); + try test__floattitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); + try test__floattitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); + try test__floattitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); + try test__floattitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); + try test__floattitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); + try test__floattitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); + try test__floattitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); + + try test__floattitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); + + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); +} + +test "floatuntitf" { + try test__floatuntitf(0, 0.0); + + try test__floatuntitf(1, 1.0); + try test__floatuntitf(2, 2.0); + try test__floatuntitf(20, 20.0); + + try test__floatuntitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatuntitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatuntitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatuntitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + try test__floatuntitf(0x7FFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFEp+59); + try test__floatuntitf(0xFFFFFFFFFFFFFFFE, 0xF.FFFFFFFFFFFFFFEp+60); + try test__floatuntitf(0xFFFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFFp+60); + + try test__floatuntitf(0x8000008000000000, 0x8.000008p+60); + try test__floatuntitf(0x8000000000000800, 0x8.0000000000008p+60); + try test__floatuntitf(0x8000010000000000, 0x8.00001p+60); + try test__floatuntitf(0x8000000000001000, 0x8.000000000001p+60); + + try test__floatuntitf(0x8000000000000000, 0x8p+60); + try test__floatuntitf(0x8000000000000001, 0x8.000000000000001p+60); + + try test__floatuntitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatuntitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatuntitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatuntitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatuntitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floatuntitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatuntitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatuntitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatuntitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatuntitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floatuntitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatuntitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); + try test__floatuntitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); + try test__floatuntitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); + try test__floatuntitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); + try test__floatuntitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); + try test__floatuntitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); + try test__floatuntitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); + try test__floatuntitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); + try test__floatuntitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); + try test__floatuntitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); + try test__floatuntitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); + try test__floatuntitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); + try test__floatuntitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); + try test__floatuntitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floatuntitf(make_uti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floatuntitf(make_uti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); + try test__floatuntitf(make_uti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); + try test__floatuntitf(make_uti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); + try test__floatuntitf(make_uti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); + + try test__floatuntitf(make_uti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); + + try test__floatuntitf(make_uti(0xFFFFFFFFFFFFFFFF, 0x0000000000000000), 0x1.FFFFFFFFFFFFFFFEp+127); + try test__floatuntitf(make_uti(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 0x1.0000000000000000p+128); + + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); +} + +fn make_ti(high: u64, low: u64) i128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(i128, result); +} + +fn make_uti(high: u64, low: u64) u128 { + var result: u128 = high; + result <<= 64; + result |= low; + return result; +} + +fn make_tf(high: u64, low: u64) f128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(f128, result); +} + +test "conversion to f16" { + try testing.expect(floatXiYf(f16, @as(u32, 0)) == 0.0); + try testing.expect(floatXiYf(f16, @as(u32, 1)) == 1.0); + try testing.expect(floatXiYf(f16, @as(u32, 65504)) == 65504); + try testing.expect(floatXiYf(f16, @as(u32, 65504 + (1 << 4))) == math.inf(f16)); +} + +test "conversion to f32" { + try testing.expect(floatXiYf(f32, @as(u32, 0)) == 0.0); + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u32))) != 1.0); + try testing.expect(floatXiYf(f32, @as(i32, math.minInt(i32))) != 1.0); + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24))) == math.maxInt(u24)); + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 1) == math.maxInt(u24) + 1); // 0x100_0000 - Exact + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 2) == math.maxInt(u24) + 1); // 0x100_0001 - Tie: Rounds down to even + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 3) == math.maxInt(u24) + 3); // 0x100_0002 - Exact + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 4) == math.maxInt(u24) + 5); // 0x100_0003 - Tie: Rounds up to even + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 5) == math.maxInt(u24) + 5); // 0x100_0004 - Exact +} + +test "conversion to f80" { + if (builtin.zig_backend == .stage1 and builtin.cpu.arch != .x86_64) + return error.SkipZigTest; // https://github.com/ziglang/zig/issues/11408 + + try testing.expect(floatXiYf(f80, @as(i80, -12)) == -12); + try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u64, math.maxInt(u64)) + 0)) == math.maxInt(u64) + 0); + try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); + + try testing.expect(floatXiYf(f80, @as(u32, 0)) == 0.0); + try testing.expect(floatXiYf(f80, @as(u32, 1)) == 1.0); + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u32, math.maxInt(u24)) + 0)) == math.maxInt(u24)); + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 0)) == math.maxInt(u64)); + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); // Exact + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 2)) == math.maxInt(u64) + 1); // Rounds down + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 3)) == math.maxInt(u64) + 3); // Tie - Exact + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 4)) == math.maxInt(u64) + 5); // Rounds up + + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 0)) == math.maxInt(u65) + 1); // Rounds up + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 1)) == math.maxInt(u65) + 1); // Exact + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 2)) == math.maxInt(u65) + 1); // Rounds down + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 3)) == math.maxInt(u65) + 1); // Tie - Rounds down + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 4)) == math.maxInt(u65) + 5); // Rounds up + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 5)) == math.maxInt(u65) + 5); // Exact +} diff --git a/lib/std/special/compiler_rt/floatXisf.zig b/lib/std/special/compiler_rt/floatXisf.zig deleted file mode 100644 index de3f4495cb..0000000000 --- a/lib/std/special/compiler_rt/floatXisf.zig +++ /dev/null @@ -1,90 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const FLT_MANT_DIG = 24; - -inline fn floatXisf(comptime T: type, arg: T) f32 { - @setRuntimeSafety(builtin.is_test); - - const bits = @typeInfo(T).Int.bits; - const Z = std.meta.Int(.unsigned, bits); - const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - - if (arg == 0) { - return @as(f32, 0.0); - } - - var ai = arg; - const N: u32 = bits; - const si = ai >> @intCast(S, (N - 1)); - ai = ((ai ^ si) -% si); - var a = @bitCast(Z, ai); - - const sd = @bitCast(i32, N - @clz(Z, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - - if (sd > FLT_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit FLT_MANT_DIG-1 bits to the right of 1 - // Q = bit FLT_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - FLT_MANT_DIG + 1 => { - a <<= 1; - }, - FLT_MANT_DIG + 2 => {}, - else => { - const shift1_amt = @intCast(i32, sd - (FLT_MANT_DIG + 2)); - const shift1_amt_u7 = @intCast(S, shift1_amt); - - const shift2_amt = @intCast(i32, N + (FLT_MANT_DIG + 2)) - sd; - const shift2_amt_u7 = @intCast(S, shift2_amt); - - a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(Z, maxInt(Z)) >> shift2_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (@as(Z, 1) << FLT_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to FLT_MANT_DIG bits - } else { - a <<= @intCast(S, FLT_MANT_DIG - sd); - // a is now rounded to FLT_MANT_DIG bits - } - - const s = @bitCast(Z, arg) >> (@typeInfo(T).Int.bits - 32); - const r = (@intCast(u32, s) & 0x80000000) | // sign - (@intCast(u32, (e + 127)) << 23) | // exponent - (@truncate(u32, a) & 0x007fffff); // mantissa-high - - return @bitCast(f32, r); -} - -pub fn __floatdisf(arg: i64) callconv(.C) f32 { - return floatXisf(i64, arg); -} - -pub fn __floattisf(arg: i128) callconv(.C) f32 { - return floatXisf(i128, arg); -} - -pub fn __aeabi_l2f(arg: i64) callconv(.AAPCS) f32 { - return floatXisf(i64, arg); -} - -test { - _ = @import("floattisf_test.zig"); -} -test { - _ = @import("floattisf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatdidf.zig b/lib/std/special/compiler_rt/floatdidf.zig deleted file mode 100644 index 16a514b615..0000000000 --- a/lib/std/special/compiler_rt/floatdidf.zig +++ /dev/null @@ -1,27 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const twop52: f64 = 0x1.0p52; -const twop32: f64 = 0x1.0p32; - -pub fn __floatdidf(a: i64) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); - - if (a == 0) return 0; - - var low = @bitCast(i64, twop52); - const high = @intToFloat(f64, @truncate(i32, a >> 32)) * twop32; - - low |= @bitCast(i64, a & 0xFFFFFFFF); - - return (high - twop52) + @bitCast(f64, low); -} - -pub fn __aeabi_l2d(arg: i64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatdidf, .{arg}); -} - -test { - _ = @import("floatdidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatdidf_test.zig b/lib/std/special/compiler_rt/floatdidf_test.zig deleted file mode 100644 index 6b01ac5f3f..0000000000 --- a/lib/std/special/compiler_rt/floatdidf_test.zig +++ /dev/null @@ -1,53 +0,0 @@ -const __floatdidf = @import("floatdidf.zig").__floatdidf; -const testing = @import("std").testing; - -fn test__floatdidf(a: i64, expected: f64) !void { - const r = __floatdidf(a); - try testing.expect(r == expected); -} - -test "floatdidf" { - try test__floatdidf(0, 0.0); - try test__floatdidf(1, 1.0); - try test__floatdidf(2, 2.0); - try test__floatdidf(20, 20.0); - try test__floatdidf(-1, -1.0); - try test__floatdidf(-2, -2.0); - try test__floatdidf(-20, -20.0); - try test__floatdidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatdidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatdidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatdidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000008000000000)), -0x1.FFFFFEp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000800)), -0x1.FFFFFFFFFFFFEp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000010000000000)), -0x1.FFFFFCp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000001000)), -0x1.FFFFFFFFFFFFCp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000000)), -0x1.000000p+63); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000001)), -0x1.000000p+63); - try test__floatdidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatdidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatdidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatdidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatdidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatdidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - try test__floatdidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatdidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatdidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatdidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatdidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - try test__floatdidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floatdidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floatdidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floatdidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floatdidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); -} diff --git a/lib/std/special/compiler_rt/floatdisf_test.zig b/lib/std/special/compiler_rt/floatdisf_test.zig deleted file mode 100644 index 010c4faddd..0000000000 --- a/lib/std/special/compiler_rt/floatdisf_test.zig +++ /dev/null @@ -1,32 +0,0 @@ -const __floatdisf = @import("floatXisf.zig").__floatdisf; -const testing = @import("std").testing; - -fn test__floatdisf(a: i64, expected: f32) !void { - const x = __floatdisf(a); - try testing.expect(x == expected); -} - -test "floatdisf" { - try test__floatdisf(0, 0.0); - try test__floatdisf(1, 1.0); - try test__floatdisf(2, 2.0); - try test__floatdisf(-1, -1.0); - try test__floatdisf(-2, -2.0); - try test__floatdisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatdisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatdisf(0x8000008000000000, -0x1.FFFFFEp+62); - try test__floatdisf(0x8000010000000000, -0x1.FFFFFCp+62); - try test__floatdisf(0x8000000000000000, -0x1.000000p+63); - try test__floatdisf(0x8000000000000001, -0x1.000000p+63); - try test__floatdisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floatdisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); -} diff --git a/lib/std/special/compiler_rt/floatditf.zig b/lib/std/special/compiler_rt/floatditf.zig deleted file mode 100644 index fef696e6fb..0000000000 --- a/lib/std/special/compiler_rt/floatditf.zig +++ /dev/null @@ -1,38 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const significandBits = 112; -const exponentBias = 16383; -const implicitBit = (@as(u128, 1) << significandBits); - -pub fn __floatditf(arg: i64) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - // All other cases begin by extracting the sign and absolute value of a - var sign: u128 = 0; - var aAbs = @bitCast(u64, arg); - if (arg < 0) { - sign = 1 << 127; - aAbs = ~@bitCast(u64, arg) + 1; - } - - // Exponent of (fp_t)a is the width of abs(a). - const exponent = 63 - @clz(u64, aAbs); - var result: u128 = undefined; - - // Shift a into the significand field, rounding if it is a right-shift - const shift = significandBits - exponent; - result = @as(u128, aAbs) << shift ^ implicitBit; - - result += (@as(u128, exponent) + exponentBias) << significandBits; - return @bitCast(f128, result | sign); -} - -test { - _ = @import("floatditf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatditf_test.zig b/lib/std/special/compiler_rt/floatditf_test.zig deleted file mode 100644 index cf8a81c68c..0000000000 --- a/lib/std/special/compiler_rt/floatditf_test.zig +++ /dev/null @@ -1,26 +0,0 @@ -const __floatditf = @import("floatditf.zig").__floatditf; -const testing = @import("std").testing; - -fn test__floatditf(a: i64, expected: f128) !void { - const x = __floatditf(a); - try testing.expect(x == expected); -} - -test "floatditf" { - try test__floatditf(0x7fffffffffffffff, make_ti(0x403dffffffffffff, 0xfffc000000000000)); - try test__floatditf(0x123456789abcdef1, make_ti(0x403b23456789abcd, 0xef10000000000000)); - try test__floatditf(0x2, make_ti(0x4000000000000000, 0x0)); - try test__floatditf(0x1, make_ti(0x3fff000000000000, 0x0)); - try test__floatditf(0x0, make_ti(0x0, 0x0)); - try test__floatditf(@bitCast(i64, @as(u64, 0xffffffffffffffff)), make_ti(0xbfff000000000000, 0x0)); - try test__floatditf(@bitCast(i64, @as(u64, 0xfffffffffffffffe)), make_ti(0xc000000000000000, 0x0)); - try test__floatditf(-0x123456789abcdef1, make_ti(0xc03b23456789abcd, 0xef10000000000000)); - try test__floatditf(@bitCast(i64, @as(u64, 0x8000000000000000)), make_ti(0xc03e000000000000, 0x0)); -} - -fn make_ti(high: u64, low: u64) f128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(f128, result); -} diff --git a/lib/std/special/compiler_rt/floatfmodl_test.zig b/lib/std/special/compiler_rt/floatfmodl_test.zig deleted file mode 100644 index 58636ef6f7..0000000000 --- a/lib/std/special/compiler_rt/floatfmodl_test.zig +++ /dev/null @@ -1,46 +0,0 @@ -const std = @import("std"); -const fmodl = @import("floatfmodl.zig"); -const testing = std.testing; - -fn test_fmodl(a: f128, b: f128, exp: f128) !void { - const res = fmodl.fmodl(a, b); - try testing.expect(exp == res); -} - -fn test_fmodl_nans() !void { - try testing.expect(std.math.isNan(fmodl.fmodl(1.0, std.math.nan_f128))); - try testing.expect(std.math.isNan(fmodl.fmodl(1.0, -std.math.nan_f128))); - try testing.expect(std.math.isNan(fmodl.fmodl(std.math.nan_f128, 1.0))); - try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.nan_f128, 1.0))); -} - -fn test_fmodl_infs() !void { - try testing.expect(fmodl.fmodl(1.0, std.math.inf_f128) == 1.0); - try testing.expect(fmodl.fmodl(1.0, -std.math.inf_f128) == 1.0); - try testing.expect(std.math.isNan(fmodl.fmodl(std.math.inf_f128, 1.0))); - try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.inf_f128, 1.0))); -} - -test "fmodl" { - try test_fmodl(6.8, 4.0, 2.8); - try test_fmodl(6.8, -4.0, 2.8); - try test_fmodl(-6.8, 4.0, -2.8); - try test_fmodl(-6.8, -4.0, -2.8); - try test_fmodl(3.0, 2.0, 1.0); - try test_fmodl(-5.0, 3.0, -2.0); - try test_fmodl(3.0, 2.0, 1.0); - try test_fmodl(1.0, 2.0, 1.0); - try test_fmodl(0.0, 1.0, 0.0); - try test_fmodl(-0.0, 1.0, -0.0); - try test_fmodl(7046119.0, 5558362.0, 1487757.0); - try test_fmodl(9010357.0, 1957236.0, 1181413.0); - - // Denormals - const a: f128 = 0xedcb34a235253948765432134674p-16494; - const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; - const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; - try test_fmodl(a, b, exp); - - try test_fmodl_nans(); - try test_fmodl_infs(); -} diff --git a/lib/std/special/compiler_rt/floatfmodl.zig b/lib/std/special/compiler_rt/floatfmodq.zig index 942a7c1125..b8da727c90 100644 --- a/lib/std/special/compiler_rt/floatfmodl.zig +++ b/lib/std/special/compiler_rt/floatfmodq.zig @@ -1,9 +1,9 @@ const builtin = @import("builtin"); const std = @import("std"); -// fmodl - floating modulo large, returns the remainder of division for f128 types +// fmodq - floating modulo large, returns the remainder of division for f128 types // Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits -pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { +pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { @setRuntimeSafety(builtin.is_test); var amod = a; var bmod = b; @@ -30,9 +30,9 @@ pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { var expB = bPtr_u16[exp_and_sign_index] & 0x7fff; // There are 3 cases where the answer is undefined, check for: - // - fmodl(val, 0) - // - fmodl(val, NaN) - // - fmodl(inf, val) + // - fmodq(val, 0) + // - fmodq(val, NaN) + // - fmodq(inf, val) // The sign on checked values does not matter. // Doing (a * b) / (a * b) procudes undefined results // because the three cases always produce undefined calculations: @@ -122,5 +122,5 @@ pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { } test { - _ = @import("floatfmodl_test.zig"); + _ = @import("floatfmodq_test.zig"); } diff --git a/lib/std/special/compiler_rt/floatfmodq_test.zig b/lib/std/special/compiler_rt/floatfmodq_test.zig new file mode 100644 index 0000000000..a272b797e3 --- /dev/null +++ b/lib/std/special/compiler_rt/floatfmodq_test.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const fmodq = @import("floatfmodq.zig"); +const testing = std.testing; + +fn test_fmodq(a: f128, b: f128, exp: f128) !void { + const res = fmodq.fmodq(a, b); + try testing.expect(exp == res); +} + +fn test_fmodq_nans() !void { + try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128)))); + try testing.expect(std.math.isNan(fmodq.fmodq(1.0, -std.math.nan(f128)))); + try testing.expect(std.math.isNan(fmodq.fmodq(std.math.nan(f128), 1.0))); + try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0))); +} + +fn test_fmodq_infs() !void { + try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0); + try testing.expect(fmodq.fmodq(1.0, -std.math.inf(f128)) == 1.0); + try testing.expect(std.math.isNan(fmodq.fmodq(std.math.inf(f128), 1.0))); + try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.inf(f128), 1.0))); +} + +test "fmodq" { + try test_fmodq(6.8, 4.0, 2.8); + try test_fmodq(6.8, -4.0, 2.8); + try test_fmodq(-6.8, 4.0, -2.8); + try test_fmodq(-6.8, -4.0, -2.8); + try test_fmodq(3.0, 2.0, 1.0); + try test_fmodq(-5.0, 3.0, -2.0); + try test_fmodq(3.0, 2.0, 1.0); + try test_fmodq(1.0, 2.0, 1.0); + try test_fmodq(0.0, 1.0, 0.0); + try test_fmodq(-0.0, 1.0, -0.0); + try test_fmodq(7046119.0, 5558362.0, 1487757.0); + try test_fmodq(9010357.0, 1957236.0, 1181413.0); + + // Denormals + const a: f128 = 0xedcb34a235253948765432134674p-16494; + const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; + const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; + try test_fmodq(a, b, exp); + + try test_fmodq_nans(); + try test_fmodq_infs(); +} diff --git a/lib/std/special/compiler_rt/floatsiXf.zig b/lib/std/special/compiler_rt/floatsiXf.zig deleted file mode 100644 index ef551d1911..0000000000 --- a/lib/std/special/compiler_rt/floatsiXf.zig +++ /dev/null @@ -1,120 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -inline fn floatsiXf(comptime T: type, a: i32) T { - @setRuntimeSafety(builtin.is_test); - - const bits = @typeInfo(T).Float.bits; - const Z = std.meta.Int(.unsigned, bits); - const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - - if (a == 0) { - return @as(T, 0.0); - } - - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); - const exponentBias = ((1 << exponentBits - 1) - 1); - - const implicitBit = @as(Z, 1) << significandBits; - const signBit = @as(Z, 1 << bits - 1); - - const sign = a >> 31; - // Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31). - const abs_a = (a ^ sign) -% sign; - // The exponent is the width of abs(a) - const exp = @as(Z, 31 - @clz(i32, abs_a)); - - const sign_bit = if (sign < 0) signBit else 0; - - var mantissa: Z = undefined; - // Shift a into the significand field and clear the implicit bit. - if (exp <= significandBits) { - // No rounding needed - const shift = @intCast(S, significandBits - exp); - mantissa = @intCast(Z, @bitCast(u32, abs_a)) << shift ^ implicitBit; - } else { - const shift = @intCast(S, exp - significandBits); - // Round to the nearest number after truncation - mantissa = @intCast(Z, @bitCast(u32, abs_a)) >> shift ^ implicitBit; - // Align to the left and check if the truncated part is halfway over - const round = @bitCast(u32, abs_a) << @intCast(u5, 31 - shift); - mantissa += @boolToInt(round > 0x80000000); - // Tie to even - mantissa += mantissa & 1; - } - - // Use the addition instead of a or since we may have a carry from the - // mantissa to the exponent - var result = mantissa; - result += (exp + exponentBias) << significandBits; - result += sign_bit; - - return @bitCast(T, result); -} - -pub fn __floatsisf(arg: i32) callconv(.C) f32 { - @setRuntimeSafety(builtin.is_test); - return floatsiXf(f32, arg); -} - -pub fn __floatsidf(arg: i32) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); - return floatsiXf(f64, arg); -} - -pub fn __floatsitf(arg: i32) callconv(.C) f128 { - @setRuntimeSafety(builtin.is_test); - return floatsiXf(f128, arg); -} - -pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return floatsiXf(f64, arg); -} - -pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return floatsiXf(f32, arg); -} - -fn test_one_floatsitf(a: i32, expected: u128) !void { - const r = __floatsitf(a); - try std.testing.expect(@bitCast(u128, r) == expected); -} - -fn test_one_floatsidf(a: i32, expected: u64) !void { - const r = __floatsidf(a); - try std.testing.expect(@bitCast(u64, r) == expected); -} - -fn test_one_floatsisf(a: i32, expected: u32) !void { - const r = __floatsisf(a); - try std.testing.expect(@bitCast(u32, r) == expected); -} - -test "floatsidf" { - try test_one_floatsidf(0, 0x0000000000000000); - try test_one_floatsidf(1, 0x3ff0000000000000); - try test_one_floatsidf(-1, 0xbff0000000000000); - try test_one_floatsidf(0x7FFFFFFF, 0x41dfffffffc00000); - try test_one_floatsidf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc1e0000000000000); -} - -test "floatsisf" { - try test_one_floatsisf(0, 0x00000000); - try test_one_floatsisf(1, 0x3f800000); - try test_one_floatsisf(-1, 0xbf800000); - try test_one_floatsisf(0x7FFFFFFF, 0x4f000000); - try test_one_floatsisf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xcf000000); -} - -test "floatsitf" { - try test_one_floatsitf(0, 0); - try test_one_floatsitf(0x7FFFFFFF, 0x401dfffffffc00000000000000000000); - try test_one_floatsitf(0x12345678, 0x401b2345678000000000000000000000); - try test_one_floatsitf(-0x12345678, 0xc01b2345678000000000000000000000); - try test_one_floatsitf(@bitCast(i32, @intCast(u32, 0xffffffff)), 0xbfff0000000000000000000000000000); - try test_one_floatsitf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc01e0000000000000000000000000000); -} diff --git a/lib/std/special/compiler_rt/floattidf.zig b/lib/std/special/compiler_rt/floattidf.zig deleted file mode 100644 index 85f84a23d6..0000000000 --- a/lib/std/special/compiler_rt/floattidf.zig +++ /dev/null @@ -1,71 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const DBL_MANT_DIG = 53; - -pub fn __floattidf(arg: i128) callconv(.C) f64 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var ai = arg; - const N: u32 = 128; - const si = ai >> @intCast(u7, (N - 1)); - ai = ((ai ^ si) -% si); - var a = @bitCast(u128, ai); - - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > DBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit DBL_MANT_DIG-1 bits to the right of 1 - // Q = bit DBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - DBL_MANT_DIG + 1 => { - a <<= 1; - }, - DBL_MANT_DIG + 2 => {}, - else => { - const shift1_amt = @intCast(i32, sd - (DBL_MANT_DIG + 2)); - const shift1_amt_u7 = @intCast(u7, shift1_amt); - - const shift2_amt = @intCast(i32, N + (DBL_MANT_DIG + 2)) - sd; - const shift2_amt_u7 = @intCast(u7, shift2_amt); - - a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, maxInt(u128)) >> shift2_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << DBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to DBL_MANT_DIG bits - } else { - a <<= @intCast(u7, DBL_MANT_DIG - sd); - // a is now rounded to DBL_MANT_DIG bits - } - - const s = @bitCast(u128, arg) >> (128 - 32); - const high: u64 = (@intCast(u64, s) & 0x80000000) | // sign - (@intCast(u32, (e + 1023)) << 20) | // exponent - (@truncate(u32, a >> 32) & 0x000fffff); // mantissa-high - const low: u64 = @truncate(u32, a); // mantissa-low - - return @bitCast(f64, low | (high << 32)); -} - -test { - _ = @import("floattidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floattidf_test.zig b/lib/std/special/compiler_rt/floattidf_test.zig deleted file mode 100644 index 62b131744f..0000000000 --- a/lib/std/special/compiler_rt/floattidf_test.zig +++ /dev/null @@ -1,84 +0,0 @@ -const __floattidf = @import("floattidf.zig").__floattidf; -const testing = @import("std").testing; - -fn test__floattidf(a: i128, expected: f64) !void { - const x = __floattidf(a); - try testing.expect(x == expected); -} - -test "floattidf" { - try test__floattidf(0, 0.0); - - try test__floattidf(1, 1.0); - try test__floattidf(2, 2.0); - try test__floattidf(20, 20.0); - try test__floattidf(-1, -1.0); - try test__floattidf(-2, -2.0); - try test__floattidf(-20, -20.0); - - try test__floattidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floattidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floattidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floattidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - - try test__floattidf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); - try test__floattidf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); - try test__floattidf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); - try test__floattidf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); - - try test__floattidf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); - try test__floattidf(make_ti(0x8000000000000001, 0), -0x1.000000p+127); - - try test__floattidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floattidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floattidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floattidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floattidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floattidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floattidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floattidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floattidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floattidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floattidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floattidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floattidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floattidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floattidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floattidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floattidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); - try test__floattidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); - try test__floattidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); - try test__floattidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); - try test__floattidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); -} - -fn make_ti(high: u64, low: u64) i128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(i128, result); -} diff --git a/lib/std/special/compiler_rt/floattisf_test.zig b/lib/std/special/compiler_rt/floattisf_test.zig deleted file mode 100644 index 30b36c3f9f..0000000000 --- a/lib/std/special/compiler_rt/floattisf_test.zig +++ /dev/null @@ -1,60 +0,0 @@ -const __floattisf = @import("floatXisf.zig").__floattisf; -const testing = @import("std").testing; - -fn test__floattisf(a: i128, expected: f32) !void { - const x = __floattisf(a); - try testing.expect(x == expected); -} - -test "floattisf" { - try test__floattisf(0, 0.0); - - try test__floattisf(1, 1.0); - try test__floattisf(2, 2.0); - try test__floattisf(-1, -1.0); - try test__floattisf(-2, -2.0); - - try test__floattisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floattisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000008000000000), -0x1.FFFFFEp+62); - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000010000000000), -0x1.FFFFFCp+62); - - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000000), -0x1.000000p+63); - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000001), -0x1.000000p+63); - - try test__floattisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floattisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floattisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - - try test__floattisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); - - try test__floattisf(make_ti(0x0007FB72E8000000, 0), 0x1.FEDCBAp+114); - - try test__floattisf(make_ti(0x0007FB72EA000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72EB000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72EBFFFFFF, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72EC000000, 0), 0x1.FEDCBCp+114); - try test__floattisf(make_ti(0x0007FB72E8000001, 0), 0x1.FEDCBAp+114); - - try test__floattisf(make_ti(0x0007FB72E6000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E7000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E7FFFFFF, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E4000001, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E4000000, 0), 0x1.FEDCB8p+114); -} - -fn make_ti(high: u64, low: u64) i128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(i128, result); -} diff --git a/lib/std/special/compiler_rt/floattitf.zig b/lib/std/special/compiler_rt/floattitf.zig deleted file mode 100644 index aa83f6686e..0000000000 --- a/lib/std/special/compiler_rt/floattitf.zig +++ /dev/null @@ -1,71 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const LDBL_MANT_DIG = 113; - -pub fn __floattitf(arg: i128) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var ai = arg; - const N: u32 = 128; - const si = ai >> @intCast(u7, (N - 1)); - ai = ((ai ^ si) -% si); - var a = @bitCast(u128, ai); - - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > LDBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit LDBL_MANT_DIG-1 bits to the right of 1 - // Q = bit LDBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - LDBL_MANT_DIG + 1 => { - a <<= 1; - }, - LDBL_MANT_DIG + 2 => {}, - else => { - const shift1_amt = @intCast(i32, sd - (LDBL_MANT_DIG + 2)); - const shift1_amt_u7 = @intCast(u7, shift1_amt); - - const shift2_amt = @intCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; - const shift2_amt_u7 = @intCast(u7, shift2_amt); - - a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, maxInt(u128)) >> shift2_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << LDBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to LDBL_MANT_DIG bits - } else { - a <<= @intCast(u7, LDBL_MANT_DIG - sd); - // a is now rounded to LDBL_MANT_DIG bits - } - - const s = @bitCast(u128, arg) >> (128 - 64); - const high: u128 = (@intCast(u64, s) & 0x8000000000000000) | // sign - (@intCast(u64, (e + 16383)) << 48) | // exponent - (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high - const low = @truncate(u64, a); // mantissa-low - - return @bitCast(f128, low | (high << 64)); -} - -test { - _ = @import("floattitf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floattitf_test.zig b/lib/std/special/compiler_rt/floattitf_test.zig deleted file mode 100644 index 76dfc8fbfc..0000000000 --- a/lib/std/special/compiler_rt/floattitf_test.zig +++ /dev/null @@ -1,96 +0,0 @@ -const __floattitf = @import("floattitf.zig").__floattitf; -const testing = @import("std").testing; - -fn test__floattitf(a: i128, expected: f128) !void { - const x = __floattitf(a); - try testing.expect(x == expected); -} - -test "floattitf" { - try test__floattitf(0, 0.0); - - try test__floattitf(1, 1.0); - try test__floattitf(2, 2.0); - try test__floattitf(20, 20.0); - try test__floattitf(-1, -1.0); - try test__floattitf(-2, -2.0); - try test__floattitf(-20, -20.0); - - try test__floattitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floattitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floattitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floattitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - - try test__floattitf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); - try test__floattitf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); - try test__floattitf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); - try test__floattitf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); - - try test__floattitf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); - try test__floattitf(make_ti(0x8000000000000001, 0), -0x1.FFFFFFFFFFFFFFFCp+126); - - try test__floattitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floattitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floattitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floattitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floattitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floattitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floattitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floattitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floattitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floattitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floattitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floattitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floattitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); - try test__floattitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); - try test__floattitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); - try test__floattitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); - try test__floattitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); - try test__floattitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); - try test__floattitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); - try test__floattitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); - try test__floattitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); - try test__floattitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); - try test__floattitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); - try test__floattitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); - try test__floattitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); - try test__floattitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floattitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floattitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); - try test__floattitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); - try test__floattitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); - try test__floattitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); - try test__floattitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); - try test__floattitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); - try test__floattitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); - try test__floattitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); - try test__floattitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); - try test__floattitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); - try test__floattitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); - try test__floattitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); - try test__floattitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); - try test__floattitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); - - try test__floattitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); - - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); -} - -fn make_ti(high: u64, low: u64) i128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(i128, result); -} diff --git a/lib/std/special/compiler_rt/floatundidf.zig b/lib/std/special/compiler_rt/floatundidf.zig deleted file mode 100644 index 14e7434490..0000000000 --- a/lib/std/special/compiler_rt/floatundidf.zig +++ /dev/null @@ -1,29 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const twop52: f64 = 0x1.0p52; -const twop84: f64 = 0x1.0p84; -const twop84_plus_twop52: f64 = 0x1.00000001p84; - -pub fn __floatundidf(a: u64) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); - - if (a == 0) return 0; - - var high = @bitCast(u64, twop84); - var low = @bitCast(u64, twop52); - - high |= a >> 32; - low |= a & 0xFFFFFFFF; - - return (@bitCast(f64, high) - twop84_plus_twop52) + @bitCast(f64, low); -} - -pub fn __aeabi_ul2d(arg: u64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatundidf, .{arg}); -} - -test { - _ = @import("floatundidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatundidf_test.zig b/lib/std/special/compiler_rt/floatundidf_test.zig deleted file mode 100644 index 71bfdd3087..0000000000 --- a/lib/std/special/compiler_rt/floatundidf_test.zig +++ /dev/null @@ -1,50 +0,0 @@ -const __floatundidf = @import("floatundidf.zig").__floatundidf; -const testing = @import("std").testing; - -fn test__floatundidf(a: u64, expected: f64) !void { - const r = __floatundidf(a); - try testing.expect(r == expected); -} - -test "floatundidf" { - try test__floatundidf(0, 0.0); - try test__floatundidf(1, 1.0); - try test__floatundidf(2, 2.0); - try test__floatundidf(20, 20.0); - try test__floatundidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatundidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatundidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatundidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - try test__floatundidf(0x8000008000000000, 0x1.000001p+63); - try test__floatundidf(0x8000000000000800, 0x1.0000000000001p+63); - try test__floatundidf(0x8000010000000000, 0x1.000002p+63); - try test__floatundidf(0x8000000000001000, 0x1.0000000000002p+63); - try test__floatundidf(0x8000000000000000, 0x1p+63); - try test__floatundidf(0x8000000000000001, 0x1p+63); - try test__floatundidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatundidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatundidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatundidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatundidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatundidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - try test__floatundidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatundidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatundidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatundidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatundidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - try test__floatundidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floatundidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floatundidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floatundidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floatundidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); -} diff --git a/lib/std/special/compiler_rt/floatundisf.zig b/lib/std/special/compiler_rt/floatundisf.zig deleted file mode 100644 index ffbe3ef252..0000000000 --- a/lib/std/special/compiler_rt/floatundisf.zig +++ /dev/null @@ -1,94 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const FLT_MANT_DIG = 24; - -inline fn floatundisf(arg: u64) f32 { - @setRuntimeSafety(builtin.is_test); - - if (arg == 0) return 0; - - var a = arg; - const N: usize = @typeInfo(@TypeOf(a)).Int.bits; - // Number of significant digits - const sd = N - @clz(u64, a); - // 8 exponent - var e = @intCast(u32, sd) - 1; - - if (sd > FLT_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit FLT_MANT_DIG-1 bits to the right of 1 - // Q = bit FLT_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - FLT_MANT_DIG + 1 => a <<= 1, - FLT_MANT_DIG + 2 => {}, - else => { - const shift_amt = @intCast(u6, ((N + FLT_MANT_DIG + 2) - sd)); - const all_ones: u64 = maxInt(u64); - a = (a >> @intCast(u6, sd - (FLT_MANT_DIG + 2))) | - @boolToInt(a & (all_ones >> shift_amt) != 0); - }, - } - // Or P into R - a |= @boolToInt((a & 4) != 0); - // round - this step may add a significant bit - a += 1; - // dump Q and R - a >>= 2; - // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (@as(u64, 1) << FLT_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to FLT_MANT_DIG bits - } else { - a <<= @intCast(u6, FLT_MANT_DIG - sd); - // a is now rounded to FLT_MANT_DIG bits - } - - const result: u32 = ((e + 127) << 23) | // exponent - @truncate(u32, a & 0x007FFFFF); // mantissa - return @bitCast(f32, result); -} - -pub fn __floatundisf(arg: u64) callconv(.C) f32 { - return floatundisf(arg); -} - -pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { - return floatundisf(arg); -} - -fn test__floatundisf(a: u64, expected: f32) !void { - try std.testing.expectEqual(expected, __floatundisf(a)); -} - -test "floatundisf" { - try test__floatundisf(0, 0.0); - try test__floatundisf(1, 1.0); - try test__floatundisf(2, 2.0); - try test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatundisf(0x8000008000000000, 0x1p+63); - try test__floatundisf(0x8000010000000000, 0x1.000002p+63); - try test__floatundisf(0x8000000000000000, 0x1p+63); - try test__floatundisf(0x8000000000000001, 0x1p+63); - try test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); - try test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); - try test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); -} diff --git a/lib/std/special/compiler_rt/floatunditf.zig b/lib/std/special/compiler_rt/floatunditf.zig deleted file mode 100644 index b304c8cdba..0000000000 --- a/lib/std/special/compiler_rt/floatunditf.zig +++ /dev/null @@ -1,28 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); - -pub fn __floatunditf(a: u64) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (a == 0) { - return 0; - } - - const mantissa_bits = std.math.floatMantissaBits(f128); - const exponent_bits = std.math.floatExponentBits(f128); - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const implicit_bit = 1 << mantissa_bits; - - const exp: u128 = (64 - 1) - @clz(u64, a); - const shift: u7 = mantissa_bits - @intCast(u7, exp); - - var result: u128 = (@intCast(u128, a) << shift) ^ implicit_bit; - result += (exp + exponent_bias) << mantissa_bits; - - return @bitCast(f128, result); -} - -test { - _ = @import("floatunditf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatunditf_test.zig b/lib/std/special/compiler_rt/floatunditf_test.zig deleted file mode 100644 index ae6834c082..0000000000 --- a/lib/std/special/compiler_rt/floatunditf_test.zig +++ /dev/null @@ -1,32 +0,0 @@ -const __floatunditf = @import("floatunditf.zig").__floatunditf; - -fn test__floatunditf(a: u64, expected_hi: u64, expected_lo: u64) !void { - const x = __floatunditf(a); - - const x_repr = @bitCast(u128, x); - const x_hi = @intCast(u64, x_repr >> 64); - const x_lo = @truncate(u64, x_repr); - - if (x_hi == expected_hi and x_lo == expected_lo) { - return; - } - // nan repr - else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { - if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { - return; - } - } - - @panic("__floatunditf test failure"); -} - -test "floatunditf" { - try test__floatunditf(0xffffffffffffffff, 0x403effffffffffff, 0xfffe000000000000); - try test__floatunditf(0xfffffffffffffffe, 0x403effffffffffff, 0xfffc000000000000); - try test__floatunditf(0x8000000000000000, 0x403e000000000000, 0x0); - try test__floatunditf(0x7fffffffffffffff, 0x403dffffffffffff, 0xfffc000000000000); - try test__floatunditf(0x123456789abcdef1, 0x403b23456789abcd, 0xef10000000000000); - try test__floatunditf(0x2, 0x4000000000000000, 0x0); - try test__floatunditf(0x1, 0x3fff000000000000, 0x0); - try test__floatunditf(0x0, 0x0, 0x0); -} diff --git a/lib/std/special/compiler_rt/floatunsidf.zig b/lib/std/special/compiler_rt/floatunsidf.zig deleted file mode 100644 index f474c1de8f..0000000000 --- a/lib/std/special/compiler_rt/floatunsidf.zig +++ /dev/null @@ -1,41 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const implicitBit = @as(u64, 1) << 52; - -inline fn floatunsidf(arg: u32) f64 { - @setRuntimeSafety(builtin.is_test); - - if (arg == 0) return 0.0; - - // The exponent is the width of abs(a) - const exp = @as(u64, 31) - @clz(u32, arg); - // Shift a into the significand field and clear the implicit bit - const shift = @intCast(u6, 52 - exp); - const mant = @as(u64, arg) << shift ^ implicitBit; - - 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 { - return floatunsidf(arg); -} - -fn test_one_floatunsidf(a: u32, expected: u64) !void { - const r = __floatunsidf(a); - try std.testing.expect(@bitCast(u64, r) == expected); -} - -test "floatsidf" { - // Test the produced bit pattern - try test_one_floatunsidf(0, 0x0000000000000000); - try test_one_floatunsidf(1, 0x3ff0000000000000); - try test_one_floatunsidf(0x7FFFFFFF, 0x41dfffffffc00000); - try test_one_floatunsidf(@intCast(u32, 0x80000000), 0x41e0000000000000); - try test_one_floatunsidf(@intCast(u32, 0xFFFFFFFF), 0x41efffffffe00000); -} diff --git a/lib/std/special/compiler_rt/floatunsisf.zig b/lib/std/special/compiler_rt/floatunsisf.zig deleted file mode 100644 index d267baee01..0000000000 --- a/lib/std/special/compiler_rt/floatunsisf.zig +++ /dev/null @@ -1,61 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const significandBits = 23; -const exponentBias = 127; -const implicitBit = @as(u32, 1) << significandBits; - -inline fn floatunsisf(arg: u32) f32 { - @setRuntimeSafety(builtin.is_test); - - if (arg == 0) return 0.0; - - // The exponent is the width of abs(a) - const exp = @as(u32, 31) - @clz(u32, arg); - - var mantissa: u32 = undefined; - if (exp <= significandBits) { - // Shift a into the significand field and clear the implicit bit - const shift = @intCast(u5, significandBits - exp); - mantissa = @as(u32, arg) << shift ^ implicitBit; - } else { - const shift = @intCast(u5, exp - significandBits); - // Round to the nearest number after truncation - mantissa = @as(u32, arg) >> shift ^ implicitBit; - // Align to the left and check if the truncated part is halfway over - const round = arg << @intCast(u5, 31 - shift); - mantissa += @boolToInt(round > 0x80000000); - // Tie to even - mantissa += mantissa & 1; - } - - // Use the addition instead of a or since we may have a carry from the - // mantissa to the exponent - var result = mantissa; - result += (exp + exponentBias) << significandBits; - - return @bitCast(f32, result); -} - -pub fn __floatunsisf(arg: u32) callconv(.C) f32 { - return floatunsisf(arg); -} - -pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { - return floatunsisf(arg); -} - -fn test_one_floatunsisf(a: u32, expected: u32) !void { - const r = __floatunsisf(a); - try std.testing.expect(@bitCast(u32, r) == expected); -} - -test "floatunsisf" { - // Test the produced bit pattern - try test_one_floatunsisf(0, 0); - try test_one_floatunsisf(1, 0x3f800000); - try test_one_floatunsisf(0x7FFFFFFF, 0x4f000000); - try test_one_floatunsisf(0x80000000, 0x4f000000); - try test_one_floatunsisf(0xFFFFFFFF, 0x4f800000); -} diff --git a/lib/std/special/compiler_rt/floatunsitf.zig b/lib/std/special/compiler_rt/floatunsitf.zig deleted file mode 100644 index 8774d486ea..0000000000 --- a/lib/std/special/compiler_rt/floatunsitf.zig +++ /dev/null @@ -1,29 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); - -pub fn __floatunsitf(a: u32) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (a == 0) { - return 0; - } - - const mantissa_bits = std.math.floatMantissaBits(f128); - const exponent_bits = std.math.floatExponentBits(f128); - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const implicit_bit = 1 << mantissa_bits; - - const exp = (32 - 1) - @clz(u32, a); - const shift = mantissa_bits - @intCast(u7, exp); - - // TODO(#1148): @bitCast alignment error - var result align(16) = (@intCast(u128, a) << shift) ^ implicit_bit; - result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits; - - return @bitCast(f128, result); -} - -test { - _ = @import("floatunsitf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatunsitf_test.zig b/lib/std/special/compiler_rt/floatunsitf_test.zig deleted file mode 100644 index 7ae7c43281..0000000000 --- a/lib/std/special/compiler_rt/floatunsitf_test.zig +++ /dev/null @@ -1,28 +0,0 @@ -const __floatunsitf = @import("floatunsitf.zig").__floatunsitf; - -fn test__floatunsitf(a: u32, expected_hi: u64, expected_lo: u64) !void { - const x = __floatunsitf(a); - - const x_repr = @bitCast(u128, x); - const x_hi = @intCast(u64, x_repr >> 64); - const x_lo = @truncate(u64, x_repr); - - if (x_hi == expected_hi and x_lo == expected_lo) { - return; - } - // nan repr - else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { - if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { - return; - } - } - - @panic("__floatunsitf test failure"); -} - -test "floatunsitf" { - try test__floatunsitf(0x7fffffff, 0x401dfffffffc0000, 0x0); - try test__floatunsitf(0, 0x0, 0x0); - try test__floatunsitf(0xffffffff, 0x401efffffffe0000, 0x0); - try test__floatunsitf(0x12345678, 0x401b234567800000, 0x0); -} diff --git a/lib/std/special/compiler_rt/floatuntidf.zig b/lib/std/special/compiler_rt/floatuntidf.zig deleted file mode 100644 index 2c4729a9d8..0000000000 --- a/lib/std/special/compiler_rt/floatuntidf.zig +++ /dev/null @@ -1,62 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const DBL_MANT_DIG = 53; - -pub fn __floatuntidf(arg: u128) callconv(.C) f64 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var a = arg; - const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > DBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit DBL_MANT_DIG-1 bits to the right of 1 - // Q = bit DBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - DBL_MANT_DIG + 1 => { - a <<= 1; - }, - DBL_MANT_DIG + 2 => {}, - else => { - const shift_amt = @bitCast(i32, N + (DBL_MANT_DIG + 2)) - sd; - const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd - (DBL_MANT_DIG + 2))) | - @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << DBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to DBL_MANT_DIG bits - } else { - a <<= @intCast(u7, DBL_MANT_DIG - sd); - // a is now rounded to DBL_MANT_DIG bits - } - - const high: u64 = @bitCast(u32, (e + 1023) << 20) | // exponent - (@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high - const low = @truncate(u32, a); // mantissa-low - - return @bitCast(f64, low | (high << 32)); -} - -test { - _ = @import("floatuntidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatuntidf_test.zig b/lib/std/special/compiler_rt/floatuntidf_test.zig deleted file mode 100644 index 5fc6a47150..0000000000 --- a/lib/std/special/compiler_rt/floatuntidf_test.zig +++ /dev/null @@ -1,81 +0,0 @@ -const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; -const testing = @import("std").testing; - -fn test__floatuntidf(a: u128, expected: f64) !void { - const x = __floatuntidf(a); - try testing.expect(x == expected); -} - -test "floatuntidf" { - try test__floatuntidf(0, 0.0); - - try test__floatuntidf(1, 1.0); - try test__floatuntidf(2, 2.0); - try test__floatuntidf(20, 20.0); - - try test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - - try test__floatuntidf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); - try test__floatuntidf(make_ti(0x8000000000000800, 0), 0x1.0000000000001p+127); - try test__floatuntidf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); - try test__floatuntidf(make_ti(0x8000000000001000, 0), 0x1.0000000000002p+127); - - try test__floatuntidf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); - try test__floatuntidf(make_ti(0x8000000000000001, 0), 0x1.0000000000000002p+127); - - try test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floatuntidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); -} - -fn make_ti(high: u64, low: u64) u128 { - var result: u128 = high; - result <<= 64; - result |= low; - return result; -} diff --git a/lib/std/special/compiler_rt/floatuntisf.zig b/lib/std/special/compiler_rt/floatuntisf.zig deleted file mode 100644 index 2a54c2e0f3..0000000000 --- a/lib/std/special/compiler_rt/floatuntisf.zig +++ /dev/null @@ -1,61 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const FLT_MANT_DIG = 24; - -pub fn __floatuntisf(arg: u128) callconv(.C) f32 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var a = arg; - const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > FLT_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit FLT_MANT_DIG-1 bits to the right of 1 - // Q = bit FLT_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - FLT_MANT_DIG + 1 => { - a <<= 1; - }, - FLT_MANT_DIG + 2 => {}, - else => { - const shift_amt = @bitCast(i32, N + (FLT_MANT_DIG + 2)) - sd; - const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd - (FLT_MANT_DIG + 2))) | - @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << FLT_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to FLT_MANT_DIG bits - } else { - a <<= @intCast(u7, FLT_MANT_DIG - sd); - // a is now rounded to FLT_MANT_DIG bits - } - - const high = @bitCast(u32, (e + 127) << 23); // exponent - const low = @truncate(u32, a) & 0x007fffff; // mantissa - - return @bitCast(f32, high | low); -} - -test { - _ = @import("floatuntisf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatuntisf_test.zig b/lib/std/special/compiler_rt/floatuntisf_test.zig deleted file mode 100644 index dd06b7e3d7..0000000000 --- a/lib/std/special/compiler_rt/floatuntisf_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __floatuntisf = @import("floatuntisf.zig").__floatuntisf; -const testing = @import("std").testing; - -fn test__floatuntisf(a: u128, expected: f32) !void { - const x = __floatuntisf(a); - try testing.expect(x == expected); -} - -test "floatuntisf" { - try test__floatuntisf(0, 0.0); - - try test__floatuntisf(1, 1.0); - try test__floatuntisf(2, 2.0); - try test__floatuntisf(20, 20.0); - - try test__floatuntisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatuntisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - - try test__floatuntisf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); - try test__floatuntisf(make_ti(0x8000000000000800, 0), 0x1.0p+127); - try test__floatuntisf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); - - try test__floatuntisf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); - - try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - - try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - - try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floatuntisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); - try test__floatuntisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); - - try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floatuntisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - - try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); - - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCB90000000000001), 0x1.FEDCBAp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBA0000000000000), 0x1.FEDCBAp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBAFFFFFFFFFFFFF), 0x1.FEDCBAp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000000), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000001), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBBFFFFFFFFFFFFF), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000000), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000001), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000000), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000001), 0x1.FEDCBEp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBDFFFFFFFFFFFFF), 0x1.FEDCBEp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBE0000000000000), 0x1.FEDCBEp+76); -} - -fn make_ti(high: u64, low: u64) u128 { - var result: u128 = high; - result <<= 64; - result |= low; - return result; -} diff --git a/lib/std/special/compiler_rt/floatuntitf.zig b/lib/std/special/compiler_rt/floatuntitf.zig deleted file mode 100644 index c35b4dcf74..0000000000 --- a/lib/std/special/compiler_rt/floatuntitf.zig +++ /dev/null @@ -1,62 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const LDBL_MANT_DIG = 113; - -pub fn __floatuntitf(arg: u128) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var a = arg; - const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > LDBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit LDBL_MANT_DIG-1 bits to the right of 1 - // Q = bit LDBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - LDBL_MANT_DIG + 1 => { - a <<= 1; - }, - LDBL_MANT_DIG + 2 => {}, - else => { - const shift_amt = @bitCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; - const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd - (LDBL_MANT_DIG + 2))) | - @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << LDBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to LDBL_MANT_DIG bits - } else { - a <<= @intCast(u7, LDBL_MANT_DIG - sd); - // a is now rounded to LDBL_MANT_DIG bits - } - - const high: u128 = (@intCast(u64, (e + 16383)) << 48) | // exponent - (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high - const low = @truncate(u64, a); // mantissa-low - - return @bitCast(f128, low | (high << 64)); -} - -test { - _ = @import("floatuntitf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatuntitf_test.zig b/lib/std/special/compiler_rt/floatuntitf_test.zig deleted file mode 100644 index 5afbf348c6..0000000000 --- a/lib/std/special/compiler_rt/floatuntitf_test.zig +++ /dev/null @@ -1,99 +0,0 @@ -const __floatuntitf = @import("floatuntitf.zig").__floatuntitf; -const testing = @import("std").testing; - -fn test__floatuntitf(a: u128, expected: f128) !void { - const x = __floatuntitf(a); - try testing.expect(x == expected); -} - -test "floatuntitf" { - try test__floatuntitf(0, 0.0); - - try test__floatuntitf(1, 1.0); - try test__floatuntitf(2, 2.0); - try test__floatuntitf(20, 20.0); - - try test__floatuntitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatuntitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatuntitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatuntitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - try test__floatuntitf(0x7FFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFEp+59); - try test__floatuntitf(0xFFFFFFFFFFFFFFFE, 0xF.FFFFFFFFFFFFFFEp+60); - try test__floatuntitf(0xFFFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFFp+60); - - try test__floatuntitf(0x8000008000000000, 0x8.000008p+60); - try test__floatuntitf(0x8000000000000800, 0x8.0000000000008p+60); - try test__floatuntitf(0x8000010000000000, 0x8.00001p+60); - try test__floatuntitf(0x8000000000001000, 0x8.000000000001p+60); - - try test__floatuntitf(0x8000000000000000, 0x8p+60); - try test__floatuntitf(0x8000000000000001, 0x8.000000000000001p+60); - - try test__floatuntitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatuntitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatuntitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatuntitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatuntitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floatuntitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatuntitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatuntitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatuntitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatuntitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floatuntitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatuntitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); - try test__floatuntitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); - try test__floatuntitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); - try test__floatuntitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); - try test__floatuntitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); - try test__floatuntitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); - try test__floatuntitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); - try test__floatuntitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); - try test__floatuntitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); - try test__floatuntitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); - try test__floatuntitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); - try test__floatuntitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); - try test__floatuntitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); - try test__floatuntitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floatuntitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floatuntitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); - try test__floatuntitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); - try test__floatuntitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); - try test__floatuntitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); - - try test__floatuntitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); - - try test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0x0000000000000000), 0x1.FFFFFFFFFFFFFFFEp+127); - try test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 0x1.0000000000000000p+128); - - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); -} - -fn make_ti(high: u64, low: u64) u128 { - var result: u128 = high; - result <<= 64; - result |= low; - return result; -} diff --git a/lib/std/special/compiler_rt/sparc.zig b/lib/std/special/compiler_rt/sparc.zig index 3f2cbd86b5..3b33afd29a 100644 --- a/lib/std/special/compiler_rt/sparc.zig +++ b/lib/std/special/compiler_rt/sparc.zig @@ -66,19 +66,19 @@ pub fn _Qp_fge(a: *f128, b: *f128) callconv(.C) bool { // Conversion pub fn _Qp_itoq(c: *f128, a: i32) callconv(.C) void { - c.* = @import("floatsiXf.zig").__floatsitf(a); + c.* = @import("floatXiYf.zig").__floatsitf(a); } pub fn _Qp_uitoq(c: *f128, a: u32) callconv(.C) void { - c.* = @import("floatunsitf.zig").__floatunsitf(a); + c.* = @import("floatXiYf.zig").__floatunsitf(a); } pub fn _Qp_xtoq(c: *f128, a: i64) callconv(.C) void { - c.* = @import("floatditf.zig").__floatditf(a); + c.* = @import("floatXiYf.zig").__floatditf(a); } pub fn _Qp_uxtoq(c: *f128, a: u64) callconv(.C) void { - c.* = @import("floatunditf.zig").__floatunditf(a); + c.* = @import("floatXiYf.zig").__floatunditf(a); } pub fn _Qp_stoq(c: *f128, a: f32) callconv(.C) void { @@ -90,19 +90,19 @@ pub fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void { } pub fn _Qp_qtoi(a: *f128) callconv(.C) i32 { - return @import("fixtfsi.zig").__fixtfsi(a.*); + return @import("fixXfYi.zig").__fixtfsi(a.*); } pub fn _Qp_qtoui(a: *f128) callconv(.C) u32 { - return @import("fixunstfsi.zig").__fixunstfsi(a.*); + return @import("fixXfYi.zig").__fixunstfsi(a.*); } pub fn _Qp_qtox(a: *f128) callconv(.C) i64 { - return @import("fixtfdi.zig").__fixtfdi(a.*); + return @import("fixXfYi.zig").__fixtfdi(a.*); } pub fn _Qp_qtoux(a: *f128) callconv(.C) u64 { - return @import("fixunstfdi.zig").__fixunstfdi(a.*); + return @import("fixXfYi.zig").__fixunstfdi(a.*); } pub fn _Qp_qtos(a: *f128) callconv(.C) f32 { diff --git a/lib/std/special/compiler_rt/trunc_f80.zig b/lib/std/special/compiler_rt/trunc_f80.zig index 88381a28ee..107874aeeb 100644 --- a/lib/std/special/compiler_rt/trunc_f80.zig +++ b/lib/std/special/compiler_rt/trunc_f80.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const native_arch = builtin.cpu.arch; +const testing = std.testing; // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. @@ -117,13 +118,12 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { const src_abs_mask = src_sign_mask - 1; const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); - const src_qnan = 1 << (src_sig_bits - 1); - const src_nan_mask = src_qnan - 1; // Break a into a sign and representation of the absolute value const a_rep = @bitCast(u128, a); const a_abs = a_rep & src_abs_mask; const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; + const integer_bit = 1 << 63; var res: std.math.F80 = undefined; @@ -133,27 +133,41 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { // bit and inserting the (truncated) trailing NaN field. res.exp = 0x7fff; res.fraction = 0x8000000000000000; - res.fraction |= @truncate(u64, (a_abs & src_qnan) << (src_sig_bits - dst_sig_bits)); - res.fraction |= @truncate(u64, (a_abs & src_nan_mask) << (src_sig_bits - dst_sig_bits)); + res.fraction |= @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); } else { // The exponent of a is within the range of normal numbers in the // destination format. We can convert by simply right-shifting with - // rounding and adjusting the exponent. - res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); + // rounding, adding the explicit integer bit, and adjusting the exponent + res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)) | integer_bit; res.exp = @truncate(u16, a_abs >> src_sig_bits); const round_bits = a_abs & round_mask; if (round_bits > halfway) { // Round to nearest - const exp = @addWithOverflow(u64, res.fraction, 1, &res.fraction); - res.exp += @boolToInt(exp); + const carry = @boolToInt(@addWithOverflow(u64, res.fraction, 1, &res.fraction)); + res.exp += carry; + res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry } else if (round_bits == halfway) { // Ties to even - const exp = @addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction); - res.exp += @boolToInt(exp); + const carry = @boolToInt(@addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction)); + res.exp += carry; + res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry } + if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals } res.exp |= sign; return std.math.make_f80(res); } + +fn test__trunctfxf2(a: f128, expected: f80) !void { + const x = __trunctfxf2(a); + try testing.expect(x == expected); +} + +test { + try test__trunctfxf2(1.5, 1.5); + try test__trunctfxf2(2.5, 2.5); + try test__trunctfxf2(-2.5, -2.5); + try test__trunctfxf2(0.0, 0.0); +} diff --git a/lib/std/start.zig b/lib/std/start.zig index cd247c915e..f4a5cbb763 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -29,6 +29,7 @@ comptime { builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_riscv64 or + builtin.zig_backend == .stage2_sparcv9 or (builtin.zig_backend == .stage2_llvm and native_os != .linux) or (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64)) { @@ -165,6 +166,14 @@ fn exit2(code: usize) noreturn { : "rcx", "r11", "memory" ); }, + .sparcv9 => { + asm volatile ("ta 0x6d" + : + : [number] "{g1}" (1), + [arg1] "{o0}" (code), + : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + ); + }, else => @compileError("TODO"), }, // exits(0) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 4146e033b4..56cc86d769 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -574,6 +574,150 @@ test { try expectEqualStrings("foo", "foo"); } +/// Exhaustively check that allocation failures within `test_fn` are handled without +/// introducing memory leaks. If used with the `testing.allocator` as the `backing_allocator`, +/// it will also be able to detect double frees, etc (when runtime safety is enabled). +/// +/// The provided `test_fn` must have a `std.mem.Allocator` as its first argument, +/// and must have a return type of `!void`. Any extra arguments of `test_fn` can +/// be provided via the `extra_args` tuple. +/// +/// Any relevant state shared between runs of `test_fn` *must* be reset within `test_fn`. +/// +/// Expects that the `test_fn` has a deterministic number of memory allocations +/// (an error will be returned if non-deterministic allocations are detected). +/// +/// The strategy employed is to: +/// - Run the test function once to get the total number of allocations. +/// - Then, iterate and run the function X more times, incrementing +/// the failing index each iteration (where X is the total number of +/// allocations determined previously) +/// +/// --- +/// +/// Here's an example of using a simple test case that will cause a leak when the +/// allocation of `bar` fails (but will pass normally): +/// +/// ```zig +/// test { +/// const length: usize = 10; +/// const allocator = std.testing.allocator; +/// var foo = try allocator.alloc(u8, length); +/// var bar = try allocator.alloc(u8, length); +/// +/// allocator.free(foo); +/// allocator.free(bar); +/// } +/// ``` +/// +/// The test case can be converted to something that this function can use by +/// doing: +/// +/// ```zig +/// fn testImpl(allocator: std.mem.Allocator, length: usize) !void { +/// var foo = try allocator.alloc(u8, length); +/// var bar = try allocator.alloc(u8, length); +/// +/// allocator.free(foo); +/// allocator.free(bar); +/// } +/// +/// test { +/// const length: usize = 10; +/// const allocator = std.testing.allocator; +/// try std.testing.checkAllAllocationFailures(allocator, testImpl, .{length}); +/// } +/// ``` +/// +/// Running this test will show that `foo` is leaked when the allocation of +/// `bar` fails. The simplest fix, in this case, would be to use defer like so: +/// +/// ```zig +/// fn testImpl(allocator: std.mem.Allocator, length: usize) !void { +/// var foo = try allocator.alloc(u8, length); +/// defer allocator.free(foo); +/// var bar = try allocator.alloc(u8, length); +/// defer allocator.free(bar); +/// } +/// ``` +pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime test_fn: anytype, extra_args: anytype) !void { + switch (@typeInfo(@typeInfo(@TypeOf(test_fn)).Fn.return_type.?)) { + .ErrorUnion => |info| { + if (info.payload != void) { + @compileError("Return type must be !void"); + } + }, + else => @compileError("Return type must be !void"), + } + if (@typeInfo(@TypeOf(extra_args)) != .Struct) { + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(extra_args))); + } + + const ArgsTuple = std.meta.ArgsTuple(@TypeOf(test_fn)); + const fn_args_fields = @typeInfo(ArgsTuple).Struct.fields; + if (fn_args_fields.len == 0 or fn_args_fields[0].field_type != std.mem.Allocator) { + @compileError("The provided function must have an " ++ @typeName(std.mem.Allocator) ++ " as its first argument"); + } + const expected_args_tuple_len = fn_args_fields.len - 1; + if (extra_args.len != expected_args_tuple_len) { + @compileError("The provided function expects " ++ (comptime std.fmt.comptimePrint("{d}", .{expected_args_tuple_len})) ++ " extra arguments, but the provided tuple contains " ++ (comptime std.fmt.comptimePrint("{d}", .{extra_args.len}))); + } + + // Setup the tuple that will actually be used with @call (we'll need to insert + // the failing allocator in field @"0" before each @call) + var args: ArgsTuple = undefined; + inline for (@typeInfo(@TypeOf(extra_args)).Struct.fields) |field, i| { + const expected_type = fn_args_fields[i + 1].field_type; + if (expected_type != field.field_type) { + @compileError("Unexpected type for extra argument at index " ++ (comptime std.fmt.comptimePrint("{d}", .{i})) ++ ": expected " ++ @typeName(expected_type) ++ ", found " ++ @typeName(field.field_type)); + } + const arg_i_str = comptime str: { + var str_buf: [100]u8 = undefined; + const args_i = i + 1; + const str_len = std.fmt.formatIntBuf(&str_buf, args_i, 10, .lower, .{}); + break :str str_buf[0..str_len]; + }; + @field(args, arg_i_str) = @field(extra_args, field.name); + } + + // Try it once with unlimited memory, make sure it works + const needed_alloc_count = x: { + var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, std.math.maxInt(usize)); + args.@"0" = failing_allocator_inst.allocator(); + + try @call(.{}, test_fn, args); + break :x failing_allocator_inst.index; + }; + + var fail_index: usize = 0; + while (fail_index < needed_alloc_count) : (fail_index += 1) { + var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, fail_index); + args.@"0" = failing_allocator_inst.allocator(); + + if (@call(.{}, test_fn, args)) |_| { + return error.NondeterministicMemoryUsage; + } else |err| switch (err) { + error.OutOfMemory => { + if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { + print( + "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", + .{ + fail_index, + needed_alloc_count, + failing_allocator_inst.allocated_bytes, + failing_allocator_inst.freed_bytes, + failing_allocator_inst.allocations, + failing_allocator_inst.deallocations, + }, + ); + return error.MemoryLeakDetected; + } + }, + else => return err, + } + } +} + /// Given a type, reference all the declarations inside, so that the semantic analyzer sees them. pub fn refAllDecls(comptime T: type) void { if (!builtin.is_test) return; diff --git a/lib/std/time.zig b/lib/std/time.zig index 687a5336de..668a4b2cf8 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -147,7 +147,7 @@ pub const s_per_day = s_per_hour * 24; pub const s_per_week = s_per_day * 7; /// An Instant represents a timestamp with respect to the currently -/// executing program that ticks during suspend and can be used to +/// executing program that ticks during suspend and can be used to /// record elapsed time unlike `nanoTimestamp`. /// /// It tries to sample the system's fastest and most precise timer available. @@ -256,7 +256,7 @@ pub const Instant = struct { /// /// Monotonicity is ensured by saturating on the most previous sample. /// This means that while timings reported are monotonic, -/// they're not guaranteed to tick at a steady rate as this is up to the underlying system. +/// they're not guaranteed to tick at a steady rate as this is up to the underlying system. pub const Timer = struct { started: Instant, previous: Instant, @@ -290,7 +290,7 @@ pub const Timer = struct { return current.since(self.started); } - /// Returns an Instant sampled at the callsite that is + /// Returns an Instant sampled at the callsite that is /// guaranteed to be monotonic with respect to the timer's starting point. fn sample(self: *Timer) Instant { const current = Instant.now() catch unreachable; diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index e0a000dfe5..81a7ed838f 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -3,6 +3,11 @@ const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; +/// Use this to replace an unknown, unrecognized, or unrepresentable character. +/// +/// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character +pub const replacement_character: u21 = 0xFFFD; + /// Returns how many bytes the UTF-8 representation would require /// for the given codepoint. pub fn utf8CodepointSequenceLength(c: u21) !u3 { @@ -269,14 +274,7 @@ pub const Utf8Iterator = struct { pub fn nextCodepoint(it: *Utf8Iterator) ?u21 { const slice = it.nextCodepointSlice() orelse return null; - - switch (slice.len) { - 1 => return @as(u21, slice[0]), - 2 => return utf8Decode2(slice) catch unreachable, - 3 => return utf8Decode3(slice) catch unreachable, - 4 => return utf8Decode4(slice) catch unreachable, - else => unreachable, - } + return utf8Decode(slice) catch unreachable; } /// Look ahead at the next n codepoints without advancing the iterator. @@ -784,15 +782,14 @@ fn formatUtf16le( options: std.fmt.FormatOptions, writer: anytype, ) !void { - const unknown_codepoint = 0xfffd; _ = fmt; _ = options; var buf: [300]u8 = undefined; // just a random size I chose var it = Utf16LeIterator.init(utf16le); var u8len: usize = 0; - while (it.nextCodepoint() catch unknown_codepoint) |codepoint| { + while (it.nextCodepoint() catch replacement_character) |codepoint| { u8len += utf8Encode(codepoint, buf[u8len..]) catch - utf8Encode(unknown_codepoint, buf[u8len..]) catch unreachable; + utf8Encode(replacement_character, buf[u8len..]) catch unreachable; if (u8len + 3 >= buf.len) { try writer.writeAll(buf[0..u8len]); u8len = 0; diff --git a/lib/std/x/net/tcp.zig b/lib/std/x/net/tcp.zig index cb99940180..a750e27fc9 100644 --- a/lib/std/x/net/tcp.zig +++ b/lib/std/x/net/tcp.zig @@ -186,7 +186,7 @@ pub const Client = struct { /// Have keep-alive messages be sent periodically. The timing in which keep-alive messages are sent are /// dependant on operating system settings. It returns `error.UnsupportedSocketOption` if the host does - /// not support periodically sending keep-alive messages on connection-oriented sockets. + /// not support periodically sending keep-alive messages on connection-oriented sockets. pub fn setKeepAlive(self: Client, enabled: bool) !void { return self.socket.setKeepAlive(enabled); } diff --git a/lib/std/x/os/socket_posix.zig b/lib/std/x/os/socket_posix.zig index 3d8346db17..859075aa20 100644 --- a/lib/std/x/os/socket_posix.zig +++ b/lib/std/x/os/socket_posix.zig @@ -205,7 +205,7 @@ pub fn Mixin(comptime Socket: type) type { /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if - /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. + /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. pub fn setKeepAlive(self: Socket, enabled: bool) !void { if (@hasDecl(os.SO, "KEEPALIVE")) { return self.setOption(os.SOL.SOCKET, os.SO.KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled)))); @@ -243,7 +243,7 @@ pub fn Mixin(comptime Socket: type) type { /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is /// set on a non-blocking socket. - /// + /// /// Set a timeout on the socket that is to occur if no messages are successfully written /// to its bound destination after a specified number of milliseconds. A subsequent write /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. @@ -258,7 +258,7 @@ pub fn Mixin(comptime Socket: type) type { /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is /// set on a non-blocking socket. - /// + /// /// Set a timeout on the socket that is to occur if no messages are successfully read /// from its bound destination after a specified number of milliseconds. A subsequent /// read from the socket will thereafter return `error.WouldBlock` should the timeout be diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index af40d8352c..d9555c1ff8 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4712,6 +4712,28 @@ test "zig fmt: space after top level doc comment" { ); } +test "zig fmt: remove trailing whitespace after container doc comment" { + try testTransform( + \\//! top level doc comment + \\ + , + \\//! top level doc comment + \\ + ); +} + +test "zig fmt: remove trailing whitespace after doc comment" { + try testTransform( + \\/// doc comment + \\a = 0, + \\ + , + \\/// doc comment + \\a = 0, + \\ + ); +} + test "zig fmt: for loop with ptr payload and index" { try testCanonical( \\test { @@ -5459,52 +5481,24 @@ fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: * anything_changed.* = !mem.eql(u8, formatted, source); return formatted; } -fn testTransform(source: [:0]const u8, expected_source: []const u8) !void { - const needed_alloc_count = x: { - // Try it once with unlimited memory, make sure it works - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var failing_allocator = std.testing.FailingAllocator.init(fixed_allocator.allocator(), maxInt(usize)); - const allocator = failing_allocator.allocator(); - var anything_changed: bool = undefined; - const result_source = try testParse(source, allocator, &anything_changed); - try std.testing.expectEqualStrings(expected_source, result_source); - const changes_expected = source.ptr != expected_source.ptr; - if (anything_changed != changes_expected) { - print("std.zig.render returned {} instead of {}\n", .{ anything_changed, changes_expected }); - return error.TestFailed; - } - try std.testing.expect(anything_changed == changes_expected); - allocator.free(result_source); - break :x failing_allocator.index; - }; - - var fail_index: usize = 0; - while (fail_index < needed_alloc_count) : (fail_index += 1) { - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var failing_allocator = std.testing.FailingAllocator.init(fixed_allocator.allocator(), fail_index); - var anything_changed: bool = undefined; - if (testParse(source, failing_allocator.allocator(), &anything_changed)) |_| { - return error.NondeterministicMemoryUsage; - } else |err| switch (err) { - error.OutOfMemory => { - if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { - print( - "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", - .{ - fail_index, - needed_alloc_count, - failing_allocator.allocated_bytes, - failing_allocator.freed_bytes, - failing_allocator.allocations, - failing_allocator.deallocations, - }, - ); - return error.MemoryLeakDetected; - } - }, - else => return err, - } +fn testTransformImpl(allocator: mem.Allocator, fba: *std.heap.FixedBufferAllocator, source: [:0]const u8, expected_source: []const u8) !void { + // reset the fixed buffer allocator each run so that it can be re-used for each + // iteration of the failing index + fba.reset(); + var anything_changed: bool = undefined; + const result_source = try testParse(source, allocator, &anything_changed); + try std.testing.expectEqualStrings(expected_source, result_source); + const changes_expected = source.ptr != expected_source.ptr; + if (anything_changed != changes_expected) { + print("std.zig.render returned {} instead of {}\n", .{ anything_changed, changes_expected }); + return error.TestFailed; } + try std.testing.expect(anything_changed == changes_expected); + allocator.free(result_source); +} +fn testTransform(source: [:0]const u8, expected_source: []const u8) !void { + var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); + return std.testing.checkAllAllocationFailures(fixed_allocator.allocator(), testTransformImpl, .{ &fixed_allocator, source, expected_source }); } fn testCanonical(source: [:0]const u8) !void { return testTransform(source, source); diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index eaae725e9a..cf7a161b8d 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -2506,9 +2506,15 @@ fn renderContainerDocComments(ais: *Ais, tree: Ast, start_token: Ast.TokenIndex) fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 { var ret = tree.tokenSlice(token_index); - if (tree.tokens.items(.tag)[token_index] == .multiline_string_literal_line) { - assert(ret[ret.len - 1] == '\n'); - ret.len -= 1; + switch (tree.tokens.items(.tag)[token_index]) { + .multiline_string_literal_line => { + assert(ret[ret.len - 1] == '\n'); + ret.len -= 1; + }, + .container_doc_comment, .doc_comment => { + ret = mem.trimRight(u8, ret, &std.ascii.spaces); + }, + else => {}, } return ret; } diff --git a/lib/std/zig/string_literal.zig b/lib/std/zig/string_literal.zig index 07ce08f491..ebee8a3c9f 100644 --- a/lib/std/zig/string_literal.zig +++ b/lib/std/zig/string_literal.zig @@ -61,7 +61,7 @@ pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral { } } -/// Parse an escape sequence from `slice[offset..]`. If parsing is successful, +/// Parse an escape sequence from `slice[offset..]`. If parsing is successful, /// offset is updated to reflect the characters consumed. fn parseEscapeSequence(slice: []const u8, offset: *usize) ParsedCharLiteral { assert(slice.len > offset.*); diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index 36f6207677..f917ee8e34 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -473,7 +473,7 @@ pub fn abiAndDynamicLinkerFromFile( _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); - if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { elf.ELFDATA2LSB => .Little, elf.ELFDATA2MSB => .Big, diff --git a/src/AstGen.zig b/src/AstGen.zig index 75882c761b..ccce4b0bc8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -85,12 +85,12 @@ fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 { } fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void { - const coerced = @bitCast([]const u32, refs); + const coerced = @ptrCast([]const u32, refs); return astgen.extra.appendSlice(astgen.gpa, coerced); } fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void { - const coerced = @bitCast([]const u32, refs); + const coerced = @ptrCast([]const u32, refs); astgen.extra.appendSliceAssumeCapacity(coerced); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 338be582d8..6c486de36a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4531,6 +4531,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca .i386 => .stage2_x86, .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, .riscv64 => .stage2_riscv64, + .sparcv9 => .stage2_sparcv9, else => .other, }; }; diff --git a/src/InternArena.zig b/src/InternPool.zig index c2397b8b42..95947df61b 100644 --- a/src/InternArena.zig +++ b/src/InternPool.zig @@ -2,17 +2,17 @@ map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, items: std.MultiArrayList(Item) = .{}, extra: std.ArrayListUnmanaged(u32) = .{}, -const InternArena = @This(); +const InternPool = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const KeyAdapter = struct { - intern_arena: *const InternArena, + intern_pool: *const InternPool, pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { _ = b_void; - return ctx.intern_arena.indexToKey(@intToEnum(Index, b_map_index)).eql(a); + return ctx.intern_pool.indexToKey(@intToEnum(Index, b_map_index)).eql(a); } pub fn hash(ctx: @This(), a: Key) u32 { @@ -94,10 +94,10 @@ pub const Item = struct { }; /// Represents an index into `map`. It represents the canonical index -/// of a `Value` within this `InternArena`. The values are typed. +/// of a `Value` within this `InternPool`. The values are typed. /// Two values which have the same type can be equality compared simply /// by checking if their indexes are equal, provided they are both in -/// the same `InternArena`. +/// the same `InternPool`. pub const Index = enum(u32) { none = std.math.maxInt(u32), _, @@ -180,14 +180,14 @@ pub const Array = struct { child: Index, }; -pub fn deinit(ia: *InternArena, gpa: Allocator) void { - ia.map.deinit(gpa); - ia.items.deinit(gpa); - ia.extra.deinit(gpa); +pub fn deinit(ip: *InternPool, gpa: Allocator) void { + ip.map.deinit(gpa); + ip.items.deinit(gpa); + ip.extra.deinit(gpa); } -pub fn indexToKey(ia: InternArena, index: Index) Key { - const item = ia.items.get(@enumToInt(index)); +pub fn indexToKey(ip: InternPool, index: Index) Key { + const item = ip.items.get(@enumToInt(index)); const data = item.data; return switch (item.tag) { .type_int_signed => .{ @@ -203,7 +203,7 @@ pub fn indexToKey(ia: InternArena, index: Index) Key { }, }, .type_array => { - const array_info = ia.extraData(Array, data); + const array_info = ip.extraData(Array, data); return .{ .array_type = .{ .len = array_info.len, .child = array_info.child, @@ -216,9 +216,9 @@ pub fn indexToKey(ia: InternArena, index: Index) Key { }; } -pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { - const adapter: KeyAdapter = .{ .intern_arena = ia }; - const gop = try ia.map.getOrPutAdapted(gpa, key, adapter); +pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { + const adapter: KeyAdapter = .{ .intern_pool = ip }; + const gop = try ip.map.getOrPutAdapted(gpa, key, adapter); if (gop.found_existing) { return @intToEnum(Index, gop.index); } @@ -228,7 +228,7 @@ pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { .signed => .type_int_signed, .unsigned => .type_int_unsigned, }; - try ia.items.append(gpa, .{ + try ip.items.append(gpa, .{ .tag = tag, .data = int_type.bits, }); @@ -236,9 +236,9 @@ pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { .array_type => |array_type| { const len = @intCast(u32, array_type.len); // TODO have a big_array encoding assert(array_type.sentinel == .none); // TODO have a sentinel_array encoding - try ia.items.append(gpa, .{ + try ip.items.append(gpa, .{ .tag = .type_array, - .data = try ia.addExtra(gpa, Array{ + .data = try ip.addExtra(gpa, Array{ .len = len, .child = array_type.child, }), @@ -246,20 +246,20 @@ pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { }, else => @panic("TODO"), } - return @intToEnum(Index, ia.items.len - 1); + return @intToEnum(Index, ip.items.len - 1); } -fn addExtra(ia: *InternArena, gpa: Allocator, extra: anytype) Allocator.Error!u32 { +fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { const fields = std.meta.fields(@TypeOf(extra)); - try ia.extra.ensureUnusedCapacity(gpa, fields.len); - return ia.addExtraAssumeCapacity(extra); + try ip.extra.ensureUnusedCapacity(gpa, fields.len); + return ip.addExtraAssumeCapacity(extra); } -fn addExtraAssumeCapacity(ia: *InternArena, extra: anytype) u32 { +fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 { const fields = std.meta.fields(@TypeOf(extra)); - const result = @intCast(u32, ia.extra.items.len); + const result = @intCast(u32, ip.extra.items.len); inline for (fields) |field| { - ia.extra.appendAssumeCapacity(switch (field.field_type) { + ip.extra.appendAssumeCapacity(switch (field.field_type) { u32 => @field(extra, field.name), Index => @enumToInt(@field(extra, field.name)), i32 => @bitCast(u32, @field(extra, field.name)), @@ -269,15 +269,15 @@ fn addExtraAssumeCapacity(ia: *InternArena, extra: anytype) u32 { return result; } -fn extraData(ia: InternArena, comptime T: type, index: usize) T { +fn extraData(ip: InternPool, comptime T: type, index: usize) T { const fields = std.meta.fields(T); var i: usize = index; var result: T = undefined; inline for (fields) |field| { @field(result, field.name) = switch (field.field_type) { - u32 => ia.extra.items[i], - Index => @intToEnum(Index, ia.extra.items[i]), - i32 => @bitCast(i32, ia.extra.items[i]), + u32 => ip.extra.items[i], + Index => @intToEnum(Index, ip.extra.items[i]), + i32 => @bitCast(i32, ip.extra.items[i]), else => @compileError("bad field type"), }; i += 1; @@ -288,26 +288,26 @@ fn extraData(ia: InternArena, comptime T: type, index: usize) T { test "basic usage" { const gpa = std.testing.allocator; - var ia: InternArena = .{}; - defer ia.deinit(gpa); + var ip: InternPool = .{}; + defer ip.deinit(gpa); - const i32_type = try ia.get(gpa, .{ .int_type = .{ + const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32, } }); - const array_i32 = try ia.get(gpa, .{ .array_type = .{ + const array_i32 = try ip.get(gpa, .{ .array_type = .{ .len = 10, .child = i32_type, .sentinel = .none, } }); - const another_i32_type = try ia.get(gpa, .{ .int_type = .{ + const another_i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32, } }); try std.testing.expect(another_i32_type == i32_type); - const another_array_i32 = try ia.get(gpa, .{ .array_type = .{ + const another_array_i32 = try ip.get(gpa, .{ .array_type = .{ .len = 10, .child = i32_type, .sentinel = .none, diff --git a/src/Liveness.zig b/src/Liveness.zig index 59f7f5be91..be4344ab90 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -178,11 +178,50 @@ pub fn deinit(l: *Liveness, gpa: Allocator) void { l.* = undefined; } +pub fn iterateBigTomb(l: Liveness, inst: Air.Inst.Index) BigTomb { + return .{ + .tomb_bits = l.getTombBits(inst), + .extra_start = l.special.get(inst) orelse 0, + .extra_offset = 0, + .extra = l.extra, + .bit_index = 0, + }; +} + /// How many tomb bits per AIR instruction. pub const bpi = 4; pub const Bpi = std.meta.Int(.unsigned, bpi); pub const OperandInt = std.math.Log2Int(Bpi); +/// Useful for decoders of Liveness information. +pub const BigTomb = struct { + tomb_bits: Liveness.Bpi, + bit_index: u32, + extra_start: u32, + extra_offset: u32, + extra: []const u32, + + /// Returns whether the next operand dies. + pub fn feed(bt: *BigTomb) bool { + const this_bit_index = bt.bit_index; + bt.bit_index += 1; + + const small_tombs = Liveness.bpi - 1; + if (this_bit_index < small_tombs) { + const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; + return dies; + } + + const big_bit_index = this_bit_index - small_tombs; + while (big_bit_index - bt.extra_offset * 31 >= 31) { + bt.extra_offset += 1; + } + const dies = @truncate(u1, bt.extra[bt.extra_start + bt.extra_offset] >> + @intCast(u5, big_bit_index - bt.extra_offset * 31)) != 0; + return dies; + } +}; + /// In-progress data; on successful analysis converted into `Liveness`. const Analysis = struct { gpa: Allocator, @@ -415,7 +454,7 @@ fn analyzeInst( const inst_data = inst_datas[inst].pl_op; const callee = inst_data.operand; const extra = a.air.extraData(Air.Call, inst_data.payload); - const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]); if (args.len + 1 <= bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); buf[0] = callee; @@ -428,6 +467,7 @@ fn analyzeInst( .inst = inst, .main_tomb = main_tomb, }; + defer extra_tombs.deinit(); try extra_tombs.feed(callee); for (args) |arg| { try extra_tombs.feed(arg); @@ -455,7 +495,7 @@ fn analyzeInst( const ty_pl = inst_datas[inst].ty_pl; const aggregate_ty = a.air.getRefType(ty_pl.ty); const len = @intCast(usize, aggregate_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]); if (elements.len <= bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); @@ -468,6 +508,7 @@ fn analyzeInst( .inst = inst, .main_tomb = main_tomb, }; + defer extra_tombs.deinit(); for (elements) |elem| { try extra_tombs.feed(elem); } @@ -530,9 +571,9 @@ fn analyzeInst( .assembly => { const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; simple: { @@ -555,6 +596,7 @@ fn analyzeInst( .inst = inst, .main_tomb = main_tomb, }; + defer extra_tombs.deinit(); for (outputs) |output| { if (output != .none) { try extra_tombs.feed(output); @@ -790,31 +832,48 @@ const ExtraTombs = struct { bit_index: usize = 0, tomb_bits: Bpi = 0, big_tomb_bits: u32 = 0, + big_tomb_bits_extra: std.ArrayListUnmanaged(u32) = .{}, fn feed(et: *ExtraTombs, op_ref: Air.Inst.Ref) !void { const this_bit_index = et.bit_index; - assert(this_bit_index < 32); // TODO mechanism for when there are greater than 32 operands et.bit_index += 1; const gpa = et.analysis.gpa; - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len); + const op_index = Air.refToIndex(op_ref) orelse return; const prev = try et.analysis.table.fetchPut(gpa, op_index, {}); if (prev == null) { // Death. if (et.new_set) |ns| try ns.putNoClobber(gpa, op_index, {}); - if (this_bit_index < bpi - 1) { + const available_tomb_bits = bpi - 1; + if (this_bit_index < available_tomb_bits) { et.tomb_bits |= @as(Bpi, 1) << @intCast(OperandInt, this_bit_index); } else { - const big_bit_index = this_bit_index - (bpi - 1); - et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, big_bit_index); + const big_bit_index = this_bit_index - available_tomb_bits; + while (big_bit_index >= (et.big_tomb_bits_extra.items.len + 1) * 31) { + // We need another element in the extra array. + try et.big_tomb_bits_extra.append(gpa, et.big_tomb_bits); + et.big_tomb_bits = 0; + } else { + const final_bit_index = big_bit_index - et.big_tomb_bits_extra.items.len * 31; + et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, final_bit_index); + } } } } fn finish(et: *ExtraTombs) !void { et.tomb_bits |= @as(Bpi, @boolToInt(et.main_tomb)) << (bpi - 1); + // Signal the terminal big_tomb_bits element. + et.big_tomb_bits |= @as(u32, 1) << 31; + et.analysis.storeTombBits(et.inst, et.tomb_bits); - try et.analysis.special.put(et.analysis.gpa, et.inst, et.big_tomb_bits); + const extra_index = @intCast(u32, et.analysis.extra.items.len); + try et.analysis.extra.ensureUnusedCapacity(et.analysis.gpa, et.big_tomb_bits_extra.items.len + 1); + try et.analysis.special.put(et.analysis.gpa, et.inst, extra_index); + et.analysis.extra.appendSliceAssumeCapacity(et.big_tomb_bits_extra.items); + et.analysis.extra.appendAssumeCapacity(et.big_tomb_bits); + } + + fn deinit(et: *ExtraTombs) void { + et.big_tomb_bits_extra.deinit(et.analysis.gpa); } }; diff --git a/src/Module.zig b/src/Module.zig index fdf61c4bf6..53c72ccec2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1394,7 +1394,15 @@ pub const Fn = struct { /// there is a `TypedValue` here for each parameter of the function. /// Non-comptime parameters are marked with a `generic_poison` for the value. /// Non-anytype parameters are marked with a `generic_poison` for the type. - comptime_args: ?[*]TypedValue = null, + /// These never have .generic_poison for the Type + /// because the Type is needed to pass to `Type.eql` and for inserting comptime arguments + /// into the inst_map when analyzing the body of a generic function instantiation. + /// Instead, the is_anytype knowledge is communicated via `anytype_args`. + comptime_args: ?[*]TypedValue, + /// When comptime_args is null, this is undefined. Otherwise, this flags each + /// parameter and tells whether it is anytype. + /// TODO apply the same enhancement for param_names below to this field. + anytype_args: [*]bool, /// The ZIR instruction that is a function instruction. Use this to find /// the body. We store this rather than the body directly so that when ZIR /// is regenerated on update(), we can map this to the new corresponding @@ -4585,7 +4593,7 @@ pub fn clearDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, - .nvptx => .{ .nvptx = .{} }, + .nvptx => .{ .nvptx = {} }, }; } if (decl.getInnerNamespace()) |namespace| { @@ -4782,18 +4790,24 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem else => continue, }; - if (func.comptime_args) |comptime_args| { + + const param_ty = if (func.comptime_args) |comptime_args| t: { const arg_tv = comptime_args[total_param_index]; - if (arg_tv.val.tag() != .generic_poison) { - // We have a comptime value for this parameter. - const arg = try sema.addConstant(arg_tv.ty, arg_tv.val); - sema.inst_map.putAssumeCapacityNoClobber(inst, arg); - total_param_index += 1; - continue; - } - } - const param_type = fn_ty_info.param_types[runtime_param_index]; - const opt_opv = sema.typeHasOnePossibleValue(&inner_block, param.src, param_type) catch |err| switch (err) { + + const arg_val = if (arg_tv.val.tag() != .generic_poison) + arg_tv.val + else if (arg_tv.ty.onePossibleValue()) |opv| + opv + else + break :t arg_tv.ty; + + const arg = try sema.addConstant(arg_tv.ty, arg_val); + sema.inst_map.putAssumeCapacityNoClobber(inst, arg); + total_param_index += 1; + continue; + } else fn_ty_info.param_types[runtime_param_index]; + + const opt_opv = sema.typeHasOnePossibleValue(&inner_block, param.src, param_ty) catch |err| switch (err) { error.NeededSourceLocation => unreachable, error.GenericPoison => unreachable, error.ComptimeReturn => unreachable, @@ -4801,7 +4815,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem else => |e| return e, }; if (opt_opv) |opv| { - const arg = try sema.addConstant(param_type, opv); + const arg = try sema.addConstant(param_ty, opv); sema.inst_map.putAssumeCapacityNoClobber(inst, arg); total_param_index += 1; runtime_param_index += 1; @@ -4811,7 +4825,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem inner_block.instructions.appendAssumeCapacity(arg_index); sema.air_instructions.appendAssumeCapacity(.{ .tag = .arg, - .data = .{ .ty = param_type }, + .data = .{ .ty = param_ty }, }); sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index)); total_param_index += 1; @@ -4961,7 +4975,7 @@ pub fn allocateNewDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, - .nvptx => .{ .nvptx = .{} }, + .nvptx => .{ .nvptx = {} }, }, .generation = 0, .is_pub = false, diff --git a/src/Sema.zig b/src/Sema.zig index ae13298337..1562b5d6f8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4722,6 +4722,8 @@ const GenericCallAdapter = struct { generic_fn: *Module.Fn, precomputed_hash: u64, func_ty_info: Type.Payload.Function.Data, + /// Unlike comptime_args, the Type here is not always present. + /// .generic_poison is used to communicate non-anytype parameters. comptime_tvs: []const TypedValue, target: std.Target, @@ -4734,20 +4736,29 @@ const GenericCallAdapter = struct { const other_comptime_args = other_key.comptime_args.?; for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| { - if (other_arg.ty.tag() != .generic_poison) { - // anytype parameter - if (!other_arg.ty.eql(ctx.comptime_tvs[i].ty, ctx.target)) { + const this_arg = ctx.comptime_tvs[i]; + const this_is_comptime = this_arg.val.tag() != .generic_poison; + const other_is_comptime = other_arg.val.tag() != .generic_poison; + const this_is_anytype = this_arg.ty.tag() != .generic_poison; + const other_is_anytype = other_key.anytype_args[i]; + + if (other_is_anytype != this_is_anytype) return false; + if (other_is_comptime != this_is_comptime) return false; + + if (this_is_anytype) { + // Both are anytype parameters. + if (!this_arg.ty.eql(other_arg.ty, ctx.target)) { return false; } - } - if (other_arg.val.tag() != .generic_poison) { - // comptime parameter - if (ctx.comptime_tvs[i].val.tag() == .generic_poison) { - // No match because the instantiation has a comptime parameter - // but the callsite does not. - return false; + if (this_is_comptime) { + // Both are comptime and anytype parameters with matching types. + if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) { + return false; + } } - if (!other_arg.val.eql(ctx.comptime_tvs[i].val, other_arg.ty, ctx.target)) { + } else if (this_is_comptime) { + // Both are comptime parameters but not anytype parameters. + if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) { return false; } } @@ -5242,28 +5253,61 @@ fn instantiateGenericCall( const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len); const target = sema.mod.getTarget(); - for (func_ty_info.param_types) |param_ty, i| { - const is_comptime = func_ty_info.paramIsComptime(i); - if (is_comptime) { - const arg_src = call_src; // TODO better source location - const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src); - if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| { - if (param_ty.tag() != .generic_poison) { - arg_val.hash(param_ty, &hasher, target); + { + var i: usize = 0; + for (fn_info.param_body) |inst| { + var is_comptime = false; + var is_anytype = false; + switch (zir_tags[inst]) { + .param => { + is_comptime = func_ty_info.paramIsComptime(i); + }, + .param_comptime => { + is_comptime = true; + }, + .param_anytype => { + is_anytype = true; + is_comptime = func_ty_info.paramIsComptime(i); + }, + .param_anytype_comptime => { + is_anytype = true; + is_comptime = true; + }, + else => continue, + } + + if (is_comptime) { + const arg_src = call_src; // TODO better source location + const arg_ty = sema.typeOf(uncasted_args[i]); + const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]); + arg_val.hash(arg_ty, &hasher, target); + if (is_anytype) { + arg_ty.hashWithHasher(&hasher, target); + comptime_tvs[i] = .{ + .ty = arg_ty, + .val = arg_val, + }; + } else { + comptime_tvs[i] = .{ + .ty = Type.initTag(.generic_poison), + .val = arg_val, + }; } + } else if (is_anytype) { + const arg_ty = sema.typeOf(uncasted_args[i]); + arg_ty.hashWithHasher(&hasher, target); comptime_tvs[i] = .{ - // This will be different than `param_ty` in the case of `generic_poison`. - .ty = sema.typeOf(casted_arg), - .val = arg_val, + .ty = arg_ty, + .val = Value.initTag(.generic_poison), }; } else { - return sema.failWithNeededComptime(block, arg_src); + comptime_tvs[i] = .{ + .ty = Type.initTag(.generic_poison), + .val = Value.initTag(.generic_poison), + }; } - } else { - comptime_tvs[i] = .{ - .ty = sema.typeOf(uncasted_args[i]), - .val = Value.initTag(.generic_poison), - }; + + i += 1; } } @@ -5426,19 +5470,48 @@ fn instantiateGenericCall( errdefer new_func.deinit(gpa); assert(new_func == new_module_func); + const anytype_args = try new_decl_arena_allocator.alloc(bool, func_ty_info.param_types.len); + new_func.anytype_args = anytype_args.ptr; arg_i = 0; for (fn_info.param_body) |inst| { + var is_comptime = false; + var is_anytype = false; switch (zir_tags[inst]) { - .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, + .param => { + is_comptime = func_ty_info.paramIsComptime(arg_i); + }, + .param_comptime => { + is_comptime = true; + }, + .param_anytype => { + is_anytype = true; + is_comptime = func_ty_info.paramIsComptime(arg_i); + }, + .param_anytype_comptime => { + is_anytype = true; + is_comptime = true; + }, else => continue, } + + // We populate the Type here regardless because it is needed by + // `GenericCallAdapter.eql` as well as function body analysis. + // Whether it is anytype is communicated by `anytype_args`. const arg = child_sema.inst_map.get(inst).?; const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); - if (child_sema.resolveMaybeUndefValAllowVariables( - &child_block, - .unneeded, - arg, - ) catch unreachable) |arg_val| { + anytype_args[arg_i] = is_anytype; + + const arg_src = call_src; // TODO: better source location + if (try sema.typeRequiresComptime(block, arg_src, copied_arg_ty)) { + is_comptime = true; + } + + if (is_comptime) { + const arg_val = (child_sema.resolveMaybeUndefValAllowVariables( + &child_block, + .unneeded, + arg, + ) catch unreachable).?; child_sema.comptime_args[arg_i] = .{ .ty = copied_arg_ty, .val = try arg_val.copy(new_decl_arena_allocator), @@ -5495,22 +5568,7 @@ fn instantiateGenericCall( const comptime_args = callee.comptime_args.?; const new_fn_info = callee.owner_decl.ty.fnInfo(); - const runtime_args_len = count: { - var count: u32 = 0; - var arg_i: usize = 0; - for (fn_info.param_body) |inst| { - switch (zir_tags[inst]) { - .param_comptime, .param_anytype_comptime, .param, .param_anytype => { - if (comptime_args[arg_i].val.tag() == .generic_poison) { - count += 1; - } - arg_i += 1; - }, - else => continue, - } - } - break :count count; - }; + const runtime_args_len = @intCast(u32, new_fn_info.param_types.len); const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); { var runtime_i: u32 = 0; @@ -5520,7 +5578,9 @@ fn instantiateGenericCall( .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, else => continue, } - const is_runtime = comptime_args[total_i].val.tag() == .generic_poison; + const is_runtime = comptime_args[total_i].val.tag() == .generic_poison and + comptime_args[total_i].ty.hasRuntimeBits() and + !comptime_args[total_i].ty.comptimeOnly(); if (is_runtime) { const param_ty = new_fn_info.param_types[runtime_i]; const arg_src = call_src; // TODO: better source location @@ -6577,6 +6637,7 @@ fn funcCommon( .zir_body_inst = func_inst, .owner_decl = sema.owner_decl, .comptime_args = comptime_args, + .anytype_args = undefined, .hash = hash, .lbrace_line = src_locs.lbrace_line, .rbrace_line = src_locs.rbrace_line, @@ -6629,7 +6690,9 @@ fn zirParam( // partial type for generic functions but we still need to // detect if a function parameter is a generic function // to force the parent function to also be generic. - break :err error.GenericPoison; + if (!sema.inst_map.contains(inst)) { + break :err error.GenericPoison; + } } break :param_ty param_ty; } else |err| break :err err; @@ -7288,9 +7351,14 @@ fn zirSwitchCapture( } }, else => { - return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{ - operand_ty.fmt(target), - }); + // In this case the capture value is just the passed-through value of the + // switch condition. + if (is_ref) { + assert(operand_is_ref); + return operand_ptr; + } else { + return operand; + } }, } } @@ -12702,7 +12770,12 @@ fn finishStructInit( } if (is_ref) { - const alloc = try block.addTy(.alloc, struct_ty); + const target = sema.mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, target, .{ + .pointee_type = struct_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + const alloc = try block.addTy(.alloc, alloc_ty); for (field_inits) |field_init, i_usize| { const i = @intCast(u32, i_usize); const field_src = src; @@ -14052,21 +14125,27 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.checkPtrType(block, dest_ty_src, dest_ty); try sema.checkPtrOperand(block, operand_src, operand_ty); - if (dest_ty.isSlice()) { + + const dest_is_slice = dest_ty.isSlice(); + const operand_is_slice = operand_ty.isSlice(); + if (dest_is_slice and !operand_is_slice) { return sema.fail(block, dest_ty_src, "illegal pointer cast to slice", .{}); } - const ptr = if (operand_ty.isSlice()) + const ptr = if (operand_is_slice and !dest_is_slice) try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty) else operand; - try sema.resolveTypeLayout(block, dest_ty_src, dest_ty.elemType2()); + const dest_elem_ty = dest_ty.elemType2(); + try sema.resolveTypeLayout(block, dest_ty_src, dest_elem_ty); const dest_align = dest_ty.ptrAlignment(target); - try sema.resolveTypeLayout(block, operand_src, operand_ty.elemType2()); + + const operand_elem_ty = operand_ty.elemType2(); + try sema.resolveTypeLayout(block, operand_src, operand_elem_ty); const operand_align = operand_ty.ptrAlignment(target); // If the destination is less aligned than the source, preserve the source alignment - var aligned_dest_ty = if (operand_align <= dest_align) dest_ty else blk: { + const aligned_dest_ty = if (operand_align <= dest_align) dest_ty else blk: { // Unwrap the pointer (or pointer-like optional) type, set alignment, and re-wrap into result if (dest_ty.zigTypeTag() == .Optional) { var buf: Type.Payload.ElemType = undefined; @@ -14080,6 +14159,16 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } }; + if (dest_is_slice) { + const operand_elem_size = operand_elem_ty.abiSize(target); + const dest_elem_size = dest_elem_ty.abiSize(target); + if (operand_elem_size != dest_elem_size) { + // note that this is not implemented in stage1 so we should probably wait + // until that codebase is replaced before implementing this in stage2. + return sema.fail(block, dest_ty_src, "TODO: implement @ptrCast between slices changing the length", .{}); + } + } + return sema.coerceCompatiblePtrs(block, aligned_dest_ty, ptr, operand_src); } @@ -15685,7 +15774,7 @@ fn zirMinMax( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - air_tag: Air.Inst.Tag, + comptime air_tag: Air.Inst.Tag, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; @@ -15705,7 +15794,7 @@ fn analyzeMinMax( src: LazySrcLoc, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, - air_tag: Air.Inst.Tag, + comptime air_tag: Air.Inst.Tag, lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { @@ -18042,8 +18131,7 @@ fn coerce( const array_ty = inst_ty.childType(); if (array_ty.zigTypeTag() != .Array) break :src_array_ptr; const len0 = array_ty.arrayLen() == 0; - // We resolve here so that the backend has the layout of the elem type. - const array_elem_type = try sema.resolveTypeFields(block, inst_src, array_ty.childType()); + const array_elem_type = array_ty.childType(); const dest_is_mut = dest_info.mutable; if (inst_ty.isConstPtr() and dest_is_mut and !len0) break :src_array_ptr; if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr; @@ -18187,6 +18275,19 @@ fn coerce( { return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); } + + // empty tuple to zero-length slice + // note that this allows coercing to a mutable slice. + if (inst_ty.isSinglePointer() and + inst_ty.childType().tag() == .empty_struct_literal and + dest_info.size == .Slice) + { + const slice_val = try Value.Tag.slice.create(sema.arena, .{ + .ptr = Value.undef, + .len = Value.zero, + }); + return sema.addConstant(dest_ty, slice_val); + } }, .Many => p: { if (!inst_ty.isSlice()) break :p; @@ -18461,6 +18562,17 @@ fn coerceInMemoryAllowed( if (dest_ty.eql(src_ty, target)) return .ok; + // Differently-named integers with the same number of bits. + if (dest_ty.zigTypeTag() == .Int and src_ty.zigTypeTag() == .Int) { + const dest_info = dest_ty.intInfo(target); + const src_info = src_ty.intInfo(target); + if (dest_info.signedness == src_info.signedness and + dest_info.bits == src_info.bits) + { + return .ok; + } + } + // Pointers / Pointer-like Optionals var dest_buf: Type.Payload.ElemType = undefined; var src_buf: Type.Payload.ElemType = undefined; @@ -20913,7 +21025,7 @@ fn resolvePeerTypes( sema: *Sema, block: *Block, src: LazySrcLoc, - instructions: []Air.Inst.Ref, + instructions: []const Air.Inst.Ref, candidate_srcs: Module.PeerTypeCandidateSrc, ) !Type { switch (instructions.len) { @@ -21562,6 +21674,8 @@ fn resolveUnionLayout( union_obj.status = .have_layout; } +/// Returns `error.AnalysisFail` if any of the types (recursively) failed to +/// be resolved. pub fn resolveTypeFully( sema: *Sema, block: *Block, @@ -21610,18 +21724,29 @@ fn resolveStructFully( const resolved_ty = try sema.resolveTypeFields(block, src, ty); const payload = resolved_ty.castTag(.@"struct") orelse return; const struct_obj = payload.data; + switch (struct_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, } - // After we have resolve struct layout we have to go over the fields again to - // make sure pointer fields get their child types resolved as well - struct_obj.status = .fully_resolved_wip; - for (struct_obj.fields.values()) |field| { - try sema.resolveTypeFully(block, src, field.ty); + log.debug("resolveStructFully {*} ('{s}')", .{ + struct_obj.owner_decl, struct_obj.owner_decl.name, + }); + + { + // After we have resolve struct layout we have to go over the fields again to + // make sure pointer fields get their child types resolved as well. + // See also similar code for unions. + const prev_status = struct_obj.status; + errdefer struct_obj.status = prev_status; + + struct_obj.status = .fully_resolved_wip; + for (struct_obj.fields.values()) |field| { + try sema.resolveTypeFully(block, src, field.ty); + } + struct_obj.status = .fully_resolved; } - struct_obj.status = .fully_resolved; // And let's not forget comptime-only status. _ = try sema.typeRequiresComptime(block, src, ty); @@ -21642,12 +21767,19 @@ fn resolveUnionFully( .fully_resolved_wip, .fully_resolved => return, } - // Same goes for unions (see comment about structs) - union_obj.status = .fully_resolved_wip; - for (union_obj.fields.values()) |field| { - try sema.resolveTypeFully(block, src, field.ty); + { + // After we have resolve union layout we have to go over the fields again to + // make sure pointer fields get their child types resolved as well. + // See also similar code for structs. + const prev_status = union_obj.status; + errdefer union_obj.status = prev_status; + + union_obj.status = .fully_resolved_wip; + for (union_obj.fields.values()) |field| { + try sema.resolveTypeFully(block, src, field.ty); + } + union_obj.status = .fully_resolved; } - union_obj.status = .fully_resolved; // And let's not forget comptime-only status. _ = try sema.typeRequiresComptime(block, src, ty); @@ -22776,7 +22908,7 @@ pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { } fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { - const coerced = @bitCast([]const u32, refs); + const coerced = @ptrCast([]const u32, refs); sema.air_extra.appendSliceAssumeCapacity(coerced); } diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index ac95def319..36d004cfc6 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -12,7 +12,12 @@ idle_queue: IdleQueue = .{}, const IdleQueue = std.SinglyLinkedList(std.Thread.ResetEvent); const RunQueue = std.SinglyLinkedList(Runnable); const Runnable = struct { - runFn: fn (*Runnable) void, + runFn: RunProto, +}; + +const RunProto = switch (builtin.zig_backend) { + .stage1 => fn (*Runnable) void, + else => *const fn (*Runnable) void, }; const Worker = struct { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index ea946d6ba6..95d2a8a607 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -202,26 +202,12 @@ const BlockData = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -2412,7 +2398,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { @@ -2879,7 +2865,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // TODO track the new register / stack allocation } - self.branch_stack.pop().deinit(self.gpa); + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } // We already took care of pl_op.operand earlier, so we're going // to pass .none here @@ -3176,9 +3165,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -3291,9 +3280,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } @@ -3702,7 +3689,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 9a660ceff6..f71ceaba89 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -224,26 +224,12 @@ const BlockData = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -3158,7 +3144,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { @@ -3664,7 +3650,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // TODO track the new register / stack allocation } - self.branch_stack.pop().deinit(self.gpa); + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } // We already took care of pl_op.operand earlier, so we're going // to pass .none here @@ -3965,9 +3954,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -4076,9 +4065,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } @@ -4751,7 +4738,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; return self.fail("TODO implement airAggregateInit for arm", .{}); diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 77fa82d1d2..209ab137a6 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -2,6 +2,7 @@ //! machine code const Emit = @This(); +const builtin = @import("builtin"); const std = @import("std"); const math = std.math; const Mir = @import("Mir.zig"); @@ -417,7 +418,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), @@ -449,7 +450,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { }; const dbg_info = &dw.dbg_info; - try dbg_info.append(link.File.Dwarf.abbrev_parameter); + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); // Get length of the LEB128 stack offset var counting_writer = std.io.countingWriter(std.io.null_writer); @@ -622,12 +623,17 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { } else return emit.fail("TODO mirLoadStack larger offsets", .{}); const ldr = switch (tag) { - .ldr_stack_argument => Instruction.ldr, - .ldrb_stack_argument => Instruction.ldrb, + .ldr_stack_argument => &Instruction.ldr, + .ldrb_stack_argument => &Instruction.ldrb, else => unreachable, }; - try emit.writeInstruction(ldr( + const ldr_workaround = switch (builtin.zig_backend) { + .stage1 => ldr.*, + else => ldr, + }; + + try emit.writeInstruction(ldr_workaround( cond, r_stack_offset.rt, .fp, @@ -643,13 +649,18 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { } else return emit.fail("TODO mirLoadStack larger offsets", .{}); const ldr = switch (tag) { - .ldrh_stack_argument => Instruction.ldrh, - .ldrsb_stack_argument => Instruction.ldrsb, - .ldrsh_stack_argument => Instruction.ldrsh, + .ldrh_stack_argument => &Instruction.ldrh, + .ldrsb_stack_argument => &Instruction.ldrsb, + .ldrsh_stack_argument => &Instruction.ldrsh, else => unreachable, }; - try emit.writeInstruction(ldr( + const ldr_workaround = switch (builtin.zig_backend) { + .stage1 => ldr.*, + else => ldr, + }; + + try emit.writeInstruction(ldr_workaround( cond, r_stack_offset.rt, .fp, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index ac12bbceaf..cf9e5fefcd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -194,26 +194,12 @@ const Reloc = union(enum) { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -1574,7 +1560,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), @@ -1654,7 +1640,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const fn_ty = self.air.typeOf(pl_op.operand); const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); var info = try self.resolveCallingConventionValues(fn_ty); defer info.deinit(self); @@ -2089,9 +2075,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -2198,9 +2184,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } @@ -2429,7 +2413,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; return self.fail("TODO implement airAggregateInit for riscv64", .{}); diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index db5811dfda..7de035bc5c 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1,23 +1,235 @@ //! SPARCv9 codegen. //! This lowers AIR into MIR. const std = @import("std"); +const assert = std.debug.assert; +const log = std.log.scoped(.codegen); +const math = std.math; +const mem = std.mem; +const Allocator = mem.Allocator; const builtin = @import("builtin"); const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); +const TypedValue = @import("../../TypedValue.zig"); +const ErrorMsg = Module.ErrorMsg; const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); - +const Type = @import("../../type.zig").Type; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; +const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs); + +const build_options = @import("build_options"); const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const Register = bits.Register; const Self = @This(); +const InnerError = error{ + OutOfMemory, + CodegenFail, + OutOfRegisters, +}; + +const RegisterView = enum(u1) { + caller, + callee, +}; + +gpa: Allocator, +air: Air, +liveness: Liveness, +bin_file: *link.File, +target: *const std.Target, +mod_fn: *const Module.Fn, +code: *std.ArrayList(u8), +debug_output: DebugInfoOutput, +err_msg: ?*ErrorMsg, +args: []MCValue, +ret_mcv: MCValue, +fn_type: Type, +arg_index: usize, +src_loc: Module.SrcLoc, +stack_align: u32, + +/// MIR Instructions +mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, +/// MIR extra data +mir_extra: std.ArrayListUnmanaged(u32) = .{}, + +/// Byte offset within the source file of the ending curly. +end_di_line: u32, +end_di_column: u32, + +/// The value is an offset into the `Function` `code` from the beginning. +/// To perform the reloc, write 32-bit signed little-endian integer +/// which is a relative jump, based on the address following the reloc. +exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, + +/// Whenever there is a runtime branch, we push a Branch onto this stack, +/// and pop it off when the runtime branch joins. This provides an "overlay" +/// of the table of mappings from instructions to `MCValue` from within the branch. +/// This way we can modify the `MCValue` for an instruction in different ways +/// within different branches. Special consideration is needed when a branch +/// joins with its parent, to make sure all instructions have the same MCValue +/// across each runtime branch upon joining. +branch_stack: *std.ArrayList(Branch), + +// Key is the block instruction +blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, + +register_manager: RegisterManager = .{}, + +/// Maps offset to what is stored there. +stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, + +/// Offset from the stack base, representing the end of the stack frame. +max_end_stack: u32 = 0, +/// Represents the current end stack offset. If there is no existing slot +/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. +next_stack_offset: u32 = 0, + +/// Debug field, used to find bugs in the compiler. +air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, + +const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; + +const MCValue = union(enum) { + /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. + /// TODO Look into deleting this tag and using `dead` instead, since every use + /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. + none, + /// Control flow will not allow this value to be observed. + unreach, + /// No more references to this value remain. + dead, + /// The value is undefined. + undef, + /// A pointer-sized integer that fits in a register. + /// If the type is a pointer, this is the pointer address in virtual address space. + immediate: u64, + /// The value is in a target-specific register. + register: Register, + /// The value is in memory at a hard-coded address. + /// If the type is a pointer, it means the pointer address is at this memory location. + memory: u64, + /// The value is one of the stack variables. + /// If the type is a pointer, it means the pointer address is in the stack at this offset. + stack_offset: u32, + /// The value is a pointer to one of the stack variables (payload is stack offset). + ptr_stack_offset: u32, + + fn isMemory(mcv: MCValue) bool { + return switch (mcv) { + .memory, .stack_offset => true, + else => false, + }; + } + + fn isImmediate(mcv: MCValue) bool { + return switch (mcv) { + .immediate => true, + else => false, + }; + } + + fn isMutable(mcv: MCValue) bool { + return switch (mcv) { + .none => unreachable, + .unreach => unreachable, + .dead => unreachable, + + .immediate, + .memory, + .ptr_stack_offset, + .undef, + => false, + + .register, + .stack_offset, + => true, + }; + } +}; + +const Branch = struct { + inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, + + fn deinit(self: *Branch, gpa: Allocator) void { + self.inst_table.deinit(gpa); + self.* = undefined; + } +}; + +const StackAllocation = struct { + inst: Air.Inst.Index, + /// TODO do we need size? should be determined by inst.ty.abiSize() + size: u32, +}; + +const BlockData = struct { + relocs: std.ArrayListUnmanaged(Mir.Inst.Index), + /// The first break instruction encounters `null` here and chooses a + /// machine code value for the block result, populating this field. + /// Following break instructions encounter that value and use it for + /// the location to store their block results. + mcv: MCValue, +}; + +const CallMCValues = struct { + args: []MCValue, + return_value: MCValue, + stack_byte_count: u32, + stack_align: u32, + + fn deinit(self: *CallMCValues, func: *Self) void { + func.gpa.free(self.args); + self.* = undefined; + } +}; + +const BigTomb = struct { + function: *Self, + inst: Air.Inst.Index, + tomb_bits: Liveness.Bpi, + big_tomb_bits: u32, + bit_index: usize, + + fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { + const this_bit_index = bt.bit_index; + bt.bit_index += 1; + + const op_int = @enumToInt(op_ref); + if (op_int < Air.Inst.Ref.typed_value_map.len) return; + const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + + if (this_bit_index < Liveness.bpi - 1) { + const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; + if (!dies) return; + } else { + const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); + const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; + if (!dies) return; + } + bt.function.processDeath(op_index); + } + + fn finishAir(bt: *BigTomb, result: MCValue) void { + const is_used = !bt.function.liveness.isUnused(bt.inst); + if (is_used) { + log.debug("%{d} => {}", .{ bt.inst, result }); + const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); + } + bt.function.finishAirBookkeeping(); + } +}; + pub fn generate( bin_file: *link.File, src_loc: Module.SrcLoc, @@ -27,13 +239,1439 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) GenerateSymbolError!FnResult { - _ = bin_file; - _ = src_loc; - _ = module_fn; - _ = air; - _ = liveness; - _ = code; - _ = debug_output; - - @panic("TODO implement SPARCv9 codegen"); + if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { + @panic("Attempted to compile for architecture that was disabled by build configuration"); + } + + assert(module_fn.owner_decl.has_tv); + const fn_type = module_fn.owner_decl.ty; + + var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); + defer { + assert(branch_stack.items.len == 1); + branch_stack.items[0].deinit(bin_file.allocator); + branch_stack.deinit(); + } + try branch_stack.append(.{}); + + var function = Self{ + .gpa = bin_file.allocator, + .air = air, + .liveness = liveness, + .target = &bin_file.options.target, + .bin_file = bin_file, + .mod_fn = module_fn, + .code = code, + .debug_output = debug_output, + .err_msg = null, + .args = undefined, // populated after `resolveCallingConventionValues` + .ret_mcv = undefined, // populated after `resolveCallingConventionValues` + .fn_type = fn_type, + .arg_index = 0, + .branch_stack = &branch_stack, + .src_loc = src_loc, + .stack_align = undefined, + .end_di_line = module_fn.rbrace_line, + .end_di_column = module_fn.rbrace_column, + }; + defer function.stack.deinit(bin_file.allocator); + defer function.blocks.deinit(bin_file.allocator); + defer function.exitlude_jump_relocs.deinit(bin_file.allocator); + + var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { + error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }; + defer call_info.deinit(&function); + + function.args = call_info.args; + function.ret_mcv = call_info.return_value; + function.stack_align = call_info.stack_align; + function.max_end_stack = call_info.stack_byte_count; + + function.gen() catch |err| switch (err) { + error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }; + + var mir = Mir{ + .instructions = function.mir_instructions.toOwnedSlice(), + .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), + }; + defer mir.deinit(bin_file.allocator); + + var emit = Emit{ + .mir = mir, + .bin_file = bin_file, + .debug_output = debug_output, + .target = &bin_file.options.target, + .src_loc = src_loc, + .code = code, + .prev_di_pc = 0, + .prev_di_line = module_fn.lbrace_line, + .prev_di_column = module_fn.lbrace_column, + }; + defer emit.deinit(); + + emit.emitMir() catch |err| switch (err) { + error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, + else => |e| return e, + }; + + if (function.err_msg) |em| { + return FnResult{ .fail = em }; + } else { + return FnResult{ .appended = {} }; + } +} + +fn gen(self: *Self) !void { + const cc = self.fn_type.fnCallingConvention(); + if (cc != .Naked) { + // TODO Finish function prologue and epilogue for sparcv9. + + // TODO Backpatch stack offset + // save %sp, -176, %sp + _ = try self.addInst(.{ + .tag = .save, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = .sp, + .rs1 = .sp, + .rs2_or_imm = .{ .imm = -176 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .dbg_prologue_end, + .data = .{ .nop = {} }, + }); + + try self.genBody(self.air.getMainBody()); + + _ = try self.addInst(.{ + .tag = .dbg_epilogue_begin, + .data = .{ .nop = {} }, + }); + + // exitlude jumps + if (self.exitlude_jump_relocs.items.len > 0 and + self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) + { + // If the last Mir instruction (apart from the + // dbg_epilogue_begin) is the last exitlude jump + // relocation (which would just jump one instruction + // further), it can be safely removed + self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); + } + + for (self.exitlude_jump_relocs.items) |jmp_reloc| { + _ = jmp_reloc; + return self.fail("TODO add branches in sparcv9", .{}); + } + + // return %i7 + 8 + _ = try self.addInst(.{ + .tag = .@"return", + .data = .{ + .arithmetic_2op = .{ + .is_imm = true, + .rs1 = .@"i7", + .rs2_or_imm = .{ .imm = 8 }, + }, + }, + }); + + // TODO Find a way to fill this slot + // nop + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + } else { + _ = try self.addInst(.{ + .tag = .dbg_prologue_end, + .data = .{ .nop = {} }, + }); + + try self.genBody(self.air.getMainBody()); + + _ = try self.addInst(.{ + .tag = .dbg_epilogue_begin, + .data = .{ .nop = {} }, + }); + } + + // Drop them off at the rbrace. + _ = try self.addInst(.{ + .tag = .dbg_line, + .data = .{ .dbg_line_column = .{ + .line = self.end_di_line, + .column = self.end_di_column, + } }, + }); +} + +fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { + const air_tags = self.air.instructions.items(.tag); + + for (body) |inst| { + const old_air_bookkeeping = self.air_bookkeeping; + try self.ensureProcessDeathCapacity(Liveness.bpi); + + switch (air_tags[inst]) { + // zig fmt: off + .add, .ptr_add => @panic("TODO try self.airBinOp(inst)"), + .addwrap => @panic("TODO try self.airAddWrap(inst)"), + .add_sat => @panic("TODO try self.airAddSat(inst)"), + .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"), + .subwrap => @panic("TODO try self.airSubWrap(inst)"), + .sub_sat => @panic("TODO try self.airSubSat(inst)"), + .mul => @panic("TODO try self.airMul(inst)"), + .mulwrap => @panic("TODO try self.airMulWrap(inst)"), + .mul_sat => @panic("TODO try self.airMulSat(inst)"), + .rem => @panic("TODO try self.airRem(inst)"), + .mod => @panic("TODO try self.airMod(inst)"), + .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), + .shl_sat => @panic("TODO try self.airShlSat(inst)"), + .min => @panic("TODO try self.airMin(inst)"), + .max => @panic("TODO try self.airMax(inst)"), + .slice => @panic("TODO try self.airSlice(inst)"), + + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => @panic("TODO try self.airUnaryMath(inst)"), + + .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"), + .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"), + .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), + .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), + + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + + .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"), + .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"), + .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"), + .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"), + .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"), + .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"), + .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), + .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"), + + .bool_and => @panic("TODO try self.airBoolOp(inst)"), + .bool_or => @panic("TODO try self.airBoolOp(inst)"), + .bit_and => @panic("TODO try self.airBitAnd(inst)"), + .bit_or => @panic("TODO try self.airBitOr(inst)"), + .xor => @panic("TODO try self.airXor(inst)"), + .shr, .shr_exact => @panic("TODO try self.airShr(inst)"), + + .alloc => @panic("TODO try self.airAlloc(inst)"), + .ret_ptr => try self.airRetPtr(inst), + .arg => try self.airArg(inst), + .assembly => try self.airAsm(inst), + .bitcast => @panic("TODO try self.airBitCast(inst)"), + .block => try self.airBlock(inst), + .br => @panic("TODO try self.airBr(inst)"), + .breakpoint => try self.airBreakpoint(), + .ret_addr => @panic("TODO try self.airRetAddr(inst)"), + .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), + .fence => @panic("TODO try self.airFence()"), + .cond_br => @panic("TODO try self.airCondBr(inst)"), + .dbg_stmt => try self.airDbgStmt(inst), + .fptrunc => @panic("TODO try self.airFptrunc(inst)"), + .fpext => @panic("TODO try self.airFpext(inst)"), + .intcast => @panic("TODO try self.airIntCast(inst)"), + .trunc => @panic("TODO try self.airTrunc(inst)"), + .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"), + .is_non_null => @panic("TODO try self.airIsNonNull(inst)"), + .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), + .is_null => @panic("TODO try self.airIsNull(inst)"), + .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), + .is_non_err => @panic("TODO try self.airIsNonErr(inst)"), + .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), + .is_err => @panic("TODO try self.airIsErr(inst)"), + .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), + .load => @panic("TODO try self.airLoad(inst)"), + .loop => @panic("TODO try self.airLoop(inst)"), + .not => @panic("TODO try self.airNot(inst)"), + .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), + .ret => try self.airRet(inst), + .ret_load => try self.airRetLoad(inst), + .store => try self.airStore(inst), + .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), + .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"), + .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"), + .int_to_float => @panic("TODO try self.airIntToFloat(inst)"), + .float_to_int => @panic("TODO try self.airFloatToInt(inst)"), + .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"), + .cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"), + .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"), + .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"), + .memcpy => @panic("TODO try self.airMemcpy(inst)"), + .memset => @panic("TODO try self.airMemset(inst)"), + .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"), + .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"), + .clz => @panic("TODO try self.airClz(inst)"), + .ctz => @panic("TODO try self.airCtz(inst)"), + .popcount => @panic("TODO try self.airPopcount(inst)"), + .byte_swap => @panic("TODO try self.airByteSwap(inst)"), + .bit_reverse => @panic("TODO try self.airBitReverse(inst)"), + .tag_name => @panic("TODO try self.airTagName(inst)"), + .error_name => @panic("TODO try self.airErrorName(inst)"), + .splat => @panic("TODO try self.airSplat(inst)"), + .select => @panic("TODO try self.airSelect(inst)"), + .shuffle => @panic("TODO try self.airShuffle(inst)"), + .reduce => @panic("TODO try self.airReduce(inst)"), + .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"), + .union_init => @panic("TODO try self.airUnionInit(inst)"), + .prefetch => @panic("TODO try self.airPrefetch(inst)"), + .mul_add => @panic("TODO try self.airMulAdd(inst)"), + + .dbg_var_ptr, + .dbg_var_val, + => try self.airDbgVar(inst), + + .dbg_inline_begin, + .dbg_inline_end, + => try self.airDbgInline(inst), + + .dbg_block_begin, + .dbg_block_end, + => try self.airDbgBlock(inst), + + .call => try self.airCall(inst, .auto), + .call_always_tail => try self.airCall(inst, .always_tail), + .call_never_tail => try self.airCall(inst, .never_tail), + .call_never_inline => try self.airCall(inst, .never_inline), + + .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"), + .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"), + .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"), + .atomic_store_seq_cst => @panic("TODO try self.airAtomicStore(inst, .SeqCst)"), + + .struct_field_ptr_index_0 => @panic("TODO try self.airStructFieldPtrIndex(inst, 0)"), + .struct_field_ptr_index_1 => @panic("TODO try self.airStructFieldPtrIndex(inst, 1)"), + .struct_field_ptr_index_2 => @panic("TODO try self.airStructFieldPtrIndex(inst, 2)"), + .struct_field_ptr_index_3 => @panic("TODO try self.airStructFieldPtrIndex(inst, 3)"), + + .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"), + + .switch_br => try self.airSwitch(inst), + .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"), + .slice_len => @panic("TODO try self.airSliceLen(inst)"), + + .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"), + .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), + + .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"), + .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"), + .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), + .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), + .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"), + + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .unreach => self.finishAirBookkeeping(), + + .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"), + .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"), + .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"), + .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"), + .unwrap_errunion_payload => @panic("TODO try self.airUnwrapErrPayload(inst)"), + .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), + .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), + .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"), + + .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), + .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"), + .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, + // zig fmt: on + } + + if (std.debug.runtime_safety) { + if (self.air_bookkeeping < old_air_bookkeeping + 1) { + std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] }); + } + } + } +} + +fn airAsm(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = (extra.data.flags & 0x80000000) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]); + extra_i += inputs.len; + + const dead = !is_volatile and self.liveness.isUnused(inst); + const result: MCValue = if (dead) .dead else result: { + if (outputs.len > 1) { + return self.fail("TODO implement codegen for asm with more than 1 output", .{}); + } + + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return self.fail("TODO implement codegen for non-expr asm", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; + + for (inputs) |input| { + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + + if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { + return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); + } + const reg_name = constraint[1 .. constraint.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail("unrecognized register: '{s}'", .{reg_name}); + + const arg_mcv = try self.resolveInst(input); + try self.register_manager.getReg(reg, null); + try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } + } + + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; + + if (mem.eql(u8, asm_source, "ta 0x6d")) { + _ = try self.addInst(.{ + .tag = .tcc, + .data = .{ + .trap = .{ + .is_imm = true, + .cond = 0b1000, // TODO need to look into changing this into an enum + .rs2_or_imm = .{ .imm = 0x6d }, + }, + }, + }); + } else { + return self.fail("TODO implement a full SPARCv9 assembly parsing", .{}); + } + + if (output_constraint) |output| { + if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { + return self.fail("unrecognized asm output constraint: '{s}'", .{output}); + } + const reg_name = output[2 .. output.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail("unrecognized register: '{s}'", .{reg_name}); + break :result MCValue{ .register = reg }; + } else { + break :result MCValue{ .none = {} }; + } + }; + + simple: { + var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); + var buf_index: usize = 0; + for (outputs) |output| { + if (output == .none) continue; + + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); + return self.finishAir(inst, result, buf); + } + + var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); + for (outputs) |output| { + if (output == .none) continue; + + bt.feed(output); + } + for (inputs) |input| { + bt.feed(input); + } + return bt.finishAir(result); +} + +fn airArg(self: *Self, inst: Air.Inst.Index) !void { + const arg_index = self.arg_index; + self.arg_index += 1; + + const ty = self.air.typeOfIndex(inst); + _ = ty; + + const result = self.args[arg_index]; + // TODO support stack-only arguments + // TODO Copy registers to the stack + const mcv = result; + + _ = try self.addInst(.{ + .tag = .dbg_arg, + .data = .{ + .dbg_arg_info = .{ + .air_inst = inst, + .arg_index = arg_index, + }, + }, + }); + + if (self.liveness.isUnused(inst)) + return self.finishAirBookkeeping(); + + switch (mcv) { + .register => |reg| { + self.register_manager.getRegAssumeFree(reg, inst); + }, + else => {}, + } + + return self.finishAir(inst, mcv, .{ .none, .none, .none }); +} + +fn airBlock(self: *Self, inst: Air.Inst.Index) !void { + try self.blocks.putNoClobber(self.gpa, inst, .{ + // A block is a setup to be able to jump to the end. + .relocs = .{}, + // It also acts as a receptacle for break operands. + // Here we use `MCValue.none` to represent a null value so that the first + // break instruction will choose a MCValue for the block result and overwrite + // this field. Following break instructions will use that MCValue to put their + // block results. + .mcv = MCValue{ .none = {} }, + }); + defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Block, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + try self.genBody(body); + + // relocations for `bpcc` instructions + const relocs = &self.blocks.getPtr(inst).?.relocs; + if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { + // If the last Mir instruction is the last relocation (which + // would just jump one instruction further), it can be safely + // removed + self.mir_instructions.orderedRemove(relocs.pop()); + } + for (relocs.items) |reloc| { + try self.performReloc(reloc); + } + + const result = self.blocks.getPtr(inst).?.mcv; + return self.finishAir(inst, result, .{ .none, .none, .none }); +} + +fn airBreakpoint(self: *Self) !void { + // ta 0x01 + _ = try self.addInst(.{ + .tag = .tcc, + .data = .{ + .trap = .{ + .is_imm = true, + .cond = 0b1000, // TODO need to look into changing this into an enum + .rs2_or_imm = .{ .imm = 0x01 }, + }, + }, + }); + return self.finishAirBookkeeping(); +} + +fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void { + if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); + + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const callee = pl_op.operand; + const extra = self.air.extraData(Air.Call, pl_op.payload); + const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]); + const ty = self.air.typeOf(callee); + const fn_ty = switch (ty.zigTypeTag()) { + .Fn => ty, + .Pointer => ty.childType(), + else => unreachable, + }; + + var info = try self.resolveCallingConventionValues(fn_ty, .caller); + defer info.deinit(self); + for (info.args) |mc_arg, arg_i| { + const arg = args[arg_i]; + const arg_ty = self.air.typeOf(arg); + const arg_mcv = try self.resolveInst(arg); + + switch (mc_arg) { + .none => continue, + .undef => unreachable, + .immediate => unreachable, + .unreach => unreachable, + .dead => unreachable, + .memory => unreachable, + .register => |reg| { + try self.register_manager.getReg(reg, null); + try self.genSetReg(arg_ty, reg, arg_mcv); + }, + .stack_offset => { + return self.fail("TODO implement calling with parameters in memory", .{}); + }, + .ptr_stack_offset => { + return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); + }, + } + } + + // Due to incremental compilation, how function calls are generated depends + // on linking. + if (self.air.value(callee)) |func_value| { + if (self.bin_file.tag == link.File.Elf.base_tag) { + if (func_value.castTag(.function)) |func_payload| { + const func = func_payload.data; + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + } else unreachable; + + try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); + + _ = try self.addInst(.{ + .tag = .jmpl, + .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + }); + } else if (func_value.castTag(.extern_fn)) |_| { + return self.fail("TODO implement calling extern functions", .{}); + } else { + return self.fail("TODO implement calling bitcasted functions", .{}); + } + } else @panic("TODO SPARCv9 currently does not support non-ELF binaries"); + } else { + assert(ty.zigTypeTag() == .Pointer); + const mcv = try self.resolveInst(callee); + try self.genSetReg(ty, .o7, mcv); + + _ = try self.addInst(.{ + .tag = .jmpl, + .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + }); + } + + const result = info.return_value; + + if (args.len + 1 <= Liveness.bpi - 1) { + var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); + buf[0] = callee; + std.mem.copy(Air.Inst.Ref, buf[1..], args); + return self.finishAir(inst, result, buf); + } + + @panic("TODO handle return value with BigTomb"); +} + +fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { + // TODO emit debug info lexical block + return self.finishAir(inst, .dead, .{ .none, .none, .none }); +} + +fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const function = self.air.values[ty_pl.payload].castTag(.function).?.data; + // TODO emit debug info for function change + _ = function; + return self.finishAir(inst, .dead, .{ .none, .none, .none }); +} + +fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { + const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; + + _ = try self.addInst(.{ + .tag = .dbg_line, + .data = .{ + .dbg_line_column = .{ + .line = dbg_stmt.line, + .column = dbg_stmt.column, + }, + }, + }); + + return self.finishAirBookkeeping(); +} + +fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const name = self.air.nullTerminatedString(pl_op.payload); + const operand = pl_op.operand; + // TODO emit debug info for this variable + _ = name; + return self.finishAir(inst, .dead, .{ operand, .none, .none }); +} + +fn airDiv(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airRet(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + try self.ret(operand); + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); +} + +fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const ptr = try self.resolveInst(un_op); + _ = ptr; + return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); + //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); +} + +fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { + const stack_offset = try self.allocMemPtr(inst); + return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); +} + +fn airStore(self: *Self, inst: Air.Inst.Index) !void { + _ = self; + _ = inst; + + return self.fail("TODO implement store for {}", .{self.target.cpu.arch}); +} + +fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { + _ = self; + _ = inst; + + return self.fail("TODO implement switch for {}", .{self.target.cpu.arch}); +} + +// Common helper functions + +fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { + const gpa = self.gpa; + + try self.mir_instructions.ensureUnusedCapacity(gpa, 1); + + const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); + self.mir_instructions.appendAssumeCapacity(inst); + return result_index; +} + +fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { + if (abi_align > self.stack_align) + self.stack_align = abi_align; + // TODO find a free slot instead of always appending + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); + self.next_stack_offset = offset + abi_size; + if (self.next_stack_offset > self.max_end_stack) + self.max_end_stack = self.next_stack_offset; + try self.stack.putNoClobber(self.gpa, offset, .{ + .inst = inst, + .size = abi_size, + }); + return offset; +} + +/// Use a pointer instruction as the basis for allocating stack memory. +fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { + const elem_ty = self.air.typeOfIndex(inst).elemType(); + + if (!elem_ty.hasRuntimeBits()) { + // As this stack item will never be dereferenced at runtime, + // return the stack offset 0. Stack offset 0 will be where all + // zero-sized stack allocations live as non-zero-sized + // allocations will always have an offset > 0. + return @as(u32, 0); + } + + const target = self.target.*; + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + }; + // TODO swap this for inst.ty.ptrAlign + const abi_align = elem_ty.abiAlignment(self.target.*); + return self.allocMem(inst, abi_size, abi_align); +} + +fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { + const elem_ty = self.air.typeOfIndex(inst); + const target = self.target.*; + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + }; + const abi_align = elem_ty.abiAlignment(self.target.*); + if (abi_align > self.stack_align) + self.stack_align = abi_align; + + if (reg_ok) { + // Make sure the type can fit in a register before we try to allocate one. + if (abi_size <= 8) { + if (self.register_manager.tryAllocReg(inst)) |reg| { + return MCValue{ .register = reg }; + } + } + } + const stack_offset = try self.allocMem(inst, abi_size, abi_align); + return MCValue{ .stack_offset = stack_offset }; +} + +/// Copies a value to a register without tracking the register. The register is not considered +/// allocated. A second call to `copyToTmpRegister` may return the same register. +/// This can have a side effect of spilling instructions to the stack to free up a register. +fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { + const reg = try self.register_manager.allocReg(null); + try self.genSetReg(ty, reg, mcv); + return reg; +} + +fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { + const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; + try table.ensureUnusedCapacity(self.gpa, additional_count); +} + +fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { + @setCold(true); + assert(self.err_msg == null); + self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + return error.CodegenFail; +} + +/// Called when there are no operands, and the instruction is always unreferenced. +fn finishAirBookkeeping(self: *Self) void { + if (std.debug.runtime_safety) { + self.air_bookkeeping += 1; + } +} + +fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { + var tomb_bits = self.liveness.getTombBits(inst); + for (operands) |op| { + const dies = @truncate(u1, tomb_bits) != 0; + tomb_bits >>= 1; + if (!dies) continue; + const op_int = @enumToInt(op); + if (op_int < Air.Inst.Ref.typed_value_map.len) continue; + const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + self.processDeath(op_index); + } + const is_used = @truncate(u1, tomb_bits) == 0; + if (is_used) { + log.debug("%{d} => {}", .{ inst, result }); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacityNoClobber(inst, result); + + switch (result) { + .register => |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } + }, + else => {}, + } + } + self.finishAirBookkeeping(); +} + +fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { + assert(off_type == Register or off_type == i13); + + const is_imm = (off_type == i13); + const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; + + switch (abi_size) { + 1 => { + _ = try self.addInst(.{ + .tag = .ldub, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 2 => { + _ = try self.addInst(.{ + .tag = .lduh, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 4 => { + _ = try self.addInst(.{ + .tag = .lduw, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 8 => { + _ = try self.addInst(.{ + .tag = .ldx, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), + else => unreachable, + } +} + +fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { + switch (mcv) { + .dead => unreachable, + .unreach, .none => return, // Nothing to do. + .undef => { + if (!self.wantSafety()) + return; // The already existing value will do just fine. + // Write the debug undefined value. + return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); + }, + .ptr_stack_offset => |off| { + const simm13 = math.cast(u12, off) catch + return self.fail("TODO larger stack offsets", .{}); + + _ = try self.addInst(.{ + .tag = .add, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = .sp, + .rs2_or_imm = .{ .imm = simm13 }, + }, + }, + }); + }, + .immediate => |x| { + if (x <= math.maxInt(u12)) { + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = .g0, + .rs2_or_imm = .{ .imm = @truncate(u12, x) }, + }, + }, + }); + } else if (x <= math.maxInt(u32)) { + _ = try self.addInst(.{ + .tag = .sethi, + .data = .{ + .sethi = .{ + .rd = reg, + .imm = @truncate(u22, x >> 10), + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = @truncate(u10, x) }, + }, + }, + }); + } else if (x <= math.maxInt(u44)) { + try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) }); + + _ = try self.addInst(.{ + .tag = .sllx, + .data = .{ + .shift = .{ + .is_imm = true, + .width = .shift64, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = 12 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = @truncate(u12, x) }, + }, + }, + }); + } else { + // Need to allocate a temporary register to load 64-bit immediates. + const tmp_reg = try self.register_manager.allocReg(null); + + try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) }); + try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) }); + + _ = try self.addInst(.{ + .tag = .sllx, + .data = .{ + .shift = .{ + .is_imm = true, + .width = .shift64, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = 32 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .rs2 = tmp_reg }, + }, + }, + }); + } + }, + .register => |src_reg| { + // If the registers are the same, nothing to do. + if (src_reg.id() == reg.id()) + return; + + // or %g0, src, dst (aka mov src, dst) + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = reg, + .rs1 = .g0, + .rs2_or_imm = .{ .rs2 = src_reg }, + }, + }, + }); + }, + .memory => |addr| { + // The value is in memory at a hard-coded address. + // If the type is a pointer, it means the pointer address is at this memory location. + try self.genSetReg(ty, reg, .{ .immediate = addr }); + try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); + }, + .stack_offset => |off| { + const simm13 = math.cast(u12, off) catch + return self.fail("TODO larger stack offsets", .{}); + try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); + }, + } +} + +fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { + const abi_size = ty.abiSize(self.target.*); + switch (mcv) { + .dead => unreachable, + .unreach, .none => return, // Nothing to do. + .undef => { + if (!self.wantSafety()) + return; // The already existing value will do just fine. + // TODO Upgrade this to a memset call when we have that available. + switch (ty.abiSize(self.target.*)) { + 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), + 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), + 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), + 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + else => return self.fail("TODO implement memset", .{}), + } + }, + .immediate, + .ptr_stack_offset, + => { + const reg = try self.copyToTmpRegister(ty, mcv); + return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); + }, + .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}), + .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), + } +} + +fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { + if (typed_value.val.isUndef()) + return MCValue{ .undef = {} }; + + if (typed_value.val.castTag(.decl_ref)) |payload| { + return self.lowerDeclRef(typed_value, payload.data); + } + if (typed_value.val.castTag(.decl_ref_mut)) |payload| { + return self.lowerDeclRef(typed_value, payload.data.decl); + } + const target = self.target.*; + + switch (typed_value.ty.zigTypeTag()) { + .Int => { + const info = typed_value.ty.intInfo(self.target.*); + if (info.bits <= 64) { + const unsigned = switch (info.signedness) { + .signed => blk: { + const signed = typed_value.val.toSignedInt(); + break :blk @bitCast(u64, signed); + }, + .unsigned => typed_value.val.toUnsignedInt(target), + }; + + return MCValue{ .immediate = unsigned }; + } else { + return self.fail("TODO implement int genTypedValue of > 64 bits", .{}); + } + }, + .ComptimeInt => unreachable, // semantic analysis prevents this + .ComptimeFloat => unreachable, // semantic analysis prevents this + else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), + } +} + +fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { + // Treat each stack item as a "layer" on top of the previous one. + var i: usize = self.branch_stack.items.len; + while (true) { + i -= 1; + if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { + assert(mcv != .dead); + return mcv; + } + } +} + +fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { + try self.ensureProcessDeathCapacity(operand_count + 1); + return BigTomb{ + .function = self, + .inst = inst, + .tomb_bits = self.liveness.getTombBits(inst), + .big_tomb_bits = self.liveness.special.get(inst) orelse 0, + .bit_index = 0, + }; +} + +fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + + // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? + if (tv.ty.zigTypeTag() == .Pointer) blk: { + if (tv.ty.castPtrToFn()) |_| break :blk; + if (!tv.ty.elemType2().hasRuntimeBits()) { + return MCValue.none; + } + } + + decl.alive = true; + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; + return MCValue{ .memory = got_addr }; + } else { + return self.fail("TODO codegen non-ELF const Decl pointer", .{}); + } +} + +fn parseRegName(name: []const u8) ?Register { + if (@hasDecl(Register, "parseRegName")) { + return Register.parseRegName(name); + } + return std.meta.stringToEnum(Register, name); +} + +fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { + const tag = self.mir_instructions.items(.tag)[inst]; + switch (tag) { + .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), + else => unreachable, + } +} + +/// Asserts there is already capacity to insert into top branch inst_table. +fn processDeath(self: *Self, inst: Air.Inst.Index) void { + const air_tags = self.air.instructions.items(.tag); + if (air_tags[inst] == .constant) return; // Constants are immortal. + // When editing this function, note that the logic must synchronize with `reuseOperand`. + const prev_value = self.getResolvedInstValue(inst); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacity(inst, .dead); + switch (prev_value) { + .register => |reg| { + self.register_manager.freeReg(reg); + }, + else => {}, // TODO process stack allocation death + } +} + +/// Caller must call `CallMCValues.deinit`. +fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues { + const cc = fn_ty.fnCallingConvention(); + const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); + defer self.gpa.free(param_types); + fn_ty.fnParamTypes(param_types); + var result: CallMCValues = .{ + .args = try self.gpa.alloc(MCValue, param_types.len), + // These undefined values must be populated before returning from this function. + .return_value = undefined, + .stack_byte_count = undefined, + .stack_align = undefined, + }; + errdefer self.gpa.free(result.args); + + const ret_ty = fn_ty.fnReturnType(); + + switch (cc) { + .Naked => { + assert(result.args.len == 0); + result.return_value = .{ .unreach = {} }; + result.stack_byte_count = 0; + result.stack_align = 1; + return result; + }, + .Unspecified, .C => { + // SPARC Compliance Definition 2.4.1, Chapter 3 + // Low-Level System Information (64-bit psABI) - Function Calling Sequence + + var next_register: usize = 0; + var next_stack_offset: u32 = 0; + + // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee. + const argument_registers = switch (role) { + .caller => abi.c_abi_int_param_regs_caller_view, + .callee => abi.c_abi_int_param_regs_callee_view, + }; + + for (param_types) |ty, i| { + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (param_size <= 8) { + if (next_register < argument_registers.len) { + result.args[i] = .{ .register = argument_registers[next_register] }; + next_register += 1; + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } else if (param_size <= 16) { + if (next_register < argument_registers.len - 1) { + return self.fail("TODO MCValues with 2 registers", .{}); + } else if (next_register < argument_registers.len) { + return self.fail("TODO MCValues split register + stack", .{}); + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } + + result.stack_byte_count = next_stack_offset; + result.stack_align = 16; + + if (ret_ty.zigTypeTag() == .NoReturn) { + result.return_value = .{ .unreach = {} }; + } else if (!ret_ty.hasRuntimeBits()) { + result.return_value = .{ .none = {} }; + } else { + const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); + // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller. + if (ret_ty_size <= 8) { + result.return_value = switch (role) { + .caller => .{ .register = abi.c_abi_int_return_regs_caller_view[0] }, + .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] }, + }; + } else { + return self.fail("TODO support more return values for sparcv9", .{}); + } + } + }, + else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}), + } + + return result; +} + +fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { + // First section of indexes correspond to a set number of constant values. + const ref_int = @enumToInt(inst); + if (ref_int < Air.Inst.Ref.typed_value_map.len) { + const tv = Air.Inst.Ref.typed_value_map[ref_int]; + if (!tv.ty.hasRuntimeBits()) { + return MCValue{ .none = {} }; + } + return self.genTypedValue(tv); + } + + // If the type has no codegen bits, no need to store it. + const inst_ty = self.air.typeOf(inst); + if (!inst_ty.hasRuntimeBits()) + return MCValue{ .none = {} }; + + const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); + switch (self.air.instructions.items(.tag)[inst_index]) { + .constant => { + // Constants have static lifetimes, so they are always memoized in the outer most table. + const branch = &self.branch_stack.items[0]; + const gop = try branch.inst_table.getOrPut(self.gpa, inst_index); + if (!gop.found_existing) { + const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; + gop.value_ptr.* = try self.genTypedValue(.{ + .ty = inst_ty, + .val = self.air.values[ty_pl.payload], + }); + } + return gop.value_ptr.*; + }, + .const_ty => unreachable, + else => return self.getResolvedInstValue(inst_index), + } +} + +fn ret(self: *Self, mcv: MCValue) !void { + const ret_ty = self.fn_type.fnReturnType(); + try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); + + // Just add space for an instruction, patch this later + const index = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + try self.exitlude_jump_relocs.append(self.gpa, index); +} + +fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { + if (!self.liveness.operandDies(inst, op_index)) + return false; + + switch (mcv) { + .register => |reg| { + // If it's in the registers table, need to associate the register with the + // new instruction. + if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { + if (!self.register_manager.isRegFree(reg)) { + self.register_manager.registers[index] = inst; + } + } + log.debug("%{d} => {} (reused)", .{ inst, reg }); + }, + .stack_offset => |off| { + log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); + }, + else => return false, + } + + // Prevent the operand deaths processing code from deallocating it. + self.liveness.clearOperandDeath(inst, op_index); + + // That makes us responsible for doing the rest of the stuff that processDeath would have done. + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead); + + return true; +} + +/// Sets the value without any modifications to register allocation metadata or stack allocation metadata. +fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { + switch (loc) { + .none => return, + .register => |reg| return self.genSetReg(ty, reg, val), + .stack_offset => |off| return self.genSetStack(ty, off, val), + .memory => { + return self.fail("TODO implement setRegOrMem for memory", .{}); + }, + else => unreachable, + } +} + +pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { + const stack_mcv = try self.allocRegOrMem(inst, false); + log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); + const reg_mcv = self.getResolvedInstValue(inst); + assert(reg == reg_mcv.register); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + try branch.inst_table.put(self.gpa, inst, stack_mcv); + try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); +} + +/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. +fn wantSafety(self: *Self) bool { + return switch (self.bin_file.options.optimize_mode) { + .Debug => true, + .ReleaseSafe => true, + .ReleaseFast => false, + .ReleaseSmall => false, + }; } diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index ba644ede7e..b811a3567f 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -1,6 +1,298 @@ //! This file contains the functionality for lowering SPARCv9 MIR into //! machine code +const std = @import("std"); +const Endian = std.builtin.Endian; +const assert = std.debug.assert; +const link = @import("../../link.zig"); +const Module = @import("../../Module.zig"); +const ErrorMsg = Module.ErrorMsg; +const Liveness = @import("../../Liveness.zig"); +const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const DW = std.dwarf; +const leb128 = std.leb; + const Emit = @This(); const Mir = @import("Mir.zig"); const bits = @import("bits.zig"); +const Instruction = bits.Instruction; +const Register = bits.Register; + +mir: Mir, +bin_file: *link.File, +debug_output: DebugInfoOutput, +target: *const std.Target, +err_msg: ?*ErrorMsg = null, +src_loc: Module.SrcLoc, +code: *std.ArrayList(u8), + +prev_di_line: u32, +prev_di_column: u32, +/// Relative to the beginning of `code`. +prev_di_pc: usize, + +const InnerError = error{ + OutOfMemory, + EmitFail, +}; + +pub fn emitMir( + emit: *Emit, +) InnerError!void { + const mir_tags = emit.mir.instructions.items(.tag); + + // Emit machine code + for (mir_tags) |tag, index| { + const inst = @intCast(u32, index); + switch (tag) { + .dbg_arg => try emit.mirDbgArg(inst), + .dbg_line => try emit.mirDbgLine(inst), + .dbg_prologue_end => try emit.mirDebugPrologueEnd(), + .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), + + .add => try emit.mirArithmetic3Op(inst), + + .bpcc => @panic("TODO implement sparcv9 bpcc"), + + .call => @panic("TODO implement sparcv9 call"), + + .jmpl => @panic("TODO implement sparcv9 jmpl"), + .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), + + .ldub => try emit.mirArithmetic3Op(inst), + .lduh => try emit.mirArithmetic3Op(inst), + .lduw => try emit.mirArithmetic3Op(inst), + .ldx => try emit.mirArithmetic3Op(inst), + + .@"or" => try emit.mirArithmetic3Op(inst), + + .nop => try emit.mirNop(), + + .@"return" => try emit.mirArithmetic2Op(inst), + + .save => try emit.mirArithmetic3Op(inst), + .restore => try emit.mirArithmetic3Op(inst), + + .sethi => try emit.mirSethi(inst), + + .sllx => @panic("TODO implement sparcv9 sllx"), + + .sub => try emit.mirArithmetic3Op(inst), + + .tcc => try emit.mirTrap(inst), + } + } +} + +pub fn deinit(emit: *Emit) void { + emit.* = undefined; +} + +fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info; + _ = dbg_arg_info; + + switch (tag) { + .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index), + else => unreachable, + } +} + +fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; + + switch (tag) { + .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), + else => unreachable, + } +} + +fn mirDebugPrologueEnd(self: *Emit) !void { + switch (self.debug_output) { + .dwarf => |dbg_out| { + try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } +} + +fn mirDebugEpilogueBegin(self: *Emit) !void { + switch (self.debug_output) { + .dwarf => |dbg_out| { + try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } +} + +fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op; + + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)), + else => unreachable, + } + } +} + +fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op; + + const rd = data.rd; + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), + .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)), + .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), + .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), + .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)), + .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), + .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), + .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), + .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), + .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)), + .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), + .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), + .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)), + .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), + .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), + .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), + .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)), + else => unreachable, + } + } +} + +fn mirNop(emit: *Emit) !void { + try emit.writeInstruction(Instruction.nop()); +} + +fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].sethi; + + const imm = data.imm; + const rd = data.rd; + + assert(tag == .sethi); + try emit.writeInstruction(Instruction.sethi(imm, rd)); +} + +fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].trap; + + const cond = data.cond; + const ccr = data.ccr; + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)), + else => unreachable, + } + } +} + +// Common helper functions + +fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { + const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); + const delta_pc: usize = self.code.items.len - self.prev_di_pc; + switch (self.debug_output) { + .dwarf => |dbg_out| { + // TODO Look into using the DWARF special opcodes to compress this data. + // It lets you emit single-byte opcodes that add different numbers to + // both the PC and the line number at the same time. + try dbg_out.dbg_line.ensureUnusedCapacity(11); + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + if (delta_line != 0) { + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + } + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + self.prev_di_pc = self.code.items.len; + self.prev_di_line = line; + self.prev_di_column = column; + self.prev_di_pc = self.code.items.len; + }, + .plan9 => |dbg_out| { + if (delta_pc <= 0) return; // only do this when the pc changes + // we have already checked the target in the linker to make sure it is compatable + const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; + + // increasing the line number + try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); + // increasing the pc + const d_pc_p9 = @intCast(i64, delta_pc) - quant; + if (d_pc_p9 > 0) { + // minus one because if its the last one, we want to leave space to change the line which is one quanta + try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); + if (dbg_out.pcop_change_index.*) |pci| + dbg_out.dbg_line.items[pci] += 1; + dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); + } else if (d_pc_p9 == 0) { + // we don't need to do anything, because adding the quant does it for us + } else unreachable; + if (dbg_out.start_line.* == null) + dbg_out.start_line.* = self.prev_di_line; + dbg_out.end_line.* = line; + // only do this if the pc changed + self.prev_di_line = line; + self.prev_di_column = column; + self.prev_di_pc = self.code.items.len; + }, + .none => {}, + } +} + +fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { + @setCold(true); + assert(emit.err_msg == null); + emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + return error.EmitFail; +} + +fn writeInstruction(emit: *Emit, instruction: Instruction) !void { + // SPARCv9 instructions are always arranged in BE regardless of the + // endianness mode the CPU is running in (Section 3.1 of the ISA specification). + // This is to ease porting in case someone wants to do a LE SPARCv9 backend. + const endian = Endian.Big; + + std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian); +} diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index f0d3b1dfbd..c79ebdcac1 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -6,6 +6,264 @@ //! The main purpose of MIR is to postpone the assignment of offsets until Isel, //! so that, for example, the smaller encodings of jump instructions can be used. +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; + const Mir = @This(); const bits = @import("bits.zig"); +const Air = @import("../../Air.zig"); + +const Instruction = bits.Instruction; const Register = bits.Register; + +instructions: std.MultiArrayList(Inst).Slice, + +/// The meaning of this data is determined by `Inst.Tag` value. +extra: []const u32, + +pub const Inst = struct { + tag: Tag, + /// The meaning of this depends on `tag`. + data: Data, + + pub const Tag = enum(u16) { + /// Pseudo-instruction: Argument + dbg_arg, + /// Pseudo-instruction: End of prologue + dbg_prologue_end, + /// Pseudo-instruction: Beginning of epilogue + dbg_epilogue_begin, + /// Pseudo-instruction: Update debug line + dbg_line, + + // All the real instructions are ordered by their section number + // in The SPARC Architecture Manual, Version 9. + + /// A.2 Add + /// Those uses the arithmetic_3op field. + // TODO add other operations. + add, + + /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) + /// It uses the branch_predict field. + bpcc, + + /// A.8 Call and Link + /// It uses the branch_link field. + call, + + /// A.24 Jump and Link + /// jmpl (far direct jump) uses the branch_link field, + /// while jmpl_i (indirect jump) uses the branch_link_indirect field. + /// Those two MIR instructions will be lowered into SPARCv9 jmpl instruction. + jmpl, + jmpl_i, + + /// A.27 Load Integer + /// Those uses the arithmetic_3op field. + /// Note that the ldd variant of this instruction is deprecated, so do not emit + /// it unless specifically requested (e.g. by inline assembly). + // TODO add other operations. + ldub, + lduh, + lduw, + ldx, + + /// A.31 Logical Operations + /// Those uses the arithmetic_3op field. + // TODO add other operations. + @"or", + + /// A.40 No Operation + /// It uses the nop field. + nop, + + /// A.45 RETURN + /// It uses the arithmetic_2op field. + @"return", + + /// A.46 SAVE and RESTORE + /// Those uses the arithmetic_3op field. + save, + restore, + + /// A.48 SETHI + /// It uses the sethi field. + sethi, + + /// A.49 Shift + /// Those uses the shift field. + // TODO add other operations. + sllx, + + /// A.56 Subtract + /// Those uses the arithmetic_3op field. + // TODO add other operations. + sub, + + /// A.61 Trap on Integer Condition Codes (Tcc) + /// It uses the trap field. + tcc, + }; + + /// The position of an MIR instruction within the `Mir` instructions array. + pub const Index = u32; + + /// All instructions have a 8-byte payload, which is contained within + /// this union. `Tag` determines which union field is active, as well as + /// how to interpret the data within. + // TODO this is a quick-n-dirty solution that needs to be cleaned up. + pub const Data = union { + /// Debug info: argument + /// + /// Used by e.g. dbg_arg + dbg_arg_info: struct { + air_inst: Air.Inst.Index, + arg_index: usize, + }, + + /// Debug info: line and column + /// + /// Used by e.g. dbg_line + dbg_line_column: struct { + line: u32, + column: u32, + }, + + /// Two operand arithmetic. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. return + arithmetic_2op: struct { + is_imm: bool, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: i13, + }, + }, + + /// Three operand arithmetic. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. add, sub + arithmetic_3op: struct { + is_imm: bool, + rd: Register, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: i13, + }, + }, + + /// Branch and link (always unconditional). + /// Used by e.g. call + branch_link: struct { + inst: Index, + link: Register = .o7, + }, + + /// Indirect branch and link (always unconditional). + /// Used by e.g. jmpl_i + branch_link_indirect: struct { + reg: Register, + link: Register = .o7, + }, + + /// Branch with prediction. + /// Used by e.g. bpcc + branch_predict: struct { + annul: bool = false, + pt: bool = true, + ccr: Instruction.CCR, + cond: Instruction.Condition, + inst: Index, + }, + + /// No additional data + /// + /// Used by e.g. flushw + nop: void, + + /// SETHI operands. + /// + /// Used by sethi + sethi: struct { + rd: Register, + imm: u22, + }, + + /// Shift operands. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. add, sub + shift: struct { + is_imm: bool, + width: Instruction.ShiftWidth, + rd: Register, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: u6, + }, + }, + + /// Trap. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. tcc + trap: struct { + is_imm: bool = true, + cond: Instruction.Condition, + ccr: Instruction.CCR = .icc, + rs1: Register = .g0, + rs2_or_imm: union { + rs2: Register, + imm: u7, + }, + }, + }; + + // Make sure we don't accidentally make instructions bigger than expected. + // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. + comptime { + if (builtin.mode != .Debug) { + // TODO clean up the definition of Data before enabling this. + // I'll do that after the PoC backend can produce usable binaries. + + // assert(@sizeOf(Data) == 8); + } + } +}; + +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + mir.instructions.deinit(gpa); + gpa.free(mir.extra); + mir.* = undefined; +} + +/// Returns the requested data, as well as the new index which is at the start of the +/// trailers for the object. +pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { + const fields = std.meta.fields(T); + var i: usize = index; + var result: T = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.field_type) { + u32 => mir.extra[i], + i32 => @bitCast(i32, mir.extra[i]), + else => @compileError("bad field type"), + }; + i += 1; + } + return .{ + .data = result, + .end = i, + }; +} diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index 5c9ea979fc..4cb10a99ea 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,12 +1,25 @@ const bits = @import("bits.zig"); const Register = bits.Register; -// Register windowing mechanism will take care of preserving registers -// so no need to do it manually -pub const callee_preserved_regs = [_]Register{}; +// There are no callee-preserved registers since the windowing +// mechanism already takes care of them. +// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling +// something, though, as those are shared with the callee and might be +// thrashed by it. +pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 }; + +// Try to allocate i, l, o, then g sets of registers, in order of priority. +pub const allocatable_regs = [_]Register{ + // zig fmt: off + .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5", + .l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7, + .o0, .o1, .o2, .o3, .o4, .o5, + .g1, .g4, .g5, + // zig fmt: on +}; pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; -pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; -pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; +pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 }; +pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" }; diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 07cbf7fc91..bc8b8822b7 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -164,27 +164,33 @@ pub const Instruction = union(enum) { // name them with letters since there's no official naming scheme. // TODO: need to rename the minor formats to a more descriptive name. + // I am using regular structs instead of packed ones to avoid + // endianness-dependent behavior when constructing the actual + // assembly instructions. + // See also: https://github.com/ziglang/zig/issues/10113 + // TODO: change it back to packed structs once the issue is resolved. + // Format 1 (op = 1): CALL - format_1: packed struct { + format_1: struct { op: u2 = 0b01, disp30: u30, }, // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc) - format_2a: packed struct { + format_2a: struct { op: u2 = 0b00, rd: u5, op2: u3, imm22: u22, }, - format_2b: packed struct { + format_2b: struct { op: u2 = 0b00, a: u1, cond: u4, op2: u3, disp22: u22, }, - format_2c: packed struct { + format_2c: struct { op: u2 = 0b00, a: u1, cond: u4, @@ -194,7 +200,7 @@ pub const Instruction = union(enum) { p: u1, disp19: u19, }, - format_2d: packed struct { + format_2d: struct { op: u2 = 0b00, a: u1, fixed: u1 = 0b0, @@ -207,7 +213,7 @@ pub const Instruction = union(enum) { }, // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store - format_3a: packed struct { + format_3a: struct { op: u2, rd: u5, op3: u6, @@ -224,7 +230,7 @@ pub const Instruction = union(enum) { i: u1 = 0b1, simm13: u13, }, - format_3c: packed struct { + format_3c: struct { op: u2, reserved1: u5 = 0b00000, op3: u6, @@ -241,7 +247,7 @@ pub const Instruction = union(enum) { i: u1 = 0b1, simm13: u13, }, - format_3e: packed struct { + format_3e: struct { op: u2, rd: u5, op3: u6, @@ -260,7 +266,7 @@ pub const Instruction = union(enum) { rcond: u3, simm10: u10, }, - format_3g: packed struct { + format_3g: struct { op: u2, rd: u5, op3: u6, @@ -269,7 +275,7 @@ pub const Instruction = union(enum) { reserved: u8 = 0b00000000, rs2: u5, }, - format_3h: packed struct { + format_3h: struct { op: u2 = 0b10, fixed1: u5 = 0b00000, op3: u6 = 0b101000, @@ -279,7 +285,7 @@ pub const Instruction = union(enum) { cmask: u3, mmask: u4, }, - format_3i: packed struct { + format_3i: struct { op: u2, rd: u5, op3: u6, @@ -288,13 +294,13 @@ pub const Instruction = union(enum) { imm_asi: u8, rs2: u5, }, - format_3j: packed struct { + format_3j: struct { op: u2, impl_dep1: u5, op3: u6, impl_dep2: u19, }, - format_3k: packed struct { + format_3k: struct { op: u2, rd: u5, op3: u6, @@ -304,7 +310,7 @@ pub const Instruction = union(enum) { reserved: u7 = 0b0000000, rs2: u5, }, - format_3l: packed struct { + format_3l: struct { op: u2, rd: u5, op3: u6, @@ -314,7 +320,7 @@ pub const Instruction = union(enum) { reserved: u7 = 0b0000000, shcnt32: u5, }, - format_3m: packed struct { + format_3m: struct { op: u2, rd: u5, op3: u6, @@ -324,7 +330,7 @@ pub const Instruction = union(enum) { reserved: u6 = 0b000000, shcnt64: u6, }, - format_3n: packed struct { + format_3n: struct { op: u2, rd: u5, op3: u6, @@ -332,7 +338,7 @@ pub const Instruction = union(enum) { opf: u9, rs2: u5, }, - format_3o: packed struct { + format_3o: struct { op: u2, fixed: u3 = 0b000, cc1: u1, @@ -342,7 +348,7 @@ pub const Instruction = union(enum) { opf: u9, rs2: u5, }, - format_3p: packed struct { + format_3p: struct { op: u2, rd: u5, op3: u6, @@ -350,20 +356,20 @@ pub const Instruction = union(enum) { opf: u9, rs2: u5, }, - format_3q: packed struct { + format_3q: struct { op: u2, rd: u5, op3: u6, rs1: u5, reserved: u14 = 0b00000000000000, }, - format_3r: packed struct { + format_3r: struct { op: u2, fcn: u5, op3: u6, reserved: u19 = 0b0000000000000000000, }, - format_3s: packed struct { + format_3s: struct { op: u2, rd: u5, op3: u6, @@ -371,7 +377,7 @@ pub const Instruction = union(enum) { }, //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc - format_4a: packed struct { + format_4a: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -392,7 +398,7 @@ pub const Instruction = union(enum) { cc0: u1, simm11: u11, }, - format_4c: packed struct { + format_4c: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -415,7 +421,7 @@ pub const Instruction = union(enum) { cc0: u1, simm11: u11, }, - format_4e: packed struct { + format_4e: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -426,7 +432,7 @@ pub const Instruction = union(enum) { reserved: u4 = 0b0000, sw_trap: u7, }, - format_4f: packed struct { + format_4f: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -436,7 +442,7 @@ pub const Instruction = union(enum) { opf_low: u5, rs2: u5, }, - format_4g: packed struct { + format_4g: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -512,40 +518,43 @@ pub const Instruction = union(enum) { pub fn toU32(self: Instruction) u32 { // TODO: Remove this once packed structs work. return switch (self) { - .format_1 => |v| @bitCast(u32, v), - .format_2a => |v| @bitCast(u32, v), - .format_2b => |v| @bitCast(u32, v), - .format_2c => |v| @bitCast(u32, v), - .format_2d => |v| @bitCast(u32, v), - .format_3a => |v| @bitCast(u32, v), + .format_1 => |v| (@as(u32, v.op) << 30) | @as(u32, v.disp30), + .format_2a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.imm22), + .format_2b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.disp22), + .format_2c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.cc1) << 21) | (@as(u32, v.cc0) << 20) | (@as(u32, v.p) << 19) | @as(u32, v.disp19), + .format_2d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.fixed) << 28) | (@as(u32, v.rcond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.d16hi) << 20) | (@as(u32, v.p) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.d16lo), + .format_3a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13), - .format_3c => |v| @bitCast(u32, v), + .format_3c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved2) << 5) | @as(u32, v.rs2), .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13), - .format_3e => |v| @bitCast(u32, v), + .format_3e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10), - .format_3g => |v| @bitCast(u32, v), - .format_3h => |v| @bitCast(u32, v), - .format_3i => |v| @bitCast(u32, v), - .format_3j => |v| @bitCast(u32, v), - .format_3k => |v| @bitCast(u32, v), - .format_3l => |v| @bitCast(u32, v), - .format_3m => |v| @bitCast(u32, v), - .format_3n => |v| @bitCast(u32, v), - .format_3o => |v| @bitCast(u32, v), - .format_3p => |v| @bitCast(u32, v), - .format_3q => |v| @bitCast(u32, v), - .format_3r => |v| @bitCast(u32, v), - .format_3s => |v| @bitCast(u32, v), - .format_4a => |v| @bitCast(u32, v), + .format_3g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), + .format_3h => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed2) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 7) | (@as(u32, v.cmask) << 4) | @as(u32, v.mmask), + .format_3i => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.imm_asi) << 5) | @as(u32, v.rs2), + .format_3j => |v| (@as(u32, v.op) << 30) | (@as(u32, v.impl_dep1) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.impl_dep2), + .format_3k => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), + .format_3l => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.shcnt32), + .format_3m => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 6) | @as(u32, v.shcnt64), + .format_3n => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.reserved) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2), + .format_3o => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed) << 27) | (@as(u32, v.cc1) << 26) | (@as(u32, v.cc0) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2), + .format_3p => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2), + .format_3q => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.reserved), + .format_3r => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fcn) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved), + .format_3s => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved), + .format_4a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11), - .format_4c => |v| @bitCast(u32, v), + .format_4c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11), - .format_4e => |v| @bitCast(u32, v), - .format_4f => |v| @bitCast(u32, v), - .format_4g => |v| @bitCast(u32, v), + .format_4e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 7) | @as(u32, v.sw_trap), + .format_4f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.fixed) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2), + .format_4g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.opf_cc) << 11) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2), }; } + // SPARCv9 Instruction formats. + // See section 6.2 of the SPARCv9 ISA manual. + fn format1(disp: i32) Instruction { const udisp = @bitCast(u32, disp); @@ -561,7 +570,7 @@ pub const Instruction = union(enum) { }; } - fn format2a(op2: u3, rd: Register, imm: u22) Instruction { + fn format2a(op2: u3, imm: u22, rd: Register) Instruction { return Instruction{ .format_2a = .{ .rd = rd.enc(), @@ -956,6 +965,106 @@ pub const Instruction = union(enum) { }, }; } + + // SPARCv9 Instruction definition. + // See appendix A of the SPARCv9 ISA manual. + + pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0000, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0001, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0010, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0000, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_1011, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_1011, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn nop() Instruction { + return sethi(0, .g0); + } + + pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction { + return switch (s2) { + Register => format3c(0b10, 0b11_1001, rs1, rs2), + i13 => format3d(0b10, 0b11_1001, rs1, rs2), + else => unreachable, + }; + } + + pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b11_1100, rs1, rs2, rd), + i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b11_1101, rs1, rs2, rd), + i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn sethi(imm: u22, rd: Register) Instruction { + return format2a(0b100, imm, rd); + } + + pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction { + // Tcc instructions abuse the rd field to store the conditionals. + return switch (s2) { + Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)), + u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2), + else => unreachable, + }; + } }; test "Serialize formats" { @@ -973,7 +1082,7 @@ test "Serialize formats" { .expected = 0b01_000000000000000000000000000001, }, .{ - .inst = Instruction.format2a(4, .g0, 0), + .inst = Instruction.format2a(4, 0, .g0), .expected = 0b00_00000_100_0000000000000000000000, }, .{ @@ -1096,6 +1205,10 @@ test "Serialize formats" { for (testcases) |case| { const actual = case.inst.toU32(); - try testing.expectEqual(case.expected, actual); + testing.expectEqual(case.expected, actual) catch |err| { + std.debug.print("error: {x}\n", .{err}); + std.debug.print("case: {x}\n", .{case}); + return err; + }; } } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index f52506c393..b701299e73 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2425,7 +2425,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { var highest_maybe: ?i32 = null; while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; const values = try self.gpa.alloc(CaseValue, items.len); @@ -3328,7 +3328,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); switch (result_ty.zigTypeTag()) { .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}), @@ -3889,27 +3889,26 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const result = try self.allocLocal(ty); - - try self.startBlock(.block, wasm.block_empty); - try self.startBlock(.block, wasm.block_empty); - - // check if LHS is greater/lesser than RHS - const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); - try self.addLabel(.local_get, cmp_result.local); - try self.addLabel(.br_if, 0); // break to outer loop if LHS is greater/lesser than RHS - - // set RHS as max/min + // operands to select from + try self.emitWValue(lhs); try self.emitWValue(rhs); - try self.addLabel(.local_set, result.local); - try self.addLabel(.br, 1); // break out of all blocks - try self.endBlock(); - // set LHS as max/min + // operands to compare try self.emitWValue(lhs); - try self.addLabel(.local_set, result.local); - try self.endBlock(); + try self.emitWValue(rhs); + const opcode = buildOpcode(.{ + .op = if (op == .max) .gt else .lt, + .signedness = if (ty.isSignedInt()) .signed else .unsigned, + .valtype1 = typeToValtype(ty, self.target), + }); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + + // based on the result from comparison, return operand 0 or 1. + try self.addTag(.select); + // store result in local + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); return result; } diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 6fc2dfa3b3..bcbff8d195 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -96,6 +96,8 @@ pub fn emitMir(emit: *Emit) InnerError!void { .@"return" => try emit.emitTag(tag), .@"unreachable" => try emit.emitTag(tag), + .select => try emit.emitTag(tag), + // arithmetic .i32_eqz => try emit.emitTag(tag), .i32_eq => try emit.emitTag(tag), diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 27f683cf1e..87e64ce9e0 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -10,7 +10,7 @@ const Mir = @This(); const std = @import("std"); -/// A struct of array that represents each individual wasm +/// A struct of array that represents each individual wasm instructions: std.MultiArrayList(Inst).Slice, /// A slice of indexes where the meaning of the data is determined by the /// `Inst.Tag` value. @@ -77,6 +77,10 @@ pub const Inst = struct { /// /// Uses `label` call_indirect = 0x11, + /// Pops three values from the stack and pushes + /// the first or second value dependent on the third value. + /// Uses `tag` + select = 0x1B, /// Loads a local at given index onto the stack. /// /// Uses `label` @@ -538,7 +542,7 @@ pub const Inst = struct { /// Contains an u32 index into a wasm section entry, such as a local. /// Note: This is not an index to another instruction. /// - /// Used by e.g. `local_get`, `local_set`, etc. + /// Used by e.g. `local_get`, `local_set`, etc. label: u32, /// A 32-bit immediate value. /// diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0a11c1480f..10271df122 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -48,6 +48,7 @@ gpa: Allocator, air: Air, liveness: Liveness, bin_file: *link.File, +debug_output: DebugInfoOutput, target: *const std.Target, mod_fn: *const Module.Fn, err_msg: ?*ErrorMsg, @@ -271,26 +272,12 @@ const BlockData = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -337,6 +324,7 @@ pub fn generate( .liveness = liveness, .target = &bin_file.options.target, .bin_file = bin_file, + .debug_output = debug_output, .mod_fn = module_fn, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` @@ -382,7 +370,6 @@ pub fn generate( }; var mir = Mir{ - .function = &function, .instructions = function.mir_instructions.toOwnedSlice(), .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), }; @@ -391,7 +378,6 @@ pub fn generate( var emit = Emit{ .mir = mir, .bin_file = bin_file, - .function = &function, .debug_output = debug_output, .target = &bin_file.options.target, .src_loc = src_loc, @@ -3425,17 +3411,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const arg_index = self.arg_index; self.arg_index += 1; + const ty = self.air.typeOfIndex(inst); const mcv = self.args[arg_index]; - const payload = try self.addExtra(Mir.ArgDbgInfo{ - .air_inst = inst, - .arg_index = arg_index, - .max_stack = self.max_end_stack, - }); - _ = try self.addInst(.{ - .tag = .arg_dbg_info, - .ops = undefined, - .data = .{ .payload = payload }, - }); + const name = self.mod_fn.getParamName(arg_index); + const name_with_null = name.ptr[0 .. name.len + 1]; + if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -3443,10 +3423,46 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .register => |reg| { self.register_manager.getRegAssumeFree(reg.to64(), inst); + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } break :blk mcv; }, .stack_offset => |off| { const offset = @intCast(i32, self.max_end_stack) - off + 16; + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // we will backpatch it after we encode the displacement in LEB128 + DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), offset) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + + }, + .plan9 => {}, + .none => {}, + } break :blk MCValue{ .stack_offset = -offset }; }, else => return self.fail("TODO implement arg for {}", .{mcv}), @@ -3485,7 +3501,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { @@ -3668,7 +3684,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ops = (Mir.Ops{ .flags = 0b01, }).encode(), - .data = .{ .imm = @bitCast(i32, @intCast(u32, fn_got_addr)) }, + .data = .{ .imm = @intCast(u32, fn_got_addr) }, }); } else return self.fail("TODO implement calling extern fn on plan9", .{}); } else { @@ -3885,13 +3901,126 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const name = self.air.nullTerminatedString(pl_op.payload); const operand = pl_op.operand; - // TODO emit debug info for this variable - _ = name; + const ty = self.air.typeOf(operand); + const mcv = try self.resolveInst(operand); + + log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv }); + + const name = self.air.nullTerminatedString(pl_op.payload); + + const tag = self.air.instructions.items(.tag)[inst]; + switch (tag) { + .dbg_var_ptr => try self.genVarDbgInfo(tag, ty.childType(), mcv, name), + .dbg_var_val => try self.genVarDbgInfo(tag, ty, mcv, name), + else => unreachable, + } + return self.finishAir(inst, .dead, .{ operand, .none, .none }); } +fn genVarDbgInfo( + self: *Self, + tag: Air.Inst.Tag, + ty: Type, + mcv: MCValue, + name: [:0]const u8, +) !void { + const name_with_null = name.ptr[0 .. name.len + 1]; + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + + switch (mcv) { + .register => |reg| { + try dbg_info.ensureUnusedCapacity(2); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + }, + .ptr_stack_offset, .stack_offset => |off| { + try dbg_info.ensureUnusedCapacity(7); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // we will backpatch it after we encode the displacement in LEB128 + DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + }, + .memory, .got_load, .direct_load => { + const endian = self.target.cpu.arch.endian(); + const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); + const is_ptr = switch (tag) { + .dbg_var_ptr => true, + .dbg_var_val => false, + else => unreachable, + }; + try dbg_info.ensureUnusedCapacity(2 + ptr_width); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1 + ptr_width + @boolToInt(is_ptr), + DW.OP.addr, // literal address + }); + const offset = @intCast(u32, dbg_info.items.len); + const addr = switch (mcv) { + .memory => |addr| addr, + else => 0, + }; + switch (ptr_width) { + 0...4 => { + try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian); + }, + 5...8 => { + try dbg_info.writer().writeInt(u64, addr, endian); + }, + else => unreachable, + } + if (is_ptr) { + // We need deref the address as we point to the value via GOT entry. + try dbg_info.append(DW.OP.deref); + } + switch (mcv) { + .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr), + else => {}, + } + }, + else => { + log.debug("TODO generate debug info for {}", .{mcv}); + }, + } + + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } +} + +/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, +/// after codegen for this symbol is done. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { + switch (self.debug_output) { + .dwarf => |dw| { + assert(ty.hasRuntimeBits()); + const dbg_info = &dw.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => &self.mod_fn.owner_decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + .plan9 => {}, + .none => {}, + } +} + fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { @@ -4091,7 +4220,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // TODO track the new register / stack allocation } - self.branch_stack.pop().deinit(self.gpa); + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } // We already took care of pl_op.operand earlier, so we're going // to pass .none here @@ -4433,7 +4565,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; @@ -4486,7 +4618,10 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { if (switch_br.data.else_body_len > 0) { const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; try self.branch_stack.append(.{}); - defer self.branch_stack.pop().deinit(self.gpa); + defer { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } const else_deaths = liveness.deaths.len - 1; try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); @@ -4576,9 +4711,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -4731,9 +4866,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } @@ -5848,7 +5981,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const abi_size = @intCast(u32, result_ty.abiSize(self.target.*)); const abi_align = result_ty.abiAlignment(self.target.*); const result: MCValue = res: { @@ -5919,7 +6052,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } -fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { +pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { @@ -5989,6 +6122,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV } fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { + log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -6000,7 +6134,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } } - decl.alive = true; + decl.markAlive(); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; @@ -6020,8 +6155,6 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } else { return self.fail("TODO codegen non-ELF const Decl pointer", .{}); } - - _ = tv; } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { @@ -6044,6 +6177,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { + log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); @@ -6081,8 +6215,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; }, - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this .Optional => { if (typed_value.ty.isPtrLikeOptional()) { if (typed_value.val.isNull()) @@ -6143,6 +6275,18 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } } }, + + .ComptimeInt => unreachable, + .ComptimeFloat => unreachable, + .Type => unreachable, + .EnumLiteral => unreachable, + .Void => unreachable, + .NoReturn => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .BoundFn => unreachable, + .Opaque => unreachable, + else => {}, } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 3296339419..6af2c07974 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -30,7 +30,6 @@ const Type = @import("../../type.zig").Type; mir: Mir, bin_file: *link.File, -function: *const CodeGen, debug_output: DebugInfoOutput, target: *const std.Target, err_msg: ?*ErrorMsg = null, @@ -187,7 +186,6 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), - .arg_dbg_info => try emit.mirArgDbgInfo(inst), .push_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.push, inst), .pop_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.pop, inst), @@ -1057,92 +1055,6 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .arg_dbg_info); - const payload = emit.mir.instructions.items(.data)[inst].payload; - const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data; - const mcv = emit.mir.function.args[arg_dbg_info.arg_index]; - try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack, arg_dbg_info.arg_index); -} - -fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void { - const ty = emit.mir.function.air.instructions.items(.data)[inst].ty; - const name = emit.mir.function.mod_fn.getParamName(arg_index); - const name_with_null = name.ptr[0 .. name.len + 1]; - - switch (mcv) { - .register => |reg| { - switch (emit.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // ULEB128 dwarf expression length - reg.dwarfLocOp(), - }); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - }, - .stack_offset => |off| { - switch (emit.debug_output) { - .dwarf => |dw| { - // we add here +16 like we do in airArg in CodeGen since we refer directly to - // rbp as the start of function frame minus 8 bytes for caller's rbp preserved in the - // prologue, and 8 bytes for return address. - // TODO we need to make this more generic if we don't use rbp as the frame pointer - // for example when -fomit-frame-pointer is set. - const disp = @intCast(i32, max_stack) - off + 16; - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(8); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - const fixup = dbg_info.items.len; - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // we will backpatch it after we encode the displacement in LEB128 - DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer - }); - leb128.writeILEB128(dbg_info.writer(), disp) catch unreachable; - dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - - }, - .plan9 => {}, - .none => {}, - } - }, - else => {}, - } -} - -/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, -/// after codegen for this symbol is done. -fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - assert(ty.hasRuntimeBits()); - const dbg_info = &dw.dbg_info; - const index = dbg_info.items.len; - try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const atom = switch (emit.bin_file.tag) { - .elf => &emit.function.mod_fn.owner_decl.link.elf.dbg_info_atom, - .macho => &emit.function.mod_fn.owner_decl.link.macho.dbg_info_atom, - else => unreachable, - }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); - }, - .plan9 => {}, - .none => {}, - } -} - const Tag = enum { adc, add, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 183a76e4b7..50f28d7f19 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -16,7 +16,6 @@ const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); const Register = bits.Register; -function: *const CodeGen, instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, @@ -364,11 +363,8 @@ pub const Inst = struct { /// update debug line dbg_line, - /// arg debug info - arg_dbg_info, - /// push registers from the callee_preserved_regs - /// data is the bitfield of which regs to push + /// data is the bitfield of which regs to push /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; }; /// so to push rcx and r8 one would make data 0b00000000_00000000_00000000_00001001 (the first and fourth bits are set) /// ops is unused @@ -453,18 +449,6 @@ pub const DbgLineColumn = struct { column: u32, }; -pub const ArgDbgInfo = struct { - air_inst: Air.Inst.Index, - arg_index: u32, - max_stack: u32, -}; - -pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { - mir.instructions.deinit(gpa); - gpa.free(mir.extra); - mir.* = undefined; -} - pub const Ops = struct { reg1: Register = .none, reg2: Register = .none, @@ -490,6 +474,12 @@ pub const Ops = struct { } }; +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + mir.instructions.deinit(gpa); + gpa.free(mir.extra); + mir.* = undefined; +} + pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { const fields = std.meta.fields(T); var i: usize = index; diff --git a/src/arch/x86_64/PrintMir.zig b/src/arch/x86_64/PrintMir.zig index 8c07350c2d..e457d859ea 100644 --- a/src/arch/x86_64/PrintMir.zig +++ b/src/arch/x86_64/PrintMir.zig @@ -147,7 +147,7 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap .call_extern => try print.mirCallExtern(inst, w), - .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin, .arg_dbg_info => try w.print("{s}\n", .{@tagName(tag)}), + .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin => try w.print("{s}\n", .{@tagName(tag)}), .push_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.push, inst, w), .pop_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.pop, inst, w), diff --git a/src/codegen.zig b/src/codegen.zig index 2ace45c8cb..68e1f3697f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -203,7 +203,6 @@ pub fn generateSymbol( }, .Array => switch (typed_value.val.tag()) { .bytes => { - // TODO populate .debug_info for the array const payload = typed_value.val.castTag(.bytes).?; const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel()); // The bytes payload already includes the sentinel, if any @@ -212,7 +211,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .aggregate => { - // TODO populate .debug_info for the array const elem_vals = typed_value.val.castTag(.aggregate).?.data; const elem_ty = typed_value.ty.elemType(); const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel()); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4085305941..54f8285291 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -220,6 +220,9 @@ fn formatIdent( } pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { + if (builtin.zig_backend != .stage1) { + @panic("TODO"); + } return .{ .data = ident }; } @@ -1348,7 +1351,7 @@ pub const DeclGen = struct { return w.writeAll(name); }, .ErrorSet => { - comptime std.debug.assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2); + comptime assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2); return w.writeAll("uint16_t"); }, .ErrorUnion => { @@ -2310,7 +2313,6 @@ fn airWrapOp( const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1)); break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }, @@ -2336,7 +2338,6 @@ fn airWrapOp( const val = std.math.pow(u64, 2, pow_bits) - 1; break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }; @@ -2418,7 +2419,6 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }, @@ -2444,7 +2444,6 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { const val = std.math.pow(u65, 2, pow_bits) - 1; break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }; @@ -2702,7 +2701,7 @@ fn airCall( } const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); const callee_ty = f.air.typeOf(pl_op.operand); const fn_ty = switch (callee_ty.zigTypeTag()) { .Fn => callee_ty, @@ -2959,7 +2958,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { var case_i: u32 = 0; while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = f.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + case.data.items_len + case_body.len; @@ -2990,9 +2989,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; @@ -3860,7 +3859,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const vector_ty = f.air.getRefType(ty_pl.ty); const len = vector_ty.vectorLen(); - const elements = @bitCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index db4c08f86f..f55d326a47 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3657,7 +3657,7 @@ pub const FuncGen = struct { fn airCall(self: *FuncGen, inst: Air.Inst.Index, attr: llvm.CallAttr) !?*const llvm.Value { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const callee_ty = self.air.typeOf(pl_op.operand); const zig_fn_ty = switch (callee_ty.zigTypeTag()) { .Fn => callee_ty, @@ -4037,7 +4037,7 @@ pub const FuncGen = struct { while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + case.data.items_len + case_body.len; @@ -4538,9 +4538,9 @@ pub const FuncGen = struct { if (!is_volatile and self.liveness.isUnused(inst)) return null; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; if (outputs.len > 1) { @@ -6660,7 +6660,7 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const llvm_result_ty = try self.dg.llvmType(result_ty); const target = self.dg.module.getTarget(); diff --git a/src/link.zig b/src/link.zig index 7c135a7405..dbef400189 100644 --- a/src/link.zig +++ b/src/link.zig @@ -649,6 +649,11 @@ pub const File = struct { } } + pub const UpdateDeclExportsError = error{ + OutOfMemory, + AnalysisFail, + }; + /// May be called before or after updateDecl, but must be called after /// allocateDeclIndexes for any given Decl. pub fn updateDeclExports( @@ -656,7 +661,7 @@ pub const File = struct { module: *Module, decl: *Module.Decl, exports: []const *Module.Export, - ) !void { + ) UpdateDeclExportsError!void { log.debug("updateDeclExports {*} ({s})", .{ decl, decl.name }); assert(decl.has_tv); switch (base.tag) { @@ -717,19 +722,38 @@ pub const File = struct { // directly, and remove this function from link.zig. _ = base; while (true) { - std.fs.rename( - cache_directory.handle, - tmp_dir_sub_path, - cache_directory.handle, - o_sub_path, - ) catch |err| switch (err) { - error.PathAlreadyExists => { + if (builtin.os.tag == .windows) { + // workaround windows `renameW` can't fail with `PathAlreadyExists` + // See https://github.com/ziglang/zig/issues/8362 + if (cache_directory.handle.access(o_sub_path, .{})) |_| { try cache_directory.handle.deleteTree(o_sub_path); continue; - }, - else => |e| return e, - }; - break; + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + try std.fs.rename( + cache_directory.handle, + tmp_dir_sub_path, + cache_directory.handle, + o_sub_path, + ); + break; + } else { + std.fs.rename( + cache_directory.handle, + tmp_dir_sub_path, + cache_directory.handle, + o_sub_path, + ) catch |err| switch (err) { + error.PathAlreadyExists => { + try cache_directory.handle.deleteTree(o_sub_path); + continue; + }, + else => |e| return e, + }; + break; + } } } diff --git a/src/link/C.zig b/src/link/C.zig index 85b7c24487..229990fc8e 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -89,8 +89,9 @@ pub fn deinit(self: *C) void { pub fn freeDecl(self: *C, decl: *Module.Decl) void { const gpa = self.base.allocator; - if (self.decl_table.fetchSwapRemove(decl)) |*kv| { - kv.value.deinit(gpa); + if (self.decl_table.fetchSwapRemove(decl)) |kv| { + var decl_block = kv.value; + decl_block.deinit(gpa); } } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 907a21b774..248521c544 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -39,7 +39,7 @@ atom_last: ?*Atom = null, abbrev_table_offset: ?u64 = null, -/// TODO replace with InternArena +/// TODO replace with InternPool /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -79,6 +79,7 @@ pub const DeclState = struct { std.hash_map.default_max_load_percentage, ) = .{}, abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, + exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{}, fn init(gpa: Allocator, target: std.Target) DeclState { return .{ @@ -97,6 +98,16 @@ pub const DeclState = struct { self.abbrev_table.deinit(self.gpa); self.abbrev_resolver.deinit(self.gpa); self.abbrev_relocs.deinit(self.gpa); + self.exprloc_relocs.deinit(self.gpa); + } + + pub fn addExprlocReloc(self: *DeclState, target: u32, offset: u32, is_ptr: bool) !void { + log.debug("{x}: target sym @{d}, via GOT {}", .{ offset, target, is_ptr }); + try self.exprloc_relocs.append(self.gpa, .{ + .@"type" = if (is_ptr) .got_load else .direct_load, + .target = target, + .offset = offset, + }); } pub fn addTypeReloc( @@ -148,11 +159,11 @@ pub const DeclState = struct { switch (ty.zigTypeTag()) { .NoReturn => unreachable, .Void => { - try dbg_info_buffer.append(abbrev_pad1); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.pad1)); }, .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ - abbrev_base_type, + @enumToInt(AbbrevKind.base_type), DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 1, // DW.AT.byte_size, DW.FORM.data1 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string @@ -161,7 +172,7 @@ pub const DeclState = struct { .Int => { const info = ty.intInfo(target); try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.base_type)); // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { .signed => DW.ATE.signed, @@ -175,7 +186,7 @@ pub const DeclState = struct { .Optional => { if (ty.isPtrLikeOptional()) { try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.base_type)); // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); // DW.AT.byte_size, DW.FORM.data1 @@ -187,7 +198,7 @@ pub const DeclState = struct { var buf = try arena.create(Type.Payload.ElemType); const payload_ty = ty.optionalChild(buf); // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -195,7 +206,7 @@ pub const DeclState = struct { try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("maybe"); dbg_info_buffer.appendAssumeCapacity(0); @@ -207,7 +218,7 @@ pub const DeclState = struct { try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("val"); dbg_info_buffer.appendAssumeCapacity(0); @@ -227,14 +238,14 @@ pub const DeclState = struct { // Slices are structs: struct { .ptr = *, .len = N } // DW.AT.structure_type try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("ptr"); dbg_info_buffer.appendAssumeCapacity(0); @@ -248,7 +259,7 @@ pub const DeclState = struct { try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("len"); dbg_info_buffer.appendAssumeCapacity(0); @@ -263,16 +274,37 @@ pub const DeclState = struct { dbg_info_buffer.appendAssumeCapacity(0); } else { try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.ptr_type)); // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); } }, + .Array => { + // DW.AT.array_type + try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_type)); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); + // DW.AT.subrange_type + try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim)); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); + // DW.AT.count, DW.FORM.udata + const len = ty.arrayLenIncludingSentinel(); + try leb128.writeULEB128(dbg_info_buffer.writer(), len); + // DW.AT.array_type delimit children + try dbg_info_buffer.append(0); + }, .Struct => blk: { // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -285,7 +317,7 @@ pub const DeclState = struct { const fields = ty.tupleFields(); for (fields.types) |field, field_index| { // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); // DW.AT.type, DW.FORM.ref4 @@ -315,7 +347,7 @@ pub const DeclState = struct { const field = fields.get(field_name).?; // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -335,7 +367,7 @@ pub const DeclState = struct { }, .Enum => { // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -355,7 +387,7 @@ pub const DeclState = struct { for (fields.keys()) |field_name, field_i| { // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -385,7 +417,7 @@ pub const DeclState = struct { // for untagged unions. if (is_tagged) { // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), layout.abi_size); // DW.AT.name, DW.FORM.string @@ -395,7 +427,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(9); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("payload"); dbg_info_buffer.appendAssumeCapacity(0); @@ -408,7 +440,7 @@ pub const DeclState = struct { } // DW.AT.union_type - try dbg_info_buffer.append(abbrev_union_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.union_type)); // DW.AT.byte_size, DW.FORM.sdata, try leb128.writeULEB128(dbg_info_buffer.writer(), layout.payload_size); // DW.AT.name, DW.FORM.string @@ -423,7 +455,7 @@ pub const DeclState = struct { const field = fields.get(field_name).?; if (!field.ty.hasRuntimeBits()) continue; // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{s}\x00", .{field_name}); // DW.AT.type, DW.FORM.ref4 @@ -439,7 +471,7 @@ pub const DeclState = struct { if (is_tagged) { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("tag"); dbg_info_buffer.appendAssumeCapacity(0); @@ -471,7 +503,7 @@ pub const DeclState = struct { const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string @@ -480,7 +512,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("value"); dbg_info_buffer.appendAssumeCapacity(0); @@ -493,7 +525,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("err"); dbg_info_buffer.appendAssumeCapacity(0); @@ -509,7 +541,7 @@ pub const DeclState = struct { }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); - try dbg_info_buffer.append(abbrev_pad1); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.pad1)); }, } } @@ -528,6 +560,18 @@ pub const AbbrevRelocation = struct { addend: u32, }; +pub const ExprlocRelocation = struct { + /// Type of the relocation: direct load ref, or GOT load ref (via GOT table) + @"type": enum { + direct_load, + got_load, + }, + /// Index of the target in the linker's locals symbol table. + target: u32, + /// Offset within the debug info buffer where to patch up the address value. + offset: u32, +}; + pub const SrcFn = struct { /// Offset from the beginning of the Debug Line Program header that contains this function. off: u32, @@ -550,18 +594,23 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; -pub const abbrev_compile_unit = 1; -pub const abbrev_subprogram = 2; -pub const abbrev_subprogram_retvoid = 3; -pub const abbrev_base_type = 4; -pub const abbrev_ptr_type = 5; -pub const abbrev_struct_type = 6; -pub const abbrev_struct_member = 7; -pub const abbrev_enum_type = 8; -pub const abbrev_enum_variant = 9; -pub const abbrev_union_type = 10; -pub const abbrev_pad1 = 11; -pub const abbrev_parameter = 12; +pub const AbbrevKind = enum(u8) { + compile_unit = 1, + subprogram, + subprogram_retvoid, + base_type, + ptr_type, + struct_type, + struct_member, + enum_type, + enum_variant, + union_type, + pad1, + parameter, + variable, + array_type, + array_dim, +}; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. @@ -670,9 +719,9 @@ pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !DeclState { const fn_ret_type = decl.ty.fnReturnType(); const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); if (fn_ret_has_bits) { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.subprogram)); } else { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.subprogram_retvoid)); } // These get overwritten after generating the machine code. These values are // "relocations" and have to be in this fixed place so that functions can be @@ -926,7 +975,7 @@ pub fn commitDeclState( else => unreachable, }; - { + if (decl_state.abbrev_table.items.len > 0) { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual // relocations yet. @@ -983,6 +1032,26 @@ pub fn commitDeclState( } } + while (decl_state.exprloc_relocs.popOrNull()) |reloc| { + switch (self.tag) { + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + try d_sym.relocs.append(d_sym.base.base.allocator, .{ + .@"type" = switch (reloc.@"type") { + .direct_load => .direct_load, + .got_load => .got_load, + }, + .target = reloc.target, + .offset = reloc.offset + atom.off, + .addend = 0, + .prev_vaddr = 0, + }); + }, + else => unreachable, + } + } + try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); } @@ -1244,14 +1313,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. const abbrev_buf = [_]u8{ - abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header - DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, - DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, - DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, - DW.FORM.strp, DW.AT.producer, DW.FORM.strp, - DW.AT.language, DW.FORM.data2, 0, + @enumToInt(AbbrevKind.compile_unit), DW.TAG.compile_unit, DW.CHILDREN.yes, // header + DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, + DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, + DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, + DW.FORM.strp, DW.AT.producer, DW.FORM.strp, + DW.AT.language, DW.FORM.data2, 0, 0, // table sentinel - abbrev_subprogram, + @enumToInt(AbbrevKind.subprogram), DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, @@ -1262,15 +1331,15 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.ref4, DW.AT.name, DW.FORM.string, - 0, 0, // table sentinel - abbrev_subprogram_retvoid, + 0, 0, // table sentinel + @enumToInt(AbbrevKind.subprogram_retvoid), DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, DW.FORM.addr, DW.AT.high_pc, DW.FORM.data4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel - abbrev_base_type, + @enumToInt(AbbrevKind.base_type), DW.TAG.base_type, DW.CHILDREN.no, // header DW.AT.encoding, @@ -1281,14 +1350,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_ptr_type, + @enumToInt(AbbrevKind.ptr_type), DW.TAG.pointer_type, DW.CHILDREN.no, // header DW.AT.type, DW.FORM.ref4, 0, 0, // table sentinel - abbrev_struct_type, + @enumToInt(AbbrevKind.struct_type), DW.TAG.structure_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1297,7 +1366,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_struct_member, + @enumToInt(AbbrevKind.struct_member), DW.TAG.member, DW.CHILDREN.no, // header DW.AT.name, @@ -1308,7 +1377,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.sdata, 0, 0, // table sentinel - abbrev_enum_type, + @enumToInt(AbbrevKind.enum_type), DW.TAG.enumeration_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1317,7 +1386,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_enum_variant, + @enumToInt(AbbrevKind.enum_variant), DW.TAG.enumerator, DW.CHILDREN.no, // header DW.AT.name, @@ -1326,7 +1395,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.data8, 0, 0, // table sentinel - abbrev_union_type, + @enumToInt(AbbrevKind.union_type), DW.TAG.union_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1335,18 +1404,37 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_pad1, + @enumToInt(AbbrevKind.pad1), DW.TAG.unspecified_type, DW.CHILDREN.no, // header 0, 0, // table sentinel - abbrev_parameter, + @enumToInt(AbbrevKind.parameter), DW.TAG.formal_parameter, DW.CHILDREN.no, // header DW.AT.location, DW.FORM.exprloc, DW.AT.type, DW.FORM.ref4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel + @enumToInt(AbbrevKind.variable), + DW.TAG.variable, DW.CHILDREN.no, // header + DW.AT.location, DW.FORM.exprloc, + DW.AT.type, DW.FORM.ref4, + DW.AT.name, DW.FORM.string, + 0, + 0, // table sentinel + @enumToInt(AbbrevKind.array_type), + DW.TAG.array_type, DW.CHILDREN.yes, // header + DW.AT.name, DW.FORM.string, + DW.AT.type, DW.FORM.ref4, + 0, + 0, // table sentinel + @enumToInt(AbbrevKind.array_dim), + DW.TAG.subrange_type, DW.CHILDREN.no, // header + DW.AT.type, DW.FORM.ref4, + DW.AT.count, DW.FORM.udata, + 0, + 0, // table sentinel 0, 0, 0, // section sentinel @@ -1459,7 +1547,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 const comp_dir_strp = try self.makeString(module.root_pkg.root_src_directory.path orelse "."); const producer_strp = try self.makeString(link.producer_string); - di_buf.appendAssumeCapacity(abbrev_compile_unit); + di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); if (self.tag == .macho) { mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc); @@ -1606,7 +1694,7 @@ fn pwriteDbgInfoNops( const tracy = trace(@src()); defer tracy.end(); - const page_of_nops = [1]u8{abbrev_pad1} ** 4096; + const page_of_nops = [1]u8{@enumToInt(AbbrevKind.pad1)} ** 4096; var vecs: [32]std.os.iovec_const = undefined; var vec_index: usize = 0; { @@ -1673,7 +1761,7 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { .p32 => @as(usize, 4), .p64 => 12, }; - const ptr_width_bytes: u8 = self.ptrWidthBytes(); + const ptr_width_bytes = self.ptrWidthBytes(); // Enough for all the data without resizing. When support for more compilation units // is added, the size of this section will become more variable. @@ -2040,7 +2128,7 @@ fn addDbgInfoErrorSet( const target_endian = target.cpu.arch.endian(); // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -2051,7 +2139,7 @@ fn addDbgInfoErrorSet( // DW.AT.enumerator const no_error = "(no error)"; try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(no_error); dbg_info_buffer.appendAssumeCapacity(0); @@ -2063,7 +2151,7 @@ fn addDbgInfoErrorSet( const kv = module.getErrorValue(error_name) catch unreachable; // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(error_name); dbg_info_buffer.appendAssumeCapacity(0); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 636b2ba7df..e5e65011cd 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -65,7 +65,7 @@ phdr_load_rw_index: ?u16 = null, phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, entry_addr: ?u64 = null, -page_size: u16, +page_size: u32, shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, shstrtab_index: ?u16 = null, @@ -100,11 +100,11 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, shstrtab_dirty: bool = false, -debug_strtab_dirty: bool = false, offset_table_count_dirty: bool = false, + +debug_strtab_dirty: bool = false, debug_abbrev_section_dirty: bool = false, debug_aranges_section_dirty: bool = false, - debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, @@ -154,7 +154,7 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, /// const Foo = struct{ /// a: u8, /// }; -/// +/// /// pub fn main() void { /// var foo = Foo{ .a = 1 }; /// _ = foo; @@ -304,7 +304,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { }; const self = try gpa.create(Elf); errdefer gpa.destroy(self); - const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB + + const page_size: u32 = switch (options.target.cpu.arch) { + .powerpc64le => 0x10000, + .sparcv9 => 0x2000, + else => 0x1000, + }; var dwarf: ?Dwarf = if (!options.strip and options.module != null) Dwarf.init(gpa, .elf, options.target) @@ -472,7 +477,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { return min_pos - start; } -pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { +pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 { var start: u64 = 0; while (self.detectAllocCollision(start, object_size)) |item_end| { start = mem.alignForwardGeneric(u64, item_end, min_alignment); @@ -749,127 +754,129 @@ pub fn populateMissingMetadata(self: *Elf) !void { try self.writeSymbol(0); } - if (self.debug_str_section_index == null) { - self.debug_str_section_index = @intCast(u16, self.sections.items.len); - assert(self.dwarf.?.strtab.items.len == 0); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_str"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, - .sh_addr = 0, - .sh_offset = 0, - .sh_size = 0, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 1, - }); - self.debug_strtab_dirty = true; - self.shdr_table_dirty = true; - } + if (self.dwarf) |dw| { + if (self.debug_str_section_index == null) { + self.debug_str_section_index = @intCast(u16, self.sections.items.len); + assert(dw.strtab.items.len == 0); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_str"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = 0, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 1, + }); + self.debug_strtab_dirty = true; + self.shdr_table_dirty = true; + } - if (self.debug_info_section_index == null) { - self.debug_info_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_info_section_index == null) { + self.debug_info_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 200; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_info"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_info_header_dirty = true; - } + const file_size_hint = 200; + const p_align = 1; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_info"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_info_header_dirty = true; + } - if (self.debug_abbrev_section_index == null) { - self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_abbrev_section_index == null) { + self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 128; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_abbrev"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_abbrev_section_dirty = true; - } + const file_size_hint = 128; + const p_align = 1; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_abbrev"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_abbrev_section_dirty = true; + } - if (self.debug_aranges_section_index == null) { - self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_aranges_section_index == null) { + self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 160; - const p_align = 16; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_aranges"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_aranges_section_dirty = true; - } + const file_size_hint = 160; + const p_align = 16; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_aranges"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_aranges_section_dirty = true; + } - if (self.debug_line_section_index == null) { - self.debug_line_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_line_section_index == null) { + self.debug_line_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 250; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_line"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_line_header_dirty = true; + const file_size_hint = 250; + const p_align = 1; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_line"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_line_header_dirty = true; + } } const shsize: u64 = switch (self.ptr_width) { @@ -1001,40 +1008,42 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { // mixing local and global symbols within a symbol table. try self.writeAllGlobalSymbols(); - if (self.debug_abbrev_section_dirty) { - try self.dwarf.?.writeDbgAbbrev(&self.base); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_abbrev_section_index.?); + if (self.dwarf) |*dw| { + if (self.debug_abbrev_section_dirty) { + try dw.writeDbgAbbrev(&self.base); + if (!self.shdr_table_dirty) { + // Then it won't get written with the others and we need to do it. + try self.writeSectHeader(self.debug_abbrev_section_index.?); + } + self.debug_abbrev_section_dirty = false; } - self.debug_abbrev_section_dirty = false; - } - if (self.debug_info_header_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - const low_pc = text_phdr.p_vaddr; - const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; - try self.dwarf.?.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); - self.debug_info_header_dirty = false; - } + if (self.debug_info_header_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + const low_pc = text_phdr.p_vaddr; + const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; + try dw.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); + self.debug_info_header_dirty = false; + } - if (self.debug_aranges_section_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - try self.dwarf.?.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_aranges_section_index.?); + if (self.debug_aranges_section_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + try dw.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); + if (!self.shdr_table_dirty) { + // Then it won't get written with the others and we need to do it. + try self.writeSectHeader(self.debug_aranges_section_index.?); + } + self.debug_aranges_section_dirty = false; } - self.debug_aranges_section_dirty = false; - } - if (self.debug_line_header_dirty) { - try self.dwarf.?.writeDbgLineHeader(&self.base, module); - self.debug_line_header_dirty = false; + if (self.debug_line_header_dirty) { + try dw.writeDbgLineHeader(&self.base, module); + self.debug_line_header_dirty = false; + } } if (self.phdr_table_dirty) { @@ -1105,9 +1114,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { } } - { + if (self.dwarf) |dwarf| { const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?]; - const dwarf = self.dwarf.?; if (self.debug_strtab_dirty or dwarf.strtab.items.len != debug_strtab_sect.sh_size) { const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset); const needed_size = dwarf.strtab.items.len; @@ -1827,7 +1835,7 @@ fn writeElfHeader(self: *Elf) !void { var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; var index: usize = 0; - hdr_buf[0..4].* = "\x7fELF".*; + hdr_buf[0..4].* = elf.MAGIC.*; index += 4; hdr_buf[index] = switch (self.ptr_width) { @@ -2105,14 +2113,16 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al phdr.p_memsz = needed_size; phdr.p_filesz = needed_size; - // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address - // range of the compilation unit. When we expand the text section, this range changes, - // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. - self.debug_info_header_dirty = true; - // This becomes dirty for the same reason. We could potentially make this more - // fine-grained with the addition of support for more compilation units. It is planned to - // model each package as a different compilation unit. - self.debug_aranges_section_dirty = true; + if (self.dwarf) |_| { + // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address + // range of the compilation unit. When we expand the text section, this range changes, + // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. + self.debug_info_header_dirty = true; + // This becomes dirty for the same reason. We could potentially make this more + // fine-grained with the addition of support for more compilation units. It is planned to + // model each package as a different compilation unit. + self.debug_aranges_section_dirty = true; + } self.phdr_table_dirty = true; // TODO look into making only the one program header dirty self.shdr_table_dirty = true; // TODO look into making only the one section dirty @@ -2482,7 +2492,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ - .none = .{}, + .none = {}, }, .{ .parent_atom_index = atom.local_sym_index, }); @@ -2794,6 +2804,22 @@ fn writeAllGlobalSymbols(self: *Elf) !void { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; + const sym_align: u16 = switch (self.ptr_width) { + .p32 => @alignOf(elf.Elf32_Sym), + .p64 => @alignOf(elf.Elf64_Sym), + }; + const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; + if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { + // Move all the symbols to a new file location. + const new_offset = self.findFreeSpace(needed_size, sym_align); + const existing_size = @as(u64, syms_sect.sh_info) * sym_size; + const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size); + if (amt != existing_size) return error.InputOutput; + syms_sect.sh_offset = new_offset; + } + syms_sect.sh_size = needed_size; // anticipating adding the global symbols later + self.shdr_table_dirty = true; // TODO look into only writing one section + const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; switch (self.ptr_width) { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 1d75eb442a..d359a3fd5d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -232,7 +232,7 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, /// const Foo = struct{ /// a: u8, /// }; -/// +/// /// pub fn main() void { /// var foo = Foo{ .a = 1 }; /// _ = foo; @@ -3472,6 +3472,9 @@ pub fn closeFiles(self: MachO) void { for (self.dylibs.items) |dylib| { dylib.file.close(); } + if (self.d_sym) |ds| { + ds.file.close(); + } } fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void { @@ -4274,6 +4277,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {}; self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined }; _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index }); + + if (self.d_sym) |*d_sym| { + d_sym.swapRemoveRelocs(decl.link.macho.local_sym_index); + } + log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, decl.link.macho.local_sym_index, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 885f0ca6a8..aa7a29fcd1 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -59,6 +59,19 @@ debug_aranges_section_dirty: bool = false, debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, +relocs: std.ArrayListUnmanaged(Reloc) = .{}, + +pub const Reloc = struct { + @"type": enum { + direct_load, + got_load, + }, + target: u32, + offset: u64, + addend: u32, + prev_vaddr: u64, +}; + /// You must call this function *after* `MachO.populateMissingMetadata()` /// has been called to get a viable debug symbols output. pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { @@ -254,6 +267,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti // Zig source code. const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + for (self.relocs.items) |*reloc| { + const sym = switch (reloc.@"type") { + .direct_load => self.base.locals.items[reloc.target], + .got_load => blk: { + const got_index = self.base.got_entries_table.get(.{ .local = reloc.target }).?; + const got_entry = self.base.got_entries.items[got_index]; + break :blk self.base.locals.items[got_entry.atom.local_sym_index]; + }, + }; + if (sym.n_value == reloc.prev_vaddr) continue; + + const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; + const sect = &seg.sections.items[self.debug_info_section_index.?]; + const file_offset = sect.offset + reloc.offset; + log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{ + reloc.target, + sym.n_value, + self.base.getString(sym.n_strx), + file_offset, + }); + try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset); + reloc.prev_vaddr = sym.n_value; + } + if (self.debug_abbrev_section_dirty) { try self.dwarf.writeDbgAbbrev(&self.base.base); self.load_commands_dirty = true; @@ -330,7 +367,20 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { } self.load_commands.deinit(allocator); self.dwarf.deinit(); - self.file.close(); + self.relocs.deinit(allocator); +} + +pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void { + // TODO re-implement using a hashmap with free lists + var last_index: usize = 0; + while (last_index < self.relocs.items.len) { + const reloc = self.relocs.items[last_index]; + if (reloc.target == target) { + _ = self.relocs.swapRemove(last_index); + } else { + last_index += 1; + } + } } fn copySegmentCommand( diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 6620c99b49..255d7053d4 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -625,7 +625,6 @@ pub fn parseDataInCode(self: *Object, allocator: Allocator) !void { while (true) { const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) { error.EndOfStream => break, - else => |e| return e, }; try self.data_in_code_entries.append(allocator, dice); } diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 89a2272dd1..1f8a6a2e84 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -40,7 +40,6 @@ pub fn getLibraryOffset(reader: anytype, target: std.Target) !u64 { // fine because we can keep looking for one that might match. const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) { error.UnsupportedCpuArchitecture => continue, - else => |e| return e, }; if (lib_arch == target.cpu.arch) { // We have found a matching architecture! diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 8096b2d38c..3269cb67d4 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -307,7 +307,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, - }, &code_buffer, .{ .none = .{} }, .{ + }, &code_buffer, .{ .none = {} }, .{ .parent_atom_index = @intCast(u32, sym_index), }); const code = switch (res) { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index c717b42bb6..961e382112 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -101,8 +101,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{}, /// When this is non-zero, we must emit a table entry, /// as well as an 'elements' section. /// -/// Note: Key is symbol index, value represents the index into the table -function_table: std.AutoHashMapUnmanaged(u32, u32) = .{}, +/// Note: Key is symbol location, value represents the index into the table +function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, /// All object files and their data which are linked into the final binary objects: std.ArrayListUnmanaged(Object) = .{}, @@ -363,6 +363,9 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { .index = sym_index, }; const sym_name = object.string_table.get(symbol.name); + if (mem.eql(u8, sym_name, "__indirect_function_table")) { + continue; + } const sym_name_index = try self.string_table.put(self.base.allocator, sym_name); if (symbol.isLocal()) { @@ -837,7 +840,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { /// Appends a new entry to the indirect function table pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void { const index = @intCast(u32, self.function_table.count()); - try self.function_table.put(self.base.allocator, symbol_index, index); + try self.function_table.put(self.base.allocator, .{ .file = null, .index = symbol_index }, index); } /// Assigns indexes to all indirect functions. @@ -959,11 +962,6 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { const segment: *Segment = &self.segments.items[final_index]; segment.alignment = std.math.max(segment.alignment, atom.alignment); - segment.size = std.mem.alignForwardGeneric( - u32, - std.mem.alignForwardGeneric(u32, segment.size, atom.alignment) + atom.size, - segment.alignment, - ); if (self.atoms.getPtr(final_index)) |last| { last.*.next = atom; @@ -975,9 +973,10 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { } fn allocateAtoms(self: *Wasm) !void { - var it = self.atoms.valueIterator(); - while (it.next()) |current_atom| { - var atom: *Atom = current_atom.*.getFirst(); + var it = self.atoms.iterator(); + while (it.next()) |entry| { + const segment = &self.segments.items[entry.key_ptr.*]; + var atom: *Atom = entry.value_ptr.*.getFirst(); var offset: u32 = 0; while (true) { offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); @@ -993,6 +992,7 @@ fn allocateAtoms(self: *Wasm) !void { self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers atom = atom.next orelse break; } + segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); } } @@ -1017,6 +1017,9 @@ fn setupImports(self: *Wasm) !void { } const symbol = symbol_loc.getSymbol(self); + if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) { + continue; + } if (symbol.tag == .data or !symbol.requiresImport()) { continue; } @@ -1166,13 +1169,20 @@ fn setupExports(self: *Wasm) !void { if (!symbol.isExported()) continue; const sym_name = sym_loc.getName(self); - const export_name = if (self.export_names.get(sym_loc)) |name| name else symbol.name; + const export_name = if (self.export_names.get(sym_loc)) |name| name else blk: { + if (sym_loc.file == null) break :blk symbol.name; + break :blk try self.string_table.put(self.base.allocator, sym_name); + }; const exp: types.Export = .{ .name = export_name, .kind = symbol.tag.externalType(), .index = symbol.index, }; - log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ sym_name, self.string_table.get(exp.name), exp.index }); + log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ + sym_name, + self.string_table.get(exp.name), + exp.index, + }); try self.exports.append(self.base.allocator, exp); } @@ -1553,8 +1563,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self); } - try self.setupMemory(); try self.allocateAtoms(); + try self.setupMemory(); self.mapFunctionTable(); try self.mergeSections(); try self.mergeTypes(); @@ -1767,8 +1777,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try leb.writeULEB128(writer, @as(u8, 0)); try leb.writeULEB128(writer, @intCast(u32, self.function_table.count())); var symbol_it = self.function_table.keyIterator(); - while (symbol_it.next()) |symbol_index_ptr| { - try leb.writeULEB128(writer, self.symbols.items[symbol_index_ptr.*].index); + while (symbol_it.next()) |symbol_loc_ptr| { + try leb.writeULEB128(writer, symbol_loc_ptr.*.getSymbol(self).index); } try writeVecSectionHeader( @@ -1819,7 +1829,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { segment_count += 1; const atom_index = entry.value_ptr.*; var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); - var segment = self.segments.items[atom_index]; + const segment = self.segments.items[atom_index]; // flag and index to memory section (currently, there can only be 1 memory section in wasm) try leb.writeULEB128(writer, @as(u32, 0)); @@ -2551,7 +2561,8 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * .iov_base = payload.items.ptr, .iov_len = payload.items.len, }; - try file.writevAll(&.{iovec}); + var iovecs = [_]std.os.iovec_const{iovec}; + try file.writevAll(&iovecs); } fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { @@ -2576,7 +2587,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { .iov_base = payload.items.ptr, .iov_len = payload.items.len, }; - try file.writevAll(&.{iovec}); + var iovecs = [_]std.os.iovec_const{iovec}; + try file.writevAll(&iovecs); } fn getULEB128Size(uint_value: anytype) u32 { @@ -2635,12 +2647,14 @@ fn emitCodeRelocations( var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); try payload.insertSlice(reloc_start, &buf); - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, + var iovecs = [_]std.os.iovec_const{ + .{ + .iov_base = payload.items.ptr, + .iov_len = payload.items.len, + }, }; const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&.{iovec}); + try file.writevAll(&iovecs); const size = @intCast(u32, payload.items.len); try writeCustomSectionHeader(file, header_offset, size); } @@ -2694,12 +2708,14 @@ fn emitDataRelocations( var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); try payload.insertSlice(reloc_start, &buf); - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, + var iovecs = [_]std.os.iovec_const{ + .{ + .iov_base = payload.items.ptr, + .iov_len = payload.items.len, + }, }; const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&.{iovec}); + try file.writevAll(&iovecs); const size = @intCast(u32, payload.items.len); try writeCustomSectionHeader(file, header_offset, size); } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index a3e1c25190..fc45648d9a 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -158,7 +158,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_TABLE_INDEX_I64, .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, - => return wasm_bin.function_table.get(relocation.index) orelse 0, + => return wasm_bin.function_table.get(target_loc) orelse 0, .R_WASM_TYPE_INDEX_LEB => return wasm_bin.functions.items[symbol.index].type_index, .R_WASM_GLOBAL_INDEX_I32, .R_WASM_GLOBAL_INDEX_LEB, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 011ec2e9e4..9f8c046275 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -133,6 +133,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void { gpa.free(self.memories); gpa.free(self.globals); gpa.free(self.exports); + for (self.elements) |el| { + gpa.free(el.func_indexes); + } gpa.free(self.elements); gpa.free(self.features); for (self.relocations.values()) |val| { @@ -848,15 +851,12 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin reloc.offset -= relocatable_data.offset; try atom.relocs.append(gpa, reloc); - // TODO: Automatically append the target symbol to the indirect - // function table when the relocation is a table index. - // - // if (relocation.isTableIndex()) { - // try wasm_bin.elements.appendSymbol(gpa, .{ - // .file = object_index, - // .sym_index = relocation.index, - // }); - // } + if (relocation.isTableIndex()) { + try wasm_bin.function_table.putNoClobber(gpa, .{ + .file = object_index, + .index = relocation.index, + }, 0); + } } } @@ -865,11 +865,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; segment.alignment = std.math.max(segment.alignment, atom.alignment); - segment.size = std.mem.alignForwardGeneric( - u32, - std.mem.alignForwardGeneric(u32, segment.size, atom.alignment) + atom.size, - segment.alignment, - ); if (wasm_bin.atoms.getPtr(final_index)) |last| { last.*.next = atom; diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index 1dda5cdb5c..2c99f0f003 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -67,6 +67,18 @@ pub const Relocation = struct { }; } + /// Returns true when the relocation represents a table index relocatable + pub fn isTableIndex(self: Relocation) bool { + return switch (self.relocation_type) { + .R_WASM_TABLE_INDEX_I32, + .R_WASM_TABLE_INDEX_I64, + .R_WASM_TABLE_INDEX_SLEB, + .R_WASM_TABLE_INDEX_SLEB64, + => true, + else => false, + }; + } + pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = options; diff --git a/src/print_air.zig b/src/print_air.zig index 69d7b63b2a..8a1a8fa950 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -328,7 +328,7 @@ const Writer = struct { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const vector_ty = w.air.getRefType(ty_pl.ty); const len = @intCast(usize, vector_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); try s.print("{}, [", .{vector_ty.fmtDebug()}); for (elements) |elem, i| { @@ -533,9 +533,9 @@ const Writer = struct { try s.writeAll(", volatile"); } - const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; for (outputs) |output| { @@ -604,7 +604,7 @@ const Writer = struct { fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]); try w.writeOperand(s, inst, 0, pl_op.operand); try s.writeAll(", ["); for (args) |arg, i| { @@ -674,7 +674,7 @@ const Writer = struct { while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = w.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]); const case_body = w.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + case.data.items_len + case_body.len; @@ -724,11 +724,21 @@ const Writer = struct { op_index: usize, operand: Air.Inst.Ref, ) @TypeOf(s).Error!void { - const dies = if (op_index < Liveness.bpi - 1) + const small_tomb_bits = Liveness.bpi - 1; + const dies = if (op_index < small_tomb_bits) w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index)) else blk: { - // TODO - break :blk false; + var extra_index = w.liveness.special.get(inst).?; + var tomb_op_index: usize = small_tomb_bits; + while (true) { + const bits = w.liveness.extra[extra_index]; + if (op_index < tomb_op_index + 31) { + break :blk @truncate(u1, bits >> @intCast(u5, op_index - tomb_op_index)) != 0; + } + if ((bits >> 31) != 0) break :blk false; + extra_index += 1; + tomb_op_index += 31; + } else unreachable; }; return w.writeInstRef(s, operand, dies); } diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 11f47c592b..c3157b6539 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -23052,6 +23052,12 @@ static Stage1AirInst *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, Stage1ZirI if (type_is_invalid(src_type)) return ira->codegen->invalid_inst_gen; + // This logic is not quite right; this is just to get stage1 to accept valid code + // we use in the self-hosted compiler. + if (is_slice(dest_type) && is_slice(src_type)) { + return ir_analyze_bit_cast(ira, instruction->base.scope, instruction->base.source_node, ptr, dest_type); + } + bool keep_bigger_alignment = true; return ir_analyze_ptr_cast(ira, instruction->base.scope, instruction->base.source_node, ptr, instruction->ptr->source_node, dest_type, dest_type_value->source_node, diff --git a/src/target.zig b/src/target.zig index 2eff4f8445..aafd65e327 100644 --- a/src/target.zig +++ b/src/target.zig @@ -669,6 +669,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 { return switch (target.cpu.arch) { .arm, .armeb => 4, .aarch64, .aarch64_32, .aarch64_be => 4, + .sparc, .sparcel, .sparcv9 => 4, .riscv64 => 2, else => 1, }; diff --git a/src/test.zig b/src/test.zig index a0d0d202d1..8c2a15e844 100644 --- a/src/test.zig +++ b/src/test.zig @@ -46,6 +46,7 @@ test { defer stage2_dir.close(); // TODO make this incremental once the bug is solved that it triggers + // See: https://github.com/ziglang/zig/issues/11344 ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, .independent); } @@ -653,7 +654,14 @@ pub const TestContext = struct { case.compiles(fixed_src); } - const Strategy = enum { incremental, independent }; + const Strategy = enum { + /// Execute tests as independent compilations, unless they are explicitly + /// incremental ("foo.0.zig", "foo.1.zig", etc.) + independent, + /// Execute all tests as incremental updates to a single compilation. Explicitly + /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order + incremental, + }; /// Adds a compile-error test for each file in the provided directory, using the /// selected backend and output mode. If `one_test_case_per_file` is true, a new @@ -681,6 +689,66 @@ pub const TestContext = struct { }; } + /// For a filename in the format "<filename>.X.<ext>" or "<filename>.<ext>", returns + /// "<filename>", "<ext>" and X parsed as a decimal number. If X is not present, or + /// cannot be parsed as a decimal number, it is treated as part of <filename> + fn getTestFileNameParts(name: []const u8) struct { + base_name: []const u8, + file_ext: []const u8, + test_index: ?usize, + } { + const file_ext = std.fs.path.extension(name); + const trimmed = name[0 .. name.len - file_ext.len]; // Trim off ".<ext>" + const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" + + // Attempt to parse index + const index: ?usize = if (maybe_index.len > 0) + std.fmt.parseInt(usize, maybe_index[1..], 10) catch null + else + null; + + // Adjust "<filename>" extent based on parsing success + const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; + return .{ + .base_name = name[0..base_name_end], + .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, + .test_index = index, + }; + } + + /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", + /// "foo.1.zig", etc.) are contiguous and appear in numerical order. + fn sortTestFilenames( + filenames: [][]const u8, + ) void { + const Context = struct { + pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { + const a_parts = getTestFileNameParts(a); + const b_parts = getTestFileNameParts(b); + + // Sort "<base_name>.X.<file_ext>" based on "<base_name>" and "<file_ext>" first + return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { + .lt => true, + .gt => false, + .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { + .lt => true, + .gt => false, + .eq => b: { // a and b differ only in their ".X" part + + // Sort "<base_name>.<file_ext>" before any "<base_name>.X.<file_ext>" + if (a_parts.test_index == null) break :b true; + if (b_parts.test_index == null) break :b false; + + // Make sure that incremental tests appear in linear order + return a_parts.test_index.? < b_parts.test_index.?; + }, + }, + }; + } + }; + std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); + } + fn addErrorCasesFromDirInner( ctx: *TestContext, name: []const u8, @@ -696,6 +764,9 @@ pub const TestContext = struct { var opt_case: ?*Case = null; var it = dir.iterate(); + var filenames = std.ArrayList([]const u8).init(ctx.arena); + defer filenames.deinit(); + while (try it.next()) |entry| { if (entry.kind != .File) continue; @@ -704,11 +775,46 @@ pub const TestContext = struct { .unknown => continue, else => {}, } + try filenames.append(try ctx.arena.dupe(u8, entry.name)); + } - current_file.* = try ctx.arena.dupe(u8, entry.name); + // Sort filenames, so that incremental tests are contiguous and in-order + sortTestFilenames(filenames.items); + + var prev_filename: []const u8 = ""; + for (filenames.items) |filename| { + current_file.* = filename; + + { // First, check if this file is part of an incremental update sequence + + // Split filename into "<base_name>.<index>.<file_ext>" + const prev_parts = getTestFileNameParts(prev_filename); + const new_parts = getTestFileNameParts(filename); + + // If base_name and file_ext match, these files are in the same test sequence + // and the new one should be the incremented version of the previous test + if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and + std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) + { + + // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 + if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; + } else { + + // This is not the same test sequence, so the new file must be the first file + // in a new sequence ("*.0.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; + + if (strategy == .independent) + opt_case = null; // Generate a new independent test case for this update + } + } + prev_filename = filename; const max_file_size = 10 * 1024 * 1024; - const src = try dir.readFileAllocOptions(ctx.arena, entry.name, max_file_size, null, 1, 0); + const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); // The manifest is the last contiguous block of comments in the file // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" @@ -720,14 +826,17 @@ pub const TestContext = struct { // Move to beginning of line while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1; - // Check if line is non-empty and does not start with "//" - if (cursor + 1 < src.len and src[cursor + 1] != '\n' and src[cursor + 1] != '\r') { - if (std.mem.startsWith(u8, src[cursor..], "//")) { - manifest_start = cursor; - } else { - break; - } - } else manifest_end = cursor; + if (std.mem.startsWith(u8, src[cursor..], "//")) { + manifest_start = cursor; // Contiguous comment line, include in manifest + } else { + if (manifest_start != null) break; // Encountered non-comment line, end of manifest + + // We ignore all-whitespace lines following the comment block, but anything else + // means that there is no manifest present. + if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) { + manifest_end = cursor; + } else break; // If it's not whitespace, there is no manifest + } // Move to previous line if (cursor != 0) cursor -= 1 else break; @@ -738,6 +847,7 @@ pub const TestContext = struct { if (manifest_start) |start| { // Due to the above processing, we know that this is a contiguous block of comments + // and do not need to re-validate the leading "//" on each line var manifest_it = std.mem.tokenize(u8, src[start..manifest_end], "\r\n"); // First line is the test case name @@ -770,7 +880,6 @@ pub const TestContext = struct { .independent => { case.name = case_name; case.addError(src, errors.items); - opt_case = null; }, .incremental => { case.addErrorNamed(case_name, src, errors.items); @@ -1292,7 +1401,7 @@ pub const TestContext = struct { } if (any_failed) { - print("\nupdate_index={d} ", .{update_index}); + print("\nupdate_index={d}\n", .{update_index}); return error.WrongCompileErrors; } }, diff --git a/src/type.zig b/src/type.zig index 735227f9d2..b087f7ab23 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1451,7 +1451,7 @@ pub const Type = extern union { var duped_names = Module.ErrorSet.NameMap{}; try duped_names.ensureTotalCapacity(allocator, names.len); for (names) |name| { - duped_names.putAssumeCapacityNoClobber(name, .{}); + duped_names.putAssumeCapacityNoClobber(name, {}); } return Tag.error_set_merged.create(allocator, duped_names); }, @@ -1520,6 +1520,13 @@ pub const Type = extern union { ) @TypeOf(writer).Error!void { _ = options; comptime assert(unused_format_string.len == 0); + if (@import("builtin").zig_backend != .stage1) { + // This is disabled to work around a stage2 bug where this function recursively + // causes more generic function instantiations resulting in an infinite loop + // in the compiler. + try writer.writeAll("[TODO fix internal compiler bug regarding dump]"); + return; + } var ty = start_type; while (true) { const t = ty.tag(); diff --git a/src/value.zig b/src/value.zig index c7960741f6..713cacb7b0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -954,6 +954,10 @@ pub const Value = extern union { assert(ty.enumFieldCount() == 1); break :blk 0; }, + .enum_literal => i: { + const name = val.castTag(.enum_literal).?.data; + break :i ty.enumFieldIndex(name).?; + }, // Assume it is already an integer and return it directly. else => return val, }; @@ -2023,6 +2027,11 @@ pub const Value = extern union { /// This function is used by hash maps and so treats floating-point NaNs as equal /// to each other, and not equal to other floating-point values. /// Similarly, it treats `undef` as a distinct value from all other values. + /// This function has to be able to support implicit coercion of `a` to `ty`. That is, + /// `ty` will be an exactly correct Type for `b` but it may be a post-coerced Type + /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication + /// is required in order to make generic function instantiation effecient - specifically + /// the insertion into the monomorphized function table. pub fn eql(a: Value, b: Value, ty: Type, target: Target) bool { const a_tag = a.tag(); const b_tag = b.tag(); @@ -2122,19 +2131,22 @@ pub const Value = extern union { const b_union = b.castTag(.@"union").?.data; switch (ty.containerLayout()) { .Packed, .Extern => { - // In this case, we must disregard mismatching tags and compare - // based on the in-memory bytes of the payloads. - @panic("TODO implement comparison of extern union values"); + const tag_ty = ty.unionTagTypeHypothetical(); + if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { + // In this case, we must disregard mismatching tags and compare + // based on the in-memory bytes of the payloads. + @panic("TODO comptime comparison of extern union values with mismatching tags"); + } }, .Auto => { const tag_ty = ty.unionTagTypeHypothetical(); if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { return false; } - const active_field_ty = ty.unionFieldType(a_union.tag, target); - return a_union.val.eql(b_union.val, active_field_ty, target); }, } + const active_field_ty = ty.unionFieldType(a_union.tag, target); + return a_union.val.eql(b_union.val, active_field_ty, target); }, else => {}, } else if (a_tag == .null_value or b_tag == .null_value) { @@ -2197,8 +2209,18 @@ pub const Value = extern union { } return order(a, b, target).compare(.eq); }, - else => return order(a, b, target).compare(.eq), + .Optional => { + if (a.tag() != .opt_payload and b.tag() == .opt_payload) { + var buffer: Payload.SubValue = .{ + .base = .{ .tag = .opt_payload }, + .data = a, + }; + return eql(Value.initPayload(&buffer.base), b, ty, target); + } + }, + else => {}, } + return order(a, b, target).compare(.eq); } /// This function is used by hash maps and so treats floating-point NaNs as equal diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 119dc289b1..27d0bbf1d6 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -118,3 +118,144 @@ test "result location of function call argument through runtime condition and st .e = if (!runtime) .a else .b, }); } + +test "function call with 40 arguments" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest(thirty_nine: i32) !void { + const result = add( + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + thirty_nine, + 40, + ); + try expect(result == 820); + try expect(thirty_nine == 39); + } + + fn add( + a0: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, + a10: i32, + a11: i32, + a12: i32, + a13: i32, + a14: i32, + a15: i32, + a16: i32, + a17: i32, + a18: i32, + a19: i32, + a20: i32, + a21: i32, + a22: i32, + a23: i32, + a24: i32, + a25: i32, + a26: i32, + a27: i32, + a28: i32, + a29: i32, + a30: i32, + a31: i32, + a32: i32, + a33: i32, + a34: i32, + a35: i32, + a36: i32, + a37: i32, + a38: i32, + a39: i32, + a40: i32, + ) i32 { + return a0 + + a1 + + a2 + + a3 + + a4 + + a5 + + a6 + + a7 + + a8 + + a9 + + a10 + + a11 + + a12 + + a13 + + a14 + + a15 + + a16 + + a17 + + a18 + + a19 + + a20 + + a21 + + a22 + + a23 + + a24 + + a25 + + a26 + + a27 + + a28 + + a29 + + a30 + + a31 + + a32 + + a33 + + a34 + + a35 + + a36 + + a37 + + a38 + + a39 + + a40; + } + }; + try S.doTheTest(39); +} diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4e952828c5..8793e7cb19 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -973,7 +973,8 @@ test "variable initialization uses result locations properly with regards to the } test "cast between C pointer with different but compatible types" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn foo(arg: [*]c_ushort) u16 { @@ -985,6 +986,7 @@ test "cast between C pointer with different but compatible types" { } }; try S.doTheTest(); + comptime try S.doTheTest(); } test "peer type resolve string lit with sentinel-terminated mutable slice" { @@ -1386,3 +1388,8 @@ test "coerce undefined single-item pointer of array to error union of slice" { const s = try b; try expect(s.len == 0); } + +test "pointer to empty struct literal to mutable slice" { + var x: []i32 = &.{}; + try expect(x.len == 0); +} diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 08f1e5bad9..2a18135fe0 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -306,3 +306,21 @@ test "anonymous struct return type referencing comptime parameter" { try expect(s.data == 1234); try expect(s.end == 5678); } + +test "generic function instantiation non-duplicates" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const S = struct { + fn copy(comptime T: type, dest: []T, source: []const T) void { + @export(foo, .{ .name = "test_generic_instantiation_non_dupe" }); + for (source) |s, i| dest[i] = s; + } + + fn foo() callconv(.C) void {} + }; + var buffer: [100]u8 = undefined; + S.copy(u8, &buffer, "hello"); + S.copy(u8, &buffer, "hello2"); +} diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 32945e452d..a41f638396 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -923,6 +923,8 @@ test "comptime float rem int" { } test "remainder division" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + comptime try remdiv(f16); comptime try remdiv(f32); comptime try remdiv(f64); @@ -938,11 +940,7 @@ fn remdiv(comptime T: type) !void { } test "float remainder division using @rem" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO comptime try frem(f16); comptime try frem(f32); @@ -973,11 +971,7 @@ fn frem(comptime T: type) !void { } test "float modulo division using @mod" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO comptime try fmod(f16); comptime try fmod(f32); diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index d9a3892664..10138cff01 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -217,3 +217,19 @@ test "implicit optional pointer to optional anyopaque pointer" { var z = @ptrCast(*[4]u8, y); try expect(std.mem.eql(u8, z, "aoeu")); } + +test "@ptrCast slice to slice" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(slice: []u32) []i32 { + return @ptrCast([]i32, slice); + } + }; + var buf: [4]u32 = .{ 0, 0, 0, 0 }; + const alias = S.foo(&buf); + alias[1] = 42; + try expect(buf[1] == 42); + try expect(alias.len == 4); +} diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b988f32a38..2d10ad0a13 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -656,3 +656,25 @@ test "switch capture copies its payload" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "capture of integer forwards the switch condition directly" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(x: u8) !void { + switch (x) { + 40...45 => |capture| { + try expect(capture == 42); + }, + else => |capture| { + try expect(capture == 100); + }, + } + } + }; + try S.foo(42); + try S.foo(100); + comptime try S.foo(42); + comptime try S.foo(100); +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 87efcbd5f1..8315ea8a22 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1168,3 +1168,18 @@ test "union with a large struct field" { var s: S = undefined; U.foo(U{ .s = s }); } + +test "comptime equality of extern unions with same tag" { + const S = struct { + const U = extern union { + a: i32, + b: f32, + }; + fn foo(comptime x: U) i32 { + return x.a; + } + }; + const a = S.U{ .a = 1234 }; + const b = S.U{ .a = 1234 }; + try expect(S.foo(a) == S.foo(b)); +} diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index ccf22a2094..aaf61745b0 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -641,7 +641,7 @@ test "vector reduce operation" { // equal. } else { const F = @TypeOf(expected); - const tolerance = @sqrt(math.epsilon(TX)); + const tolerance = @sqrt(math.floatEps(TX)); try expect(std.math.approxEqRel(F, expected, r, tolerance)); } }, diff --git a/test/cases.zig b/test/cases.zig index 942119f780..0fb6f381dd 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -16,6 +16,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("stage2/riscv64.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); + try @import("stage2/sparcv9.zig").addCases(ctx); // https://github.com/ziglang/zig/issues/10968 //try @import("stage2/nvptx.zig").addCases(ctx); } diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index d2f40d922c..84334c2a29 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -159,7 +159,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", macos_aarch64); case.addError("", &[_][]const u8{ - ":108:9: error: struct 'tmp.tmp' has no member named 'main'", + ":109:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type diff --git a/test/stage2/sparcv9.zig b/test/stage2/sparcv9.zig new file mode 100644 index 0000000000..d5611a7fae --- /dev/null +++ b/test/stage2/sparcv9.zig @@ -0,0 +1,39 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; + +const linux_sparcv9 = std.zig.CrossTarget{ + .cpu_arch = .sparcv9, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exe("sparcv9 hello world", linux_sparcv9); + // Regular old hello world + case.addCompareOutput( + \\const msg = "Hello, World!\n"; + \\ + \\pub export fn _start() noreturn { + \\ asm volatile ("ta 0x6d" + \\ : + \\ : [number] "{g1}" (4), + \\ [arg1] "{o0}" (1), + \\ [arg2] "{o1}" (@ptrToInt(msg)), + \\ [arg3] "{o2}" (msg.len) + \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + \\ ); + \\ + \\ asm volatile ("ta 0x6d" + \\ : + \\ : [number] "{g1}" (1), + \\ [arg1] "{o0}" (0) + \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + \\ ); + \\ + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } +} diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index a7ebce36d3..214b32b025 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -1925,7 +1925,7 @@ fn addLinuxTestCases(ctx: *TestContext) !void { var case = ctx.exe("hello world with updates", linux_x64); case.addError("", &[_][]const u8{ - ":108:9: error: struct 'tmp.tmp' has no member named 'main'", + ":109:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type @@ -2176,7 +2176,7 @@ fn addMacOsTestCases(ctx: *TestContext) !void { { var case = ctx.exe("darwin hello world with updates", macos_x64); case.addError("", &[_][]const u8{ - ":108:9: error: struct 'tmp.tmp' has no member named 'main'", + ":109:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type |
