Compare commits

...

29 commits

Author SHA1 Message Date
Jay Petacat
a17657f881
Sema: Allow small integer types to coerce to floats
If the float can store all possible values of the integer without
rounding, coercion is allowed. The integer's precision must be less than
or equal to the float's significand precision.

Closes #18614
2025-12-04 01:03:12 -07:00
jedisct1
d73fbcc3ae Merge pull request 'Argon2: use the std.Io interface' (#30084) from jedisct1/zig:argon2 into master
Some checks failed
ci / aarch64-linux-debug (push) Has been cancelled
ci / aarch64-linux-release (push) Has been cancelled
ci / aarch64-macos-debug (push) Has been cancelled
ci / aarch64-macos-release (push) Has been cancelled
ci / loongarch64-linux-debug (push) Has been cancelled
ci / loongarch64-linux-release (push) Has been cancelled
ci / riscv64-linux-debug (push) Has been cancelled
ci / riscv64-linux-release (push) Has been cancelled
ci / s390x-linux-debug (push) Has been cancelled
ci / s390x-linux-release (push) Has been cancelled
ci / x86_64-freebsd-debug (push) Has been cancelled
ci / x86_64-freebsd-release (push) Has been cancelled
ci / x86_64-linux-debug (push) Has been cancelled
ci / x86_64-linux-debug-llvm (push) Has been cancelled
ci / x86_64-linux-release (push) Has been cancelled
ci / x86_64-windows-debug (push) Has been cancelled
ci / x86_64-windows-release (push) Has been cancelled
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30084
2025-12-03 12:18:09 +01:00
Zihad
cb115cf73a std.process.ArgIteratorWasi: fix no-args deinit 2025-12-03 08:35:24 +01:00
Alex Rønne Petersen
be9649f4ea
ci: set a sensible maxrss in x86_64-windows scripts
Some checks are pending
ci / aarch64-linux-debug (push) Waiting to run
ci / aarch64-linux-release (push) Waiting to run
ci / aarch64-macos-debug (push) Waiting to run
ci / aarch64-macos-release (push) Waiting to run
ci / loongarch64-linux-debug (push) Waiting to run
ci / loongarch64-linux-release (push) Waiting to run
ci / riscv64-linux-debug (push) Waiting to run
ci / riscv64-linux-release (push) Waiting to run
ci / s390x-linux-debug (push) Waiting to run
ci / s390x-linux-release (push) Waiting to run
ci / x86_64-freebsd-debug (push) Waiting to run
ci / x86_64-freebsd-release (push) Waiting to run
ci / x86_64-linux-debug (push) Waiting to run
ci / x86_64-linux-debug-llvm (push) Waiting to run
ci / x86_64-linux-release (push) Waiting to run
ci / x86_64-windows-debug (push) Waiting to run
ci / x86_64-windows-release (push) Waiting to run
2025-12-03 00:24:58 +01:00
Frank Denis
6fe95c28cf Argon2: use the std.Io interface
Also reduce the memory required by tests.

4GB for every test is way too much and doesn't provide much benefits
in testing the algorithms.
2025-12-02 23:03:52 +01:00
Andrew Kelley
52ad126bb4 Merge pull request 'std.Io.Threaded: rework cancellation' (#30033) from cancellation into master
Some checks are pending
ci / aarch64-linux-debug (push) Waiting to run
ci / aarch64-linux-release (push) Waiting to run
ci / aarch64-macos-debug (push) Waiting to run
ci / aarch64-macos-release (push) Waiting to run
ci / loongarch64-linux-debug (push) Waiting to run
ci / loongarch64-linux-release (push) Waiting to run
ci / riscv64-linux-debug (push) Waiting to run
ci / riscv64-linux-release (push) Waiting to run
ci / s390x-linux-debug (push) Waiting to run
ci / s390x-linux-release (push) Waiting to run
ci / x86_64-freebsd-debug (push) Waiting to run
ci / x86_64-freebsd-release (push) Waiting to run
ci / x86_64-linux-debug (push) Waiting to run
ci / x86_64-linux-debug-llvm (push) Waiting to run
ci / x86_64-linux-release (push) Waiting to run
ci / x86_64-windows-debug (push) Waiting to run
ci / x86_64-windows-release (push) Waiting to run
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30033
2025-12-02 17:58:29 +01:00
Andrew Kelley
bb3f56d5d5 std.Io.Threaded: separate out ECANCELED handling again
If ECANCELED occurs, it's from pthread_cancel which will *permanently*
set that thread to be in a "canceling" state, which means the cancel
cannot be ignored. That means it cannot be retried, like EINTR. It must
be acknowledged.
2025-12-01 19:17:52 -08:00
Andrew Kelley
cf82064ebc std.Io.Threaded: don't use pthread_cancel with musl
It doesn't support setting the "canceled" status to false, so once a
thread has been canceled, all operations on the thread start permanently
failing.
2025-12-01 19:17:52 -08:00
Andrew Kelley
bf0ffc45b9 std.Io.Threaded: musl: handle ECANCELED same as EINTR
Otherwise the pthread_cancel can affect unrelated tasks.
2025-12-01 19:17:52 -08:00
Andrew Kelley
54a84964f8 std.os.linux: SIG enum is non-exhaustive 2025-12-01 19:17:52 -08:00
Andrew Kelley
57f5de5b77 std.Io.Threaded: use the correct mmsghdr struct 2025-12-01 19:17:52 -08:00
Andrew Kelley
103467fa6c std.Io.Threaded: make is_musl linux-only 2025-12-01 19:17:52 -08:00
David Rubin
85053a6a36 link.Elf: implement aarch64 relocation 2025-12-01 19:17:52 -08:00
Andrew Kelley
c4f5dda135 std.Io.Threaded: re-introduce retry logic behind config 2025-12-01 19:17:52 -08:00
Andrew Kelley
de87bad4c3 std.Io.Threaded: don't solve the cancel race after all
Unfortunately, trying again until the cancellation request is
acknowledged has been observed to incur a large amount of overhead,
and usually strong cancellation guarantees are not needed, so the
race condition is not handled here. Users who want to avoid this
have this menu of options instead:
* Use no libc, in which case Zig std lib can avoid the race (tracking
  issue: https://codeberg.org/ziglang/zig/issues/30049)
* Use musl libc
* Use `std.Io.Evented`. But this is not implemented yet. Tracked by
  - https://codeberg.org/ziglang/zig/issues/30050
  - https://codeberg.org/ziglang/zig/issues/30051

glibc + threaded is the only problematic combination.
2025-12-01 19:17:52 -08:00
Andrew Kelley
144206856e std.Io.Threaded: fix compilation for riscv32-linux 2025-12-01 19:17:52 -08:00
Andrew Kelley
9e981c3ae5 std.os.linux: delete unnecessary @compileError
Without this, it already fails to compile with a sufficiently helpful
error message.
2025-12-01 19:17:52 -08:00
Andrew Kelley
39ac40209b std.Io.Threaded: use musl's beautiful pthread_cancel semantics 2025-12-01 19:17:52 -08:00
Andrew Kelley
d60760d61e std.Io.Threaded: tune requestCancel
On a heavily loaded Linux 6.17.5, I observed a maximum of 20 attempts
not acknowledged before the timeout (including exponential backoff) was
sufficient, despite the heavy load.

The time wasted here sleeping is mitigated by the fact that, later on,
the system will likely wait for the canceled task, causing it to
indefinitely yield until the canceled task finishes, and the task must
acknowledge the cancel before it proceeds to that point.
2025-12-01 19:17:52 -08:00
Andrew Kelley
29e418cbfb std.Io.Threaded: fix the cancellation race
Now, before a syscall is entered, beginSyscall is called, which may
return error.Canceled. After syscall returns, whether error or success,
endSyscall is called. If the syscall returns EINTR then checkCancel is
called.

`cancelRequested` is removed from the std.Io VTable for now, with plans
to replace it with a more powerful API that allows protection against
cancellation requests.

closes #25751
2025-12-01 19:17:52 -08:00
rpkak
95f93a0b28 std.zig.Ast: count addrspace tokens correctly
Some checks are pending
ci / aarch64-linux-debug (push) Waiting to run
ci / aarch64-linux-release (push) Waiting to run
ci / aarch64-macos-debug (push) Waiting to run
ci / aarch64-macos-release (push) Waiting to run
ci / loongarch64-linux-debug (push) Waiting to run
ci / loongarch64-linux-release (push) Waiting to run
ci / riscv64-linux-debug (push) Waiting to run
ci / riscv64-linux-release (push) Waiting to run
ci / s390x-linux-debug (push) Waiting to run
ci / s390x-linux-release (push) Waiting to run
ci / x86_64-freebsd-debug (push) Waiting to run
ci / x86_64-freebsd-release (push) Waiting to run
ci / x86_64-linux-debug (push) Waiting to run
ci / x86_64-linux-debug-llvm (push) Waiting to run
ci / x86_64-linux-release (push) Waiting to run
ci / x86_64-windows-debug (push) Waiting to run
ci / x86_64-windows-release (push) Waiting to run
Before this PR this

```zig
const namespace = struct {
    extern const num: u8 addrspace(.generic);
};
// comment
```

got formatted to this

```zig
const namespace = struct {
    extern const num: u8 addrspace(.generic);
(
```

Co-authored-by: rpkak <rpkak@noreply.codeberg.org>
Co-committed-by: rpkak <rpkak@noreply.codeberg.org>
2025-12-01 12:56:11 +01:00
jedisct1
1d1e2b7780 Merge pull request 'std.crypto.aes: expose the inverse MixColumns operation' (#30052) from jedisct1/zig:invmixcolumns into master
Some checks are pending
ci / aarch64-linux-debug (push) Waiting to run
ci / aarch64-linux-release (push) Waiting to run
ci / aarch64-macos-debug (push) Waiting to run
ci / aarch64-macos-release (push) Waiting to run
ci / loongarch64-linux-debug (push) Waiting to run
ci / loongarch64-linux-release (push) Waiting to run
ci / riscv64-linux-debug (push) Waiting to run
ci / riscv64-linux-release (push) Waiting to run
ci / s390x-linux-debug (push) Waiting to run
ci / s390x-linux-release (push) Waiting to run
ci / x86_64-freebsd-debug (push) Waiting to run
ci / x86_64-freebsd-release (push) Waiting to run
ci / x86_64-linux-debug (push) Waiting to run
ci / x86_64-linux-debug-llvm (push) Waiting to run
ci / x86_64-linux-release (push) Waiting to run
ci / x86_64-windows-debug (push) Waiting to run
ci / x86_64-windows-release (push) Waiting to run
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30052
2025-12-01 11:16:26 +01:00
fn ⌃ ⌥
bfe3317059 Return a usize from @abs if given an isize
Also:
- `c_ushort` for `c_short`
- `c_uint` for `c_int`
- `c_ulong` for `c_long`
- `c_ulonglong` for `c_longlong`
2025-11-29 21:09:08 +01:00
mlugg
44e99edd7a Merge pull request 'Sema: initialize OPV comptime allocs correctly' (#30043) from reify-empty-struct into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30043
2025-11-29 20:21:30 +01:00
Nikolay Govorov
a0289d0cce std.posix.accept: handle non-listening socket EINVAL 2025-11-29 19:57:59 +01:00
Frank Denis
5e00a0c9b5 std.crypto.aes: expose the inverse MixColumns operation
The inverse MixColumns operation is already used internally for
AES decryption, but it wasn’t exposed in the public API because
it didn’t seem necessary at the time.

Since then, several new AES-based block ciphers and permutations
(such as Vistrutah and Areion) have been developed, and they require
this operation to be implementable in Zig.
Since then, new interesting AES-based block ciphers and permutations
(Vistrutah, Areion, etc). have been invented, and require that
operation to be implementable in Zig.
2025-11-29 19:25:22 +01:00
Alex Rønne Petersen
7d9ad992ab
issue templates: update issue labels to match the actual org labels 2025-11-29 19:02:04 +01:00
Matthew Lugg
8f5db19791
Sema: initialize OPV comptime allocs correctly
This was caused a `[0]std.builtin.Type.StructField.Attributes` to be
considered `undefined`, even though that type is OPV so should prefer
its OPV `.{}` over `undefined`.

Resolves: #30039
2025-11-29 11:55:36 +00:00
Matthew Lugg
e52232cd57
print_zir: fix typo 2025-11-29 11:55:26 +00:00
31 changed files with 2488 additions and 1307 deletions

View file

@ -1,6 +1,6 @@
name: Bug Report name: Bug Report
description: File a bug report description: File a bug report
labels: ["kind/bug"] labels: ["bug"]
body: body:
- type: markdown - type: markdown
attributes: attributes:

View file

@ -1,6 +1,6 @@
name: Error message improvement name: Error message improvement
description: Compiler produces an unhelpful or misleading error message. description: Compiler produces an unhelpful or misleading error message.
labels: ["kind/error message"] labels: ["error message"]
body: body:
- type: input - type: input
id: version id: version

View file

@ -54,6 +54,7 @@ CheckLastExitCode
Write-Output "Main test suite..." Write-Output "Main test suite..."
& "stage3-debug\bin\zig.exe" build test docs ` & "stage3-debug\bin\zig.exe" build test docs `
--maxrss 32212254720 `
--zig-lib-dir "$ZIG_LIB_DIR" ` --zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" ` --search-prefix "$PREFIX_PATH" `
-Dstatic-llvm ` -Dstatic-llvm `

View file

@ -54,6 +54,7 @@ CheckLastExitCode
Write-Output "Main test suite..." Write-Output "Main test suite..."
& "stage3-release\bin\zig.exe" build test docs ` & "stage3-release\bin\zig.exe" build test docs `
--maxrss 32212254720 `
--zig-lib-dir "$ZIG_LIB_DIR" ` --zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" ` --search-prefix "$PREFIX_PATH" `
-Dstatic-llvm ` -Dstatic-llvm `

View file

@ -3449,6 +3449,16 @@ void do_a_thing(struct Foo *foo) {
</p> </p>
{#code|test_integer_widening.zig#} {#code|test_integer_widening.zig#}
{#header_close#}
{#header_open|Type Coercion: Int to Float#}
<p>
{#link|Integers#} coerce to {#link|Floats#} if every possible integer value can be stored in the float
without rounding (i.e. the integer's precision does not exceed the float's significand precision).
Larger integer types that cannot be safely coerced must be explicitly casted with {#link|@floatFromInt#}.
</p>
{#code|test_int_to_float_coercion.zig#}
{#code|test_failed_int_to_float_coercion.zig#}
{#header_close#} {#header_close#}
{#header_open|Type Coercion: Float to Int#} {#header_open|Type Coercion: Float to Int#}
<p> <p>

View file

@ -0,0 +1,8 @@
test "integer type is too large for implicit cast to float" {
var int: u25 = 123;
_ = &int;
const float: f32 = int;
_ = float;
}
// test_error=

View file

@ -0,0 +1,12 @@
const std = @import("std");
const expectEqual = std.testing.expectEqual;
test "implicit integer to float" {
var int: u8 = 123;
_ = &int;
const float: f32 = int;
const int_from_float: u8 = @intFromFloat(float);
try expectEqual(int, int_from_float);
}
// test

View file

@ -620,11 +620,6 @@ pub const VTable = struct {
result: []u8, result: []u8,
result_alignment: std.mem.Alignment, result_alignment: std.mem.Alignment,
) void, ) void,
/// Returns whether the current thread of execution is known to have
/// been requested to cancel.
///
/// Thread-safe.
cancelRequested: *const fn (?*anyopaque) bool,
/// When this function returns, implementation guarantees that `start` has /// When this function returns, implementation guarantees that `start` has
/// either already been called, or a unit of concurrency has been assigned /// either already been called, or a unit of concurrency has been assigned

File diff suppressed because it is too large Load diff

View file

@ -1333,6 +1333,10 @@ pub const Server = struct {
/// Not enough free memory. This often means that the memory allocation is limited /// Not enough free memory. This often means that the memory allocation is limited
/// by the socket buffer limits, not by the system memory. /// by the socket buffer limits, not by the system memory.
SystemResources, SystemResources,
/// Either `listen` was never called, or `shutdown` was called (possibly while
/// this call was blocking). This allows `shutdown` to be used as a concurrent
/// cancellation mechanism.
SocketNotListening,
/// The network subsystem has failed. /// The network subsystem has failed.
NetworkDown, NetworkDown,
/// No connection is already queued and ready to be accepted, and /// No connection is already queued and ready to be accepted, and

View file

@ -10881,6 +10881,23 @@ pub extern "c" fn pthread_create(
start_routine: *const fn (?*anyopaque) callconv(.c) ?*anyopaque, start_routine: *const fn (?*anyopaque) callconv(.c) ?*anyopaque,
noalias arg: ?*anyopaque, noalias arg: ?*anyopaque,
) E; ) E;
pub const pthread_cancelstate = switch (native_os) {
.ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => enum(c_int) {
ENABLE = 1,
DISABLE = 0,
},
.linux => if (native_abi.isMusl()) enum(c_int) {
ENABLE = 0,
DISABLE = 1,
MASKED = 2,
} else if (native_abi.isGnu()) enum(c_int) {
ENABLE = 0,
DISABLE = 1,
},
else => void,
};
pub extern "c" fn pthread_setcancelstate(pthread_cancelstate, ?*pthread_cancelstate) E;
pub extern "c" fn pthread_cancel(pthread_t) E;
pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) E; pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) E;
pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *anyopaque, stacksize: usize) E; pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *anyopaque, stacksize: usize) E;
pub extern "c" fn pthread_attr_setstacksize(attr: *pthread_attr_t, stacksize: usize) E; pub extern "c" fn pthread_attr_setstacksize(attr: *pthread_attr_t, stacksize: usize) E;

View file

@ -108,6 +108,36 @@ test "expand 128-bit key" {
} }
} }
test "invMixColumns" {
const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
const enc_ctx = Aes128.initEnc(key);
const dec_ctx = Aes128.initDec(key);
for (1..10) |i| {
const enc_rk = enc_ctx.key_schedule.round_keys[10 - i];
const dec_rk = dec_ctx.key_schedule.round_keys[i];
const computed = enc_rk.invMixColumns();
try testing.expectEqualSlices(u8, &dec_rk.toBytes(), &computed.toBytes());
}
}
test "BlockVec invMixColumns" {
const input = [_]u8{
0x5f, 0x57, 0xf7, 0x1d, 0x72, 0xf5, 0xbe, 0xb9, 0x64, 0xbc, 0x3b, 0xf9, 0x15, 0x92, 0x29, 0x1a,
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
const vec2 = BlockVec(2).fromBytes(&input);
const result_vec = vec2.invMixColumns();
const result_bytes = result_vec.toBytes();
for (0..2) |i| {
const block = Block.fromBytes(input[i * 16 ..][0..16]);
const expected = block.invMixColumns().toBytes();
try testing.expectEqualSlices(u8, &expected, result_bytes[i * 16 ..][0..16]);
}
}
test "expand 256-bit key" { test "expand 256-bit key" {
const key = [_]u8{ const key = [_]u8{
0x60, 0x3d, 0xeb, 0x10, 0x60, 0x3d, 0xeb, 0x10,

View file

@ -96,6 +96,17 @@ pub const Block = struct {
return Block{ .repr = block1.repr | block2.repr }; return Block{ .repr = block1.repr | block2.repr };
} }
/// Apply the inverse MixColumns operation to a block.
pub fn invMixColumns(block: Block) Block {
return Block{
.repr = asm (
\\ vaesimc %[in], %[out]
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
),
};
}
/// Perform operations on multiple blocks in parallel. /// Perform operations on multiple blocks in parallel.
pub const parallel = struct { pub const parallel = struct {
const cpu = std.Target.x86.cpu; const cpu = std.Target.x86.cpu;
@ -308,6 +319,17 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
} }
return out; return out;
} }
/// Apply the inverse MixColumns operation to each block in the vector.
pub fn invMixColumns(block_vec: Self) Self {
var out_bytes: [blocks_count * 16]u8 = undefined;
const in_bytes = block_vec.toBytes();
inline for (0..blocks_count) |i| {
const block = Block.fromBytes(in_bytes[i * 16 ..][0..16]);
out_bytes[i * 16 ..][0..16].* = block.invMixColumns().toBytes();
}
return fromBytes(&out_bytes);
}
}; };
} }

View file

@ -99,6 +99,17 @@ pub const Block = struct {
return Block{ .repr = block1.repr | block2.repr }; return Block{ .repr = block1.repr | block2.repr };
} }
/// Apply the inverse MixColumns operation to a block.
pub fn invMixColumns(block: Block) Block {
return Block{
.repr = asm (
\\ aesimc %[out].16b, %[in].16b
: [out] "=x" (-> Repr),
: [in] "x" (block.repr),
),
};
}
/// Perform operations on multiple blocks in parallel. /// Perform operations on multiple blocks in parallel.
pub const parallel = struct { pub const parallel = struct {
/// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation. /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
@ -275,6 +286,15 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
} }
return out; return out;
} }
/// Apply the inverse MixColumns operation to each block in the vector.
pub fn invMixColumns(block_vec: Self) Self {
var out: Self = undefined;
inline for (0..native_words) |i| {
out.repr[i] = block_vec.repr[i].invMixColumns();
}
return out;
}
}; };
} }

