diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/libc/glibc/sysdeps/arm/arm-features.h | 59 | ||||
| -rw-r--r-- | lib/std/Thread/Condition.zig | 196 | ||||
| -rw-r--r-- | lib/std/builtin.zig | 22 | ||||
| -rw-r--r-- | lib/std/crypto/25519/ed25519.zig | 1 | ||||
| -rw-r--r-- | lib/std/fs/path.zig | 390 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 4 | ||||
| -rw-r--r-- | lib/std/fs/wasi.zig | 5 | ||||
| -rw-r--r-- | lib/std/target.zig | 4 | ||||
| -rw-r--r-- | lib/zig.h | 152 |
9 files changed, 584 insertions, 249 deletions
diff --git a/lib/libc/glibc/sysdeps/arm/arm-features.h b/lib/libc/glibc/sysdeps/arm/arm-features.h new file mode 100644 index 0000000000..80a1e2272b --- /dev/null +++ b/lib/libc/glibc/sysdeps/arm/arm-features.h @@ -0,0 +1,59 @@ +/* Macros to test for CPU features on ARM. Generic ARM version. + Copyright (C) 2012-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library. If not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef _ARM_ARM_FEATURES_H +#define _ARM_ARM_FEATURES_H 1 + +/* An OS-specific arm-features.h file should define ARM_HAVE_VFP to + an appropriate expression for testing at runtime whether the VFP + hardware is present. We'll then redefine it to a constant if we + know at compile time that we can assume VFP. */ + +#ifndef __SOFTFP__ +/* The compiler is generating VFP instructions, so we're already + assuming the hardware exists. */ +# undef ARM_HAVE_VFP +# define ARM_HAVE_VFP 1 +#endif + +/* An OS-specific arm-features.h file may define ARM_ASSUME_NO_IWMMXT + to indicate at compile time that iWMMXt hardware is never present + at runtime (or that we never care about its state) and so need not + be checked for. */ + +/* A more-specific arm-features.h file may define ARM_ALWAYS_BX to indicate + that instructions using pc as a destination register must never be used, + so a "bx" (or "blx") instruction is always required. */ + +/* The log2 of the minimum alignment required for an address that + is the target of a computed branch (i.e. a "bx" instruction). + A more-specific arm-features.h file may define this to set a more + stringent requirement. + + Using this only makes sense for code in ARM mode (where instructions + always have a fixed size of four bytes), or for Thumb-mode code that is + specifically aligning all the related branch targets to match (since + Thumb instructions might be either two or four bytes). */ +#ifndef ARM_BX_ALIGN_LOG2 +# define ARM_BX_ALIGN_LOG2 2 +#endif + +/* An OS-specific arm-features.h file may define ARM_NO_INDEX_REGISTER to + indicate that the two-register addressing modes must never be used. */ + +#endif /* arm-features.h */ diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index 1482c8166d..3625aab576 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -194,42 +194,27 @@ const FutexImpl = struct { const signal_mask = 0xffff << 16; fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { - // Register that we're waiting on the state by incrementing the wait count. - // This assumes that there can be at most ((1<<16)-1) or 65,355 threads concurrently waiting on the same Condvar. - // If this is hit in practice, then this condvar not working is the least of your concerns. + // Observe the epoch, then check the state again to see if we should wake up. + // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock: + // + // - T1: s = LOAD(&state) + // - T2: UPDATE(&s, signal) + // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch) + // - T1: e = LOAD(&epoch) (was reordered after the state load) + // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change) + // + // Acquire barrier to ensure the epoch load happens before the state load. + var epoch = self.epoch.load(.Acquire); var state = self.state.fetchAdd(one_waiter, .Monotonic); assert(state & waiter_mask != waiter_mask); state += one_waiter; - // Temporarily release the mutex in order to block on the condition variable. mutex.unlock(); defer mutex.lock(); var futex_deadline = Futex.Deadline.init(timeout); - while (true) { - // Try to wake up by consuming a signal and decremented the waiter we added previously. - // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. - while (state & signal_mask != 0) { - const new_state = state - one_waiter - one_signal; - state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return; - } - - // Observe the epoch, then check the state again to see if we should wake up. - // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock: - // - // - T1: s = LOAD(&state) - // - T2: UPDATE(&s, signal) - // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch) - // - T1: e = LOAD(&epoch) (was reordered after the state load) - // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change) - // - // Acquire barrier to ensure the epoch load happens before the state load. - const epoch = self.epoch.load(.Acquire); - state = self.state.load(.Monotonic); - if (state & signal_mask != 0) { - continue; - } + while (true) { futex_deadline.wait(&self.epoch, epoch) catch |err| switch (err) { // On timeout, we must decrement the waiter we added above. error.Timeout => { @@ -247,6 +232,16 @@ const FutexImpl = struct { } }, }; + + epoch = self.epoch.load(.Acquire); + state = self.state.load(.Monotonic); + + // Try to wake up by consuming a signal and decremented the waiter we added previously. + // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. + while (state & signal_mask != 0) { + const new_state = state - one_waiter - one_signal; + state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return; + } } } @@ -536,3 +531,150 @@ test "Condition - broadcasting" { t.join(); } } + +test "Condition - broadcasting - wake all threads" { + // Tests issue #12877 + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + var num_runs: usize = 1; + const num_threads = 10; + + while (num_runs > 0) : (num_runs -= 1) { + const BroadcastTest = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + completed: Condition = .{}, + count: usize = 0, + thread_id_to_wake: usize = 0, + threads: [num_threads]std.Thread = undefined, + wakeups: usize = 0, + + fn run(self: *@This(), thread_id: usize) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + // The last broadcast thread to start tells the main test thread it's completed. + self.count += 1; + if (self.count == num_threads) { + self.completed.signal(); + } + + while (self.thread_id_to_wake != thread_id) { + self.cond.timedWait(&self.mutex, 1 * std.time.ns_per_s) catch std.debug.panic("thread_id {d} timeout {d}", .{ thread_id, self.thread_id_to_wake }); + self.wakeups += 1; + } + if (self.thread_id_to_wake <= num_threads) { + // Signal next thread to wake up. + self.thread_id_to_wake += 1; + self.cond.broadcast(); + } + } + }; + + var broadcast_test = BroadcastTest{}; + var thread_id: usize = 1; + for (broadcast_test.threads) |*t| { + t.* = try std.Thread.spawn(.{}, BroadcastTest.run, .{ &broadcast_test, thread_id }); + thread_id += 1; + } + + { + broadcast_test.mutex.lock(); + defer broadcast_test.mutex.unlock(); + + // Wait for all the broadcast threads to spawn. + // timedWait() to detect any potential deadlocks. + while (broadcast_test.count != num_threads) { + try broadcast_test.completed.timedWait( + &broadcast_test.mutex, + 1 * std.time.ns_per_s, + ); + } + + // Signal thread 1 to wake up + broadcast_test.thread_id_to_wake = 1; + broadcast_test.cond.broadcast(); + } + + for (broadcast_test.threads) |t| { + t.join(); + } + } +} + +test "Condition - signal wakes one" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + var num_runs: usize = 1; + const num_threads = 3; + const timeoutDelay = 10 * std.time.ns_per_ms; + + while (num_runs > 0) : (num_runs -= 1) { + + // Start multiple runner threads, wait for them to start and send the signal + // then. Expect that one thread wake up and all other times out. + // + // Test depends on delay in timedWait! If too small all threads can timeout + // before any one gets wake up. + + const Runner = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + completed: Condition = .{}, + count: usize = 0, + threads: [num_threads]std.Thread = undefined, + wakeups: usize = 0, + timeouts: usize = 0, + + fn run(self: *@This()) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + // The last started thread tells the main test thread it's completed. + self.count += 1; + if (self.count == num_threads) { + self.completed.signal(); + } + + self.cond.timedWait(&self.mutex, timeoutDelay) catch { + self.timeouts += 1; + return; + }; + self.wakeups += 1; + } + }; + + // Start threads + var runner = Runner{}; + for (runner.threads) |*t| { + t.* = try std.Thread.spawn(.{}, Runner.run, .{&runner}); + } + + { + runner.mutex.lock(); + defer runner.mutex.unlock(); + + // Wait for all the threads to spawn. + // timedWait() to detect any potential deadlocks. + while (runner.count != num_threads) { + try runner.completed.timedWait(&runner.mutex, 1 * std.time.ns_per_s); + } + // Signal one thread, the others should get timeout. + runner.cond.signal(); + } + + for (runner.threads) |t| { + t.join(); + } + + // Expect that only one got singal + try std.testing.expectEqual(runner.wakeups, 1); + try std.testing.expectEqual(runner.timeouts, num_threads - 1); + } +} diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 33604dfee7..1f7d48ccb9 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -168,7 +168,7 @@ pub const AddressSpace = enum { gs, fs, ss, - // GPU address spaces + // GPU address spaces. global, constant, param, @@ -869,6 +869,26 @@ pub noinline fn returnError(st: *StackTrace) void { addErrRetTraceAddr(st, @returnAddress()); } +pub const panic_messages = struct { + pub const unreach = "reached unreachable code"; + pub const unwrap_null = "attempt to use null value"; + pub const cast_to_null = "cast causes pointer to be null"; + pub const incorrect_alignment = "incorrect alignment"; + pub const invalid_error_code = "invalid error code"; + pub const cast_truncated_data = "integer cast truncated bits"; + pub const negative_to_unsigned = "attempt to cast negative value to unsigned integer"; + pub const integer_overflow = "integer overflow"; + pub const shl_overflow = "left shift overflowed bits"; + pub const shr_overflow = "right shift overflowed bits"; + pub const divide_by_zero = "division by zero"; + pub const exact_division_remainder = "exact division produced remainder"; + pub const inactive_union_field = "access of inactive union field"; + pub const integer_part_out_of_bounds = "integer part of floating point value out of bounds"; + pub const corrupt_switch = "switch on corrupt value"; + pub const shift_rhs_too_big = "shift amount is greater than the type size"; + pub const invalid_enum_value = "invalid enum value"; +}; + pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void { if (st.index < st.instruction_addresses.len) st.instruction_addresses[st.index] = addr; diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 552bd114a1..149191040a 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -318,6 +318,7 @@ pub const Ed25519 = struct { h.update(&scalar_and_prefix.prefix); var noise2: [noise_length]u8 = undefined; crypto.random.bytes(&noise2); + h.update(&noise2); if (noise) |*z| { h.update(z); } diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 0d102493cf..feacf38daf 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 { /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. /// Note: all usage of this function should be audited due to the existence of symlinks. /// Without performing actual syscalls, resolving `..` could be incorrect. +/// This API may break in the future: https://github.com/ziglang/zig/issues/13613 pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { - if (paths.len == 0) { - assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd - return process.getCwdAlloc(allocator); - } + assert(paths.len > 0); // determine which disk designator we will result with, if any var result_drive_buf = "_:".*; - var result_disk_designator: []const u8 = ""; - var have_drive_kind = WindowsPath.Kind.None; + var disk_designator: []const u8 = ""; + var drive_kind = WindowsPath.Kind.None; var have_abs_path = false; var first_index: usize = 0; - var max_size: usize = 0; for (paths) |p, i| { const parsed = windowsParsePath(p); if (parsed.is_abs) { have_abs_path = true; first_index = i; - max_size = result_disk_designator.len; } switch (parsed.kind) { - WindowsPath.Kind.Drive => { + .Drive => { result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]); - result_disk_designator = result_drive_buf[0..]; - have_drive_kind = WindowsPath.Kind.Drive; + disk_designator = result_drive_buf[0..]; + drive_kind = WindowsPath.Kind.Drive; }, - WindowsPath.Kind.NetworkShare => { - result_disk_designator = parsed.disk_designator; - have_drive_kind = WindowsPath.Kind.NetworkShare; + .NetworkShare => { + disk_designator = parsed.disk_designator; + drive_kind = WindowsPath.Kind.NetworkShare; }, - WindowsPath.Kind.None => {}, + .None => {}, } - max_size += p.len + 1; } // if we will result with a disk designator, loop again to determine // which is the last time the disk designator is absolutely specified, if any // and count up the max bytes for paths related to this disk designator - if (have_drive_kind != WindowsPath.Kind.None) { + if (drive_kind != WindowsPath.Kind.None) { have_abs_path = false; first_index = 0; - max_size = result_disk_designator.len; var correct_disk_designator = false; for (paths) |p, i| { const parsed = windowsParsePath(p); if (parsed.kind != WindowsPath.Kind.None) { - if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); + if (parsed.kind == drive_kind) { + correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator); } else { continue; } @@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { } if (parsed.is_abs) { first_index = i; - max_size = result_disk_designator.len; have_abs_path = true; } - max_size += p.len + 1; } } - // Allocate result and fill in the disk designator, calling getCwd if we have to. - var result: []u8 = undefined; - var result_index: usize = 0; - - if (have_abs_path) { - switch (have_drive_kind) { - WindowsPath.Kind.Drive => { - result = try allocator.alloc(u8, max_size); + // Allocate result and fill in the disk designator. + var result = std.ArrayList(u8).init(allocator); + defer result.deinit(); - mem.copy(u8, result, result_disk_designator); - result_index += result_disk_designator.len; + const disk_designator_len: usize = l: { + if (!have_abs_path) break :l 0; + switch (drive_kind) { + .Drive => { + try result.appendSlice(disk_designator); + break :l disk_designator.len; }, - WindowsPath.Kind.NetworkShare => { - result = try allocator.alloc(u8, max_size); + .NetworkShare => { var it = mem.tokenize(u8, paths[first_index], "/\\"); const server_name = it.next().?; const other_name = it.next().?; - result[result_index] = '\\'; - result_index += 1; - result[result_index] = '\\'; - result_index += 1; - mem.copy(u8, result[result_index..], server_name); - result_index += server_name.len; - result[result_index] = '\\'; - result_index += 1; - mem.copy(u8, result[result_index..], other_name); - result_index += other_name.len; - - result_disk_designator = result[0..result_index]; + try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len); + result.appendSliceAssumeCapacity("\\\\"); + result.appendSliceAssumeCapacity(server_name); + result.appendAssumeCapacity('\\'); + result.appendSliceAssumeCapacity(other_name); + + break :l result.items.len; }, - WindowsPath.Kind.None => { - assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd - const cwd = try process.getCwdAlloc(allocator); - defer allocator.free(cwd); - const parsed_cwd = windowsParsePath(cwd); - result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1); - mem.copy(u8, result, parsed_cwd.disk_designator); - result_index += parsed_cwd.disk_designator.len; - result_disk_designator = result[0..parsed_cwd.disk_designator.len]; - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - result[0] = ascii.toUpper(result[0]); - } - have_drive_kind = parsed_cwd.kind; + .None => { + break :l 1; }, } - } else { - assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd - // TODO call get cwd for the result_disk_designator instead of the global one - const cwd = try process.getCwdAlloc(allocator); - defer allocator.free(cwd); - - result = try allocator.alloc(u8, max_size + cwd.len + 1); - - mem.copy(u8, result, cwd); - result_index += cwd.len; - const parsed_cwd = windowsParsePath(result[0..result_index]); - result_disk_designator = parsed_cwd.disk_designator; - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - result[0] = ascii.toUpper(result[0]); - // Remove the trailing slash if present, eg. if the cwd is a root - // directory. - if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) { - result_index -= 1; - } - } - have_drive_kind = parsed_cwd.kind; - } - errdefer allocator.free(result); + }; - // Now we know the disk designator to use, if any, and what kind it is. And our result - // is big enough to append all the paths to. var correct_disk_designator = true; + var negative_count: usize = 0; + for (paths[first_index..]) |p| { const parsed = windowsParsePath(p); - if (parsed.kind != WindowsPath.Kind.None) { - if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); + if (parsed.kind != .None) { + if (parsed.kind == drive_kind) { + const dd = result.items[0..disk_designator_len]; + correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator); } else { continue; } @@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { continue; } var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\"); - while (it.next()) |component| { + component: while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; } else if (mem.eql(u8, component, "..")) { while (true) { - if (result_index == 0 or result_index == result_disk_designator.len) - break; - result_index -= 1; - if (result[result_index] == '\\' or result[result_index] == '/') + if (result.items.len == 0) { + negative_count += 1; + continue :component; + } + if (result.items.len == disk_designator_len) { break; + } + const end_with_sep = switch (result.items[result.items.len - 1]) { + '\\', '/' => true, + else => false, + }; + result.items.len -= 1; + if (end_with_sep) break; } + } else if (!have_abs_path and result.items.len == 0) { + try result.appendSlice(component); } else { - result[result_index] = sep_windows; - result_index += 1; - mem.copy(u8, result[result_index..], component); - result_index += component.len; + try result.ensureUnusedCapacity(1 + component.len); + result.appendAssumeCapacity('\\'); + result.appendSliceAssumeCapacity(component); } } } - if (result_index == result_disk_designator.len) { - result[result_index] = '\\'; - result_index += 1; + if (disk_designator_len != 0 and result.items.len == disk_designator_len) { + try result.append('\\'); + return result.toOwnedSlice(); + } + + if (result.items.len == 0) { + if (negative_count == 0) { + return allocator.dupe(u8, "."); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count - 1); + var count = negative_count - 1; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "..\\".*; + i += 3; + } + real_result[i..][0..2].* = "..".*; + return real_result; + } } - return allocator.shrink(result, result_index); + if (negative_count == 0) { + return result.toOwnedSlice(); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len); + var count = negative_count; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "..\\".*; + i += 3; + } + mem.copy(u8, real_result[i..], result.items); + return real_result; + } } /// This function is like a series of `cd` statements executed one after another. /// It resolves "." and "..". /// The result does not have a trailing path separator. -/// If all paths are relative it uses the current working directory as a starting point. -/// Note: all usage of this function should be audited due to the existence of symlinks. -/// Without performing actual syscalls, resolving `..` could be incorrect. -pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 { - if (paths.len == 0) { - assert(native_os != .windows); // resolvePosix called on windows can't use getCwd - return process.getCwdAlloc(allocator); - } +/// This function does not perform any syscalls. Executing this series of path +/// lookups on the actual filesystem may produce different results due to +/// symlinks. +pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) Allocator.Error![]u8 { + assert(paths.len > 0); - var first_index: usize = 0; - var have_abs = false; - var max_size: usize = 0; - for (paths) |p, i| { - if (isAbsolutePosix(p)) { - first_index = i; - have_abs = true; - max_size = 0; - } - max_size += p.len + 1; - } - - var result: []u8 = undefined; - var result_index: usize = 0; + var result = std.ArrayList(u8).init(allocator); + defer result.deinit(); - if (have_abs) { - result = try allocator.alloc(u8, max_size); - } else { - assert(native_os != .windows); // resolvePosix called on windows can't use getCwd - const cwd = try process.getCwdAlloc(allocator); - defer allocator.free(cwd); - result = try allocator.alloc(u8, max_size + cwd.len + 1); - mem.copy(u8, result, cwd); - result_index += cwd.len; - } - errdefer allocator.free(result); + var negative_count: usize = 0; + var is_abs = false; - for (paths[first_index..]) |p| { + for (paths) |p| { + if (isAbsolutePosix(p)) { + is_abs = true; + negative_count = 0; + result.clearRetainingCapacity(); + } var it = mem.tokenize(u8, p, "/"); - while (it.next()) |component| { + component: while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; } else if (mem.eql(u8, component, "..")) { while (true) { - if (result_index == 0) - break; - result_index -= 1; - if (result[result_index] == '/') - break; + if (result.items.len == 0) { + negative_count += @boolToInt(!is_abs); + continue :component; + } + const ends_with_slash = result.items[result.items.len - 1] == '/'; + result.items.len -= 1; + if (ends_with_slash) break; } + } else if (result.items.len > 0 or is_abs) { + try result.ensureUnusedCapacity(1 + component.len); + result.appendAssumeCapacity('/'); + result.appendSliceAssumeCapacity(component); } else { - result[result_index] = '/'; - result_index += 1; - mem.copy(u8, result[result_index..], component); - result_index += component.len; + try result.appendSlice(component); } } } - if (result_index == 0) { - result[0] = '/'; - result_index += 1; + if (result.items.len == 0) { + if (is_abs) { + return allocator.dupe(u8, "/"); + } + if (negative_count == 0) { + return allocator.dupe(u8, "."); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count - 1); + var count = negative_count - 1; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "../".*; + i += 3; + } + real_result[i..][0..2].* = "..".*; + return real_result; + } } - return allocator.shrink(result, result_index); + if (negative_count == 0) { + return result.toOwnedSlice(); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len); + var count = negative_count; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "../".*; + i += 3; + } + mem.copy(u8, real_result[i..], result.items); + return real_result; + } } test "resolve" { - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); + try testResolveWindows(&[_][]const u8{ "a\\b\\c\\", "..\\..\\.." }, ".."); + try testResolveWindows(&[_][]const u8{"."}, "."); - const cwd = try process.getCwdAlloc(testing.allocator); - defer testing.allocator.free(cwd); - if (native_os == .windows) { - if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { - cwd[0] = ascii.toUpper(cwd[0]); - } - try testResolveWindows(&[_][]const u8{"."}, cwd); - } else { - try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, cwd); - try testResolvePosix(&[_][]const u8{"."}, cwd); - } + try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, ".."); + try testResolvePosix(&[_][]const u8{"."}, "."); } test "resolveWindows" { - if (builtin.target.cpu.arch == .aarch64) { - // TODO https://github.com/ziglang/zig/issues/3288 - return error.SkipZigTest; - } - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); - if (native_os == .windows) { - const cwd = try process.getCwdAlloc(testing.allocator); - defer testing.allocator.free(cwd); - const parsed_cwd = windowsParsePath(cwd); - { - const expected = try join(testing.allocator, &[_][]const u8{ - parsed_cwd.disk_designator, - "usr\\local\\lib\\zig\\std\\array_list.zig", - }); - defer testing.allocator.free(expected); - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]); - } - try testResolveWindows(&[_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }, expected); - } - { - const expected = try join(testing.allocator, &[_][]const u8{ - cwd, - "usr\\local\\lib\\zig", - }); - defer testing.allocator.free(expected); - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]); - } - try testResolveWindows(&[_][]const u8{ "usr/local", "lib\\zig" }, expected); - } - } + try testResolveWindows( + &[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" }, + "Z:\\usr\\local\\lib\\zig\\std\\array_list.zig", + ); + try testResolveWindows( + &[_][]const u8{ "z:\\", "usr/local", "lib\\zig" }, + "Z:\\usr\\local\\lib\\zig", + ); try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok"); try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a"); @@ -781,12 +747,12 @@ test "resolveWindows" { try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\"); try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir"); try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js"); + + // Keep relative paths relative. + try testResolveWindows(&[_][]const u8{"a/b"}, "a\\b"); } test "resolvePosix" { - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); - try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a"); @@ -797,18 +763,21 @@ test "resolvePosix" { try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file"); try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute"); try testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }, "/foo/tmp.3/cycles/root.js"); + + // Keep relative paths relative. + try testResolvePosix(&[_][]const u8{"a/b"}, "a/b"); } fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void { const actual = try resolveWindows(testing.allocator, paths); defer testing.allocator.free(actual); - try testing.expect(mem.eql(u8, actual, expected)); + try testing.expectEqualStrings(expected, actual); } fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void { const actual = try resolvePosix(testing.allocator, paths); defer testing.allocator.free(actual); - try testing.expect(mem.eql(u8, actual, expected)); + try testing.expectEqualStrings(expected, actual); } /// Strip the last component from a file path. @@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! if (parsed_from.kind != parsed_to.kind) { break :x true; } else switch (parsed_from.kind) { - WindowsPath.Kind.NetworkShare => { + .NetworkShare => { break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator); }, - WindowsPath.Kind.Drive => { + .Drive => { break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]); }, - else => unreachable, + .None => { + break :x false; + }, } }; @@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] } test "relative" { - if (builtin.target.cpu.arch == .aarch64) { - // TODO https://github.com/ziglang/zig/issues/3288 - return error.SkipZigTest; - } - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); - try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); @@ -1226,6 +1190,10 @@ test "relative" { try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); + try testRelativeWindows("a/b/c", "a\\b", ".."); + try testRelativeWindows("a/b/c", "a", "..\\.."); + try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); + try testRelativePosix("/var/lib", "/var", ".."); try testRelativePosix("/var/lib", "/bin", "../../bin"); try testRelativePosix("/var/lib", "/var/lib", ""); @@ -1243,13 +1211,13 @@ test "relative" { fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { const result = try relativePosix(testing.allocator, from, to); defer testing.allocator.free(result); - try testing.expectEqualSlices(u8, expected_output, result); + try testing.expectEqualStrings(expected_output, result); } fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { const result = try relativeWindows(testing.allocator, from, to); defer testing.allocator.free(result); - try testing.expectEqualSlices(u8, expected_output, result); + try testing.expectEqualStrings(expected_output, result); } /// Returns the extension of the file name (if any). diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index f6168054b6..00e42b6417 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1095,7 +1095,9 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { const allocator = testing.allocator; - const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"}; + const cwd = try std.process.getCwdAlloc(allocator); + defer allocator.free(cwd); + const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" }; const filename = try fs.path.resolve(allocator, &file_paths); defer allocator.free(filename); diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index 2051215dfe..6358873ede 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -202,10 +202,7 @@ pub const PreopenList = struct { // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "." const path = if (cwd_root) |cwd| blk: { const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path }; - break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) { - error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried - else => |e| return e, - }; + break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths); } else blk: { // If we were provided no CWD root, we preserve the preopen dir without resolving break :blk try self.buffer.allocator.dupe(u8, raw_path); diff --git a/lib/std/target.zig b/lib/std/target.zig index 745be7dbff..49a7bd1c7d 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1179,10 +1179,12 @@ pub const Target = struct { /// Returns whether this architecture supports the address space pub fn supportsAddressSpace(arch: Arch, address_space: std.builtin.AddressSpace) bool { const is_nvptx = arch == .nvptx or arch == .nvptx64; + const is_spirv = arch == .spirv32 or arch == .spirv64; + const is_gpu = is_nvptx or is_spirv or arch == .amdgcn; return switch (address_space) { .generic => true, .fs, .gs, .ss => arch == .x86_64 or arch == .x86, - .global, .constant, .local, .shared => arch == .amdgcn or is_nvptx, + .global, .constant, .local, .shared => is_gpu, .param => is_nvptx, }; } @@ -344,6 +344,12 @@ static inline zig_bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_ #endif } +static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, + const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); +} + zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); static inline zig_bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) @@ -358,6 +364,12 @@ static inline zig_bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_ return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); } +static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, + const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) zig_u64 full_res; @@ -370,6 +382,12 @@ static inline zig_bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_ #endif } +static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, + const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); +} + zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); static inline zig_bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) @@ -384,6 +402,12 @@ static inline zig_bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_ return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); } +static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, + const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) zig_u8 full_res; @@ -395,6 +419,12 @@ static inline zig_bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 b #endif } +static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, + const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) zig_i8 full_res; @@ -406,6 +436,12 @@ static inline zig_bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 b #endif } +static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, + const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) zig_u16 full_res; @@ -417,6 +453,12 @@ static inline zig_bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_ #endif } +static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, + const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { #if zig_has_builtin(add_overflow) zig_i16 full_res; @@ -428,6 +470,12 @@ static inline zig_bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_ #endif } +static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, + const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) zig_u32 full_res; @@ -440,6 +488,12 @@ static inline zig_bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_ #endif } +static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, + const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); +} + zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); static inline zig_bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) @@ -454,6 +508,12 @@ static inline zig_bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_ return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); } +static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, + const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) zig_u64 full_res; @@ -466,6 +526,12 @@ static inline zig_bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_ #endif } +static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, + const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); +} + zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); static inline zig_bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) @@ -480,6 +546,12 @@ static inline zig_bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_ return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); } +static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, + const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) zig_u8 full_res; @@ -491,6 +563,12 @@ static inline zig_bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 b #endif } +static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, + const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) zig_i8 full_res; @@ -502,6 +580,13 @@ static inline zig_bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 b #endif } +static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, + const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); +} + + static inline zig_bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) zig_u16 full_res; @@ -513,6 +598,13 @@ static inline zig_bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_ #endif } +static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, + const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); +} + + static inline zig_bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { #if zig_has_builtin(sub_overflow) zig_i16 full_res; @@ -524,6 +616,12 @@ static inline zig_bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_ #endif } +static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, + const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) zig_u32 full_res; @@ -536,6 +634,12 @@ static inline zig_bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_ #endif } +static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, + const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); +} + zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); static inline zig_bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) @@ -550,6 +654,12 @@ static inline zig_bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_ return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); } +static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, + const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) zig_u64 full_res; @@ -562,6 +672,12 @@ static inline zig_bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_ #endif } +static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, + const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); +} + zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); static inline zig_bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) @@ -576,6 +692,12 @@ static inline zig_bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_ return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); } +static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, + const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) zig_u8 full_res; @@ -587,6 +709,12 @@ static inline zig_bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 b #endif } +static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, + const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) zig_i8 full_res; @@ -598,6 +726,12 @@ static inline zig_bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 b #endif } +static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, + const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) zig_u16 full_res; @@ -609,6 +743,12 @@ static inline zig_bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_ #endif } +static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, + const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); +} + static inline zig_bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { #if zig_has_builtin(mul_overflow) zig_i16 full_res; @@ -620,6 +760,12 @@ static inline zig_bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_ #endif } +static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, + const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); +} + #define zig_int_builtins(w) \ static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ @@ -846,10 +992,8 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { full_res = __builtin_bitreverse8(val); #else static zig_u8 const lut[0x10] = { - 0b0000, 0b1000, 0b0100, 0b1100, - 0b0010, 0b1010, 0b0110, 0b1110, - 0b0001, 0b1001, 0b0101, 0b1101, - 0b0011, 0b1011, 0b0111, 0b1111, + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; full_res = lut[val >> 0 & 0xF] << 4 | lut[val >> 4 & 0xF] << 0; #endif |