View file

@ -265,6 +265,26 @@ pub const Block = struct {
return Block{ .repr = x }; return Block{ .repr = x };
} }
/// Apply the inverse MixColumns operation to a block.
pub fn invMixColumns(block: Block) Block {
var out: Repr = undefined;
inline for (0..4) |i| {
const col = block.repr[i];
const b0: u8 = @truncate(col);
const b1: u8 = @truncate(col >> 8);
const b2: u8 = @truncate(col >> 16);
const b3: u8 = @truncate(col >> 24);
const r0 = mul(0x0e, b0) ^ mul(0x0b, b1) ^ mul(0x0d, b2) ^ mul(0x09, b3);
const r1 = mul(0x09, b0) ^ mul(0x0e, b1) ^ mul(0x0b, b2) ^ mul(0x0d, b3);
const r2 = mul(0x0d, b0) ^ mul(0x09, b1) ^ mul(0x0e, b2) ^ mul(0x0b, b3);
const r3 = mul(0x0b, b0) ^ mul(0x0d, b1) ^ mul(0x09, b2) ^ mul(0x0e, b3);
out[i] = @as(u32, r0) | (@as(u32, r1) << 8) | (@as(u32, r2) << 16) | (@as(u32, r3) << 24);
}
return Block{ .repr = out };
}
/// Perform operations on multiple blocks in parallel. /// Perform operations on multiple blocks in parallel.
pub const parallel = struct { pub const parallel = struct {
/// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation. /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
@ -441,6 +461,15 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
} }
return out; return out;
} }
/// Apply the inverse MixColumns operation to each block in the vector.
pub fn invMixColumns(block_vec: Self) Self {
var out: Self = undefined;
for (0..native_words) |i| {
out.repr[i] = block_vec.repr[i].invMixColumns();
}
return out;
}
}; };
} }

View file

@ -7,12 +7,11 @@ const builtin = @import("builtin");
const blake2 = crypto.hash.blake2; const blake2 = crypto.hash.blake2;
const crypto = std.crypto; const crypto = std.crypto;
const Io = std.Io;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const phc_format = pwhash.phc_format; const phc_format = pwhash.phc_format;
const pwhash = crypto.pwhash; const pwhash = crypto.pwhash;
const Thread = std.Thread;
const Blake2b512 = blake2.Blake2b512; const Blake2b512 = blake2.Blake2b512;
const Blocks = std.array_list.AlignedManaged([block_length]u64, .@"16"); const Blocks = std.array_list.AlignedManaged([block_length]u64, .@"16");
const H0 = [Blake2b512.digest_length + 8]u8; const H0 = [Blake2b512.digest_length + 8]u8;
@ -204,20 +203,20 @@ fn initBlocks(
} }
fn processBlocks( fn processBlocks(
allocator: mem.Allocator,
blocks: *Blocks, blocks: *Blocks,
time: u32, time: u32,
memory: u32, memory: u32,
threads: u24, threads: u24,
mode: Mode, mode: Mode,
) KdfError!void { io: Io,
) void {
const lanes = memory / threads; const lanes = memory / threads;
const segments = lanes / sync_points; const segments = lanes / sync_points;
if (builtin.single_threaded or threads == 1) { if (builtin.single_threaded or threads == 1) {
processBlocksSt(blocks, time, memory, threads, mode, lanes, segments); processBlocksSt(blocks, time, memory, threads, mode, lanes, segments);
} else { } else {
try processBlocksMt(allocator, blocks, time, memory, threads, mode, lanes, segments); processBlocksMt(blocks, time, memory, threads, mode, lanes, segments, io);
} }
} }
@ -243,7 +242,6 @@ fn processBlocksSt(
} }
fn processBlocksMt( fn processBlocksMt(
allocator: mem.Allocator,
blocks: *Blocks, blocks: *Blocks,
time: u32, time: u32,
memory: u32, memory: u32,
@ -251,26 +249,20 @@ fn processBlocksMt(
mode: Mode, mode: Mode,
lanes: u32, lanes: u32,
segments: u32, segments: u32,
) KdfError!void { io: Io,
var threads_list = try std.array_list.Managed(Thread).initCapacity(allocator, threads); ) void {
defer threads_list.deinit();
var n: u32 = 0; var n: u32 = 0;
while (n < time) : (n += 1) { while (n < time) : (n += 1) {
var slice: u32 = 0; var slice: u32 = 0;
while (slice < sync_points) : (slice += 1) { while (slice < sync_points) : (slice += 1) {
var group: Io.Group = .init;
var lane: u24 = 0; var lane: u24 = 0;
while (lane < threads) : (lane += 1) { while (lane < threads) : (lane += 1) {
const thread = try Thread.spawn(.{}, processSegment, .{ group.async(io, processSegment, .{
blocks, time, memory, threads, mode, lanes, segments, n, slice, lane, blocks, time, memory, threads, mode, lanes, segments, n, slice, lane,
}); });
threads_list.appendAssumeCapacity(thread);
} }
lane = 0; group.wait(io);
while (lane < threads) : (lane += 1) {
threads_list.items[lane].join();
}
threads_list.clearRetainingCapacity();
} }
} }
} }
@ -489,6 +481,7 @@ pub fn kdf(
salt: []const u8, salt: []const u8,
params: Params, params: Params,
mode: Mode, mode: Mode,
io: Io,
) KdfError!void { ) KdfError!void {
if (derived_key.len < 4) return KdfError.WeakParameters; if (derived_key.len < 4) return KdfError.WeakParameters;
if (derived_key.len > max_int) return KdfError.OutputTooLong; if (derived_key.len > max_int) return KdfError.OutputTooLong;
@ -510,7 +503,7 @@ pub fn kdf(
blocks.appendNTimesAssumeCapacity(@splat(0), memory); blocks.appendNTimesAssumeCapacity(@splat(0), memory);
initBlocks(&blocks, &h0, memory, params.p); initBlocks(&blocks, &h0, memory, params.p);
try processBlocks(allocator, &blocks, params.t, memory, params.p, mode); processBlocks(&blocks, params.t, memory, params.p, mode, io);
finalize(&blocks, memory, params.p, derived_key); finalize(&blocks, memory, params.p, derived_key);
} }
@ -533,6 +526,7 @@ const PhcFormatHasher = struct {
params: Params, params: Params,
mode: Mode, mode: Mode,
buf: []u8, buf: []u8,
io: Io,
) HasherError![]const u8 { ) HasherError![]const u8 {
if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding; if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;
@ -540,7 +534,7 @@ const PhcFormatHasher = struct {
crypto.random.bytes(&salt); crypto.random.bytes(&salt);
var hash: [default_hash_len]u8 = undefined; var hash: [default_hash_len]u8 = undefined;
try kdf(allocator, &hash, password, &salt, params, mode); try kdf(allocator, &hash, password, &salt, params, mode, io);
return phc_format.serialize(HashResult{ return phc_format.serialize(HashResult{
.alg_id = @tagName(mode), .alg_id = @tagName(mode),
@ -557,6 +551,7 @@ const PhcFormatHasher = struct {
allocator: mem.Allocator, allocator: mem.Allocator,
str: []const u8, str: []const u8,
password: []const u8, password: []const u8,
io: Io,
) HasherError!void { ) HasherError!void {
const hash_result = try phc_format.deserialize(HashResult, str); const hash_result = try phc_format.deserialize(HashResult, str);
@ -572,7 +567,7 @@ const PhcFormatHasher = struct {
if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding; if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding;
const hash = hash_buf[0..expected_hash.len]; const hash = hash_buf[0..expected_hash.len];
try kdf(allocator, hash, password, hash_result.salt.constSlice(), params, mode); try kdf(allocator, hash, password, hash_result.salt.constSlice(), params, mode, io);
if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed; if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed;
} }
}; };
@ -595,6 +590,7 @@ pub fn strHash(
password: []const u8, password: []const u8,
options: HashOptions, options: HashOptions,
out: []u8, out: []u8,
io: Io,
) Error![]const u8 { ) Error![]const u8 {
const allocator = options.allocator orelse return Error.AllocatorRequired; const allocator = options.allocator orelse return Error.AllocatorRequired;
switch (options.encoding) { switch (options.encoding) {
@ -604,6 +600,7 @@ pub fn strHash(
options.params, options.params,
options.mode, options.mode,
out, out,
io,
), ),
.crypt => return Error.InvalidEncoding, .crypt => return Error.InvalidEncoding,
} }
@ -621,9 +618,10 @@ pub fn strVerify(
str: []const u8, str: []const u8,
password: []const u8, password: []const u8,
options: VerifyOptions, options: VerifyOptions,
io: Io,
) Error!void { ) Error!void {
const allocator = options.allocator orelse return Error.AllocatorRequired; const allocator = options.allocator orelse return Error.AllocatorRequired;
return PhcFormatHasher.verify(allocator, str, password); return PhcFormatHasher.verify(allocator, str, password, io);
} }
test "argon2d" { test "argon2d" {
@ -640,6 +638,7 @@ test "argon2d" {
&salt, &salt,
.{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad }, .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },
.argon2d, .argon2d,
std.testing.io,
); );
const want = [_]u8{ const want = [_]u8{
@ -665,6 +664,7 @@ test "argon2i" {
&salt, &salt,
.{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad }, .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },
.argon2i, .argon2i,
std.testing.io,
); );
const want = [_]u8{ const want = [_]u8{
@ -690,6 +690,7 @@ test "argon2id" {
&salt, &salt,
.{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad }, .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },
.argon2id, .argon2id,
std.testing.io,
); );
const want = [_]u8{ const want = [_]u8{
@ -800,44 +801,44 @@ test "kdf" {
.{ .{
.mode = .argon2i, .mode = .argon2i,
.time = 4, .time = 4,
.memory = 4096, .memory = 256,
.threads = 4, .threads = 4,
.hash = "a11f7b7f3f93f02ad4bddb59ab62d121e278369288a0d0e7", .hash = "f7dbbacbf16999e3700817a7e06f65a8db2e9fa9504ede4c",
}, },
.{ .{
.mode = .argon2d, .mode = .argon2d,
.time = 4, .time = 4,
.memory = 4096, .memory = 256,
.threads = 4, .threads = 4,
.hash = "935598181aa8dc2b720914aa6435ac8d3e3a4210c5b0fb2d", .hash = "ea2970501cf49faa5ba1d2e6370204e9b57ca90a8fea937b",
}, },
.{ .{
.mode = .argon2id, .mode = .argon2id,
.time = 4, .time = 4,
.memory = 4096, .memory = 256,
.threads = 4, .threads = 4,
.hash = "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a", .hash = "fbd40d5a8cb92f88c20bda4b3cdb1f9d5af1efa937032410",
}, },
.{ .{
.mode = .argon2i, .mode = .argon2i,
.time = 4, .time = 4,
.memory = 1024, .memory = 256,
.threads = 8, .threads = 8,
.hash = "0cdd3956aa35e6b475a7b0c63488822f774f15b43f6e6e17", .hash = "15d3c398364e53f68fd12d19baf3f21432d964254fe27467",
}, },
.{ .{
.mode = .argon2d, .mode = .argon2d,
.time = 4, .time = 4,
.memory = 1024, .memory = 256,
.threads = 8, .threads = 8,
.hash = "83604fc2ad0589b9d055578f4d3cc55bc616df3578a896e9", .hash = "23c9adc06f06e21e4612c1466a1be02627690932b02c0df0",
}, },
.{ .{
.mode = .argon2id, .mode = .argon2id,
.time = 4, .time = 4,
.memory = 1024, .memory = 256,
.threads = 8, .threads = 8,
.hash = "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f", .hash = "f22802f8ca47be93f9954e4ce20c1e944e938fbd4a125d9d",
}, },
.{ .{
.mode = .argon2i, .mode = .argon2i,
@ -863,23 +864,23 @@ test "kdf" {
.{ .{
.mode = .argon2i, .mode = .argon2i,
.time = 3, .time = 3,
.memory = 1024, .memory = 256,
.threads = 6, .threads = 6,
.hash = "d236b29c2b2a09babee842b0dec6aa1e83ccbdea8023dced", .hash = "ebc8f91964abd8ceab49a12963b0a9e57d635bfa2aad2884",
}, },
.{ .{
.mode = .argon2d, .mode = .argon2d,
.time = 3, .time = 3,
.memory = 1024, .memory = 256,
.threads = 6, .threads = 6,
.hash = "a3351b0319a53229152023d9206902f4ef59661cdca89481", .hash = "1dd7202fd68da6675f769f4034b7a1db30d8785331954117",
}, },
.{ .{
.mode = .argon2id, .mode = .argon2id,
.time = 3, .time = 3,
.memory = 1024, .memory = 256,
.threads = 6, .threads = 6,
.hash = "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016", .hash = "424436b6ee22a66b04b9d0cf78f190305c5c166bae8baa09",
}, },
}; };
for (test_vectors) |v| { for (test_vectors) |v| {
@ -894,6 +895,7 @@ test "kdf" {
salt, salt,
.{ .t = v.time, .m = v.memory, .p = v.threads }, .{ .t = v.time, .m = v.memory, .p = v.threads },
v.mode, v.mode,
std.testing.io,
); );
try std.testing.expectEqualSlices(u8, &dk, &want); try std.testing.expectEqualSlices(u8, &dk, &want);
@ -903,6 +905,7 @@ test "kdf" {
test "phc format hasher" { test "phc format hasher" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
const password = "testpass"; const password = "testpass";
const io = std.testing.io;
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
const hash = try PhcFormatHasher.create( const hash = try PhcFormatHasher.create(
@ -911,25 +914,29 @@ test "phc format hasher" {
.{ .t = 3, .m = 32, .p = 4 }, .{ .t = 3, .m = 32, .p = 4 },
.argon2id, .argon2id,
&buf, &buf,
io,
); );
try PhcFormatHasher.verify(allocator, hash, password); try PhcFormatHasher.verify(allocator, hash, password, io);
} }
test "password hash and password verify" { test "password hash and password verify" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
const password = "testpass"; const password = "testpass";
const io = std.testing.io;
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
const hash = try strHash( const hash = try strHash(
password, password,
.{ .allocator = allocator, .params = .{ .t = 3, .m = 32, .p = 4 } }, .{ .allocator = allocator, .params = .{ .t = 3, .m = 32, .p = 4 } },
&buf, &buf,
io,
); );
try strVerify(hash, password, .{ .allocator = allocator }); try strVerify(hash, password, .{ .allocator = allocator }, io);
} }
test "kdf derived key length" { test "kdf derived key length" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
const io = std.testing.io;
const password = "testpass"; const password = "testpass";
const salt = "saltsalt"; const salt = "saltsalt";
@ -937,11 +944,11 @@ test "kdf derived key length" {
const mode = Mode.argon2id; const mode = Mode.argon2id;
var dk1: [11]u8 = undefined; var dk1: [11]u8 = undefined;
try kdf(allocator, &dk1, password, salt, params, mode); try kdf(allocator, &dk1, password, salt, params, mode, io);
var dk2: [77]u8 = undefined; var dk2: [77]u8 = undefined;
try kdf(allocator, &dk2, password, salt, params, mode); try kdf(allocator, &dk2, password, salt, params, mode, io);
var dk3: [111]u8 = undefined; var dk3: [111]u8 = undefined;
try kdf(allocator, &dk3, password, salt, params, mode); try kdf(allocator, &dk3, password, salt, params, mode, io);
} }

View file

@ -450,6 +450,7 @@ fn benchmarkPwhash(
comptime ty: anytype, comptime ty: anytype,
comptime params: *const anyopaque, comptime params: *const anyopaque,
comptime count: comptime_int, comptime count: comptime_int,
io: std.Io,
) !f64 { ) !f64 {
const password = "testpass" ** 2; const password = "testpass" ** 2;
const opts = ty.HashOptions{ const opts = ty.HashOptions{
@ -459,12 +460,20 @@ fn benchmarkPwhash(
}; };
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
const strHash = ty.strHash;
const strHashFnInfo = @typeInfo(@TypeOf(strHash)).@"fn";
const needs_io = strHashFnInfo.params.len == 4;
var timer = try Timer.start(); var timer = try Timer.start();
const start = timer.lap(); const start = timer.lap();
{ {
var i: usize = 0; var i: usize = 0;
while (i < count) : (i += 1) { while (i < count) : (i += 1) {
_ = try ty.strHash(password, opts, &buf); if (needs_io) {
_ = try strHash(password, opts, &buf, io);
} else {
_ = try strHash(password, opts, &buf);
}
mem.doNotOptimizeAway(&buf); mem.doNotOptimizeAway(&buf);
} }
} }
@ -623,7 +632,7 @@ pub fn main() !void {
inline for (pwhashes) |H| { inline for (pwhashes) |H| {
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64)); const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64), io);
try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });
try stdout.flush(); try stdout.flush();
} }

View file

@ -1748,9 +1748,7 @@ pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize {
} }
pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize {
if (native_arch == .riscv32) { return syscall2(.nanosleep, @intFromPtr(req), @intFromPtr(rem));
@compileError("No nanosleep syscall on this architecture.");
} else return syscall2(.nanosleep, @intFromPtr(req), @intFromPtr(rem));
} }
pub fn pause() usize { pub fn pause() usize {
@ -3773,6 +3771,7 @@ pub const SIG = if (is_mips) enum(u32) {
PROF = 29, PROF = 29,
XCPU = 30, XCPU = 30,
XFZ = 31, XFZ = 31,
_,
} else if (is_sparc) enum(u32) { } else if (is_sparc) enum(u32) {
pub const BLOCK = 1; pub const BLOCK = 1;
pub const UNBLOCK = 2; pub const UNBLOCK = 2;
@ -3818,6 +3817,7 @@ pub const SIG = if (is_mips) enum(u32) {
LOST = 29, LOST = 29,
USR1 = 30, USR1 = 30,
USR2 = 31, USR2 = 31,
_,
} else enum(u32) { } else enum(u32) {
pub const BLOCK = 0; pub const BLOCK = 0;
pub const UNBLOCK = 1; pub const UNBLOCK = 1;
@ -3861,6 +3861,7 @@ pub const SIG = if (is_mips) enum(u32) {
IO = 29, IO = 29,
PWR = 30, PWR = 30,
SYS = 31, SYS = 31,
_,
}; };
pub const kernel_rwf = u32; pub const kernel_rwf = u32;

View file

@ -1360,6 +1360,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
.PIPE => return error.BrokenPipe, .PIPE => return error.BrokenPipe,
.CONNRESET => return error.ConnectionResetByPeer, .CONNRESET => return error.ConnectionResetByPeer,
.BUSY => return error.DeviceBusy, .BUSY => return error.DeviceBusy,
.CANCELED => return error.Canceled,
else => |err| return unexpectedErrno(err), else => |err| return unexpectedErrno(err),
} }
} }

View file

@ -690,6 +690,9 @@ pub const ArgIteratorWasi = struct {
/// Call to free the internal buffer of the iterator. /// Call to free the internal buffer of the iterator.
pub fn deinit(self: *ArgIteratorWasi) void { pub fn deinit(self: *ArgIteratorWasi) void {
// Nothing is allocated when there are no args
if (self.args.len == 0) return;
const last_item = self.args[self.args.len - 1]; const last_item = self.args[self.args.len - 1];
const last_byte_addr = @intFromPtr(last_item.ptr) + last_item.len + 1; // null terminated const last_byte_addr = @intFromPtr(last_item.ptr) + last_item.len + 1; // null terminated
const first_item_ptr = self.args[0].ptr; const first_item_ptr = self.args[0].ptr;

View file

@ -1195,6 +1195,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
if (extra.section_node.unwrap()) |section_node| { if (extra.section_node.unwrap()) |section_node| {
end_offset += 1; // for the rparen end_offset += 1; // for the rparen
n = section_node; n = section_node;
} else if (extra.addrspace_node.unwrap()) |addrspace_node| {
end_offset += 1; // for the rparen
n = addrspace_node;
} else if (extra.align_node.unwrap()) |align_node| { } else if (extra.align_node.unwrap()) |align_node| {
end_offset += 1; // for the rparen end_offset += 1; // for the rparen
n = align_node; n = align_node;

View file

@ -6084,6 +6084,16 @@ test "zig fmt: do not canonicalize invalid cast builtins" {
); );
} }
test "zig fmt: extern addrspace in struct" {
try testCanonical(
\\const namespace = struct {
\\ extern const num: u8 addrspace(.generic);
\\};
\\// comment
\\
);
}
test "recovery: top level" { test "recovery: top level" {
try testError( try testError(
\\test "" {inline} \\test "" {inline}

View file

@ -175,9 +175,11 @@ const ComptimeAlloc = struct {
/// `src` may be `null` if `is_const` will be set. /// `src` may be `null` if `is_const` will be set.
fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex { fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex {
const pt = sema.pt;
const init_val = try sema.typeHasOnePossibleValue(ty) orelse try pt.undefValue(ty);
const idx = sema.comptime_allocs.items.len; const idx = sema.comptime_allocs.items.len;
try sema.comptime_allocs.append(sema.gpa, .{ try sema.comptime_allocs.append(sema.gpa, .{
.val = .{ .interned = try sema.pt.intern(.{ .undef = ty.toIntern() }) }, .val = .{ .interned = init_val.toIntern() },
.is_const = false, .is_const = false,
.src = src, .src = src,
.alignment = alignment, .alignment = alignment,
@ -28477,7 +28479,7 @@ pub fn coerce(
}; };
} }
const CoersionError = CompileError || error{ const CoercionError = CompileError || error{
/// When coerce is called recursively, this error should be returned instead of using `fail` /// When coerce is called recursively, this error should be returned instead of using `fail`
/// to ensure correct types in compile errors. /// to ensure correct types in compile errors.
NotCoercible, NotCoercible,
@ -28516,7 +28518,7 @@ fn coerceExtra(
inst: Air.Inst.Ref, inst: Air.Inst.Ref,
inst_src: LazySrcLoc, inst_src: LazySrcLoc,
opts: CoerceOpts, opts: CoerceOpts,
) CoersionError!Air.Inst.Ref { ) CoercionError!Air.Inst.Ref {
if (dest_ty.isGenericPoison()) return inst; if (dest_ty.isGenericPoison()) return inst;
const pt = sema.pt; const pt = sema.pt;
const zcu = pt.zcu; const zcu = pt.zcu;
@ -28943,6 +28945,20 @@ fn coerceExtra(
if (!opts.report_err) return error.NotCoercible; if (!opts.report_err) return error.NotCoercible;
return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
} }
const int_info = inst_ty.intInfo(zcu);
const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed);
const float_precision: u8 = switch (dest_ty.toIntern()) {
.f16_type => 11,
.f32_type => 24,
.f64_type => 53,
.f80_type => 64,
.f128_type => 113,
else => unreachable,
};
if (int_precision <= float_precision) {
try sema.requireRuntimeBlock(block, inst_src, null);
return block.addTyOp(.float_from_int, dest_ty, inst);
}
break :int; break :int;
}; };
const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema); const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);

View file

@ -3445,13 +3445,22 @@ pub fn optEuBaseType(ty: Type, zcu: *const Zcu) Type {
pub fn toUnsigned(ty: Type, pt: Zcu.PerThread) !Type { pub fn toUnsigned(ty: Type, pt: Zcu.PerThread) !Type {
const zcu = pt.zcu; const zcu = pt.zcu;
return switch (ty.zigTypeTag(zcu)) { return switch (ty.toIntern()) {
.int => pt.intType(.unsigned, ty.intInfo(zcu).bits), // zig fmt: off
.vector => try pt.vectorType(.{ .usize_type, .isize_type => .usize,
.len = ty.vectorLen(zcu), .c_ushort_type, .c_short_type => .c_ushort,
.child = (try ty.childType(zcu).toUnsigned(pt)).toIntern(), .c_uint_type, .c_int_type => .c_uint,
}), .c_ulong_type, .c_long_type => .c_ulong,
else => unreachable, .c_ulonglong_type, .c_longlong_type => .c_ulonglong,
// zig fmt: on
else => switch (ty.zigTypeTag(zcu)) {
.int => pt.intType(.unsigned, ty.intInfo(zcu).bits),
.vector => try pt.vectorType(.{
.len = ty.vectorLen(zcu),
.child = (try ty.childType(zcu).toUnsigned(pt)).toIntern(),
}),
else => unreachable,
},
}; };
} }

View file

@ -1499,22 +1499,18 @@ const aarch64 = struct {
.ABS64 => { .ABS64 => {
try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
}, },
.ADR_PREL_PG_HI21 => { .ADR_PREL_PG_HI21 => {
try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
}, },
.ADR_GOT_PAGE => { .ADR_GOT_PAGE => {
// TODO: relax if possible // TODO: relax if possible
symbol.flags.needs_got = true; symbol.flags.needs_got = true;
}, },
.LD64_GOT_LO12_NC, .LD64_GOT_LO12_NC,
.LD64_GOTPAGE_LO15, .LD64_GOTPAGE_LO15,
=> { => {
symbol.flags.needs_got = true; symbol.flags.needs_got = true;
}, },
.CALL26, .CALL26,
.JUMP26, .JUMP26,
=> { => {
@ -1522,25 +1518,21 @@ const aarch64 = struct {
symbol.flags.needs_plt = true; symbol.flags.needs_plt = true;
} }
}, },
.TLSLE_ADD_TPREL_HI12, .TLSLE_ADD_TPREL_HI12,
.TLSLE_ADD_TPREL_LO12_NC, .TLSLE_ADD_TPREL_LO12_NC,
=> { => {
if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file); if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);
}, },
.TLSIE_ADR_GOTTPREL_PAGE21, .TLSIE_ADR_GOTTPREL_PAGE21,
.TLSIE_LD64_GOTTPREL_LO12_NC, .TLSIE_LD64_GOTTPREL_LO12_NC,
=> { => {
symbol.flags.needs_gottp = true; symbol.flags.needs_gottp = true;
}, },
.TLSGD_ADR_PAGE21, .TLSGD_ADR_PAGE21,
.TLSGD_ADD_LO12_NC, .TLSGD_ADD_LO12_NC,
=> { => {
symbol.flags.needs_tlsgd = true; symbol.flags.needs_tlsgd = true;
}, },
.TLSDESC_ADR_PAGE21, .TLSDESC_ADR_PAGE21,
.TLSDESC_LD64_LO12, .TLSDESC_LD64_LO12,
.TLSDESC_ADD_LO12, .TLSDESC_ADD_LO12,
@ -1551,18 +1543,17 @@ const aarch64 = struct {
symbol.flags.needs_tlsdesc = true; symbol.flags.needs_tlsdesc = true;
} }
}, },
.ADD_ABS_LO12_NC, .ADD_ABS_LO12_NC,
.ADR_PREL_LO21, .ADR_PREL_LO21,
.LDST8_ABS_LO12_NC, .CONDBR19,
.LDST128_ABS_LO12_NC,
.LDST16_ABS_LO12_NC, .LDST16_ABS_LO12_NC,
.LDST32_ABS_LO12_NC, .LDST32_ABS_LO12_NC,
.LDST64_ABS_LO12_NC, .LDST64_ABS_LO12_NC,
.LDST128_ABS_LO12_NC, .LDST8_ABS_LO12_NC,
.PREL32, .PREL32,
.PREL64, .PREL64,
=> {}, => {},
else => try atom.reportUnhandledRelocError(rel, elf_file), else => try atom.reportUnhandledRelocError(rel, elf_file),
} }
} }
@ -1599,7 +1590,6 @@ const aarch64 = struct {
r_offset, r_offset,
); );
}, },
.CALL26, .CALL26,
.JUMP26, .JUMP26,
=> { => {
@ -1611,27 +1601,26 @@ const aarch64 = struct {
}; };
util.writeBranchImm(disp, code); util.writeBranchImm(disp, code);
}, },
.CONDBR19 => {
const value = math.cast(i19, S + A - P) orelse return error.Overflow;
util.writeCondBrImm(value, code);
},
.PREL32 => { .PREL32 => {
const value = math.cast(i32, S + A - P) orelse return error.Overflow; const value = math.cast(i32, S + A - P) orelse return error.Overflow;
mem.writeInt(u32, code, @bitCast(value), .little); mem.writeInt(u32, code, @bitCast(value), .little);
}, },
.PREL64 => { .PREL64 => {
const value = S + A - P; const value = S + A - P;
mem.writeInt(u64, code_buffer[r_offset..][0..8], @bitCast(value), .little); mem.writeInt(u64, code_buffer[r_offset..][0..8], @bitCast(value), .little);
}, },
.ADR_PREL_LO21 => { .ADR_PREL_LO21 => {
const value = math.cast(i21, S + A - P) orelse return error.Overflow; const value = math.cast(i21, S + A - P) orelse return error.Overflow;
util.writeAdrInst(value, code); util.writeAdrInst(value, code);
}, },
.ADR_PREL_PG_HI21 => { .ADR_PREL_PG_HI21 => {
// TODO: check for relaxation of ADRP+ADD // TODO: check for relaxation of ADRP+ADD
util.writeAdrInst(try util.calcNumberOfPages(P, S + A), code); util.writeAdrInst(try util.calcNumberOfPages(P, S + A), code);
}, },
.ADR_GOT_PAGE => if (target.flags.has_got) { .ADR_GOT_PAGE => if (target.flags.has_got) {
util.writeAdrInst(try util.calcNumberOfPages(P, G + GOT + A), code); util.writeAdrInst(try util.calcNumberOfPages(P, G + GOT + A), code);
} else { } else {
@ -1644,18 +1633,15 @@ const aarch64 = struct {
r_offset, r_offset,
}); });
}, },
.LD64_GOT_LO12_NC => { .LD64_GOT_LO12_NC => {
assert(target.flags.has_got); assert(target.flags.has_got);
const taddr = @as(u64, @intCast(G + GOT + A)); const taddr = @as(u64, @intCast(G + GOT + A));
util.writeLoadStoreRegInst(@divExact(@as(u12, @truncate(taddr)), 8), code); util.writeLoadStoreRegInst(@divExact(@as(u12, @truncate(taddr)), 8), code);
}, },
.ADD_ABS_LO12_NC => { .ADD_ABS_LO12_NC => {
const taddr = @as(u64, @intCast(S + A)); const taddr = @as(u64, @intCast(S + A));
util.writeAddImmInst(@truncate(taddr), code); util.writeAddImmInst(@truncate(taddr), code);
}, },
.LDST8_ABS_LO12_NC, .LDST8_ABS_LO12_NC,
.LDST16_ABS_LO12_NC, .LDST16_ABS_LO12_NC,
.LDST32_ABS_LO12_NC, .LDST32_ABS_LO12_NC,
@ -1674,44 +1660,37 @@ const aarch64 = struct {
}; };
util.writeLoadStoreRegInst(off, code); util.writeLoadStoreRegInst(off, code);
}, },
.TLSLE_ADD_TPREL_HI12 => { .TLSLE_ADD_TPREL_HI12 => {
const value = math.cast(i12, (S + A - TP) >> 12) orelse const value = math.cast(i12, (S + A - TP) >> 12) orelse
return error.Overflow; return error.Overflow;
util.writeAddImmInst(@bitCast(value), code); util.writeAddImmInst(@bitCast(value), code);
}, },
.TLSLE_ADD_TPREL_LO12_NC => { .TLSLE_ADD_TPREL_LO12_NC => {
const value: i12 = @truncate(S + A - TP); const value: i12 = @truncate(S + A - TP);
util.writeAddImmInst(@bitCast(value), code); util.writeAddImmInst(@bitCast(value), code);
}, },
.TLSIE_ADR_GOTTPREL_PAGE21 => { .TLSIE_ADR_GOTTPREL_PAGE21 => {
const S_ = target.gotTpAddress(elf_file); const S_ = target.gotTpAddress(elf_file);
relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A });
util.writeAdrInst(try util.calcNumberOfPages(P, S_ + A), code); util.writeAdrInst(try util.calcNumberOfPages(P, S_ + A), code);
}, },
.TLSIE_LD64_GOTTPREL_LO12_NC => { .TLSIE_LD64_GOTTPREL_LO12_NC => {
const S_ = target.gotTpAddress(elf_file); const S_ = target.gotTpAddress(elf_file);
relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A });
const off: u12 = try math.divExact(u12, @truncate(@as(u64, @bitCast(S_ + A))), 8); const off: u12 = try math.divExact(u12, @truncate(@as(u64, @bitCast(S_ + A))), 8);
util.writeLoadStoreRegInst(off, code); util.writeLoadStoreRegInst(off, code);
}, },
.TLSGD_ADR_PAGE21 => { .TLSGD_ADR_PAGE21 => {
const S_ = target.tlsGdAddress(elf_file); const S_ = target.tlsGdAddress(elf_file);
relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A });
util.writeAdrInst(try util.calcNumberOfPages(P, S_ + A), code); util.writeAdrInst(try util.calcNumberOfPages(P, S_ + A), code);
}, },
.TLSGD_ADD_LO12_NC => { .TLSGD_ADD_LO12_NC => {
const S_ = target.tlsGdAddress(elf_file); const S_ = target.tlsGdAddress(elf_file);
relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A });
const off: u12 = @truncate(@as(u64, @bitCast(S_ + A))); const off: u12 = @truncate(@as(u64, @bitCast(S_ + A)));
util.writeAddImmInst(off, code); util.writeAddImmInst(off, code);
}, },
.TLSDESC_ADR_PAGE21 => { .TLSDESC_ADR_PAGE21 => {
if (target.flags.has_tlsdesc) { if (target.flags.has_tlsdesc) {
const S_ = target.tlsDescAddress(elf_file); const S_ = target.tlsDescAddress(elf_file);
@ -1722,7 +1701,6 @@ const aarch64 = struct {
util.encoding.Instruction.nop().write(code); util.encoding.Instruction.nop().write(code);
} }
}, },
.TLSDESC_LD64_LO12 => { .TLSDESC_LD64_LO12 => {
if (target.flags.has_tlsdesc) { if (target.flags.has_tlsdesc) {
const S_ = target.tlsDescAddress(elf_file); const S_ = target.tlsDescAddress(elf_file);
@ -1734,7 +1712,6 @@ const aarch64 = struct {
util.encoding.Instruction.nop().write(code); util.encoding.Instruction.nop().write(code);
} }
}, },
.TLSDESC_ADD_LO12 => { .TLSDESC_ADD_LO12 => {
if (target.flags.has_tlsdesc) { if (target.flags.has_tlsdesc) {
const S_ = target.tlsDescAddress(elf_file); const S_ = target.tlsDescAddress(elf_file);
@ -1747,13 +1724,11 @@ const aarch64 = struct {
util.encoding.Instruction.movz(.x0, value, .{ .lsl = .@"16" }).write(code); util.encoding.Instruction.movz(.x0, value, .{ .lsl = .@"16" }).write(code);
} }
}, },
.TLSDESC_CALL => if (!target.flags.has_tlsdesc) { .TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
relocs_log.debug(" relaxing br => movk(x0, {x})", .{S + A - TP}); relocs_log.debug(" relaxing br => movk(x0, {x})", .{S + A - TP});
const value: u16 = @bitCast(@as(i16, @truncate(S + A - TP))); const value: u16 = @bitCast(@as(i16, @truncate(S + A - TP)));
util.encoding.Instruction.movk(.x0, value, .{}).write(code); util.encoding.Instruction.movk(.x0, value, .{}).write(code);
}, },
else => try atom.reportUnhandledRelocError(rel, elf_file), else => try atom.reportUnhandledRelocError(rel, elf_file),
} }
} }

View file

@ -29,6 +29,12 @@ pub fn writeBranchImm(disp: i28, code: *[4]u8) void {
inst.write(code); inst.write(code);
} }
pub fn writeCondBrImm(disp: i19, code: *[4]u8) void {
var inst: encoding.Instruction = .read(code);
inst.branch_exception_generating_system.conditional_branch_immediate.group.imm19 = @intCast(@shrExact(disp, 2));
inst.write(code);
}
const assert = std.debug.assert; const assert = std.debug.assert;
const builtin = @import("builtin"); const builtin = @import("builtin");
const math = std.math; const math = std.math;

View file

@ -595,7 +595,7 @@ const Writer = struct {
}, },
.reify_slice_arg_ty => { .reify_slice_arg_ty => {
const reify_slice_arg_info: Zir.Inst.ReifySliceArgInfo = @enumFromInt(extended.operand); const reify_slice_arg_info: Zir.Inst.ReifySliceArgInfo = @enumFromInt(extended.small);
const extra = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; const extra = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
try stream.print("{t}, ", .{reify_slice_arg_info}); try stream.print("{t}, ", .{reify_slice_arg_info});
try self.writeInstRef(stream, extra.operand); try self.writeInstRef(stream, extra.operand);

View file

@ -1,5 +1,6 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert;
const expect = std.testing.expect; const expect = std.testing.expect;
test "@abs integers" { test "@abs integers" {
@ -48,6 +49,33 @@ fn testAbsIntegers() !void {
} }
} }
test "@abs signed C ABI integers" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
try testOne(isize, usize);
try testOne(c_short, c_ushort);
try testOne(c_int, c_uint);
try testOne(c_long, c_ulong);
if (!builtin.cpu.arch.isSpirV()) try testOne(c_longlong, c_ulonglong);
}
fn testOne(comptime Signed: type, comptime Unsigned: type) !void {
var negative_one: Signed = undefined;
negative_one = -1;
const one = @abs(negative_one);
comptime assert(@TypeOf(one) == Unsigned);
try expect(one == 1);
}
};
try S.doTheTest();
try comptime S.doTheTest();
}
test "@abs unsigned integers" { test "@abs unsigned integers" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -87,6 +115,32 @@ fn testAbsUnsignedIntegers() !void {
} }
} }
test "@abs unsigned C ABI integers" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
try testOne(usize);
try testOne(c_ushort);
try testOne(c_uint);
try testOne(c_ulong);
if (!builtin.cpu.arch.isSpirV()) try testOne(c_ulonglong);
}
fn testOne(comptime Unsigned: type) !void {
var one: Unsigned = undefined;
one = 1;
const still_one = @abs(one);
comptime assert(@TypeOf(still_one) == Unsigned);
try expect(still_one == 1);
}
};
try S.doTheTest();
try comptime S.doTheTest();
}
test "@abs big int <= 128 bits" { test "@abs big int <= 128 bits" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

View file

@ -157,6 +157,60 @@ test "@floatFromInt(f80)" {
try comptime S.doTheTest(i256); try comptime S.doTheTest(i256);
} }
test "type coercion from int to float" {
const check = struct {
// Check that an integer value can be coerced to a float type and
// then converted back to the original value without rounding issues.
fn value(Float: type, int: anytype) !void {
const float: Float = int;
const Int = @TypeOf(int);
try std.testing.expectEqual(int, @as(Int, @intFromFloat(float)));
if (Float != f80) { // https://codeberg.org/ziglang/zig/issues/30035
try std.testing.expectEqual(int, @as(Int, @intFromFloat(@ceil(float))));
try std.testing.expectEqual(int, @as(Int, @intFromFloat(@floor(float))));
}
}
// Exhaustively check that all possible values of the integer type can
// safely be coerced to the float type.
fn allValues(Float: type, Int: type) !void {
var int: Int = std.math.minInt(Int);
while (int < std.math.maxInt(Int)) : (int += 1)
try value(Float, int);
}
// Check that the min and max values of the integer type can safely be
// coerced to the float type.
fn edgeValues(Float: type, Int: type) !void {
var int: Int = std.math.minInt(Int);
try value(Float, int);
int = std.math.maxInt(Int);
try value(Float, int);
}
};
try check.allValues(f16, u11);
try check.allValues(f16, i12);
try check.edgeValues(f32, u24);
try check.edgeValues(f32, i25);
try check.edgeValues(f64, u53);
try check.edgeValues(f64, i54);
try check.edgeValues(f80, u64);
try check.edgeValues(f80, i65);
try check.edgeValues(f128, u113);
try check.edgeValues(f128, i114);
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
// Basic sanity check that the coercions work for vectors too.
const int_vec: @Vector(2, u24) = @splat(123);
try check.value(@Vector(2, f32), int_vec);
}
test "@intFromFloat" { test "@intFromFloat" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

View file

@ -427,3 +427,12 @@ test "undefined type value" {
}; };
comptime assert(@TypeOf(S.undef_type) == type); comptime assert(@TypeOf(S.undef_type) == type);
} }
test "reify struct with zero fields through const arrays" {
const names: [0][]const u8 = .{};
const types: [0]type = .{};
const attrs: [0]std.builtin.Type.StructField.Attributes = .{};
const S = @Struct(.auto, null, &names, &types, &attrs);
comptime assert(@typeInfo(S) == .@"struct");
comptime assert(@typeInfo(S).@"struct".fields.len == 0);
}

View file

@ -0,0 +1,52 @@
// Test that integer types above a certain size will not coerce to a float.
fn testCoerce(Float: type, Int: type) void {
var i: Int = 0;
_ = &i;
_ = @as(Float, i);
}
export fn entry() void {
testCoerce(f16, u11); // Okay
testCoerce(f16, u12); // Too big
testCoerce(f16, i12);
testCoerce(f16, i13);
testCoerce(f32, u24);
testCoerce(f32, u25);
testCoerce(f32, i25);
testCoerce(f32, i26);
testCoerce(f64, u53);
testCoerce(f64, u54);
testCoerce(f64, i54);
testCoerce(f64, i55);
testCoerce(f80, u64);
testCoerce(f80, u65);
testCoerce(f80, i65);
testCoerce(f80, i66);
testCoerce(f128, u113);
testCoerce(f128, u114);
testCoerce(f128, i114);
testCoerce(f128, i115);
}
// error
//
// :6:20: error: expected type 'f16', found 'u12'
// :6:20: error: expected type 'f16', found 'i13'
// :6:20: error: expected type 'f32', found 'u25'
// :6:20: error: expected type 'f32', found 'i26'
// :6:20: error: expected type 'f64', found 'u54'
// :6:20: error: expected type 'f64', found 'i55'
// :6:20: error: expected type 'f80', found 'u65'
// :6:20: error: expected type 'f80', found 'i66'
// :6:20: error: expected type 'f128', found 'u114'
// :6:20: error: expected type 'f128', found 'i115'