From 409b194d6d95c722b50cb85a7a6bb4a457026d71 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Aug 2025 16:21:17 -0700 Subject: [PATCH 001/112] start the 0.15.2 release cycle --- CMakeLists.txt | 2 +- build.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef371fcdd3..0ab8e1cddd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ project(zig set(ZIG_VERSION_MAJOR 0) set(ZIG_VERSION_MINOR 15) -set(ZIG_VERSION_PATCH 1) +set(ZIG_VERSION_PATCH 2) set(ZIG_VERSION "" CACHE STRING "Override Zig version string. Default is to find out with git.") if("${ZIG_VERSION}" STREQUAL "") diff --git a/build.zig b/build.zig index 37523e66a3..9e672a4ca7 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,7 @@ const assert = std.debug.assert; const DevEnv = @import("src/dev.zig").Env; const ValueInterpretMode = enum { direct, by_name }; -const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 15, .patch = 1 }; +const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 15, .patch = 2 }; const stack_size = 46 * 1024 * 1024; pub fn build(b: *std.Build) !void { From e24d13cae7453a5d1d504d3e0f2783e8a2f9c60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20=C3=85stholm?= Date: Wed, 20 Aug 2025 14:45:22 +0200 Subject: [PATCH 002/112] Use readStreaming, not readPositional, for streaming file readVec on Windows --- lib/std/fs/File.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 4465701f1a..8da2112f8b 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1398,9 +1398,9 @@ pub const Reader = struct { } const first = data[0]; if (first.len >= io_reader.buffer.len - io_reader.end) { - return readPositional(r, first); + return readStreaming(r, first); } else { - io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]); + io_reader.end += try readStreaming(r, io_reader.buffer[io_reader.end..]); return 0; } } From 913db1b1ca31df83cec0fbb9bd7e42751c0547b7 Mon Sep 17 00:00:00 2001 From: Lwazi Dube Date: Wed, 20 Aug 2025 14:23:04 -0400 Subject: [PATCH 003/112] std.os.uefi: Fix typo that causes compile time error #22809" --- lib/std/os/uefi/protocol/service_binding.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/uefi/protocol/service_binding.zig b/lib/std/os/uefi/protocol/service_binding.zig index 06cc64001e..ddbda29ee6 100644 --- a/lib/std/os/uefi/protocol/service_binding.zig +++ b/lib/std/os/uefi/protocol/service_binding.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const uefi = std.uefi; +const uefi = std.os.uefi; const Guid = uefi.Guid; const Handle = uefi.Handle; const Status = uefi.Status; From c8ec7c1294db52edb0b2feb8a32ba0ff8ff03146 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:38:08 +0100 Subject: [PATCH 004/112] std: remove lossy int to float coercion on json parse --- lib/std/json/static.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/json/static.zig b/lib/std/json/static.zig index 540f5338f9..ced6894027 100644 --- a/lib/std/json/static.zig +++ b/lib/std/json/static.zig @@ -567,8 +567,8 @@ pub fn innerParseFromValue( switch (source) { .float => |f| { if (@round(f) != f) return error.InvalidNumber; - if (f > std.math.maxInt(T)) return error.Overflow; - if (f < std.math.minInt(T)) return error.Overflow; + if (f > @as(@TypeOf(f), @floatFromInt(std.math.maxInt(T)))) return error.Overflow; + if (f < @as(@TypeOf(f), @floatFromInt(std.math.minInt(T)))) return error.Overflow; return @as(T, @intFromFloat(f)); }, .integer => |i| { @@ -770,7 +770,7 @@ fn sliceToInt(comptime T: type, slice: []const u8) !T { // Try to coerce a float to an integer. const float = try std.fmt.parseFloat(f128, slice); if (@round(float) != float) return error.InvalidNumber; - if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow; + if (float > @as(f128, @floatFromInt(std.math.maxInt(T))) or float < @as(f128, @floatFromInt(std.math.minInt(T)))) return error.Overflow; return @as(T, @intCast(@as(i128, @intFromFloat(float)))); } From cf542500f84f9bd3bc1dba3c42720c2daa689201 Mon Sep 17 00:00:00 2001 From: Justus Klausecker Date: Wed, 20 Aug 2025 23:10:34 +0200 Subject: [PATCH 005/112] zig reduce: adapt to new Writer API --- lib/compiler/reduce.zig | 39 ++++++++++++++++++------------------ lib/compiler/reduce/Walk.zig | 19 +++++++++--------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig index d9955b9e33..b20a2fcba3 100644 --- a/lib/compiler/reduce.zig +++ b/lib/compiler/reduce.zig @@ -114,10 +114,10 @@ pub fn main() !void { interestingness_argv.appendAssumeCapacity(checker_path); interestingness_argv.appendSliceAssumeCapacity(argv); - var rendered = std.array_list.Managed(u8).init(gpa); + var rendered: std.Io.Writer.Allocating = .init(gpa); defer rendered.deinit(); - var astgen_input = std.array_list.Managed(u8).init(gpa); + var astgen_input: std.Io.Writer.Allocating = .init(gpa); defer astgen_input.deinit(); var tree = try parse(gpa, root_source_file_path); @@ -138,10 +138,10 @@ pub fn main() !void { } } - var fixups: Ast.Fixups = .{}; + var fixups: Ast.Render.Fixups = .{}; defer fixups.deinit(gpa); - var more_fixups: Ast.Fixups = .{}; + var more_fixups: Ast.Render.Fixups = .{}; defer more_fixups.deinit(gpa); var rng = std.Random.DefaultPrng.init(seed); @@ -188,15 +188,14 @@ pub fn main() !void { try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups); rendered.clearRetainingCapacity(); - try tree.renderToArrayList(&rendered, fixups); + try tree.render(gpa, &rendered.writer, fixups); // The transformations we applied may have resulted in unused locals, // in which case we would like to add the respective discards. { - try astgen_input.resize(rendered.items.len); - @memcpy(astgen_input.items, rendered.items); - try astgen_input.append(0); - const source_with_null = astgen_input.items[0 .. astgen_input.items.len - 1 :0]; + try astgen_input.writer.writeAll(rendered.written()); + try astgen_input.writer.writeByte(0); + const source_with_null = astgen_input.written()[0..(astgen_input.written().len - 1) :0]; var astgen_tree = try Ast.parse(gpa, source_with_null, .zig); defer astgen_tree.deinit(gpa); if (astgen_tree.errors.len != 0) { @@ -228,12 +227,12 @@ pub fn main() !void { } if (more_fixups.count() != 0) { rendered.clearRetainingCapacity(); - try astgen_tree.renderToArrayList(&rendered, more_fixups); + try astgen_tree.render(gpa, &rendered.writer, more_fixups); } } } - try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.items }); + try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); // std.debug.print("trying this code:\n{s}\n", .{rendered.items}); const interestingness = try runCheck(arena, interestingness_argv.items); @@ -273,8 +272,8 @@ pub fn main() !void { // Revert the source back to not be transformed. fixups.clearRetainingCapacity(); rendered.clearRetainingCapacity(); - try tree.renderToArrayList(&rendered, fixups); - try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.items }); + try tree.render(gpa, &rendered.writer, fixups); + try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); return std.process.cleanExit(); } @@ -318,7 +317,7 @@ fn transformationsToFixups( arena: Allocator, root_source_file_path: []const u8, transforms: []const Walk.Transformation, - fixups: *Ast.Fixups, + fixups: *Ast.Render.Fixups, ) !void { fixups.clearRetainingCapacity(); @@ -359,7 +358,7 @@ fn transformationsToFixups( other_file_ast.deinit(gpa); } - var inlined_fixups: Ast.Fixups = .{}; + var inlined_fixups: Ast.Render.Fixups = .{}; defer inlined_fixups.deinit(gpa); if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| { inlined_fixups.rebase_imported_paths = dirname; @@ -382,16 +381,16 @@ fn transformationsToFixups( } } - var other_source = std.array_list.Managed(u8).init(gpa); + var other_source: std.io.Writer.Allocating = .init(gpa); defer other_source.deinit(); - try other_source.appendSlice("struct {\n"); - try other_file_ast.renderToArrayList(&other_source, inlined_fixups); - try other_source.appendSlice("}"); + try other_source.writer.writeAll("struct {\n"); + try other_file_ast.render(gpa, &other_source.writer, inlined_fixups); + try other_source.writer.writeAll("}"); try fixups.replace_nodes_with_string.put( gpa, inline_imported_file.builtin_call_node, - try arena.dupe(u8, other_source.items), + try arena.dupe(u8, other_source.written()), ); }, }; diff --git a/lib/compiler/reduce/Walk.zig b/lib/compiler/reduce/Walk.zig index 4e41fdf1a2..7a448fad21 100644 --- a/lib/compiler/reduce/Walk.zig +++ b/lib/compiler/reduce/Walk.zig @@ -501,6 +501,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { .@"asm", => return walkAsm(w, ast.fullAsm(node).?), + .asm_legacy => { + return walkAsmLegacy(w, ast.legacyAsm(node).?); + }, + .enum_literal => { return walkIdentifier(w, ast.nodeMainToken(node)); // name }, @@ -665,7 +669,7 @@ fn walkStructInit( fn walkCall(w: *Walk, call: Ast.full.Call) Error!void { try walkExpression(w, call.ast.fn_expr); - try walkParamList(w, call.ast.params); + try walkExpressions(w, call.ast.params); } fn walkSlice( @@ -830,7 +834,7 @@ fn walkWhile(w: *Walk, node_index: Ast.Node.Index, while_node: Ast.full.While) E } fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void { - try walkParamList(w, for_node.ast.inputs); + try walkExpressions(w, for_node.ast.inputs); try walkExpression(w, for_node.ast.then_expr); if (for_node.ast.else_expr.unwrap()) |else_expr| { try walkExpression(w, else_expr); @@ -874,15 +878,12 @@ fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void { try walkExpression(w, asm_node.ast.template); - for (asm_node.ast.items) |item| { - try walkExpression(w, item); - } + try walkExpressions(w, asm_node.ast.items); } -fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void { - for (params) |param_node| { - try walkExpression(w, param_node); - } +fn walkAsmLegacy(w: *Walk, asm_node: Ast.full.AsmLegacy) Error!void { + try walkExpression(w, asm_node.ast.template); + try walkExpressions(w, asm_node.ast.items); } /// Check if it is already gutted (i.e. its body replaced with `@trap()`). From 38ef3f899789839240437eced83f2d708b55b693 Mon Sep 17 00:00:00 2001 From: John Benediktsson Date: Fri, 22 Aug 2025 12:35:18 -0700 Subject: [PATCH 006/112] Merge pull request #24926 from mrjbq7/http-fetch http.Client: don't forget to flush --- lib/std/http/Client.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index b507128c9c..b6a1483c1d 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1797,9 +1797,10 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { if (options.payload) |payload| { req.transfer_encoding = .{ .content_length = payload.len }; - var body = try req.sendBody(&.{}); + var body = try req.sendBodyUnflushed(&.{}); try body.writer.writeAll(payload); try body.end(); + try req.connection.?.flush(); } else { try req.sendBodiless(); } From 98585af51d2611584d329ab4defe444a44b8f35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 21 Aug 2025 01:10:35 +0200 Subject: [PATCH 007/112] std.fs.Dir: fix updateFile() to flush the file before updating its times AtomicFile.finish() calls flush() which renders any previous updateTimes() calls useless. Regression introduced in f2a3ac7c0534a74ee544fdf6ef9d2176a8d62389. Closes #24927. --- lib/std/fs/Dir.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 16418d216f..768efdeff5 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -2583,8 +2583,9 @@ pub fn updateFile( error.ReadFailed => return src_reader.err.?, error.WriteFailed => return atomic_file.file_writer.err.?, }; + try atomic_file.flush(); try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); - try atomic_file.finish(); + try atomic_file.renameIntoPlace(); return .stale; } From a51e88c3b108acfa1ea1823ed31d78870e662fc0 Mon Sep 17 00:00:00 2001 From: Erik Schlyter Date: Mon, 25 Aug 2025 17:59:42 +0200 Subject: [PATCH 008/112] Fix #24999: copy left-overs before we XOR into c. (#25001) It is important we copy the left-overs in the message *before* we XOR it into the ciphertext, because if we're encrypting in-place (i.e., m == c), we will manipulate the message that will be used for tag generation. This will generate faulty tags when message length doesn't conform with 16 byte blocks. --- lib/std/crypto/aes_ocb.zig | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/std/crypto/aes_ocb.zig b/lib/std/crypto/aes_ocb.zig index b5a0e24f1a..44a0fb2430 100644 --- a/lib/std/crypto/aes_ocb.zig +++ b/lib/std/crypto/aes_ocb.zig @@ -155,12 +155,12 @@ fn AesOcb(comptime Aes: anytype) type { xorWith(&offset, lx.star); var pad = offset; aes_enc_ctx.encrypt(&pad, &pad); - for (m[i * 16 ..], 0..) |x, j| { - c[i * 16 + j] = pad[j] ^ x; - } var e = [_]u8{0} ** 16; @memcpy(e[0..leftover], m[i * 16 ..][0..leftover]); e[leftover] = 0x80; + for (m[i * 16 ..], 0..) |x, j| { + c[i * 16 + j] = pad[j] ^ x; + } xorWith(&sum, e); } var e = xorBlocks(xorBlocks(sum, offset), lx.dol); @@ -354,3 +354,32 @@ test "AesOcb test vector 4" { try Aes128Ocb.decrypt(&m2, &c, tag, &ad, nonce, k); assert(mem.eql(u8, &m, &m2)); } + +test "AesOcb in-place encryption-decryption" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + + var k: [Aes128Ocb.key_length]u8 = undefined; + var nonce: [Aes128Ocb.nonce_length]u8 = undefined; + var tag: [Aes128Ocb.tag_length]u8 = undefined; + var m: [40]u8 = undefined; + var original_m: [m.len]u8 = undefined; + _ = try hexToBytes(&k, "000102030405060708090A0B0C0D0E0F"); + _ = try hexToBytes(&m, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627"); + _ = try hexToBytes(&nonce, "BBAA9988776655443322110D"); + const ad = m; + + @memcpy(&original_m, &m); + + Aes128Ocb.encrypt(&m, &tag, &m, &ad, nonce, k); + + var expected_c: [m.len]u8 = undefined; + var expected_tag: [tag.len]u8 = undefined; + _ = try hexToBytes(&expected_tag, "ED07BA06A4A69483A7035490C5769E60"); + _ = try hexToBytes(&expected_c, "D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7A"); + + try testing.expectEqualSlices(u8, &expected_tag, &tag); + try testing.expectEqualSlices(u8, &expected_c, &m); + try Aes128Ocb.decrypt(&m, &m, tag, &ad, nonce, k); + + try testing.expectEqualSlices(u8, &original_m, &m); +} From f96321be90394c47225c41c8b760fb403be09e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 24 Aug 2025 22:16:20 +0200 Subject: [PATCH 009/112] std.zig.system: fix check for sparc "v8+" in getExternalExecutor() Targeting v9 on sparc32 doesn't by itself imply "v8+" mode (64-bit instructions in 32-bit code). --- lib/std/zig/system.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index ba43543ee2..14544bdb23 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -109,7 +109,7 @@ pub fn getExternalExecutor( .riscv64 => Executor{ .qemu = "qemu-riscv64" }, .s390x => Executor{ .qemu = "qemu-s390x" }, .sparc => Executor{ - .qemu = if (candidate.cpu.has(.sparc, .v9)) + .qemu = if (candidate.cpu.has(.sparc, .v8plus)) "qemu-sparc32plus" else "qemu-sparc", From 65af60edd004d656beff8f79e4dc19915cfcc6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 25 Aug 2025 03:50:18 +0200 Subject: [PATCH 010/112] ubsan-rt: export symbols with hidden visibility see 092352ec63d3a4e9ff59a5d8ad8f119bf0eda468 --- lib/ubsan_rt.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ubsan_rt.zig b/lib/ubsan_rt.zig index 63f1d466cb..ffca7eff28 100644 --- a/lib/ubsan_rt.zig +++ b/lib/ubsan_rt.zig @@ -627,7 +627,7 @@ fn exportHandler( // Work around x86_64 backend limitation. const linkage = if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) .internal else .weak; const N = "__ubsan_handle_" ++ sym_name; - @export(handler, .{ .name = N, .linkage = linkage }); + @export(handler, .{ .name = N, .linkage = linkage, .visibility = if (linkage == .internal) .default else .hidden }); } fn exportHandlerWithAbort( @@ -639,11 +639,11 @@ fn exportHandlerWithAbort( const linkage = if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) .internal else .weak; { const N = "__ubsan_handle_" ++ sym_name; - @export(handler, .{ .name = N, .linkage = linkage }); + @export(handler, .{ .name = N, .linkage = linkage, .visibility = if (linkage == .internal) .default else .hidden }); } { const N = "__ubsan_handle_" ++ sym_name ++ "_abort"; - @export(abort_handler, .{ .name = N, .linkage = linkage }); + @export(abort_handler, .{ .name = N, .linkage = linkage, .visibility = if (linkage == .internal) .default else .hidden }); } } From 48600bc2b3fbc3843a59d4eab7ce90a425f36272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 25 Aug 2025 06:45:00 +0200 Subject: [PATCH 011/112] Compilation: avoid ZCU strategy for ubsan-rt in Windows DLLs --- src/Compilation.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 43f4e434d3..6961489e59 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2053,7 +2053,14 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options break :s .none; // only LLD can handle ubsan-rt for this target } else true, }; - if (have_zcu and (!need_llvm or use_llvm)) break :s .zcu; + if (have_zcu and (!need_llvm or use_llvm)) { + // ubsan-rt's exports use hidden visibility. If we're building a Windows DLL and + // exported functions are going to be dllexported, LLVM will complain that + // dllexported functions must use default or protected visibility. So we can't use + // the ZCU strategy in this case. + if (options.config.dll_export_fns) break :s .lib; + break :s .zcu; + } if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm if (is_exe_or_dyn_lib) break :s .lib; break :s .obj; From 17928ef58b1ab110c1f944b5991540d4a938b057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnau=20Camprub=C3=AD?= Date: Sun, 24 Aug 2025 13:26:31 +0200 Subject: [PATCH 012/112] Add `test-obj` to the help message --- src/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.zig b/src/main.zig index 02b1b8f84b..4646ff285a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -87,6 +87,7 @@ const normal_usage = \\ build-lib Create library from source or object files \\ build-obj Create object from source or object files \\ test Perform unit testing + \\ test-obj Create object for unit testing \\ run Create executable and run immediately \\ \\ ast-check Look for simple compile errors in any set of files From d4e5d1e15c1c8ff85f611e82cb9fd7f7084a7b38 Mon Sep 17 00:00:00 2001 From: 87 <178735591+87flowers@users.noreply.github.com> Date: Sun, 15 Jun 2025 13:29:15 +0100 Subject: [PATCH 013/112] lib: Reword documentation for realloc to clarify size "byte size" is confusing. Clarify we mean number of items. --- lib/std/mem/Allocator.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index b81a916702..897f1d2b31 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -358,8 +358,10 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) t: { return mem.bytesAsSlice(T, new_memory); } -/// This function requests a new byte size for an existing allocation, which +/// This function requests a new size for an existing allocation, which /// can be larger, smaller, or the same size as the old memory allocation. +/// The result is an array of `new_n` items of the same type as the existing +/// allocation. /// /// If `new_n` is 0, this is the same as `free` and it always succeeds. /// From 38173d63d38ff6afb43ee7b52cc5ea09ede38765 Mon Sep 17 00:00:00 2001 From: Nathaniel Ketema Date: Fri, 22 Aug 2025 14:00:11 -0500 Subject: [PATCH 014/112] fix: fix typo in comment by removing repeated 'business' word --- lib/init/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/init/build.zig b/lib/init/build.zig index 481b586a44..88c42f760e 100644 --- a/lib/init/build.zig +++ b/lib/init/build.zig @@ -44,7 +44,7 @@ pub fn build(b: *std.Build) void { // Here we define an executable. An executable needs to have a root module // which needs to expose a `main` function. While we could add a main function // to the module defined above, it's sometimes preferable to split business - // business logic and the CLI into two separate modules. + // logic and the CLI into two separate modules. // // If your goal is to create a Zig library for others to use, consider if // it might benefit from also exposing a CLI tool. A parser library for a From 9ef2208e4b2a51f9c800295674265a33804bbc8a Mon Sep 17 00:00:00 2001 From: "Becker A." Date: Mon, 18 Aug 2025 15:49:48 -0600 Subject: [PATCH 015/112] Update powi.zig to fix docstring formatting Without this change, the docs are formatted s.t. the text "Edge case rules ordered by precedence:" is appended onto the prior line of text "Underflow: Absolute value of result smaller than 1", instead of getting its own line. --- lib/std/math/powi.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/math/powi.zig b/lib/std/math/powi.zig index aed79e097c..285dc5e209 100644 --- a/lib/std/math/powi.zig +++ b/lib/std/math/powi.zig @@ -13,6 +13,7 @@ const testing = std.testing; /// Errors: /// - Overflow: Integer overflow or Infinity /// - Underflow: Absolute value of result smaller than 1 +/// /// Edge case rules ordered by precedence: /// - powi(T, x, 0) = 1 unless T is i1, i0, u0 /// - powi(T, 0, x) = 0 when x > 0 From b2578329afc672f24503cbf580ff73a4a1625f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 25 Aug 2025 02:44:57 +0200 Subject: [PATCH 016/112] zig cc: don't pass -mcmodel for assembly files It does nothing but generate a warning for these. --- src/Compilation.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6961489e59..44c0952d0a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6843,10 +6843,6 @@ pub fn addCCArgs( try argv.append("-municode"); } - if (mod.code_model != .default) { - try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)})); - } - try argv.ensureUnusedCapacity(2); switch (comp.config.debug_format) { .strip => {}, @@ -7136,6 +7132,10 @@ pub fn addCCArgs( .ll, .bc, => { + if (mod.code_model != .default) { + try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)})); + } + if (target_util.clangSupportsTargetCpuArg(target)) { if (target.cpu.model.llvm_name) |llvm_name| { try argv.appendSlice(&[_][]const u8{ From 6f07cc95d0c17c7778b37eea646a20886bc7d34c Mon Sep 17 00:00:00 2001 From: Sardorbek Imomaliev <3041675+imomaliev@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:25:53 +0100 Subject: [PATCH 017/112] add macOS handling for totalSystemMemory (#24903) * add macos handling for totalSystemMemory * fix return type cast for .freebsd in totalSystemMemory * add handling for the whole Darwin family in totalSystemMemory --- lib/std/process.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 2e9cb1fe85..305bcca24b 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1762,7 +1762,20 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { error.NameTooLong, error.UnknownName => unreachable, else => return error.UnknownTotalSystemMemory, }; - return @as(usize, @intCast(physmem)); + return @as(u64, @intCast(physmem)); + }, + // whole Darwin family + .driverkit, .ios, .macos, .tvos, .visionos, .watchos => { + // "hw.memsize" returns uint64_t + var physmem: u64 = undefined; + var len: usize = @sizeOf(u64); + posix.sysctlbynameZ("hw.memsize", &physmem, &len, null, 0) catch |err| switch (err) { + error.PermissionDenied => unreachable, // only when setting values, + error.SystemResources => unreachable, // memory already on the stack + error.UnknownName => unreachable, // constant, known good value + else => return error.UnknownTotalSystemMemory, + }; + return physmem; }, .openbsd => { const mib: [2]c_int = [_]c_int{ From caa89c7601bba7520bae0e295cce1b7db896041c Mon Sep 17 00:00:00 2001 From: Qun He Date: Mon, 25 Aug 2025 09:38:44 +0800 Subject: [PATCH 018/112] os.linux: faccessat wrapper prefer to faccessat syscall when flags is zero. This make `fs.Dir.access` has compatibility like the zig version before. With this change the `zig build --search-prefix` command would work again like the zig 0.14 version when used on Ubuntu22.04, kernel version 5.4. --- lib/std/os/linux.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 1aa9ea2852..7db0634271 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1238,11 +1238,14 @@ pub fn access(path: [*:0]const u8, mode: u32) usize { if (@hasField(SYS, "access")) { return syscall2(.access, @intFromPtr(path), mode); } else { - return syscall4(.faccessat, @as(usize, @bitCast(@as(isize, AT.FDCWD))), @intFromPtr(path), mode, 0); + return faccessat(AT.FDCWD, path, mode, 0); } } pub fn faccessat(dirfd: i32, path: [*:0]const u8, mode: u32, flags: u32) usize { + if (flags == 0) { + return syscall3(.faccessat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), mode); + } return syscall4(.faccessat2, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), mode, flags); } From 628357eeb6fd017bd374e3eff3a3c506244559d9 Mon Sep 17 00:00:00 2001 From: Ian Johnson Date: Mon, 26 May 2025 19:23:42 +0000 Subject: [PATCH 019/112] std.Build.Step.Compile: fix race condition in args file creation Fixes #23993 Previously, if multiple build processes tried to create the same args file, there was a race condition with the use of the non-atomic `writeFile` function which could cause a spawned compiler to read an empty or incomplete args file. This commit avoids the race condition by first writing to a temporary file with a random path and renaming it to the desired path. --- lib/std/Build/Step/Compile.zig | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 79d3694c02..b64ce59778 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -1827,7 +1827,26 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash}); const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; - try b.cache_root.handle.writeFile(.{ .sub_path = args_file, .data = args }); + if (b.cache_root.handle.access(args_file, .{})) |_| { + // The args file is already present from a previous run. + } else |err| switch (err) { + error.FileNotFound => { + try b.cache_root.handle.makePath("tmp"); + const rand_int = std.crypto.random.int(u64); + const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + try b.cache_root.handle.writeFile(.{ .sub_path = tmp_path, .data = args }); + defer b.cache_root.handle.deleteFile(tmp_path) catch { + // It's fine if the temporary file can't be cleaned up. + }; + b.cache_root.handle.rename(tmp_path, args_file) catch |rename_err| switch (rename_err) { + error.PathAlreadyExists => { + // The args file was created by another concurrent build process. + }, + else => |other_err| return other_err, + }; + }, + else => |other_err| return other_err, + } const resolved_args_file = try mem.concat(arena, u8, &.{ "@", From e323dbfd1524b64c785a75035ef1d849d478f173 Mon Sep 17 00:00:00 2001 From: AdamGoertz <36753247+AdamGoertz@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:54:00 -0400 Subject: [PATCH 020/112] std.Build.Step.TranslateC: forward --cache-dir and --global-cache-dir flags --- lib/std/Build/Step/TranslateC.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index dabf2200df..b8ef5d334f 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -163,6 +163,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try argv_list.append("-fno-clang"); } + try argv_list.append("--cache-dir"); + try argv_list.append(b.cache_root.path orelse "."); + + try argv_list.append("--global-cache-dir"); + try argv_list.append(b.graph.global_cache_root.path orelse "."); + try argv_list.append("--listen=-"); if (!translate_c.target.query.isNative()) { From a4cb63665812384376e7730fb7c394b6a38c57f8 Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 26 Aug 2025 09:49:55 +0100 Subject: [PATCH 021/112] std.Build.WebServer: fix race Just a typo: I wasn't actually using the duped message, so the message I sent could be freed in this interval. --- lib/std/Build/WebServer.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 780e249323..451f4b9d34 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -323,7 +323,7 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { // Temporarily unlock, then re-lock after the message is sent. ws.time_report_mutex.unlock(); defer ws.time_report_mutex.lock(); - try sock.writeMessage(msg, .binary); + try sock.writeMessage(owned_msg, .binary); } } From 9c3e09cbeea79a05db965e82e12ac5bec640a66c Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:18:40 +0200 Subject: [PATCH 022/112] Fix TLS 1.2 client key exchange to use negotiated named group (#25007) The TLS 1.2 implementation was incorrectly hardcoded to always send the secp256r1 public key in the client key exchange message, regardless of which elliptic curve the server actually negotiated. This caused TLS handshake failures with servers that preferred other curves like X25519. This fix: - Tracks the negotiated named group from the server key exchange message - Dynamically selects the correct public key (X25519, secp256r1, or secp384r1) based on what the server negotiated - Properly constructs the client key exchange message with the appropriate key size for each curve type Fixes TLS 1.2 connections to servers like ziglang.freetls.fastly.net that prefer X25519 over secp256r1. --- lib/std/crypto/tls/Client.zig | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index f9c897e3f0..8cfa942399 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -320,6 +320,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client var handshake_state: HandshakeState = .hello; var handshake_cipher: tls.HandshakeCipher = undefined; var main_cert_pub_key: CertificatePublicKey = undefined; + var tls12_negotiated_group: ?tls.NamedGroup = null; const now_sec = std.time.timestamp(); var cleartext_fragment_start: usize = 0; @@ -679,6 +680,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client const curve_type = hsd.decode(u8); if (curve_type != 0x03) return error.TlsIllegalParameter; // named_curve const named_group = hsd.decode(tls.NamedGroup); + tls12_negotiated_group = named_group; const key_size = hsd.decode(u8); try hsd.ensure(key_size); const server_pub_key = hsd.slice(key_size); @@ -691,10 +693,19 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client if (cipher_state != .cleartext) return error.TlsUnexpectedMessage; if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage; - const client_key_exchange_msg = .{@intFromEnum(tls.ContentType.handshake)} ++ + const public_key_bytes: []const u8 = switch (tls12_negotiated_group orelse .secp256r1) { + .secp256r1 => &key_share.secp256r1_kp.public_key.toUncompressedSec1(), + .secp384r1 => &key_share.secp384r1_kp.public_key.toUncompressedSec1(), + .x25519 => &key_share.x25519_kp.public_key, + else => return error.TlsIllegalParameter, + }; + + const client_key_exchange_prefix = .{@intFromEnum(tls.ContentType.handshake)} ++ int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ - array(u16, u8, .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++ - array(u24, u8, array(u8, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1()))); + int(u16, @intCast(public_key_bytes.len + 5)) ++ // record length + .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++ + int(u24, @intCast(public_key_bytes.len + 1)) ++ // handshake message length + .{@as(u8, @intCast(public_key_bytes.len))}; // public key length const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++ int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec}); @@ -703,7 +714,8 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client inline else => |*p| { const P = @TypeOf(p.*).A; p.transcript_hash.update(wrapped_handshake); - p.transcript_hash.update(client_key_exchange_msg[tls.record_header_len..]); + p.transcript_hash.update(client_key_exchange_prefix[tls.record_header_len..]); + p.transcript_hash.update(public_key_bytes); const master_secret = hmacExpandLabel(P.Hmac, pre_master_secret, &.{ "master secret", &client_hello_rand, @@ -757,8 +769,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client nonce, pv.app_cipher.client_write_key, ); - var all_msgs_vec: [3][]const u8 = .{ - &client_key_exchange_msg, + var all_msgs_vec: [4][]const u8 = .{ + &client_key_exchange_prefix, + public_key_bytes, &client_change_cipher_spec_msg, &client_verify_msg, }; From 9dc3fce4c4a299df57a664f0c4dd6d8dfd07986d Mon Sep 17 00:00:00 2001 From: Brandon Black Date: Mon, 25 Aug 2025 13:38:33 -0500 Subject: [PATCH 023/112] std.c: add correct SOMAXCONN for BSDs Note the previous "28" here for openbsd was some kind of copy error long ago. That's the value of KERN.SOMAXCONN, which is an entirely different thing. --- lib/std/c.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9d5e5bf9ab..65b793bce8 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -6664,7 +6664,12 @@ pub const SOMAXCONN = switch (native_os) { .windows => ws2_32.SOMAXCONN, // https://github.com/SerenityOS/serenity/blob/ac44ec5ebc707f9dd0c3d4759a1e17e91db5d74f/Kernel/API/POSIX/sys/socket.h#L128 .solaris, .illumos, .serenity => 128, - .openbsd => 28, + // https://github.com/freebsd/freebsd-src/blob/9ab31f821ad1c6bad474510447387c50bef2c24c/sys/sys/socket.h#L434 + // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/fd3d1949d526ffa646e57037770acd6f2f3bb617/sys/sys/socket.h#L393 + // https://github.com/NetBSD/src/blob/a673fb3f8487e974c669216064f7588207229fea/sys/sys/socket.h#L472 + // https://github.com/openbsd/src/blob/8ba9cd88f10123fef7af805b8e5ccc2463ad8fa4/sys/sys/socket.h#L483 + // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/socket.h#L815 + .freebsd, .dragonfly, .netbsd, .openbsd, .driverkit, .macos, .ios, .tvos, .watchos, .visionos => 128, else => void, }; pub const IFNAMESIZE = switch (native_os) { From 49f0122e413ee5d4640878985320e36b7ce7cc26 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 26 Aug 2025 19:15:29 -0400 Subject: [PATCH 024/112] x86_64: fix multiplication overflow detection with adx Closes #24965 --- src/arch/x86_64/CodeGen.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5b0181cdcd..5c76b7af92 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -31490,7 +31490,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._x, .ado, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._x, .adc, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._mp, .j, .@"2f", ._, ._, ._ }, - .{ .@"1:", ._, .@"or", .tmp8q, .leai(.tmp1q, .tmp4), ._, ._ }, + .{ .@"1:", ._, .@"or", .tmp8q, .leaid(.tmp1q, .tmp4, 8), ._, ._ }, .{ .@"2:", ._, .sub, .tmp4d, .si(8), ._, ._ }, .{ ._, ._ae, .j, .@"1b", ._, ._, ._ }, .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, @@ -31814,7 +31814,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._x, .ado, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._x, .adc, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._mp, .j, .@"2f", ._, ._, ._ }, - .{ .@"1:", ._, .@"or", .tmp8q, .leai(.tmp1q, .tmp4), ._, ._ }, + .{ .@"1:", ._, .@"or", .tmp8q, .leaid(.tmp1q, .tmp4, 8), ._, ._ }, .{ .@"2:", ._, .sub, .tmp4d, .si(8), ._, ._ }, .{ ._, ._ae, .j, .@"1b", ._, ._, ._ }, .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, @@ -32175,7 +32175,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._x, .ado, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._x, .adc, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._mp, .j, .@"2f", ._, ._, ._ }, - .{ .@"1:", ._, .@"or", .tmp8q, .leai(.tmp1q, .tmp4), ._, ._ }, + .{ .@"1:", ._, .@"or", .tmp8q, .leaid(.tmp1q, .tmp4, 8), ._, ._ }, .{ .@"2:", ._, .sub, .tmp4d, .si(8), ._, ._ }, .{ ._, ._ae, .j, .@"1b", ._, ._, ._ }, .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, @@ -55062,7 +55062,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._x, .ado, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._x, .adc, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._mp, .j, .@"2f", ._, ._, ._ }, - .{ .@"1:", ._, .@"or", .tmp8q, .leai(.tmp1q, .tmp4), ._, ._ }, + .{ .@"1:", ._, .@"or", .tmp8q, .leaid(.tmp1q, .tmp4, 8), ._, ._ }, .{ .@"2:", ._, .sub, .tmp4d, .si(8), ._, ._ }, .{ ._, ._ae, .j, .@"1b", ._, ._, ._ }, .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, @@ -55361,7 +55361,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._x, .ado, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._x, .adc, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._mp, .j, .@"2f", ._, ._, ._ }, - .{ .@"1:", ._, .@"or", .tmp8q, .leai(.tmp1q, .tmp4), ._, ._ }, + .{ .@"1:", ._, .@"or", .tmp8q, .leaid(.tmp1q, .tmp4, 8), ._, ._ }, .{ .@"2:", ._, .sub, .tmp4d, .si(8), ._, ._ }, .{ ._, ._ae, .j, .@"1b", ._, ._, ._ }, .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, @@ -55690,7 +55690,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._x, .ado, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._x, .adc, .tmp8q, .tmp3q, ._, ._ }, .{ ._, ._mp, .j, .@"2f", ._, ._, ._ }, - .{ .@"1:", ._, .@"or", .tmp8q, .leai(.tmp1q, .tmp4), ._, ._ }, + .{ .@"1:", ._, .@"or", .tmp8q, .leaid(.tmp1q, .tmp4, 8), ._, ._ }, .{ .@"2:", ._, .sub, .tmp4d, .si(8), ._, ._ }, .{ ._, ._ae, .j, .@"1b", ._, ._, ._ }, .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, From 1231c8fbbd8be33674d33a302ddcb5dbcf52e2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 28 Aug 2025 01:21:58 +0200 Subject: [PATCH 025/112] std.os.linux: powerpc syscalls clobber ctr and xer https://git.musl-libc.org/cgit/musl/commit/?id=f6944eb3c4ce1c97dc39dc36d32390dc9f70b67b --- lib/std/os/linux/powerpc.zig | 14 +++++++------- lib/std/os/linux/powerpc64.zig | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index 018ea2309c..c8e03a1b04 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -22,7 +22,7 @@ pub fn syscall0(number: SYS) usize { \\ 1: : [ret] "={r3}" (-> usize), : [number] "{r0}" (@intFromEnum(number)), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall1(number: SYS, arg1: usize) usize { @@ -34,7 +34,7 @@ pub fn syscall1(number: SYS, arg1: usize) usize { : [ret] "={r3}" (-> usize), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { @@ -47,7 +47,7 @@ pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { @@ -61,7 +61,7 @@ pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { @@ -76,7 +76,7 @@ pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { @@ -92,7 +92,7 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall6( @@ -117,7 +117,7 @@ pub fn syscall6( [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), [arg6] "{r8}" (arg6), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn clone() callconv(.naked) usize { diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index d737738c4c..ff07ac9180 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -22,7 +22,7 @@ pub fn syscall0(number: SYS) usize { \\ 1: : [ret] "={r3}" (-> usize), : [number] "{r0}" (@intFromEnum(number)), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall1(number: SYS, arg1: usize) usize { @@ -34,7 +34,7 @@ pub fn syscall1(number: SYS, arg1: usize) usize { : [ret] "={r3}" (-> usize), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { @@ -47,7 +47,7 @@ pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { @@ -61,7 +61,7 @@ pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { @@ -76,7 +76,7 @@ pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { @@ -92,7 +92,7 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall6( @@ -117,7 +117,7 @@ pub fn syscall6( [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), [arg6] "{r8}" (arg6), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn clone() callconv(.naked) usize { From cb9821a827f972252f8c02775bd2df07435f04c6 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 28 Aug 2025 00:11:56 +0900 Subject: [PATCH 026/112] std.os.uefi: fix type error at MemoryType.format() --- lib/std/os/uefi/tables.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/uefi/tables.zig b/lib/std/os/uefi/tables.zig index ad163dbdbf..90e525c507 100644 --- a/lib/std/os/uefi/tables.zig +++ b/lib/std/os/uefi/tables.zig @@ -90,7 +90,7 @@ pub const MemoryType = enum(u32) { return @truncate(as_int - vendor_start); } - pub fn format(self: MemoryType, w: *std.io.Writer) std.io.WriteError!void { + pub fn format(self: MemoryType, w: *std.io.Writer) std.io.Writer.Error!void { if (self.toOem()) |oemval| try w.print("OEM({X})", .{oemval}) else if (self.toVendor()) |vendorval| From cbc3c0dc594341faddb8841fb466254f2f6f3b35 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Wed, 27 Aug 2025 22:16:21 -0700 Subject: [PATCH 027/112] process.totalSystemMemory: Avoid overflow on Linux when totalram is a 32-bit usize Fixes #25038 --- lib/std/process.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 305bcca24b..efe0a9b0e9 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1753,7 +1753,8 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { if (std.os.linux.E.init(result) != .SUCCESS) { return error.UnknownTotalSystemMemory; } - return info.totalram * info.mem_unit; + // Promote to u64 to avoid overflow on systems where info.totalram is a 32-bit usize + return @as(u64, info.totalram) * info.mem_unit; }, .freebsd => { var physmem: c_ulong = undefined; From 61f447523350a467fde242b846ed27b084ed1f50 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Aug 2025 18:32:51 -0700 Subject: [PATCH 028/112] LLVM backend:fix align 1 sret parameter load returned closes #25067 --- src/codegen/llvm.zig | 1 + test/behavior/struct.zig | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5c683a965a..1b12add122 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9761,6 +9761,7 @@ pub const FuncGen = struct { const ptr = try fg.resolveInst(ty_op.operand); elide: { + if (ptr_info.flags.alignment != .none) break :elide; if (!isByRef(Type.fromInterned(ptr_info.child), zcu)) break :elide; if (!canElideLoad(fg, body_tail)) break :elide; return ptr; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 29e9986e5e..f1c10a1461 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -2136,3 +2136,21 @@ test "field access through mem ptr arg" { &.{ .field = 0x0ced271f }, ) == 0x0ced271f); } + +test "align 1 struct parameter dereferenced and returned" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + + const S = extern struct { + a: u32, + + fn gimme(p: *align(1) @This()) @This() { + return p.*; + } + }; + var buffer: [5]u8 align(4) = .{ 1, 2, 3, 4, 5 }; + const s = S.gimme(@ptrCast(buffer[1..])); + switch (native_endian) { + .big => try expect(s.a == 0x02030405), + .little => try expect(s.a == 0x05040302), + } +} From 00fb5e34df33deaed78210157c4ee1ad189351ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 5 Aug 2025 23:01:42 +0200 Subject: [PATCH 029/112] std.Target: fix alignment for int/long types on m68k --- lib/std/Target.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 2aa3e0a70c..6613820255 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -3075,6 +3075,10 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 { }, else => {}, }, + .m68k => switch (c_type) { + .int, .uint, .long, .ulong => return 2, + else => {}, + }, .powerpc, .powerpcle, .powerpc64, .powerpc64le => switch (target.os.tag) { .aix => switch (c_type) { .double, .longdouble => return 4, @@ -3175,6 +3179,10 @@ pub fn cTypePreferredAlignment(target: *const Target, c_type: CType) u16 { else => {}, }, }, + .m68k => switch (c_type) { + .int, .uint, .long, .ulong => return 2, + else => {}, + }, .wasm32, .wasm64 => switch (target.os.tag) { .emscripten => switch (c_type) { .longdouble => return 8, From d8d497ffe4a3d48de3fd5402537329af9ba2870d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 4 Aug 2025 20:46:40 +0200 Subject: [PATCH 030/112] std.Thread: make unreachable errors in sleep() clearer --- lib/std/Thread.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 63af3796b7..4c559cf30a 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -83,10 +83,9 @@ pub fn sleep(nanoseconds: u64) void { req = rem; continue; }, - .FAULT, - .INVAL, - .OPNOTSUPP, - => unreachable, + .FAULT => unreachable, + .INVAL => unreachable, + .OPNOTSUPP => unreachable, else => return, } } From b72a02c592ba7a7b201b5a32af329b3730b148e3 Mon Sep 17 00:00:00 2001 From: Brandon Mercer Date: Sat, 30 Aug 2025 18:04:32 -0400 Subject: [PATCH 031/112] Populate MSG struct for OpenBSD (#25076) * update the MSG struct with the correct values for openbsd * add comment with link to sys/sys/socket.h --------- Co-authored-by: Brandon Mercer --- lib/std/c.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index 65b793bce8..37f2790a51 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -5685,6 +5685,23 @@ pub const MSG = switch (native_os) { pub const WAITFORONE = 0x2000; pub const NOTIFICATION = 0x4000; }, + // https://github.com/openbsd/src/blob/42a7be81bef70c04732f45ec573622effe56b563/sys/sys/socket.h#L506 + .openbsd => struct { + pub const OOB = 0x1; + pub const PEEK = 0x2; + pub const DONTROUTE = 0x4; + pub const EOR = 0x8; + pub const TRUNC = 0x10; + pub const CTRUNC = 0x20; + pub const WAITALL = 0x40; + pub const DONTWAIT = 0x80; + pub const BCAST = 0x100; + pub const MCAST = 0x200; + pub const NOSIGNAL = 0x400; + pub const CMSG_CLOEXEC = 0x800; + pub const WAITFORONE = 0x1000; + pub const CMSG_CLOFORK = 0x2000; + }, else => void, }; pub const SOCK = switch (native_os) { From dcddbcaa604d9228caddea7b2cd2668bf392149f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadej=20Ga=C5=A1parovi=C4=8D?= Date: Sun, 31 Aug 2025 15:40:21 +0200 Subject: [PATCH 032/112] Fix regression: std.http.Client basic authorization sending user:user instead of user:password when passed in URI --- lib/std/http/Client.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index b6a1483c1d..3757391735 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1375,7 +1375,7 @@ pub const basic_authorization = struct { var buf: [max_user_len + 1 + max_password_len]u8 = undefined; var w: Writer = .fixed(&buf); const user: Uri.Component = uri.user orelse .empty; - const password: Uri.Component = uri.user orelse .empty; + const password: Uri.Component = uri.password orelse .empty; user.formatUser(&w) catch unreachable; w.writeByte(':') catch unreachable; password.formatPassword(&w) catch unreachable; From 76e2764eb1405eabe53b10bccdde7684432c4596 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 28 Aug 2025 04:20:35 -0700 Subject: [PATCH 033/112] Fix `-M` and `--dep` splitting on every = instead of just the first Before this commit, -Mfoo=bar=baz would be incorrectly split into mod_name: `foo` and root_src_orig: `bar` After this commit, -Mfoo=bar=baz will be correctly split into mod_name: `foo` and root_src_orig: `bar=baz` Closes #25059 --- src/main.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.zig b/src/main.zig index 4646ff285a..cc334628f6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1064,8 +1064,8 @@ fn buildOutputType( } } else if (mem.eql(u8, arg, "--dep")) { var it = mem.splitScalar(u8, args_iter.nextOrFatal(), '='); - const key = it.next().?; - const value = it.next() orelse key; + const key = it.first(); + const value = if (it.peek() != null) it.rest() else key; if (mem.eql(u8, key, "std") and !mem.eql(u8, value, "std")) { fatal("unable to import as '{s}': conflicts with builtin module", .{ key, @@ -1084,8 +1084,8 @@ fn buildOutputType( }); } else if (mem.startsWith(u8, arg, "-M")) { var it = mem.splitScalar(u8, arg["-M".len..], '='); - const mod_name = it.next().?; - const root_src_orig = it.next(); + const mod_name = it.first(); + const root_src_orig = if (it.peek() != null) it.rest() else null; try handleModArg( arena, mod_name, From 30ec163d14245ac392ccb3cc65220a89cded8576 Mon Sep 17 00:00:00 2001 From: Luna Schwalbe Date: Wed, 27 Aug 2025 19:09:05 +0200 Subject: [PATCH 034/112] BitcodeReader: parse blockinfo inside block Call start/endBlock before/after `parseBlockInfoBlock` in order to not use the current block context, which is wrong and leads to e.g. incorrect abbrevlen being used. --- lib/std/zig/llvm/BitcodeReader.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/llvm/BitcodeReader.zig b/lib/std/zig/llvm/BitcodeReader.zig index e0d1632527..a876dcd2eb 100644 --- a/lib/std/zig/llvm/BitcodeReader.zig +++ b/lib/std/zig/llvm/BitcodeReader.zig @@ -154,7 +154,11 @@ pub fn next(bc: *BitcodeReader) !?Item { Abbrev.Builtin.enter_subblock.toRecordId() => { const block_id: u32 = @intCast(record.operands[0]); switch (block_id) { - Block.block_info => try bc.parseBlockInfoBlock(), + Block.block_info => { + try bc.startBlock(Block.block_info, @intCast(record.operands[1])); + try bc.parseBlockInfoBlock(); + try bc.endBlock(); + }, Block.first_reserved...Block.last_standard => return error.UnsupportedBlockId, else => { try bc.startBlock(block_id, @intCast(record.operands[1])); From 52eb9e84fb5b06581cff626051d1f09be3de8eb8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 6 Sep 2025 11:50:59 -0700 Subject: [PATCH 035/112] langref: update "Choosing an Allocator" section and delete "Implementing an Allocator" section because it is out of scope. --- doc/langref.html.in | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 94eeff3f89..b7c9c7291e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6277,10 +6277,6 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
  • Are you linking libc? In this case, {#syntax#}std.heap.c_allocator{#endsyntax#} is likely the right choice, at least for your main allocator.
  • -
  • - Need to use the same allocator in multiple threads? Use one of your choice - wrapped around {#syntax#}std.heap.ThreadSafeAllocator{#endsyntax#} -
  • Is the maximum number of bytes that you will need bounded by a number known at {#link|comptime#}? In this case, use {#syntax#}std.heap.FixedBufferAllocator{#endsyntax#}. @@ -6290,7 +6286,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val cyclical pattern (such as a video game main loop, or a web server request handler), such that it would make sense to free everything at once at the end? In this case, it is recommended to follow this pattern: - {#code|cli_allocation.zig#} + {#code|cli_allocation.zig#} When using this kind of allocator, there is no need to free anything manually. Everything gets freed at once with the call to {#syntax#}arena.deinit(){#endsyntax#}. @@ -6313,14 +6309,18 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
  • Finally, if none of the above apply, you need a general purpose allocator. - Zig's general purpose allocator is available as a function that takes a {#link|comptime#} - {#link|struct#} of configuration options and returns a type. - Generally, you will set up one {#syntax#}std.heap.GeneralPurposeAllocator{#endsyntax#} in - your main function, and then pass it or sub-allocators around to various parts of your + If you are in Debug mode, {#syntax#}std.heap.DebugAllocator{#endsyntax#} is available as a + function that takes a {#link|comptime#} {#link|struct#} of configuration options and returns a type. + Generally, you will set up exactly one in your main function, and + then pass it or sub-allocators around to various parts of your application.
  • - You can also consider {#link|Implementing an Allocator#}. + If you are compiling in ReleaseFast mode, {#syntax#}std.heap.smp_allocator{#endsyntax#} is + a solid choice for a general purpose allocator. +
  • +
  • + You can also consider implementing an allocator.
  • {#header_close#} @@ -6355,17 +6355,6 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val

    TODO: thread local variables

    {#header_close#} - {#header_open|Implementing an Allocator#} -

    Zig programmers can implement their own allocators by fulfilling the Allocator interface. - In order to do this one must read carefully the documentation comments in std/mem.zig and - then supply a {#syntax#}allocFn{#endsyntax#} and a {#syntax#}resizeFn{#endsyntax#}. -

    -

    - There are many example allocators to look at for inspiration. Look at std/heap.zig and - {#syntax#}std.heap.GeneralPurposeAllocator{#endsyntax#}. -

    - {#header_close#} - {#header_open|Heap Allocation Failure#}

    Many programming languages choose to handle the possibility of heap allocation failure by From cbe1b4571b1fab519b2b4bb95fd7507c7b7626b0 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sat, 6 Sep 2025 01:13:34 -0400 Subject: [PATCH 036/112] webui: fixup build errors in fuzz / time_report --- lib/build-web/fuzz.zig | 12 ++++++------ lib/build-web/time_report.zig | 27 ++++++++++++++------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/build-web/fuzz.zig b/lib/build-web/fuzz.zig index 29b37e3465..c694e9e69e 100644 --- a/lib/build-web/fuzz.zig +++ b/lib/build-web/fuzz.zig @@ -101,22 +101,22 @@ const SourceLocationIndex = enum(u32) { fn sourceLocationLinkHtml( sli: SourceLocationIndex, - out: *std.ArrayListUnmanaged(u8), + out: *std.ArrayList(u8), focused: bool, - ) Allocator.Error!void { + ) error{OutOfMemory}!void { const sl = sli.ptr(); - try out.writer(gpa).print("", .{ + try out.print(gpa, "", .{ @as([]const u8, if (focused) " class=\"status-running\"" else ""), }); try sli.appendPath(out); - try out.writer(gpa).print(":{d}:{d} ", .{ + try out.print(gpa, ":{d}:{d} ", .{ sl.line, sl.column, @intFromEnum(sli), }); } - fn appendPath(sli: SourceLocationIndex, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { + fn appendPath(sli: SourceLocationIndex, out: *std.ArrayList(u8)) error{OutOfMemory}!void { const sl = sli.ptr(); const file = coverage.fileAt(sl.file); const file_name = coverage.stringAt(file.basename); @@ -294,7 +294,7 @@ fn updateStats() error{OutOfMemory}!void { } fn updateEntryPoints() error{OutOfMemory}!void { - var html: std.ArrayListUnmanaged(u8) = .empty; + var html: std.ArrayList(u8) = .empty; defer html.deinit(gpa); for (entry_points.items) |sli| { try html.appendSlice(gpa, "

  • "); diff --git a/lib/build-web/time_report.zig b/lib/build-web/time_report.zig index aa37e501be..1495b7207a 100644 --- a/lib/build-web/time_report.zig +++ b/lib/build-web/time_report.zig @@ -44,7 +44,7 @@ pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len); } -pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { +pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!void { const max_table_rows = 500; if (msg_bytes.len < @sizeOf(abi.CompileResult)) @panic("malformed CompileResult message"); @@ -166,10 +166,11 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { }); defer gpa.free(inner_html); - var file_table_html: std.ArrayListUnmanaged(u8) = .empty; - defer file_table_html.deinit(gpa); + var file_table_html: std.Io.Writer.Allocating = .init(gpa); + defer file_table_html.deinit(); + for (slowest_files[0..@min(max_table_rows, slowest_files.len)]) |file| { - try file_table_html.writer(gpa).print( + try file_table_html.writer.print( \\ \\ {f} \\ {D} @@ -187,17 +188,17 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { }); } if (slowest_files.len > max_table_rows) { - try file_table_html.writer(gpa).print( + try file_table_html.writer.print( \\{d} more rows omitted \\ , .{slowest_files.len - max_table_rows}); } - var decl_table_html: std.ArrayListUnmanaged(u8) = .empty; - defer decl_table_html.deinit(gpa); + var decl_table_html: std.Io.Writer.Allocating = .init(gpa); + defer decl_table_html.deinit(); for (slowest_decls[0..@min(max_table_rows, slowest_decls.len)]) |decl| { - try decl_table_html.writer(gpa).print( + try decl_table_html.writer.print( \\ \\ {f} \\ {f} @@ -219,7 +220,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { }); } if (slowest_decls.len > max_table_rows) { - try decl_table_html.writer(gpa).print( + try decl_table_html.writer.print( \\{d} more rows omitted \\ , .{slowest_decls.len - max_table_rows}); @@ -229,10 +230,10 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { hdr.step_idx, inner_html.ptr, inner_html.len, - file_table_html.items.ptr, - file_table_html.items.len, - decl_table_html.items.ptr, - decl_table_html.items.len, + file_table_html.written().ptr, + file_table_html.written().len, + decl_table_html.written().ptr, + decl_table_html.written().len, hdr.flags.use_llvm, ); } From f06c3d8be7cad0c082d707197382d7e704c06aa7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 7 Sep 2025 20:22:38 -0700 Subject: [PATCH 037/112] std.debug.assertAligned: support const pointers --- lib/std/debug.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index ebbcad627b..51896c4bb0 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -569,7 +569,7 @@ pub fn assertReadable(slice: []const volatile u8) void { /// Invokes detectable illegal behavior when the provided array is not aligned /// to the provided amount. pub fn assertAligned(ptr: anytype, comptime alignment: std.mem.Alignment) void { - const aligned_ptr: *align(alignment.toByteUnits()) anyopaque = @ptrCast(@alignCast(ptr)); + const aligned_ptr: *align(alignment.toByteUnits()) const anyopaque = @ptrCast(@alignCast(ptr)); _ = aligned_ptr; } From 38185ee10f6c09194cda304a92436f63c427be2a Mon Sep 17 00:00:00 2001 From: LukaTD <43285559+LukaTD@users.noreply.github.com> Date: Wed, 10 Sep 2025 04:31:20 +0200 Subject: [PATCH 038/112] langref: added missing newlines to destructuring tuples example langref: added missing newlines to destructuring tuples example --- doc/langref/destructuring_block.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref/destructuring_block.zig b/doc/langref/destructuring_block.zig index 7eec318796..4aae87381b 100644 --- a/doc/langref/destructuring_block.zig +++ b/doc/langref/destructuring_block.zig @@ -15,8 +15,8 @@ pub fn main() void { break :blk .{ min, max }; }; - print("min = {}", .{ min }); - print("max = {}", .{ max }); + print("min = {}\n", .{ min }); + print("max = {}\n", .{ max }); } // exe=succeed From 965b2ab6c370c5bbb4e1ea2ba660125f889f572c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 11 Sep 2025 18:42:57 +0200 Subject: [PATCH 039/112] compiler-rt: export __aeabi_read_tp for arm-freebsd FreeBSD normally provides this symbol in libc, but it's in the FBSDprivate_1.0 namespace, so it doesn't get included in our abilists file. Fortunately, the implementation is identical for Linux and FreeBSD, so we can just provide it in compiler-rt. It's interesting to note that the same is not true for NetBSD where the implementation is more complex to support older Arm versions. But we do include the symbol in our abilists file for NetBSD libc, so that's fine. closes #25215 --- lib/compiler_rt/arm.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig index e349e18630..3c55df5f3b 100644 --- a/lib/compiler_rt/arm.zig +++ b/lib/compiler_rt/arm.zig @@ -37,7 +37,7 @@ comptime { @export(&__aeabi_memclr4, .{ .name = "__aeabi_memclr4", .linkage = common.linkage, .visibility = common.visibility }); @export(&__aeabi_memclr8, .{ .name = "__aeabi_memclr8", .linkage = common.linkage, .visibility = common.visibility }); - if (builtin.os.tag == .linux) { + if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) { @export(&__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = common.linkage, .visibility = common.visibility }); } From 4cfb58342ab40adc1a179bfde9fcb116f47bb9d7 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 14 Sep 2025 11:10:46 +0100 Subject: [PATCH 040/112] frontend: fix reference tracking through coerced function bodies This bug was manifesting for user as a nasty link error because they were calling their application's main entry point as a coerced function, which essentially broke reference tracking for the entire ZCU, causing exported symbols to silently not get exported. I've been a little unsure about how coerced functions should interact with the unit graph before, but the solution is actually really obvious now: they shouldn't! `Sema` is now responsible for unwrapping possibly-coerced functions *before* queuing analysis or marking unit references. This makes the reference graph optimal (there are no redundant edges representing coerced versions of the same function) and simplifies logic elsewhere at the expense of just a few lines in Sema. --- src/Sema.zig | 56 ++++++++++++------- src/Zcu.zig | 3 + src/Zcu/PerThread.zig | 10 ++-- test/cases/export_from_body_of_coerced_fn.zig | 19 +++++++ 4 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 test/cases/export_from_body_of_coerced_fn.zig diff --git a/src/Sema.zig b/src/Sema.zig index 4d05441a83..1c2a9fba83 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4375,8 +4375,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (zcu.intern_pool.isFuncBody(val)) { const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val)); if (try ty.fnHasRuntimeBitsSema(pt)) { - try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = val })); - try zcu.ensureFuncBodyAnalysisQueued(val); + const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val); + try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); + try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); } } @@ -5588,16 +5589,21 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } try sema.ensureMemoizedStateResolved(src, .panic); - try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"panic.call")); - - const panic_fn = Air.internedToRef(zcu.builtin_decl_values.get(.@"panic.call")); - + const panic_fn_index = zcu.builtin_decl_values.get(.@"panic.call"); const opt_usize_ty = try pt.optionalType(.usize_type); const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{ .ty = opt_usize_ty.toIntern(), .val = .none, } }))); - try sema.callBuiltin(block, src, panic_fn, .auto, &.{ coerced_msg, null_ret_addr }, .@"@panic"); + // `callBuiltin` also calls `addReferenceEntry` to the function body for us. + try sema.callBuiltin( + block, + src, + .fromIntern(panic_fn_index), + .auto, + &.{ coerced_msg, null_ret_addr }, + .@"@panic", + ); } fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -7566,8 +7572,9 @@ fn analyzeCall( ref_func: { const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func; if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; - try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = runtime_func_val.toIntern() })); - try zcu.ensureFuncBodyAnalysisQueued(runtime_func_val.toIntern()); + const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern()); + try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index })); + try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); } const call_tag: Air.Inst.Tag = switch (modifier) { @@ -26360,23 +26367,27 @@ fn explainWhyTypeIsNotPacked( /// instructions. This function ensures the panic function will be available to /// be called during that time. fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void { + const zcu = sema.pt.zcu; + // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers. // The backend will transform panics into traps instead. - if (sema.pt.zcu.backendSupportsFeature(.panic_fn)) { - _ = try sema.getPanicIdFunc(src, panic_id); - } + if (!zcu.backendSupportsFeature(.panic_fn)) return; + + const fn_index = try sema.getPanicIdFunc(src, panic_id); + const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(fn_index); + try sema.addReferenceEntry(null, src, .wrap(.{ .func = orig_fn_index })); + try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); } fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index { const zcu = sema.pt.zcu; try sema.ensureMemoizedStateResolved(src, .panic); - const panic_func = zcu.builtin_decl_values.get(panic_id.toBuiltin()); - try zcu.ensureFuncBodyAnalysisQueued(panic_func); + const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin()); switch (sema.owner.unwrap()) { .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true), } - return panic_func; + return panic_fn_index; } fn addSafetyCheck( @@ -31164,6 +31175,11 @@ fn addReferenceEntry( referenced_unit: AnalUnit, ) !void { const zcu = sema.pt.zcu; + const ip = &zcu.intern_pool; + switch (referenced_unit.unwrap()) { + .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced + else => {}, + } if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return; const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); if (gop.found_existing) return; @@ -31350,8 +31366,9 @@ fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_i const nav_val = zcu.navValue(nav_index); if (!ip.isFuncBody(nav_val.toIntern())) return; - try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = nav_val.toIntern() })); - try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern()); + const orig_fn_index = ip.unwrapCoercedFunc(nav_val.toIntern()); + try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); + try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); } fn analyzeRef( @@ -34984,8 +35001,9 @@ fn resolveInferredErrorSet( } // In this case we are dealing with the actual InferredErrorSet object that // corresponds to the function, not one created to track an inline/comptime call. - try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = func_index })); - try pt.ensureFuncBodyUpToDate(func_index); + const orig_func_index = ip.unwrapCoercedFunc(func_index); + try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index })); + try pt.ensureFuncBodyUpToDate(orig_func_index); } // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody` diff --git a/src/Zcu.zig b/src/Zcu.zig index 53da90a9c2..5f45e6395e 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3451,8 +3451,11 @@ pub fn mapOldZirToNew( /// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`. pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !void { const ip = &zcu.intern_pool; + const func = zcu.funcInfo(func_index); + assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one + if (zcu.func_body_analysis_queued.contains(func_index)) return; if (func.analysisUnordered(ip).is_analyzed) { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 3c2bb27dab..684b3a8658 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1571,7 +1571,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr return .{ .type_changed = true }; } -pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void { +pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!void { dev.check(.sema); const tracy = trace(@src()); @@ -1581,15 +1581,15 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter const gpa = zcu.gpa; const ip = &zcu.intern_pool; - _ = zcu.func_body_analysis_queued.swapRemove(maybe_coerced_func_index); + _ = zcu.func_body_analysis_queued.swapRemove(func_index); - // We only care about the uncoerced function. - const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index); const anal_unit: AnalUnit = .wrap(.{ .func = func_index }); log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); - const func = zcu.funcInfo(maybe_coerced_func_index); + const func = zcu.funcInfo(func_index); + + assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one const was_outdated = zcu.outdated.swapRemove(anal_unit) or zcu.potentially_outdated.swapRemove(anal_unit); diff --git a/test/cases/export_from_body_of_coerced_fn.zig b/test/cases/export_from_body_of_coerced_fn.zig new file mode 100644 index 0000000000..541cd0c86f --- /dev/null +++ b/test/cases/export_from_body_of_coerced_fn.zig @@ -0,0 +1,19 @@ +fn original() usize { + _ = struct { + export const val: u32 = 123; + }; + return 0; +} + +pub fn main() void { + const coerced: fn () u64 = original; + _ = coerced(); + + const S = struct { + extern const val: u32; + }; + if (S.val != 123) @panic("wrong value"); +} + +// run +// target=x86_64-linux From 05b7ca6356e4f394c084de8e806c1c93440e0b6b Mon Sep 17 00:00:00 2001 From: George Huebner Date: Mon, 25 Aug 2025 08:26:12 -0500 Subject: [PATCH 041/112] bpf: use bitCast instead of intCast in ld_imm_impl Any 32 bit immediate is allowed in a BPF instruction, including those greater than the largest positive i32 value. --- lib/std/os/linux/bpf.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux/bpf.zig b/lib/std/os/linux/bpf.zig index 86bc071030..a0d5956af8 100644 --- a/lib/std/os/linux/bpf.zig +++ b/lib/std/os/linux/bpf.zig @@ -642,7 +642,7 @@ pub const Insn = packed struct { .dst = @intFromEnum(dst), .src = @intFromEnum(src), .off = 0, - .imm = @as(i32, @intCast(@as(u32, @truncate(imm)))), + .imm = @as(i32, @bitCast(@as(u32, @truncate(imm)))), }; } @@ -652,7 +652,7 @@ pub const Insn = packed struct { .dst = 0, .src = 0, .off = 0, - .imm = @as(i32, @intCast(@as(u32, @truncate(imm >> 32)))), + .imm = @as(i32, @bitCast(@as(u32, @truncate(imm >> 32)))), }; } From 83a3365bfd9dc17067de26b3cb4c9af55a34644e Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 13 Sep 2025 11:31:17 +0100 Subject: [PATCH 042/112] std.math.big.int: normalize zero result for small multiplications Resolves: #25221 --- lib/std/math/big/int.zig | 7 +++---- test/behavior/enum.zig | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b0380444f4..427f2861b7 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -786,11 +786,10 @@ pub const Mutable = struct { assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing if (a.limbs.len == 1 and b.limbs.len == 1) { - const ov = @mulWithOverflow(a.limbs[0], b.limbs[0]); - rma.limbs[0] = ov[0]; - if (ov[1] == 0) { + rma.limbs[0], const overflow_bit = @mulWithOverflow(a.limbs[0], b.limbs[0]); + if (overflow_bit == 0) { rma.len = 1; - rma.positive = (a.positive == b.positive); + rma.positive = (a.positive == b.positive) or rma.limbs[0] == 0; return; } } diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index d719a611e6..269fba13ba 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1325,3 +1325,10 @@ test "large enum field values" { try expect(@intFromEnum(e) == std.math.maxInt(i128)); } } + +test "comptime @enumFromInt with signed arithmetic" { + const E = enum(i8) { foo = -1, bar = 0 }; + const x: E = @enumFromInt(@as(i8, -1) * 0); + comptime assert(x == .bar); + comptime assert(@intFromEnum(x) == 0); +} From 87ca304a02807c6534ea6a9ba968c539d51718a9 Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 16 Sep 2025 12:35:34 +0100 Subject: [PATCH 043/112] llvm: fix tagged union payload size in debug info Resolves: #24415 --- src/codegen/llvm.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1b12add122..bd2c7bd022 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2508,7 +2508,7 @@ pub const Object = struct { o.debug_compile_unit, // Scope 0, // Line .none, // Underlying type - ty.abiSize(zcu) * 8, + layout.payload_size * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, try o.builder.metadataTuple(fields.items), ); From e14540399c1be3c052467a576bd317ea12194cdd Mon Sep 17 00:00:00 2001 From: Silver <14016168+silversquirl@users.noreply.github.com> Date: Thu, 18 Sep 2025 05:39:47 +0100 Subject: [PATCH 044/112] fix handling of comptime-only union fields in `Type.getUnionLayout` (#25182) Fixes #25180 --- src/Type.zig | 20 +++++++++++--------- test/behavior/union.zig | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/Type.zig b/src/Type.zig index f8f13ada44..3ef95b285c 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3914,15 +3914,17 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) explicit_align else field_ty.abiAlignment(zcu); - const field_size = field_ty.abiSize(zcu); - if (field_size > payload_size) { - payload_size = field_size; - biggest_field = @intCast(field_index); - } - if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) { - most_aligned_field = @intCast(field_index); - most_aligned_field_align = field_align; - most_aligned_field_size = field_size; + if (field_ty.hasRuntimeBits(zcu)) { + const field_size = field_ty.abiSize(zcu); + if (field_size > payload_size) { + payload_size = field_size; + biggest_field = @intCast(field_index); + } + if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) { + most_aligned_field = @intCast(field_index); + most_aligned_field_align = field_align; + most_aligned_field_size = field_size; + } } payload_align = payload_align.max(field_align); } diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 04d07f91ae..f821097c27 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -2321,3 +2321,21 @@ test "initialize empty field of union inside comptime-known struct constant" { const val: Wrapper = .{ .inner = .{ .none = {} } }; comptime assert(val.inner.none == {}); } + +test "union with function body field" { + const U = union { + f: fn () void, + fn foo() void {} + fn bar() void {} + }; + const x: U = .{ .f = U.foo }; + try std.testing.expect(x.f == U.foo); + x.f(); + + comptime var y: U = .{ .f = U.bar }; + try std.testing.expect(y.f == U.bar); + y.f(); + y.f = U.foo; + try std.testing.expect(y.f == U.foo); + y.f(); +} From b8bf2780a074ad98ccacd59fb43c16ccd14fa3e1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 11 Sep 2025 05:20:45 -0400 Subject: [PATCH 045/112] Elf: implement `linksection` Closes #24330 --- src/link/Elf/ZigObject.zig | 104 ++++++++++++++++++++++++++++++++++++- test/link/elf.zig | 39 ++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 20e5b3d2e3..c9ba0cb1b3 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1140,7 +1140,109 @@ fn getNavShdrIndex( const ptr_size = elf_file.ptrWidthBytes(); const ip = &zcu.intern_pool; const nav_val = zcu.navValue(nav_index); - if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) { + const is_func = ip.isFunctionType(nav_val.typeOf(zcu).toIntern()); + if (ip.getNav(nav_index).getLinkSection().unwrap()) |@"linksection"| { + const section_name = @"linksection".toSlice(ip); + if (elf_file.sectionByName(section_name)) |osec| { + if (is_func) { + elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_EXECINSTR; + } else { + elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_WRITE; + } + return osec; + } + const osec = try elf_file.addSection(.{ + .type = elf.SHT_PROGBITS, + .flags = elf.SHF_ALLOC | @as(u64, if (is_func) elf.SHF_EXECINSTR else elf.SHF_WRITE), + .name = try elf_file.insertShString(section_name), + .addralign = 1, + }); + const section_index = try self.addSectionSymbol(gpa, try self.addString(gpa, section_name), osec); + if (std.mem.eql(u8, section_name, ".text")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR; + self.text_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".text.")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR; + } else if (std.mem.eql(u8, section_name, ".rodata")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC; + self.rodata_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".rodata.")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC; + } else if (std.mem.eql(u8, section_name, ".data.rel.ro")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + self.data_relro_index = section_index; + } else if (std.mem.eql(u8, section_name, ".data")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + self.data_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".data.")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + } else if (std.mem.eql(u8, section_name, ".bss")) { + const shdr = &elf_file.sections.items(.shdr)[osec]; + shdr.sh_type = elf.SHT_NOBITS; + shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + self.bss_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".bss.")) { + const shdr = &elf_file.sections.items(.shdr)[osec]; + shdr.sh_type = elf.SHT_NOBITS; + shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + } else if (std.mem.eql(u8, section_name, ".tdata")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS; + self.tdata_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".tdata.")) { + elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS; + } else if (std.mem.eql(u8, section_name, ".tbss")) { + const shdr = &elf_file.sections.items(.shdr)[osec]; + shdr.sh_type = elf.SHT_NOBITS; + shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS; + self.tbss_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".tbss.")) { + const shdr = &elf_file.sections.items(.shdr)[osec]; + shdr.sh_type = elf.SHT_NOBITS; + shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS; + } else if (std.mem.eql(u8, section_name, ".eh_frame")) { + const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; + const shdr = &elf_file.sections.items(.shdr)[osec]; + if (target.cpu.arch == .x86_64) shdr.sh_type = elf.SHT_X86_64_UNWIND; + shdr.sh_flags = elf.SHF_ALLOC; + self.eh_frame_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_info")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_info_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_abbrev")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_abbrev_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_aranges")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_aranges_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_str")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_str_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_line")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_line_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_line_str")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_line_str_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_loclists")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_loclists_index = section_index; + } else if (std.mem.eql(u8, section_name, ".debug_rnglists")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + self.debug_rnglists_index = section_index; + } else if (std.mem.startsWith(u8, section_name, ".debug")) { + elf_file.sections.items(.shdr)[osec].sh_flags = 0; + } else if (std.mem.eql(u8, section_name, ".init_array") or std.mem.startsWith(u8, section_name, ".init_array.")) { + const shdr = &elf_file.sections.items(.shdr)[osec]; + shdr.sh_type = elf.SHT_INIT_ARRAY; + shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + } else if (std.mem.eql(u8, section_name, ".fini_array") or std.mem.startsWith(u8, section_name, ".fini_array.")) { + const shdr = &elf_file.sections.items(.shdr)[osec]; + shdr.sh_type = elf.SHT_FINI_ARRAY; + shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE; + } + return osec; + } + if (is_func) { if (self.text_index) |symbol_index| return self.symbol(symbol_index).outputShndx(elf_file).?; const osec = try elf_file.addSection(.{ diff --git a/test/link/elf.zig b/test/link/elf.zig index 1d6de32f0d..16ee824da7 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -73,6 +73,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { elf_step.dependOn(testLinkingC(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target })); + elf_step.dependOn(testLinksection(b, .{ .target = musl_target })); elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target })); elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target })); // https://github.com/ziglang/zig/issues/17451 @@ -165,6 +166,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target })); + elf_step.dependOn(testLinksection(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu })); elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl })); @@ -2439,6 +2441,43 @@ fn testLinkingZig(b: *Build, opts: Options) *Step { return test_step; } +fn testLinksection(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "linksection", opts); + + const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes = + \\export var test_global: u32 linksection(".TestGlobal") = undefined; + \\export fn testFn() linksection(".TestFn") callconv(.c) void { + \\ TestGenericFn("A").f(); + \\} + \\fn TestGenericFn(comptime suffix: []const u8) type { + \\ return struct { + \\ fn f() linksection(".TestGenFn" ++ suffix) void {} + \\ }; + \\} + }); + + const check = obj.checkObject(); + check.checkInSymtab(); + check.checkContains("SECTION LOCAL DEFAULT .TestGlobal"); + check.checkInSymtab(); + check.checkContains("SECTION LOCAL DEFAULT .TestFn"); + check.checkInSymtab(); + check.checkContains("SECTION LOCAL DEFAULT .TestGenFnA"); + check.checkInSymtab(); + check.checkContains("OBJECT GLOBAL DEFAULT test_global"); + check.checkInSymtab(); + check.checkContains("FUNC GLOBAL DEFAULT testFn"); + + if (opts.optimize == .Debug) { + check.checkInSymtab(); + check.checkContains("FUNC LOCAL DEFAULT main.TestGenericFn("); + } + + test_step.dependOn(&check.step); + + return test_step; +} + // Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh fn testMergeStrings(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "merge-strings", opts); From 35de86906dad5e0d0794cd0c834d00355382e9b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Sep 2025 21:15:03 -0700 Subject: [PATCH 046/112] Merge pull request #25201 from jacobly0/x86_64-addsat x86_64: fix strictness edge cases in `+|` --- src/arch/x86_64/CodeGen.zig | 219 ++++++++++++++++++++++++----------- test/behavior/cast.zig | 222 +++++++++++++++++++++++++++++++++--- test/behavior/vector.zig | 152 ++++++++++++++++++++++-- 3 files changed, 498 insertions(+), 95 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5c76b7af92..c3cfb60e88 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -7263,10 +7263,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, .{ ._, .vp_d, .add, .dst0x, .src0x, .src1x, ._ }, .{ ._, .vp_d, .sra, .tmp2x, .src0x, .ui(31), ._ }, - .{ ._, .vp_d, .cmpgt, .tmp3x, .dst0x, .src0x, ._ }, + .{ ._, .vp_d, .cmpgt, .tmp3x, .src0x, .dst0x, ._ }, .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .lea(.tmp0x), ._ }, .{ ._, .vp_, .xor, .tmp3x, .tmp3x, .src1x, ._ }, - .{ ._, .v_ps, .blendv, .dst0x, .tmp2x, .dst0x, .tmp3x }, + .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .tmp2x, .tmp3x }, } }, }, .{ .required_features = .{ .sse4_1, null, null, null }, @@ -7332,10 +7332,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, .{ ._, .vp_d, .add, .dst0y, .src0y, .src1y, ._ }, .{ ._, .vp_d, .sra, .tmp2y, .src0y, .ui(31), ._ }, - .{ ._, .vp_d, .cmpgt, .tmp3y, .dst0y, .src0y, ._ }, + .{ ._, .vp_d, .cmpgt, .tmp3y, .src0y, .dst0y, ._ }, .{ ._, .vp_, .xor, .tmp2y, .tmp2y, .lea(.tmp0y), ._ }, .{ ._, .vp_, .xor, .tmp3y, .tmp3y, .src1y, ._ }, - .{ ._, .v_ps, .blendv, .dst0y, .tmp2y, .dst0y, .tmp3y }, + .{ ._, .v_ps, .blendv, .dst0y, .dst0y, .tmp2y, .tmp3y }, } }, }, .{ .required_features = .{ .avx, null, null, null }, @@ -7615,10 +7615,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .tmp2x, ._ }, .{ ._, .vp_q, .add, .dst0x, .src0x, .src1x, ._ }, .{ ._, .vp_q, .cmpgt, .tmp2x, .tmp2x, .src0x, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3x, .dst0x, .src0x, ._ }, + .{ ._, .vp_q, .cmpgt, .tmp3x, .src0x, .dst0x, ._ }, .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .lea(.tmp0x), ._ }, .{ ._, .vp_, .xor, .tmp3x, .tmp3x, .src1x, ._ }, - .{ ._, .v_pd, .blendv, .dst0x, .tmp2x, .dst0x, .tmp3x }, + .{ ._, .v_pd, .blendv, .dst0x, .dst0x, .tmp2x, .tmp3x }, } }, }, .{ .required_features = .{ .sse4_2, null, null, null }, @@ -7685,10 +7685,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_, .xor, .tmp2y, .tmp2y, .tmp2y, ._ }, .{ ._, .vp_q, .add, .dst0y, .src0y, .src1y, ._ }, .{ ._, .vp_q, .cmpgt, .tmp2y, .tmp2y, .src0y, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3y, .dst0y, .src0y, ._ }, + .{ ._, .vp_q, .cmpgt, .tmp3y, .src0y, .dst0y, ._ }, .{ ._, .vp_, .xor, .tmp2y, .tmp2y, .lea(.tmp0y), ._ }, .{ ._, .vp_, .xor, .tmp3y, .tmp3y, .src1y, ._ }, - .{ ._, .v_pd, .blendv, .dst0y, .tmp2y, .dst0y, .tmp3y }, + .{ ._, .v_pd, .blendv, .dst0y, .dst0y, .tmp2y, .tmp3y }, } }, }, .{ .required_features = .{ .avx2, null, null, null }, @@ -7724,8 +7724,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_b, .blendv, .dst0x, .dst0x, .tmp2x, .tmp3x }, .{ ._, .vp_q, .cmpeq, .tmp3x, .tmp3x, .tmp3x, ._ }, .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .tmp3x, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3x, .dst0x, .tmp2x, ._ }, - .{ ._, .vp_b, .blendv, .dst0x, .dst0x, .tmp2x, .tmp3x }, + .{ ._, .vp_q, .cmpgt, .tmp3x, .tmp2x, .dst0x, ._ }, + .{ ._, .vp_b, .blendv, .dst0x, .tmp2x, .dst0x, .tmp3x }, } }, }, .{ .required_features = .{ .avx, null, null, null }, @@ -7761,8 +7761,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_b, .blendv, .dst0x, .dst0x, .tmp2x, .tmp3x }, .{ ._, .vp_q, .cmpeq, .tmp3x, .tmp3x, .tmp3x, ._ }, .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .tmp3x, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3x, .dst0x, .tmp2x, ._ }, - .{ ._, .vp_b, .blendv, .dst0x, .dst0x, .tmp2x, .tmp3x }, + .{ ._, .vp_q, .cmpgt, .tmp3x, .tmp2x, .dst0x, ._ }, + .{ ._, .vp_b, .blendv, .dst0x, .tmp2x, .dst0x, .tmp3x }, } }, }, .{ .required_features = .{ .sse4_2, null, null, null }, @@ -7837,8 +7837,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_b, .blendv, .dst0y, .dst0y, .tmp2y, .tmp3y }, .{ ._, .vp_q, .cmpeq, .tmp3y, .tmp3y, .tmp3y, ._ }, .{ ._, .vp_, .xor, .tmp2y, .tmp2y, .tmp3y, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3y, .dst0y, .tmp2y, ._ }, - .{ ._, .vp_b, .blendv, .dst0y, .dst0y, .tmp2y, .tmp3y }, + .{ ._, .vp_q, .cmpgt, .tmp3y, .tmp2y, .dst0y, ._ }, + .{ ._, .vp_b, .blendv, .dst0y, .tmp2y, .dst0y, .tmp3y }, } }, }, .{ .required_features = .{ .avx2, null, null, null }, @@ -10714,10 +10714,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .v_dqa, .mov, .tmp4y, .memia(.src1y, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .vp_d, .add, .tmp5y, .tmp3y, .tmp4y, ._ }, .{ ._, .vp_d, .sra, .tmp6y, .tmp3y, .ui(31), ._ }, - .{ ._, .vp_d, .cmpgt, .tmp3y, .tmp5y, .tmp3y, ._ }, + .{ ._, .vp_d, .cmpgt, .tmp3y, .tmp3y, .tmp5y, ._ }, .{ ._, .vp_, .xor, .tmp6y, .tmp6y, .tmp2y, ._ }, .{ ._, .vp_, .xor, .tmp3y, .tmp3y, .tmp4y, ._ }, - .{ ._, .v_ps, .blendv, .tmp3y, .tmp6y, .tmp5y, .tmp3y }, + .{ ._, .v_ps, .blendv, .tmp3y, .tmp5y, .tmp6y, .tmp3y }, .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_unaligned_size), .tmp3y, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -10755,10 +10755,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .v_dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .vp_d, .add, .tmp5x, .tmp3x, .tmp4x, ._ }, .{ ._, .vp_d, .sra, .tmp6x, .tmp3x, .ui(31), ._ }, - .{ ._, .vp_d, .cmpgt, .tmp3x, .tmp5x, .tmp3x, ._ }, + .{ ._, .vp_d, .cmpgt, .tmp3x, .tmp3x, .tmp5x, ._ }, .{ ._, .vp_, .xor, .tmp6x, .tmp6x, .tmp2x, ._ }, .{ ._, .vp_, .xor, .tmp3x, .tmp3x, .tmp4x, ._ }, - .{ ._, .v_ps, .blendv, .tmp3x, .tmp6x, .tmp5x, .tmp3x }, + .{ ._, .v_ps, .blendv, .tmp3x, .tmp5x, .tmp6x, .tmp3x }, .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_unaligned_size), .tmp3x, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -11543,10 +11543,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_, .xor, .tmp5y, .tmp5y, .tmp5y, ._ }, .{ ._, .vp_q, .add, .tmp6y, .tmp3y, .tmp4y, ._ }, .{ ._, .vp_q, .cmpgt, .tmp5y, .tmp5y, .tmp3y, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3y, .tmp6y, .tmp3y, ._ }, + .{ ._, .vp_q, .cmpgt, .tmp3y, .tmp3y, .tmp6y, ._ }, .{ ._, .vp_, .xor, .tmp5y, .tmp5y, .tmp2y, ._ }, .{ ._, .vp_, .xor, .tmp3y, .tmp3y, .tmp4y, ._ }, - .{ ._, .v_pd, .blendv, .tmp5y, .tmp5y, .tmp6y, .tmp3y }, + .{ ._, .v_pd, .blendv, .tmp5y, .tmp6y, .tmp5y, .tmp3y }, .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_unaligned_size), .tmp5y, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -11585,10 +11585,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_, .xor, .tmp5x, .tmp5x, .tmp5x, ._ }, .{ ._, .vp_q, .add, .tmp6x, .tmp3x, .tmp4x, ._ }, .{ ._, .vp_q, .cmpgt, .tmp5x, .tmp5x, .tmp3x, ._ }, - .{ ._, .vp_q, .cmpgt, .tmp3x, .tmp6x, .tmp3x, ._ }, + .{ ._, .vp_q, .cmpgt, .tmp3x, .tmp3x, .tmp6x, ._ }, .{ ._, .vp_, .xor, .tmp5x, .tmp5x, .tmp2x, ._ }, .{ ._, .vp_, .xor, .tmp3x, .tmp3x, .tmp4x, ._ }, - .{ ._, .v_pd, .blendv, .tmp5x, .tmp5x, .tmp6x, .tmp3x }, + .{ ._, .v_pd, .blendv, .tmp5x, .tmp6x, .tmp5x, .tmp3x }, .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_unaligned_size), .tmp5x, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -11607,11 +11607,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, .{ .type = .i64, .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .none } } }, .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, - .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, - .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, - .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, - .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, .{ .type = .vector_2_i64, .kind = .{ .reg = .xmm0 } }, + .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, + .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, + .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, + .unused, .unused, .unused, .unused, @@ -11628,12 +11628,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._dqa, .mov, .tmp6x, .tmp3x, ._, ._ }, .{ ._, .p_q, .add, .tmp6x, .tmp4x, ._, ._ }, .{ ._, .p_q, .cmpgt, .tmp5x, .tmp3x, ._, ._ }, - .{ ._, ._dqa, .mov, .tmp7x, .tmp6x, ._, ._ }, - .{ ._, .p_q, .cmpgt, .tmp7x, .tmp3x, ._, ._ }, + .{ ._, .p_q, .cmpgt, .tmp3x, .tmp6x, ._, ._ }, .{ ._, .p_, .xor, .tmp5x, .tmp2x, ._, ._ }, - .{ ._, .p_, .xor, .tmp7x, .tmp4x, ._, ._ }, - .{ ._, ._pd, .blendv, .tmp5x, .tmp6x, .tmp7x, ._ }, - .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_unaligned_size), .tmp5x, ._, ._ }, + .{ ._, .p_, .xor, .tmp3x, .tmp4x, ._, ._ }, + .{ ._, ._pd, .blendv, .tmp6x, .tmp5x, .tmp3x, ._ }, + .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_unaligned_size), .tmp6x, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, } }, @@ -22284,8 +22283,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_w, .movsxb, .dst0y, .src0x, ._, ._ }, .{ ._, .vp_w, .movsxb, .tmp0y, .src1x, ._, ._ }, .{ ._, .vp_w, .mull, .dst0y, .dst0y, .tmp0y, ._ }, - .{ ._, .vp_b, .ackssw, .dst0y, .dst0y, .dst0y, ._ }, - .{ ._, .v_q, .perm, .dst0y, .dst0y, .ui(0b10_00_10_00), ._ }, + .{ ._, .v_i128, .extract, .tmp0x, .dst0y, .ui(1), ._ }, + .{ ._, .vp_b, .ackssw, .dst0x, .dst0x, .tmp0x, ._ }, } }, }, .{ .required_features = .{ .avx, null, null, null }, @@ -22415,8 +22414,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, .vp_w, .movzxb, .dst0y, .src0x, ._, ._ }, .{ ._, .vp_w, .movzxb, .tmp0y, .src1x, ._, ._ }, .{ ._, .vp_w, .mull, .dst0y, .dst0y, .tmp0y, ._ }, - .{ ._, .vp_b, .ackusw, .dst0y, .dst0y, .dst0y, ._ }, - .{ ._, .v_q, .perm, .dst0y, .dst0y, .ui(0b10_00_10_00), ._ }, + .{ ._, .v_i128, .extract, .tmp0x, .dst0y, .ui(1), ._ }, + .{ ._, .vp_b, .ackusw, .dst0x, .dst0x, .tmp0x, ._ }, } }, }, .{ .required_features = .{ .avx2, null, null, null }, @@ -22448,8 +22447,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .@"0:", .vp_w, .movsxb, .tmp1y, .memia(.src0x, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .vp_w, .movsxb, .tmp2y, .memia(.src1x, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .vp_w, .mull, .tmp1y, .tmp1y, .tmp2y, ._ }, - .{ ._, .vp_b, .ackssw, .tmp1y, .tmp1y, .tmp1y, ._ }, - .{ ._, .v_q, .perm, .tmp1y, .tmp1y, .ui(0b10_00_10_00), ._ }, + .{ ._, .v_i128, .extract, .tmp2x, .tmp1y, .ui(1), ._ }, + .{ ._, .vp_b, .ackssw, .tmp1x, .tmp1x, .tmp2x, ._ }, .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_unaligned_size), .tmp1x, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -22660,8 +22659,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .@"0:", .vp_w, .movzxb, .tmp1y, .memia(.src0x, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .vp_w, .movzxb, .tmp2y, .memia(.src1x, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .vp_w, .mull, .tmp1y, .tmp1y, .tmp2y, ._ }, - .{ ._, .vp_b, .ackusw, .tmp1y, .tmp1y, .tmp1y, ._ }, - .{ ._, .v_q, .perm, .tmp1y, .tmp1y, .ui(0b10_00_10_00), ._ }, + .{ ._, .v_i128, .extract, .tmp2x, .tmp1y, .ui(1), ._ }, + .{ ._, .vp_b, .ackusw, .tmp1x, .tmp1x, .tmp2x, ._ }, .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_unaligned_size), .tmp1x, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -82560,7 +82559,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, .{ .kind = .{ .rc = .sse } }, - .unused, + .{ .kind = .{ .rc = .sse } }, .unused, .unused, .unused, @@ -82577,8 +82576,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, .{ ._, .vp_w, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, - .{ ._, .vp_b, .ackssw, .tmp3y, .tmp3y, .tmp3y, ._ }, - .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3y, ._, ._ }, + .{ ._, .v_i128, .extract, .tmp4x, .tmp3y, .ui(1), ._ }, + .{ ._, .vp_b, .ackssw, .tmp3x, .tmp3x, .tmp4x, ._ }, + .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, .{ ._, ._, .lea, .tmp1d, .lead(.tmp1, 2), ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, @@ -82589,8 +82589,9 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, .{ ._, .vp_w, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, - .{ ._, .vp_b, .ackssw, .tmp3y, .tmp3y, .tmp3y, ._ }, - .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3y, ._, ._ }, + .{ ._, .v_i128, .extract, .tmp4x, .tmp3y, .ui(1), ._ }, + .{ ._, .vp_b, .ackssw, .tmp3x, .tmp3x, .tmp4x, ._ }, + .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, .{ ._, ._, .not, .tmp2d, ._, ._, ._ }, .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, .{ ._, ._, .lea, .tmp1d, .lead(.tmp1, 2), ._, ._ }, @@ -90325,7 +90326,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .{ .required_features = .{ .avx, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .word } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .byte } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, .any }, .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, @@ -90347,7 +90348,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .{ .required_features = .{ .sse2, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .word } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .byte } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, .any }, .patterns = &.{ .{ .src = .{ .to_mut_sse, .none, .none } }, }, @@ -90362,20 +90363,46 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, - .dst_temps = .{ .{ .mut_rc = .{ .ref = .src0, .rc = .sse } }, .unused }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, .each = .{ .once = &.{ - .{ ._, .vp_b, .ackssw, .dst0y, .src0y, .dst0y, ._ }, + .{ ._, .v_i128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_b, .ackssw, .dst0x, .src0x, .dst0x, ._ }, + } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .word } }, .any, .any }, + .dst_constraints = .{ .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, .any }, + .patterns = &.{ + .{ .src = .{ .to_sse, .none, .none } }, + }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, + .each = .{ .once = &.{ + .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_b, .ackssw, .dst0x, .src0x, .dst0x, ._ }, } }, }, .{ .required_features = .{ .avx2, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .word } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .byte } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, .any }, .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, - .dst_temps = .{ .{ .mut_rc = .{ .ref = .src0, .rc = .sse } }, .unused }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, .each = .{ .once = &.{ - .{ ._, .vp_b, .ackusw, .dst0y, .src0y, .dst0y, ._ }, + .{ ._, .v_i128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_b, .ackusw, .dst0x, .src0x, .dst0x, ._ }, + } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .word } }, .any, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, .any }, + .patterns = &.{ + .{ .src = .{ .to_sse, .none, .none } }, + }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, + .each = .{ .once = &.{ + .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_b, .ackusw, .dst0x, .src0x, .dst0x, ._ }, } }, }, .{ .required_features = .{ .slow_incdec, null, null, null }, @@ -90449,7 +90476,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .{ .required_features = .{ .avx, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .dword } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .dword, .is = .byte } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .dword, .is = .byte } }, .any }, .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, @@ -90473,7 +90500,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .{ .required_features = .{ .sse4_1, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .dword } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .dword, .is = .byte } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .dword, .is = .byte } }, .any }, .patterns = &.{ .{ .src = .{ .to_mut_sse, .none, .none } }, }, @@ -90489,22 +90516,50 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, - .dst_temps = .{ .{ .mut_rc = .{ .ref = .src0, .rc = .sse } }, .unused }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, .each = .{ .once = &.{ - .{ ._, .vp_w, .ackssd, .dst0y, .src0y, .dst0y, ._ }, - .{ ._, .vp_b, .ackssw, .dst0y, .dst0y, .dst0y, ._ }, + .{ ._, .v_i128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackssd, .dst0x, .src0x, .dst0x, ._ }, + .{ ._, .vp_b, .ackssw, .dst0x, .dst0x, .dst0x, ._ }, + } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any, .any }, + .dst_constraints = .{ .{ .scalar_signed_int = .{ .of = .qword, .is = .byte } }, .any }, + .patterns = &.{ + .{ .src = .{ .to_sse, .none, .none } }, + }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, + .each = .{ .once = &.{ + .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackssd, .dst0x, .src0x, .dst0x, ._ }, + .{ ._, .vp_b, .ackssw, .dst0x, .dst0x, .dst0x, ._ }, } }, }, .{ .required_features = .{ .avx2, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .byte } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, .any }, .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, - .dst_temps = .{ .{ .mut_rc = .{ .ref = .src0, .rc = .sse } }, .unused }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, .each = .{ .once = &.{ - .{ ._, .vp_w, .ackusd, .dst0y, .src0y, .dst0y, ._ }, - .{ ._, .vp_b, .ackusw, .dst0y, .dst0y, .dst0y, ._ }, + .{ ._, .v_i128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackusd, .dst0x, .src0x, .dst0x, ._ }, + .{ ._, .vp_b, .ackusw, .dst0x, .dst0x, .dst0x, ._ }, + } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, .any }, + .patterns = &.{ + .{ .src = .{ .to_sse, .none, .none } }, + }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, + .each = .{ .once = &.{ + .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackusd, .dst0x, .src0x, .dst0x, ._ }, + .{ ._, .vp_b, .ackusw, .dst0x, .dst0x, .dst0x, ._ }, } }, }, .{ .required_features = .{ .slow_incdec, null, null, null }, @@ -90723,7 +90778,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .{ .required_features = .{ .avx, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .dword } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .word } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .qword, .is = .word } }, .any }, .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, @@ -90745,7 +90800,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .{ .required_features = .{ .sse4_1, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .dword } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .word } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .qword, .is = .word } }, .any }, .patterns = &.{ .{ .src = .{ .to_mut_sse, .none, .none } }, }, @@ -90760,20 +90815,46 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, - .dst_temps = .{ .{ .mut_rc = .{ .ref = .src0, .rc = .sse } }, .unused }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, .each = .{ .once = &.{ - .{ ._, .vp_w, .ackssd, .dst0y, .src0y, .dst0y, ._ }, + .{ ._, .v_i128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackssd, .dst0x, .src0x, .dst0x, ._ }, + } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any, .any }, + .dst_constraints = .{ .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, .any }, + .patterns = &.{ + .{ .src = .{ .to_sse, .none, .none } }, + }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, + .each = .{ .once = &.{ + .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackssd, .dst0x, .src0x, .dst0x, ._ }, } }, }, .{ .required_features = .{ .avx2, null, null, null }, .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any, .any }, - .dst_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .word } }, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, .any }, .patterns = &.{ .{ .src = .{ .to_sse, .none, .none } }, }, - .dst_temps = .{ .{ .mut_rc = .{ .ref = .src0, .rc = .sse } }, .unused }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, .each = .{ .once = &.{ - .{ ._, .vp_w, .ackusd, .dst0y, .src0y, .dst0y, ._ }, + .{ ._, .v_i128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackusd, .dst0x, .src0x, .dst0x, ._ }, + } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any, .any }, + .dst_constraints = .{ .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, .any }, + .patterns = &.{ + .{ .src = .{ .to_sse, .none, .none } }, + }, + .dst_temps = .{ .{ .rc = .sse }, .unused }, + .each = .{ .once = &.{ + .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ }, + .{ ._, .vp_w, .ackusd, .dst0x, .src0x, .dst0x, ._ }, } }, }, .{ .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .dword, .is = .dword } }, .any, .any }, @@ -92414,7 +92495,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .clobbers = .{ .eflags = true }, .each = .{ .once = &.{ .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_unaligned_size), ._, ._ }, - .{ .@"0:", .vp_d, .movzxb, .tmp1y, .memia(.src0x, .tmp0, .add_unaligned_size), ._, ._ }, + .{ .@"0:", .vp_d, .movzxb, .tmp1y, .memia(.src0q, .tmp0, .add_unaligned_size), ._, ._ }, .{ ._, .v_dqa, .mov, .memsia(.dst0y, .@"4", .tmp0, .add_unaligned_size), .tmp1y, ._, ._ }, .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, @@ -165240,7 +165321,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .dst_temps = .{ .mem, .unused }, .each = .{ .once = &.{ .{ ._, ._, .mov, .tmp0d, .sia(-16, .dst0, .add_size), ._, ._ }, - .{ ._, .v_ps, .shuf, .tmp1x, .tmp1x, .src0x, .ui(0b00_00_00_00) }, + .{ ._, .v_ps, .shuf, .tmp1x, .src0x, .src0x, .ui(0b00_00_00_00) }, .{ .@"0:", .v_ps, .mova, .memi(.dst0x, .tmp0), .tmp1x, ._, ._ }, .{ ._, ._, .sub, .tmp0d, .si(16), ._, ._ }, .{ ._, ._nb, .j, .@"0b", ._, ._, ._ }, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 7eee20e3e0..51ce6c2c6b 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -607,26 +607,212 @@ test "@intCast on vector" { const S = struct { fn doTheTest() !void { - // Upcast (implicit, equivalent to @intCast) - var up0: @Vector(2, u8) = [_]u8{ 0x55, 0xaa }; - _ = &up0; - const up1: @Vector(2, u16) = up0; - const up2: @Vector(2, u32) = up0; - const up3: @Vector(2, u64) = up0; - // Downcast (safety-checked) - var down0 = up3; - _ = &down0; - const down1: @Vector(2, u32) = @intCast(down0); - const down2: @Vector(2, u16) = @intCast(down0); - const down3: @Vector(2, u8) = @intCast(down0); + { + // Upcast (implicit, equivalent to @intCast) + var up0: @Vector(2, u8) = .{ 0x55, 0xaa }; + _ = &up0; + const up1: @Vector(2, u16) = up0; + const up2: @Vector(2, u32) = up0; + const up3: @Vector(2, u64) = up0; - try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa })); - try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa })); - try expect(mem.eql(u64, &@as([2]u64, up3), &[2]u64{ 0x55, 0xaa })); + try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa })); + try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa })); + try expect(mem.eql(u64, &@as([2]u64, up3), &[2]u64{ 0x55, 0xaa })); - try expect(mem.eql(u32, &@as([2]u32, down1), &[2]u32{ 0x55, 0xaa })); - try expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa })); - try expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa })); + { + // Downcast (safety-checked) + const down2: @Vector(2, u32) = @intCast(up3); + const down1: @Vector(2, u16) = @intCast(up3); + const down0: @Vector(2, u8) = @intCast(up3); + + try expect(mem.eql(u32, &@as([2]u32, down2), &[2]u32{ 0x55, 0xaa })); + try expect(mem.eql(u16, &@as([2]u16, down1), &[2]u16{ 0x55, 0xaa })); + try expect(mem.eql(u8, &@as([2]u8, down0), &[2]u8{ 0x55, 0xaa })); + } + + { + // Downcast (safety-checked) + const down1: @Vector(2, u16) = @intCast(up2); + const down0: @Vector(2, u8) = @intCast(up2); + + try expect(mem.eql(u16, &@as([2]u16, down1), &[2]u16{ 0x55, 0xaa })); + try expect(mem.eql(u8, &@as([2]u8, down0), &[2]u8{ 0x55, 0xaa })); + } + + { + // Downcast (safety-checked) + const down0: @Vector(2, u8) = @intCast(up1); + + try expect(mem.eql(u8, &@as([2]u8, down0), &[2]u8{ 0x55, 0xaa })); + } + } + { + // Upcast (implicit, equivalent to @intCast) + var up0: @Vector(4, u8) = .{ 0x00, 0x55, 0xaa, 0xff }; + _ = &up0; + const up1: @Vector(4, u16) = up0; + const up2: @Vector(4, u32) = up0; + const up3: @Vector(4, u64) = up0; + + try expect(mem.eql(u16, &@as([4]u16, up1), &[4]u16{ 0x00, 0x55, 0xaa, 0xff })); + try expect(mem.eql(u32, &@as([4]u32, up2), &[4]u32{ 0x00, 0x55, 0xaa, 0xff })); + try expect(mem.eql(u64, &@as([4]u64, up3), &[4]u64{ 0x00, 0x55, 0xaa, 0xff })); + + { + // Downcast (safety-checked) + const down2: @Vector(4, u32) = @intCast(up3); + const down1: @Vector(4, u16) = @intCast(up3); + const down0: @Vector(4, u8) = @intCast(up3); + + try expect(mem.eql(u32, &@as([4]u32, down2), &[4]u32{ 0x00, 0x55, 0xaa, 0xff })); + try expect(mem.eql(u16, &@as([4]u16, down1), &[4]u16{ 0x00, 0x55, 0xaa, 0xff })); + try expect(mem.eql(u8, &@as([4]u8, down0), &[4]u8{ 0x00, 0x55, 0xaa, 0xff })); + } + + { + // Downcast (safety-checked) + const down1: @Vector(4, u16) = @intCast(up2); + const down0: @Vector(4, u8) = @intCast(up2); + + try expect(mem.eql(u16, &@as([4]u16, down1), &[4]u16{ 0x00, 0x55, 0xaa, 0xff })); + try expect(mem.eql(u8, &@as([4]u8, down0), &[4]u8{ 0x00, 0x55, 0xaa, 0xff })); + } + + { + // Downcast (safety-checked) + const down0: @Vector(4, u8) = @intCast(up1); + + try expect(mem.eql(u8, &@as([4]u8, down0), &[4]u8{ 0x00, 0x55, 0xaa, 0xff })); + } + } + { + // Upcast (implicit, equivalent to @intCast) + var up0: @Vector(8, u8) = .{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + }; + _ = &up0; + const up1: @Vector(8, u16) = up0; + const up2: @Vector(8, u32) = up0; + const up3: @Vector(8, u64) = up0; + + try expect(mem.eql(u16, &@as([8]u16, up1), &[8]u16{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + try expect(mem.eql(u32, &@as([8]u32, up2), &[8]u32{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + try expect(mem.eql(u64, &@as([8]u64, up3), &[8]u64{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + + { + // Downcast (safety-checked) + const down2: @Vector(8, u32) = @intCast(up3); + const down1: @Vector(8, u16) = @intCast(up3); + const down0: @Vector(8, u8) = @intCast(up3); + + try expect(mem.eql(u32, &@as([8]u32, down2), &[8]u32{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + try expect(mem.eql(u16, &@as([8]u16, down1), &[8]u16{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + try expect(mem.eql(u8, &@as([8]u8, down0), &[8]u8{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + } + + { + // Downcast (safety-checked) + const down1: @Vector(8, u16) = @intCast(up2); + const down0: @Vector(8, u8) = @intCast(up2); + + try expect(mem.eql(u16, &@as([8]u16, down1), &[8]u16{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + try expect(mem.eql(u8, &@as([8]u8, down0), &[8]u8{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + } + + { + // Downcast (safety-checked) + const down0: @Vector(8, u8) = @intCast(up1); + + try expect(mem.eql(u8, &@as([8]u8, down0), &[8]u8{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + })); + } + } + { + // Upcast (implicit, equivalent to @intCast) + var up0: @Vector(16, u8) = .{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + }; + _ = &up0; + const up1: @Vector(16, u16) = up0; + const up2: @Vector(16, u32) = up0; + const up3: @Vector(16, u64) = up0; + + try expect(mem.eql(u16, &@as([16]u16, up1), &[16]u16{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + try expect(mem.eql(u32, &@as([16]u32, up2), &[16]u32{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + try expect(mem.eql(u64, &@as([16]u64, up3), &[16]u64{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + + { + // Downcast (safety-checked) + const down2: @Vector(16, u32) = @intCast(up3); + const down1: @Vector(16, u16) = @intCast(up3); + const down0: @Vector(16, u8) = @intCast(up3); + + try expect(mem.eql(u32, &@as([16]u32, down2), &[16]u32{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + try expect(mem.eql(u16, &@as([16]u16, down1), &[16]u16{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + try expect(mem.eql(u8, &@as([16]u8, down0), &[16]u8{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + } + + { + // Downcast (safety-checked) + const down1: @Vector(16, u16) = @intCast(up2); + const down0: @Vector(16, u8) = @intCast(up2); + + try expect(mem.eql(u16, &@as([16]u16, down1), &[16]u16{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + try expect(mem.eql(u8, &@as([16]u8, down0), &[16]u8{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + } + + { + // Downcast (safety-checked) + const down0: @Vector(16, u8) = @intCast(up1); + + try expect(mem.eql(u8, &@as([16]u8, down0), &[16]u8{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + })); + } + } } }; diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index d90f48d86d..48213e2178 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -968,6 +968,73 @@ test "saturating add" { const expected = i8x3{ 127, 127, 127 }; try expect(mem.eql(i8, &@as([3]i8, expected), &@as([3]i8, result))); } + try testElemType(i4); + try testElemType(u4); + try testElemType(i8); + try testElemType(u8); + try testElemType(i12); + try testElemType(u12); + try testElemType(i16); + try testElemType(u16); + try testElemType(i24); + try testElemType(u24); + try testElemType(i32); + try testElemType(u32); + try testElemType(i48); + try testElemType(u48); + try testElemType(i64); + try testElemType(u64); + } + fn testElemType(comptime Elem: type) !void { + const min = std.math.minInt(Elem); + const max = std.math.maxInt(Elem); + + var v: @Vector(4, Elem) = .{ 0, 1, 0, 1 }; + v +|= .{ 0, 0, 1, 1 }; + try expect(v[0] == 0); + try expect(v[1] == 1); + try expect(v[2] == 1); + try expect(v[3] == 2); + + v = .{ 0, max, 1, max }; + v +|= .{ max, 0, max, 1 }; + try expect(v[0] == max); + try expect(v[1] == max); + try expect(v[2] == max); + try expect(v[3] == max); + + v = .{ 1, max - 1, max / 2, max }; + v +|= .{ max - 1, 1, max / 2, max }; + try expect(v[0] == max); + try expect(v[1] == max); + try expect(v[2] == max - 1); + try expect(v[3] == max); + + switch (@typeInfo(Elem).int.signedness) { + .signed => { + v = .{ -1, -1, 0, -1 }; + v +|= .{ 1, 0, -1, -1 }; + try expect(v[0] == 0); + try expect(v[1] == -1); + try expect(v[2] == -1); + try expect(v[3] == -2); + + v = .{ 0, min, -1, min }; + v +|= .{ min, 0, min, -1 }; + try expect(v[0] == min); + try expect(v[1] == min); + try expect(v[2] == min); + try expect(v[3] == min); + + v = .{ -1, min + 1, min / 2, min }; + v +|= .{ min + 1, -1, min / 2, min }; + try expect(v[0] == min); + try expect(v[1] == min); + try expect(v[2] == min); + try expect(v[3] == min); + }, + .unsigned => {}, + } } }; try S.doTheTest(); @@ -984,14 +1051,83 @@ test "saturating subtraction" { const S = struct { fn doTheTest() !void { - // Broken out to avoid https://github.com/ziglang/zig/issues/11251 - const u8x3 = @Vector(3, u8); - var lhs = u8x3{ 0, 0, 0 }; - var rhs = u8x3{ 255, 255, 255 }; - _ = .{ &lhs, &rhs }; - const result = lhs -| rhs; - const expected = u8x3{ 0, 0, 0 }; - try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); + { + // Broken out to avoid https://github.com/ziglang/zig/issues/11251 + const u8x3 = @Vector(3, u8); + var lhs = u8x3{ 0, 0, 0 }; + var rhs = u8x3{ 255, 255, 255 }; + _ = .{ &lhs, &rhs }; + const result = lhs -| rhs; + const expected = u8x3{ 0, 0, 0 }; + try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); + } + try testElemType(i4); + try testElemType(u4); + try testElemType(i8); + try testElemType(u8); + try testElemType(i12); + try testElemType(u12); + try testElemType(i16); + try testElemType(u16); + try testElemType(i24); + try testElemType(u24); + try testElemType(i32); + try testElemType(u32); + try testElemType(i48); + try testElemType(u48); + try testElemType(i64); + try testElemType(u64); + } + fn testElemType(comptime Elem: type) !void { + const min = std.math.minInt(Elem); + const max = std.math.maxInt(Elem); + + var v: @Vector(4, Elem) = .{ 0, 1, 0, 1 }; + v -|= .{ 0, 0, 1, 1 }; + try expect(v[0] == 0); + try expect(v[1] == 1); + try expect(v[2] == @max(min, -1)); + try expect(v[3] == 0); + + v = .{ 0, max, 1, max }; + v -|= .{ max, 0, max, 1 }; + try expect(v[0] == @min(min + 1, 0)); + try expect(v[1] == max); + try expect(v[2] == @min(min + 2, 0)); + try expect(v[3] == max - 1); + + v = .{ 1, max - 1, max / 2, max }; + v -|= .{ max - 1, 1, max / 2, max }; + try expect(v[0] == @min(min + 3, 0)); + try expect(v[1] == max - 2); + try expect(v[2] == 0); + try expect(v[3] == 0); + + switch (@typeInfo(Elem).int.signedness) { + .signed => { + v = .{ -1, -1, 0, -1 }; + v -|= .{ -1, 0, 1, 1 }; + try expect(v[0] == 0); + try expect(v[1] == -1); + try expect(v[2] == -1); + try expect(v[3] == -2); + + v = .{ 0, min, -1, min }; + v -|= .{ max, 0, max, 1 }; + try expect(v[0] == min + 1); + try expect(v[1] == min); + try expect(v[2] == min); + try expect(v[3] == min); + + v = .{ -1, min + 1, min / 2, min }; + v -|= .{ max, 1, max / 2, max }; + try expect(v[0] == min); + try expect(v[1] == min); + try expect(v[2] == min + 1); + try expect(v[3] == min); + }, + .unsigned => {}, + } } }; try S.doTheTest(); From 72017d4bd5f69eb243053a76227753b69bdf6b56 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 13 Sep 2025 19:32:52 -0700 Subject: [PATCH 047/112] mem.replace: Document that input/output cannot overlap --- lib/std/mem.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index f688bafcae..0b0967a566 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -3785,6 +3785,7 @@ test rotate { /// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of /// appropriate size. Use replacementSize to calculate an appropriate buffer size. +/// The `input` and `output` slices must not overlap. /// The needle must not be empty. /// Returns the number of replacements made. pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize { From ac562577381f6e94196337e0ccd8895bd1bcddb2 Mon Sep 17 00:00:00 2001 From: rohlem Date: Wed, 17 Sep 2025 23:11:46 +0200 Subject: [PATCH 048/112] langref: mention union support of `@fieldParentPtr` --- doc/langref.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index b7c9c7291e..bb3e00e656 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4888,8 +4888,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val {#header_open|@fieldParentPtr#}
    {#syntax#}@fieldParentPtr(comptime field_name: []const u8, field_ptr: *T) anytype{#endsyntax#}

    - Given a pointer to a struct field, returns a pointer to the struct containing that field. - The return type (and struct in question) is the inferred result type. + Given a pointer to a struct or union field, returns a pointer to the struct or union containing that field. + The return type (pointer to the parent struct or union in question) is the inferred result type.

    If {#syntax#}field_ptr{#endsyntax#} does not point to the {#syntax#}field_name{#endsyntax#} field of an instance of From 91eab35e08f72220fd2461bdf6da2f74aaff5c64 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Thu, 18 Sep 2025 04:54:15 +0200 Subject: [PATCH 049/112] std.sort.pdq: fix out-of-bounds access in partialInsertionSort (#25253) * std.sort.pdq: fix out-of-bounds access in partialInsertionSort When sorting a sub-range that doesn't start at index 0, the partialInsertionSort function could access indices below the range start. The loop condition `while (j >= 1)` didn't respect the arbitrary range boundaries [a, b). This changes the condition to `while (j > a)` to ensure indices never go below the range start, fixing the issue where pdqContext would access out-of-bounds indices. Fixes #25250 --- lib/std/sort/pdq.zig | 49 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/std/sort/pdq.zig b/lib/std/sort/pdq.zig index 55bd17ae93..e1f4f72136 100644 --- a/lib/std/sort/pdq.zig +++ b/lib/std/sort/pdq.zig @@ -227,7 +227,7 @@ fn partialInsertionSort(a: usize, b: usize, context: anytype) bool { // shift the smaller element to the left. if (i - a >= 2) { var j = i - 1; - while (j >= 1) : (j -= 1) { + while (j > a) : (j -= 1) { if (!context.lessThan(j, j - 1)) break; context.swap(j, j - 1); } @@ -328,3 +328,50 @@ fn reverseRange(a: usize, b: usize, context: anytype) void { j -= 1; } } + +test "pdqContext respects arbitrary range boundaries" { + // Regression test for issue #25250 + // pdqsort should never access indices outside the specified [a, b) range + var data: [2000]i32 = @splat(0); + + // Fill with data that triggers the partialInsertionSort path + for (0..data.len) |i| { + data[i] = @intCast(@mod(@as(i32, @intCast(i)) * 7, 100)); + } + + const TestContext = struct { + items: []i32, + range_start: usize, + range_end: usize, + + pub fn lessThan(ctx: @This(), a: usize, b: usize) bool { + // Assert indices are within the expected range + testing.expect(a >= ctx.range_start and a < ctx.range_end) catch @panic("index a out of range"); + testing.expect(b >= ctx.range_start and b < ctx.range_end) catch @panic("index b out of range"); + return ctx.items[a] < ctx.items[b]; + } + + pub fn swap(ctx: @This(), a: usize, b: usize) void { + // Assert indices are within the expected range + testing.expect(a >= ctx.range_start and a < ctx.range_end) catch @panic("index a out of range"); + testing.expect(b >= ctx.range_start and b < ctx.range_end) catch @panic("index b out of range"); + mem.swap(i32, &ctx.items[a], &ctx.items[b]); + } + }; + + // Test sorting a sub-range that doesn't start at 0 + const start = 1118; + const end = 1764; + const ctx = TestContext{ + .items = &data, + .range_start = start, + .range_end = end, + }; + + pdqContext(start, end, ctx); + + // Verify the range is sorted + for ((start + 1)..end) |i| { + try testing.expect(data[i - 1] <= data[i]); + } +} From 7d8a9545785b3540ebd8e87d8039fc8239f660ab Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 18 Sep 2025 23:03:53 +0200 Subject: [PATCH 050/112] zig fmt help: mention that the argument can be a directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’ve been typing `zig fmt **/.zig` for a long time, until I discovered that the argument can actually be a directory. Mention this feature explicitly in the help message. --- src/fmt.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fmt.zig b/src/fmt.zig index 1287334132..4a3c321877 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -156,7 +156,7 @@ pub fn run(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } if (input_files.items.len == 0) { - fatal("expected at least one source file argument", .{}); + fatal("expected at least one file or directory argument", .{}); } var stdout_buffer: [4096]u8 = undefined; From 90c1123d1c9f02c0c2036053dcf9ad6b35e848da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 17 Sep 2025 00:23:48 +0200 Subject: [PATCH 051/112] std.mem: work around LoongArch inline asm bug in doNotOptimizeAway() https://github.com/llvm/llvm-project/issues/159200 --- lib/std/mem.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 0b0967a566..6a5e654fab 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -4485,7 +4485,8 @@ pub fn doNotOptimizeAway(val: anytype) void { } else doNotOptimizeAway(&val); }, .float => { - if ((t.float.bits == 32 or t.float.bits == 64) and builtin.zig_backend != .stage2_c) { + // https://github.com/llvm/llvm-project/issues/159200 + if ((t.float.bits == 32 or t.float.bits == 64) and builtin.zig_backend != .stage2_c and !builtin.cpu.arch.isLoongArch()) { asm volatile ("" : : [_] "rm" (val), From c15f8b9fc97e141bb59f7d11c3bf4ad76a5036ed Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 4 Sep 2025 23:09:09 +0200 Subject: [PATCH 052/112] Fix duplicate LC_RPATH entries on macOS Tahoe When building on macOS Tahoe, binaries were getting duplicate LC_RPATH load commands which caused dyld to refuse to run them with a "duplicate LC_RPATH" error that has become a hard error. The duplicates occurred when library directories were being added to rpath_list twice: - from lib_directories - from native system paths detection which includes the same dirs --- src/main.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.zig b/src/main.zig index cc334628f6..8e6949e813 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3395,6 +3395,14 @@ fn buildOutputType( var file_system_inputs: std.ArrayListUnmanaged(u8) = .empty; defer file_system_inputs.deinit(gpa); + // Deduplicate rpath entries + var rpath_dedup = std.StringArrayHashMapUnmanaged(void){}; + for (create_module.rpath_list.items) |rpath| { + try rpath_dedup.put(arena, rpath, {}); + } + create_module.rpath_list.clearRetainingCapacity(); + try create_module.rpath_list.appendSlice(arena, rpath_dedup.keys()); + var create_diag: Compilation.CreateDiagnostic = undefined; const comp = Compilation.create(gpa, arena, &create_diag, .{ .dirs = dirs, From 8d5b651c3a37a432957aefd633e755c56c07b59d Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 19 Sep 2025 22:10:53 -0700 Subject: [PATCH 053/112] Reader.defaultReadVec: Workaround bad `r.end += r.vtable.stream()` behavior If `r.end` is updated in the `stream` implementation, then it's possible that `r.end += ...` will behave unexpectedly. What seems to happen is that it reverts back to its value before the function call and then the increment happens. Here's a reproduction: ```zig test "fill when stream modifies `end` and returns 0" { var buf: [3]u8 = undefined; var zero_reader = infiniteZeroes(&buf); _ = try zero_reader.fill(1); try std.testing.expectEqual(buf.len, zero_reader.end); } pub fn infiniteZeroes(buf: []u8) std.Io.Reader { return .{ .vtable = &.{ .stream = stream, }, .buffer = buf, .end = 0, .seek = 0, }; } fn stream(r: *std.Io.Reader, _: *std.Io.Writer, _: std.Io.Limit) std.Io.Reader.StreamError!usize { @memset(r.buffer[r.seek..], 0); r.end = r.buffer.len; return 0; } ``` When `fill` is called, it will call into `vtable.readVec` which in this case is `defaultReadVec`. In `defaultReadVec`: - Before the `r.end += r.vtable.stream` line, `r.end` will be 0 - In `r.vtable.stream`, `r.end` is modified to 3 and it returns 0 - After the `r.end += r.vtable.stream` line, `r.end` will be 0 instead of the expected 3 Separating the `r.end += stream();` into two lines fixes the problem (and this separation is done elsewhere in `Reader` so it seems possible that this class of bug has been encountered before). Potentially related issues: - https://github.com/ziglang/zig/issues/4021 - https://github.com/ziglang/zig/issues/12064 --- lib/std/Io/Reader.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index 5e08ad6d0c..8e6e96c9dc 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -400,10 +400,11 @@ pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize { .vtable = &.{ .drain = Writer.fixedDrain }, }; const limit: Limit = .limited(writer.buffer.len - writer.end); - r.end += r.vtable.stream(r, &writer, limit) catch |err| switch (err) { + const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) { error.WriteFailed => unreachable, else => |e| return e, }; + r.end += n; return 0; } From 582b96c361e9bad0a7e44b67af932689bc1afb82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 3 Sep 2025 18:30:40 -0700 Subject: [PATCH 054/112] std.zon.parse: fix not initializing array sentinel --- lib/std/zon/parse.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 5f74400c29..313e30eb48 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -717,6 +717,7 @@ const Parser = struct { elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i))); } + if (array_info.sentinel()) |s| result[result.len] = s; return result; } From b4394412bb0f5d2ff0018fb9e868ccf0f46ab911 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 10 Sep 2025 12:48:42 +0100 Subject: [PATCH 055/112] Zcu: fix analysis of type of decl with inferred type If the `nav_ty` is resolved by the `nav_val`, then we need to also mark the `nav_ty` as in progress when we begin resolving the `nav_val`. --- src/Sema.zig | 2 +- src/Zcu/PerThread.zig | 32 ++++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 1c2a9fba83..90e74f7555 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -34975,7 +34975,7 @@ fn resolveInferredErrorSet( const resolved_ty = func.resolvedErrorSetUnordered(ip); if (resolved_ty != .none) return resolved_ty; - if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .func = func_index }))) { + if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) { return sema.fail(block, src, "unable to resolve inferred error set", .{}); } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 684b3a8658..77cddb204e 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -700,7 +700,7 @@ fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); - try zcu.analysis_in_progress.put(gpa, unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, unit, {}); defer assert(zcu.analysis_in_progress.swapRemove(unit)); // Before we begin, collect: @@ -864,7 +864,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu const file = zcu.fileByIndex(inst_resolved.file); const zir = file.zir.?; - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); var analysis_arena: std.heap.ArenaAllocator = .init(gpa); @@ -958,6 +958,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu log.debug("ensureNavValUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); + assert(!zcu.analysis_in_progress.contains(anal_unit)); + // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the // status is `.unresolved`, which indicates that the value is outdated because it has *never* // been analyzed so far. @@ -1090,10 +1092,19 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_resolved.file); const zir = file.zir.?; + const zir_decl = zir.getDeclaration(inst_resolved.inst); - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); + // If there's no type body, we are also resolving the type here. + if (zir_decl.type_body == null) { + try zcu.analysis_in_progress.putNoClobber(gpa, .wrap(.{ .nav_ty = nav_id }), {}); + } + errdefer if (zir_decl.type_body == null) { + _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id })); + }; + var analysis_arena: std.heap.ArenaAllocator = .init(gpa); defer analysis_arena.deinit(); @@ -1133,8 +1144,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr }; defer block.instructions.deinit(gpa); - const zir_decl = zir.getDeclaration(inst_resolved.inst); - const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); const init_src = block.src(.{ .node_offset_var_decl_init = .zero }); const align_src = block.src(.{ .node_offset_var_decl_align = .zero }); @@ -1305,6 +1314,9 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr // Mark the unit as completed before evaluating the export! assert(zcu.analysis_in_progress.swapRemove(anal_unit)); + if (zir_decl.type_body == null) { + assert(zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }))); + } if (zir_decl.linkage == .@"export") { const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) }); @@ -1347,6 +1359,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc log.debug("ensureNavTypeUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); + assert(!zcu.analysis_in_progress.contains(anal_unit)); + const type_resolved_by_value: bool = from_val: { const analysis = nav.analysis orelse break :from_val false; const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false; @@ -1463,8 +1477,8 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr const file = zcu.fileByIndex(inst_resolved.file); const zir = file.zir.?; - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); - defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); + defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); const zir_decl = zir.getDeclaration(inst_resolved.inst); const type_body = zir_decl.type_body.?; @@ -1587,6 +1601,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); + assert(!zcu.analysis_in_progress.contains(anal_unit)); + const func = zcu.funcInfo(func_index); assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one @@ -2781,7 +2797,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE const file = zcu.fileByIndex(inst_info.file); const zir = file.zir.?; - try zcu.analysis_in_progress.put(gpa, anal_unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); func.setAnalyzed(ip); From 3ab845e028f464666856e4eaaeff2e6ab1b3da5d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Sep 2025 14:56:04 -0700 Subject: [PATCH 056/112] frontend: another packedStructFieldPtrInfo fix it was calculating host integer size in a wrong way. just use integer abi size --- src/Type.zig | 5 +---- test/behavior/union.zig | 10 +++------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Type.zig b/src/Type.zig index 3ef95b285c..b9df7a5111 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3545,10 +3545,7 @@ pub fn packedStructFieldPtrInfo(struct_ty: Type, parent_ptr_ty: Type, field_idx: parent_ptr_info.packed_offset.host_size, parent_ptr_info.packed_offset.bit_offset + bit_offset, } else .{ - switch (zcu.comp.getZigBackend()) { - else => (running_bits + 7) / 8, - .stage2_x86_64 => @intCast(struct_ty.abiSize(zcu)), - }, + @intCast(struct_ty.abiSize(zcu)), bit_offset, }; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index f821097c27..ec562b17c8 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1564,13 +1564,9 @@ test "packed union field pointer has correct alignment" { const bp = &b.u.x; const cp = &c.u.x; - const host_size = switch (builtin.zig_backend) { - else => comptime std.math.divCeil(comptime_int, @bitSizeOf(S), 8) catch unreachable, - .stage2_x86_64 => @sizeOf(S), - }; - comptime assert(@TypeOf(ap) == *align(4:2:host_size) u20); - comptime assert(@TypeOf(bp) == *align(1:2:host_size) u20); - comptime assert(@TypeOf(cp) == *align(64:2:host_size) u20); + comptime assert(@TypeOf(ap) == *align(4:2:@sizeOf(S)) u20); + comptime assert(@TypeOf(bp) == *align(1:2:@sizeOf(S)) u20); + comptime assert(@TypeOf(cp) == *align(64:2:@sizeOf(S)) u20); a.u = .{ .x = 123 }; b.u = .{ .x = 456 }; From 4d102751b6346cf614699f0a830fb5a2c1ee9f66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 22:52:30 -0700 Subject: [PATCH 057/112] frontend: fix too strict assertion field ptr can be based on C pointer --- src/Value.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Value.zig b/src/Value.zig index d2e6c9f1ac..74ae2b51f4 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -2222,7 +2222,7 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value { const aggregate_ty = parent_ptr_ty.childType(zcu); const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); - assert(parent_ptr_info.flags.size == .one); + assert(parent_ptr_info.flags.size == .one or parent_ptr_info.flags.size == .c); // Exiting this `switch` indicates that the `field` pointer representation should be used. // `field_align` may be `.none` to represent the natural alignment of `field_ty`, but is not necessarily. From 670c4fae617af3a7cfbdfcd850b65d61b94d1cf8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 6 Sep 2025 11:42:22 -0700 Subject: [PATCH 058/112] frontend: additionally handle C pointers in ptrOptPayload --- src/Value.zig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Value.zig b/src/Value.zig index 74ae2b51f4..166e18519d 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -2149,15 +2149,18 @@ pub fn makeBool(x: bool) Value { return if (x) .true else .false; } -/// `parent_ptr` must be a single-pointer to some optional. +/// `parent_ptr` must be a single-pointer or C pointer to some optional. +/// /// Returns a pointer to the payload of the optional. +/// /// May perform type resolution. pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { const zcu = pt.zcu; const parent_ptr_ty = parent_ptr.typeOf(zcu); const opt_ty = parent_ptr_ty.childType(zcu); + const ptr_size = parent_ptr_ty.ptrSize(zcu); - assert(parent_ptr_ty.ptrSize(zcu) == .one); + assert(ptr_size == .one or ptr_size == .c); assert(opt_ty.zigTypeTag(zcu) == .optional); const result_ty = try pt.ptrTypeSema(info: { @@ -2212,9 +2215,12 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { } })); } -/// `parent_ptr` must be a single-pointer to a struct, union, or slice. +/// `parent_ptr` must be a single-pointer or c pointer to a struct, union, or slice. +/// /// Returns a pointer to the aggregate field at the specified index. +/// /// For slices, uses `slice_ptr_index` and `slice_len_index`. +/// /// May perform type resolution. pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value { const zcu = pt.zcu; From 37985613c760d1d20a1fbba292fd5c25129f400a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 10 Sep 2025 03:10:46 -0400 Subject: [PATCH 059/112] x86_64: fix safety crashes in `storeRegs` --- src/arch/x86_64/CodeGen.zig | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c3cfb60e88..8a23eebf74 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -187019,15 +187019,17 @@ const Temp = struct { }, .struct_type => { assert(src_regs.len - part_index == std.math.divCeil(u32, src_abi_size, 8) catch unreachable); - break :part_ty .u64; + break :part_ty switch (src_abi_size) { + 0, 3, 5...7 => unreachable, + 1 => .u8, + 2 => .u16, + 4 => .u32, + else => .u64, + }; }, }; const part_size: u31 = @intCast(part_ty.abiSize(zcu)); const src_rc = src_reg.class(); - const part_bit_size = switch (src_rc) { - else => 8 * part_size, - .x87 => part_ty.bitSize(zcu), - }; if (src_rc == .x87 or std.math.isPowerOfTwo(part_size)) { // hack around linker relocation bugs switch (ptr.tracking(cg).short) { @@ -187036,7 +187038,15 @@ const Temp = struct { } const strat = try cg.moveStrategy(part_ty, src_rc, false); try strat.write(cg, try ptr.tracking(cg).short.deref().mem(cg, .{ - .size = .fromBitSize(part_bit_size), + .size = switch (src_rc) { + else => .fromBitSize(8 * part_size), + .x87 => switch (abi.classifySystemV(src_ty, zcu, cg.target, .other)[part_index]) { + else => unreachable, + .float => .dword, + .float_combine, .sse => .qword, + .x87 => .tbyte, + }, + }, .disp = part_disp, }), registerAlias(src_reg, part_size)); } else { From 3da6a19011267a2d69385e0b7848e7726c266897 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 3 Sep 2025 18:22:14 -0700 Subject: [PATCH 060/112] x86 codegen: handle spilled tuples --- src/arch/x86_64/CodeGen.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8a23eebf74..7d5ef2b1b1 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -187027,6 +187027,10 @@ const Temp = struct { else => .u64, }; }, + .tuple_type => |tuple_type| { + assert(tuple_type.types.len == src_regs.len); + break :part_ty .fromInterned(tuple_type.types.get(ip)[part_index]); + }, }; const part_size: u31 = @intCast(part_ty.abiSize(zcu)); const src_rc = src_reg.class(); From 798acd932e8a6aaad452f038e8c5b68efc79a8f5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 18 Sep 2025 16:19:56 +0200 Subject: [PATCH 061/112] aarch64/zonCast: don't return a pointer to a stack element Elements are computed at comptime, so don't declare them as "var". --- src/codegen/aarch64/Assemble.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/codegen/aarch64/Assemble.zig b/src/codegen/aarch64/Assemble.zig index 4b5aa8e04f..5cf9c2da3c 100644 --- a/src/codegen/aarch64/Assemble.zig +++ b/src/codegen/aarch64/Assemble.zig @@ -42,8 +42,11 @@ fn zonCast(comptime Result: type, zon_value: anytype, symbols: anytype) Result { .@"struct" => |zon_struct| switch (@typeInfo(Result)) { .pointer => |result_pointer| { comptime assert(result_pointer.size == .slice and result_pointer.is_const); - var elems: [zon_value.len]result_pointer.child = undefined; - inline for (&elems, zon_value) |*elem, zon_elem| elem.* = zonCast(result_pointer.child, zon_elem, symbols); + const elems = comptime blk: { + var temp_elems: [zon_value.len]result_pointer.child = undefined; + for (&temp_elems, zon_value) |*elem, zon_elem| elem.* = zonCast(result_pointer.child, zon_elem, symbols); + break :blk temp_elems; + }; return &elems; }, .@"struct" => |result_struct| { From 80eacd60034b71a4eaa580fe83822d80d2e09360 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 12 Sep 2025 00:30:20 -0400 Subject: [PATCH 062/112] aarch64: fix behavior failures --- src/codegen/aarch64/Select.zig | 56 +++++++++++----------- test/behavior/switch_on_captured_error.zig | 1 - test/behavior/try.zig | 2 - 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index b4550b28a5..48fc1911b8 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -5821,29 +5821,21 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .unwrap_errunion_err_ptr => { - if (isel.live_values.fetchRemove(air.inst_index)) |error_ptr_vi| unused: { - defer error_ptr_vi.value.deref(isel); + if (isel.live_values.fetchRemove(air.inst_index)) |error_vi| { + defer error_vi.value.deref(isel); const ty_op = air.data(air.inst_index).ty_op; - switch (codegen.errUnionErrorOffset( - isel.air.typeOf(ty_op.operand, ip).childType(zcu).errorUnionPayload(zcu), - zcu, - )) { - 0 => try error_ptr_vi.value.move(isel, ty_op.operand), - else => |error_offset| { - const error_ptr_ra = try error_ptr_vi.value.defReg(isel) orelse break :unused; - const error_union_ptr_vi = try isel.use(ty_op.operand); - const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel); - const lo12: u12 = @truncate(error_offset >> 0); - const hi12: u12 = @intCast(error_offset >> 12); - if (hi12 > 0) try isel.emit(.add( - error_ptr_ra.x(), - if (lo12 > 0) error_ptr_ra.x() else error_union_ptr_mat.ra.x(), - .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } }, - )); - if (lo12 > 0) try isel.emit(.add(error_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 })); - try error_union_ptr_mat.finish(isel); - }, - } + const error_union_ptr_ty = isel.air.typeOf(ty_op.operand, ip); + const error_union_ptr_info = error_union_ptr_ty.ptrInfo(zcu); + const error_union_ptr_vi = try isel.use(ty_op.operand); + const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel); + _ = try error_vi.value.load(isel, ty_op.ty.toType(), error_union_ptr_mat.ra, .{ + .offset = codegen.errUnionErrorOffset( + ZigType.fromInterned(error_union_ptr_info.child).errorUnionPayload(zcu), + zcu, + ), + .@"volatile" = error_union_ptr_info.flags.is_volatile, + }); + try error_union_ptr_mat.finish(isel); } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, @@ -8011,6 +8003,7 @@ pub fn layout( while (save_index < saves.len) { if (save_index + 2 <= saves.len and saves[save_index + 1].needs_restore and saves[save_index + 0].class == saves[save_index + 1].class and + saves[save_index + 0].size == saves[save_index + 1].size and saves[save_index + 0].offset + saves[save_index + 0].size == saves[save_index + 1].offset) { try isel.emit(.ldp( @@ -8317,7 +8310,7 @@ fn elemPtr( }), 2 => { const shift: u6 = @intCast(@ctz(elem_size)); - const temp_ra = temp_ra: switch (op) { + const temp_ra, const free_temp_ra = temp_ra: switch (op) { .add => switch (base_ra) { else => { const temp_ra = try isel.allocIntReg(); @@ -8326,7 +8319,7 @@ fn elemPtr( .register = temp_ra.x(), .shift = .{ .lsl = shift }, } })); - break :temp_ra temp_ra; + break :temp_ra .{ temp_ra, true }; }, .zr => { if (shift > 0) try isel.emit(.ubfm(elem_ptr_ra.x(), elem_ptr_ra.x(), .{ @@ -8334,7 +8327,7 @@ fn elemPtr( .immr = -%shift, .imms = ~shift, })); - break :temp_ra elem_ptr_ra; + break :temp_ra .{ elem_ptr_ra, false }; }, }, .sub => { @@ -8344,10 +8337,10 @@ fn elemPtr( .register = temp_ra.x(), .shift = .{ .lsl = shift }, } })); - break :temp_ra temp_ra; + break :temp_ra .{ temp_ra, true }; }, }; - defer if (temp_ra != elem_ptr_ra) isel.freeReg(temp_ra); + defer if (free_temp_ra) isel.freeReg(temp_ra); try isel.emit(.add(temp_ra.x(), index_mat.ra.x(), .{ .shifted_register = .{ .register = index_mat.ra.x(), .shift = .{ .lsl = @intCast(63 - @clz(elem_size) - shift) }, @@ -9276,7 +9269,14 @@ pub const Value = struct { part_offset -= part_size; var wrapped_res_part_it = res_vi.field(ty, part_offset, part_size); const wrapped_res_part_vi = try wrapped_res_part_it.only(isel); - const wrapped_res_part_ra = try wrapped_res_part_vi.?.defReg(isel) orelse if (need_carry) .zr else continue; + const wrapped_res_part_ra = wrapped_res_part_ra: { + const overflow_ra_lock: RegLock = switch (opts.overflow) { + .ra => |ra| isel.lockReg(ra), + else => .empty, + }; + defer overflow_ra_lock.unlock(isel); + break :wrapped_res_part_ra try wrapped_res_part_vi.?.defReg(isel) orelse if (need_carry) .zr else continue; + }; const unwrapped_res_part_ra = unwrapped_res_part_ra: { if (!need_wrap) break :unwrapped_res_part_ra wrapped_res_part_ra; if (int_info.bits % 32 == 0) { diff --git a/test/behavior/switch_on_captured_error.zig b/test/behavior/switch_on_captured_error.zig index 9aae1c7fbe..fcf51f6c9a 100644 --- a/test/behavior/switch_on_captured_error.zig +++ b/test/behavior/switch_on_captured_error.zig @@ -300,7 +300,6 @@ test "switch on error union catch capture" { } test "switch on error union if else capture" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; diff --git a/test/behavior/try.zig b/test/behavior/try.zig index fd120fc86f..df852abfa1 100644 --- a/test/behavior/try.zig +++ b/test/behavior/try.zig @@ -122,7 +122,6 @@ test "'return try' through conditional" { } test "try ptr propagation const" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; @@ -155,7 +154,6 @@ test "try ptr propagation const" { } test "try ptr propagation mutate" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; From ab3e34b09b031960d1673bc462a2d260f654cfd2 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 12 Sep 2025 02:50:42 -0400 Subject: [PATCH 063/112] standalone: fix misaligned stack crash --- test/standalone/stack_iterator/unwind_freestanding.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/standalone/stack_iterator/unwind_freestanding.zig b/test/standalone/stack_iterator/unwind_freestanding.zig index ec4bb5d00e..f655507b32 100644 --- a/test/standalone/stack_iterator/unwind_freestanding.zig +++ b/test/standalone/stack_iterator/unwind_freestanding.zig @@ -37,7 +37,7 @@ noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void { } // No-OS entrypoint -export fn _start() callconv(.c) noreturn { +export fn _start() callconv(.withStackAlign(.c, 1)) noreturn { var expected: [4]usize = undefined; var unwound: [4]usize = undefined; frame0(&expected, &unwound); From 92b0ec989ca181df9955309c59913e4a7e1100c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 21 Sep 2025 17:29:57 +0200 Subject: [PATCH 064/112] ci: temporarily disable riscv64-linux GitHub sucks: Sep 20 20:49:21 ganymede runsvc.sh[82817]: An error occured: Runner version v2.326.0 is deprecated and cannot receive messages. Sep 20 20:49:21 ganymede runsvc.sh[82817]: Runner listener exited with error code 1 Sep 20 20:49:21 ganymede runsvc.sh[82817]: Runner listener exit with terminated error, stop the service, no retry needed. --- .github/workflows/ci-pr-riscv64-linux.yaml | 35 ---------------------- .github/workflows/ci.yaml | 18 ----------- 2 files changed, 53 deletions(-) delete mode 100644 .github/workflows/ci-pr-riscv64-linux.yaml diff --git a/.github/workflows/ci-pr-riscv64-linux.yaml b/.github/workflows/ci-pr-riscv64-linux.yaml deleted file mode 100644 index a6ba740816..0000000000 --- a/.github/workflows/ci-pr-riscv64-linux.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: ci-pr-riscv64-linux -on: - pull_request: - types: - - labeled - - opened - - reopened - - synchronize - - unlabeled -concurrency: - # Cancels pending runs when a PR gets updated. - group: riscv64-linux-${{ github.head_ref || github.run_id }}-${{ github.actor }} - cancel-in-progress: true -permissions: - # Sets permission policy for `GITHUB_TOKEN` - contents: read -jobs: - riscv64-linux-debug: - if: contains(github.event.pull_request.labels.*.name, 'ci-riscv64-linux') - timeout-minutes: 420 - runs-on: [self-hosted, Linux, riscv64] - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build and Test - run: sh ci/riscv64-linux-debug.sh - riscv64-linux-release: - if: contains(github.event.pull_request.labels.*.name, 'ci-riscv64-linux') - timeout-minutes: 420 - runs-on: [self-hosted, Linux, riscv64] - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build and Test - run: sh ci/riscv64-linux-release.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9b3b336b30..270c0b0092 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -51,24 +51,6 @@ jobs: uses: actions/checkout@v4 - name: Build and Test run: sh ci/aarch64-linux-release.sh - riscv64-linux-debug: - if: github.event_name == 'push' - timeout-minutes: 420 - runs-on: [self-hosted, Linux, riscv64] - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build and Test - run: sh ci/riscv64-linux-debug.sh - riscv64-linux-release: - if: github.event_name == 'push' - timeout-minutes: 420 - runs-on: [self-hosted, Linux, riscv64] - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build and Test - run: sh ci/riscv64-linux-release.sh x86_64-macos-release: runs-on: "macos-13" env: From e647d1a5704d3cd66141e641d775091bf0bd8e9d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 11 Sep 2025 08:40:17 -0400 Subject: [PATCH 065/112] x86_64: rewrite vector element pointer access --- src/arch/x86_64/CodeGen.zig | 673 ++++++++++++++++++------------------ 1 file changed, 328 insertions(+), 345 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7d5ef2b1b1..2fb24020b4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2291,7 +2291,7 @@ fn genBodyBlock(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { - @setEvalBranchQuota(29_400); + @setEvalBranchQuota(29_500); const pt = cg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -86774,52 +86774,313 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const is_non_err = try cg.tempInit(.bool, .{ .eflags = .e }); try is_non_err.finish(inst, &.{un_op}, &ops, cg); }, - .load => fallback: { + .load => { const ty_op = air_datas[@intFromEnum(inst)].ty_op; const val_ty = ty_op.ty.toType(); - const ptr_ty = cg.typeOf(ty_op.operand); - const ptr_info = ptr_ty.ptrInfo(zcu); - if (ptr_info.packed_offset.host_size > 0 and - (ptr_info.flags.vector_index == .none or val_ty.toIntern() == .bool_type)) - break :fallback try cg.airLoad(inst); var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); - const res = try ops[0].load(val_ty, .{ - .disp = switch (ptr_info.flags.vector_index) { - .none => 0, - .runtime => unreachable, - else => |vector_index| @intCast(val_ty.abiSize(zcu) * @intFromEnum(vector_index)), + var res: [1]Temp = undefined; + cg.select(&res, &.{val_ty}, &ops, comptime &.{ .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .byte }, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .none, .none } }, }, - }, cg); - try res.finish(inst, &.{ty_op.operand}, &ops, cg); + .extra_temps = .{ + .{ .type = .u8, .kind = .{ .mut_rc = .{ .ref = .src0, .rc = .general_purpose } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .cc = .c }, .unused }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .movzx, .tmp0d, .lea(.src0b), ._, ._ }, + .{ ._, ._, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .word }, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .none, .none } }, + }, + .dst_temps = .{ .{ .cc = .c }, .unused }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .bt, .lea(.src0w), .ua(.src0, .add_vector_index), ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .ptr_any_bool_vec_elem, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .none, .none } }, + }, + .dst_temps = .{ .{ .cc = .c }, .unused }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .bt, .leaa(.src0d, .add_vector_index_div_8_down_4), .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + } }, + } }) catch |err| switch (err) { + error.SelectFailed => res[0] = try ops[0].load(val_ty, .{ + .disp = switch (cg.typeOf(ty_op.operand).ptrInfo(zcu).flags.vector_index) { + .none => 0, + .runtime => unreachable, + else => |vector_index| @intCast(val_ty.abiSize(zcu) * @intFromEnum(vector_index)), + }, + }, cg), + else => |e| return e, + }; + try res[0].finish(inst, &.{ty_op.operand}, &ops, cg); }, .ret => try cg.airRet(inst, false), .ret_safe => try cg.airRet(inst, true), .ret_load => try cg.airRetLoad(inst), - .store, .store_safe => |air_tag| fallback: { + .store, .store_safe => |air_tag| { const bin_op = air_datas[@intFromEnum(inst)].bin_op; - const ptr_ty = cg.typeOf(bin_op.lhs); - const ptr_info = ptr_ty.ptrInfo(zcu); - const val_ty = cg.typeOf(bin_op.rhs); - if (ptr_info.packed_offset.host_size > 0 and - (ptr_info.flags.vector_index == .none or val_ty.toIntern() == .bool_type)) - break :fallback try cg.airStore(inst, switch (air_tag) { - else => unreachable, - .store => false, - .store_safe => true, - }); var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); - try ops[0].store(&ops[1], .{ - .disp = switch (ptr_info.flags.vector_index) { - .none => 0, - .runtime => unreachable, - else => |vector_index| @intCast(val_ty.abiSize(zcu) * @intFromEnum(vector_index)), + cg.select(&.{}, &.{}, &ops, comptime &.{ .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .byte }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .{ .imm = 0 }, .none } }, }, - .safe = switch (air_tag) { - else => unreachable, - .store => false, - .store_safe => true, + .extra_temps = .{ + .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, }, - }, cg); + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .movzx, .tmp0d, .lea(.src0b), ._, ._ }, + .{ ._, ._r, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._, .mov, .lea(.src0b), .tmp0b, ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .byte }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .{ .imm = 1 }, .none } }, + }, + .extra_temps = .{ + .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .movzx, .tmp0d, .lea(.src0b), ._, ._ }, + .{ ._, ._s, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._, .mov, .lea(.src0b), .tmp0b, ._, ._ }, + } }, + }, .{ + .required_features = .{ .cmov, null, null, null }, + .src_constraints = .{ .{ .ptr_bool_vec_elem = .byte }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .to_gpr, .none } }, + }, + .extra_temps = .{ + .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, + .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .movzx, .tmp0d, .lea(.src0b), ._, ._ }, + .{ ._, ._, .mov, .tmp1d, .tmp0d, ._, ._ }, + .{ ._, ._r, .bt, .tmp1d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._s, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._, .@"test", .src1b, .si(1), ._, ._ }, + .{ ._, ._z, .cmov, .tmp0d, .tmp1d, ._, ._ }, + .{ ._, ._, .mov, .lea(.src0b), .tmp0b, ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .byte }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .to_gpr, .none } }, + }, + .extra_temps = .{ + .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .movzx, .tmp0d, .lea(.src0b), ._, ._ }, + .{ ._, ._, .@"test", .src1b, .si(1), ._, ._ }, + .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, + .{ ._, ._r, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._mp, .j, .@"1f", ._, ._, ._ }, + .{ .@"0:", ._s, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ .@"1:", ._, .mov, .lea(.src0b), .tmp0b, ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .word }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .{ .imm = 0 }, .none } }, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._r, .bt, .lea(.src0w), .ua(.src0, .add_vector_index), ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .word }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .{ .imm = 1 }, .none } }, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._s, .bt, .lea(.src0w), .ua(.src0, .add_vector_index), ._, ._ }, + } }, + }, .{ + .required_features = .{ .cmov, null, null, null }, + .src_constraints = .{ .{ .ptr_bool_vec_elem = .word }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .to_gpr, .none } }, + }, + .extra_temps = .{ + .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, + .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .movzx, .tmp0d, .lea(.src0w), ._, ._ }, + .{ ._, ._, .mov, .tmp1d, .tmp0d, ._, ._ }, + .{ ._, ._r, .bt, .tmp1d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._s, .bt, .tmp0d, .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._, .@"test", .src1b, .si(1), ._, ._ }, + .{ ._, ._z, .cmov, .tmp0d, .tmp1d, ._, ._ }, + .{ ._, ._, .mov, .lea(.src0w), .tmp0w, ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .{ .ptr_bool_vec_elem = .word }, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .to_gpr, .none } }, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .@"test", .src1b, .si(1), ._, ._ }, + .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, + .{ ._, ._r, .bt, .lea(.src0w), .ua(.src0, .add_vector_index), ._, ._ }, + .{ ._, ._mp, .j, .@"0f", ._, ._, ._ }, + .{ .@"1:", ._s, .bt, .lea(.src0w), .ua(.src0, .add_vector_index), ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .ptr_any_bool_vec_elem, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .{ .imm = 0 }, .none } }, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._r, .bt, .leaa(.src0d, .add_vector_index_div_8_down_4), .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .ptr_any_bool_vec_elem, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .{ .imm = 1 }, .none } }, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._s, .bt, .leaa(.src0d, .add_vector_index_div_8_down_4), .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + } }, + }, .{ + .required_features = .{ .cmov, null, null, null }, + .src_constraints = .{ .ptr_any_bool_vec_elem, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .to_gpr, .none } }, + }, + .extra_temps = .{ + .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, + .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .mov, .tmp0d, .leaa(.src0d, .add_vector_index_div_8_down_4), ._, ._ }, + .{ ._, ._, .mov, .tmp1d, .tmp0d, ._, ._ }, + .{ ._, ._r, .bt, .tmp1d, .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + .{ ._, ._s, .bt, .tmp0d, .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + .{ ._, ._, .@"test", .src1b, .si(1), ._, ._ }, + .{ ._, ._z, .cmov, .tmp0d, .tmp1d, ._, ._ }, + .{ ._, ._, .mov, .leaa(.src0d, .add_vector_index_div_8_down_4), .tmp0d, ._, ._ }, + } }, + }, .{ + .src_constraints = .{ .ptr_any_bool_vec_elem, .bool, .any }, + .patterns = &.{ + .{ .src = .{ .to_gpr, .to_gpr, .none } }, + }, + .clobbers = .{ .eflags = true }, + .each = .{ .once = &.{ + .{ ._, ._, .@"test", .src1b, .si(1), ._, ._ }, + .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, + .{ ._, ._r, .bt, .leaa(.src0d, .add_vector_index_div_8_down_4), .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + .{ ._, ._mp, .j, .@"0f", ._, ._, ._ }, + .{ .@"1:", ._s, .bt, .leaa(.src0d, .add_vector_index_div_8_down_4), .ua(.src0, .add_vector_index_rem_32), ._, ._ }, + } }, + } }) catch |err| switch (err) { + error.SelectFailed => try ops[0].store(&ops[1], .{ + .disp = switch (cg.typeOf(bin_op.lhs).ptrInfo(zcu).flags.vector_index) { + .none => 0, + .runtime => unreachable, + else => |vector_index| @intCast(cg.typeOf(bin_op.rhs).abiSize(zcu) * @intFromEnum(vector_index)), + }, + .safe = switch (air_tag) { + else => unreachable, + .store => false, + .store_safe => true, + }, + }, cg), + else => |e| return e, + }; for (ops) |op| try op.die(cg); }, .unreach => {}, @@ -100863,7 +101124,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .dst_temps = .{ .{ .cc = .c }, .unused }, .clobbers = .{ .eflags = true }, .each = .{ .once = &.{ - .{ ._, ._, .bt, .src0d, .ua(.none, .add_src1_rem_32), ._, ._ }, + .{ ._, ._, .bt, .src0d, .ua(.none, .add_src1), ._, ._ }, } }, }, .{ .src_constraints = .{ .{ .bool_vec = .dword }, .any, .any }, @@ -100884,7 +101145,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .dst_temps = .{ .{ .cc = .c }, .unused }, .clobbers = .{ .eflags = true }, .each = .{ .once = &.{ - .{ ._, ._, .bt, .src0q, .ua(.none, .add_src1_rem_64), ._, ._ }, + .{ ._, ._, .bt, .src0q, .ua(.none, .add_src1), ._, ._ }, } }, }, .{ .required_features = .{ .@"64bit", null, null, null }, @@ -174481,114 +174742,6 @@ fn reuseOperandAdvanced( return true; } -fn packedLoad(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - - const ptr_info = ptr_ty.ptrInfo(zcu); - const val_ty: Type = .fromInterned(ptr_info.child); - if (!val_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; - const val_abi_size: u32 = @intCast(val_ty.abiSize(zcu)); - - const val_bit_size: u32 = @intCast(val_ty.bitSize(zcu)); - const ptr_bit_off = ptr_info.packed_offset.bit_offset + switch (ptr_info.flags.vector_index) { - .none => 0, - .runtime => unreachable, - else => |vector_index| @intFromEnum(vector_index) * val_bit_size, - }; - if (ptr_bit_off % 8 == 0) { - { - const mat_ptr_mcv: MCValue = switch (ptr_mcv) { - .immediate, .register, .register_offset, .lea_frame => ptr_mcv, - else => .{ .register = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }, - }; - const mat_ptr_lock = switch (mat_ptr_mcv) { - .register => |mat_ptr_reg| self.register_manager.lockReg(mat_ptr_reg), - else => null, - }; - defer if (mat_ptr_lock) |lock| self.register_manager.unlockReg(lock); - - try self.load(dst_mcv, ptr_ty, mat_ptr_mcv.offset(@intCast(@divExact(ptr_bit_off, 8)))); - } - - if (val_abi_size * 8 > val_bit_size) { - if (dst_mcv.isRegister()) { - try self.truncateRegister(val_ty, dst_mcv.getReg().?); - } else { - const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); - - const hi_mcv = dst_mcv.address().offset(@intCast(val_bit_size / 64 * 8)).deref(); - try self.genSetReg(tmp_reg, .usize, hi_mcv, .{}); - try self.truncateRegister(val_ty, tmp_reg); - try self.genCopy(.usize, hi_mcv, .{ .register = tmp_reg }, .{}); - } - } - return; - } - - if (val_abi_size > 8) return self.fail("TODO implement packed load of {f}", .{val_ty.fmt(pt)}); - - const limb_abi_size: u31 = @min(val_abi_size, 8); - const limb_abi_bits = limb_abi_size * 8; - const val_byte_off: i32 = @intCast(ptr_bit_off / limb_abi_bits * limb_abi_size); - const val_bit_off = ptr_bit_off % limb_abi_bits; - const val_extra_bits = self.regExtraBits(val_ty); - - const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); - const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); - defer self.register_manager.unlockReg(ptr_lock); - - const dst_reg = switch (dst_mcv) { - .register => |reg| reg, - else => try self.register_manager.allocReg(null, abi.RegisterClass.gp), - }; - const dst_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); - - const load_abi_size = - if (val_bit_off < val_extra_bits) val_abi_size else val_abi_size * 2; - if (load_abi_size <= 8) { - const load_reg = registerAlias(dst_reg, load_abi_size); - try self.asmRegisterMemory(.{ ._, .mov }, load_reg, .{ - .base = .{ .reg = ptr_reg }, - .mod = .{ .rm = .{ - .size = .fromSize(load_abi_size), - .disp = val_byte_off, - } }, - }); - try self.spillEflagsIfOccupied(); - try self.asmRegisterImmediate(.{ ._r, .sh }, load_reg, .u(val_bit_off)); - } else { - const tmp_reg = - registerAlias(try self.register_manager.allocReg(null, abi.RegisterClass.gp), val_abi_size); - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); - - const dst_alias = registerAlias(dst_reg, val_abi_size); - try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ - .base = .{ .reg = ptr_reg }, - .mod = .{ .rm = .{ - .size = .fromSize(val_abi_size), - .disp = val_byte_off, - } }, - }); - try self.asmRegisterMemory(.{ ._, .mov }, tmp_reg, .{ - .base = .{ .reg = ptr_reg }, - .mod = .{ .rm = .{ - .size = .fromSize(val_abi_size), - .disp = val_byte_off + limb_abi_size, - } }, - }); - try self.spillEflagsIfOccupied(); - try self.asmRegisterRegisterImmediate(.{ ._rd, .sh }, dst_alias, tmp_reg, .u(val_bit_off)); - } - - if (val_extra_bits > 0) try self.truncateRegister(val_ty, dst_reg); - try self.genCopy(val_ty, dst_mcv, .{ .register = dst_reg }, .{}); -} - fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void { const pt = self.pt; const zcu = pt.zcu; @@ -174636,174 +174789,6 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE } } -fn airLoad(self: *CodeGen, inst: Air.Inst.Index) !void { - const pt = self.pt; - const zcu = pt.zcu; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const elem_ty = self.typeOfIndex(inst); - const result: MCValue = result: { - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; - - try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); - const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); - defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); - - const ptr_ty = self.typeOf(ty_op.operand); - const elem_size = elem_ty.abiSize(zcu); - - const elem_rs = self.regSetForType(elem_ty); - const ptr_rs = self.regSetForType(ptr_ty); - - const ptr_mcv = try self.resolveInst(ty_op.operand); - const dst_mcv = if (elem_size <= 8 and std.math.isPowerOfTwo(elem_size) and - elem_rs.supersetOf(ptr_rs) and self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv)) - // The MCValue that holds the pointer can be re-used as the value. - ptr_mcv - else - try self.allocRegOrMem(inst, true); - - const ptr_info = ptr_ty.ptrInfo(zcu); - if (ptr_info.flags.vector_index != .none or ptr_info.packed_offset.host_size > 0) { - try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv); - } else { - try self.load(dst_mcv, ptr_ty, ptr_mcv); - } - - if (elem_ty.isAbiInt(zcu) and elem_size * 8 > elem_ty.bitSize(zcu)) { - const high_mcv: MCValue = switch (dst_mcv) { - .register => |dst_reg| .{ .register = dst_reg }, - .register_pair => |dst_regs| .{ .register = dst_regs[1] }, - else => dst_mcv.address().offset(@intCast((elem_size - 1) / 8 * 8)).deref(), - }; - const high_reg = if (high_mcv.isRegister()) - high_mcv.getReg().? - else - try self.copyToTmpRegister(.usize, high_mcv); - const high_lock = self.register_manager.lockReg(high_reg); - defer if (high_lock) |lock| self.register_manager.unlockReg(lock); - - try self.truncateRegister(elem_ty, high_reg); - if (!high_mcv.isRegister()) try self.genCopy( - if (elem_size <= 8) elem_ty else .usize, - high_mcv, - .{ .register = high_reg }, - .{}, - ); - } - break :result dst_mcv; - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - -fn packedStore(self: *CodeGen, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerError!void { - const pt = self.pt; - const zcu = pt.zcu; - const ptr_info = ptr_ty.ptrInfo(zcu); - const src_ty: Type = .fromInterned(ptr_info.child); - if (!src_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; - - const limb_abi_size: u16 = @min(ptr_info.packed_offset.host_size, 8); - const limb_abi_bits = limb_abi_size * 8; - const limb_ty = try pt.intType(.unsigned, limb_abi_bits); - - const src_bit_size = src_ty.bitSize(zcu); - const ptr_bit_off = ptr_info.packed_offset.bit_offset + switch (ptr_info.flags.vector_index) { - .none => 0, - .runtime => unreachable, - else => |vector_index| @intFromEnum(vector_index) * src_bit_size, - }; - const src_byte_off: i32 = @intCast(ptr_bit_off / limb_abi_bits * limb_abi_size); - const src_bit_off = ptr_bit_off % limb_abi_bits; - - const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); - const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); - defer self.register_manager.unlockReg(ptr_lock); - - const mat_src_mcv: MCValue = mat_src_mcv: switch (src_mcv) { - .register => if (src_bit_size > 64) { - const frame_index = try self.allocFrameIndex(.initSpill(src_ty, self.pt.zcu)); - try self.genSetMem(.{ .frame = frame_index }, 0, src_ty, src_mcv, .{}); - break :mat_src_mcv .{ .load_frame = .{ .index = frame_index } }; - } else src_mcv, - else => src_mcv, - }; - - var limb_i: u16 = 0; - while (limb_i * limb_abi_bits < src_bit_off + src_bit_size) : (limb_i += 1) { - const part_bit_off = if (limb_i == 0) src_bit_off else 0; - const part_bit_size = - @min(src_bit_off + src_bit_size - limb_i * limb_abi_bits, limb_abi_bits) - part_bit_off; - const limb_mem: Memory = .{ - .base = .{ .reg = ptr_reg }, - .mod = .{ .rm = .{ - .size = .fromSize(limb_abi_size), - .disp = src_byte_off + limb_i * limb_abi_size, - } }, - }; - - const part_mask = (@as(u64, std.math.maxInt(u64)) >> @intCast(64 - part_bit_size)) << - @intCast(part_bit_off); - const part_mask_not = part_mask ^ (@as(u64, std.math.maxInt(u64)) >> @intCast(64 - limb_abi_bits)); - if (limb_abi_size <= 4) { - try self.asmMemoryImmediate(.{ ._, .@"and" }, limb_mem, .u(part_mask_not)); - } else if (std.math.cast(i32, @as(i64, @bitCast(part_mask_not)))) |small| { - try self.asmMemoryImmediate(.{ ._, .@"and" }, limb_mem, .s(small)); - } else { - const part_mask_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); - try self.asmRegisterImmediate(.{ ._, .mov }, part_mask_reg, .u(part_mask_not)); - try self.asmMemoryRegister(.{ ._, .@"and" }, limb_mem, part_mask_reg); - } - - if (src_bit_size <= 64) { - const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); - const tmp_mcv = MCValue{ .register = tmp_reg }; - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); - - try self.genSetReg(tmp_reg, limb_ty, mat_src_mcv, .{}); - switch (limb_i) { - 0 => try self.genShiftBinOpMir( - .{ ._l, .sh }, - limb_ty, - tmp_mcv, - .u8, - .{ .immediate = src_bit_off }, - ), - 1 => try self.genShiftBinOpMir( - .{ ._r, .sh }, - limb_ty, - tmp_mcv, - .u8, - .{ .immediate = limb_abi_bits - src_bit_off }, - ), - else => unreachable, - } - try self.genBinOpMir(.{ ._, .@"and" }, limb_ty, tmp_mcv, .{ .immediate = part_mask }); - try self.asmMemoryRegister( - .{ ._, .@"or" }, - limb_mem, - registerAlias(tmp_reg, limb_abi_size), - ); - } else if (src_bit_size <= 128 and src_bit_off == 0) { - const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); - const tmp_mcv = MCValue{ .register = tmp_reg }; - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); - - try self.genSetReg(tmp_reg, limb_ty, switch (limb_i) { - 0 => mat_src_mcv, - else => mat_src_mcv.address().offset(limb_i * limb_abi_size).deref(), - }, .{}); - try self.genBinOpMir(.{ ._, .@"and" }, limb_ty, tmp_mcv, .{ .immediate = part_mask }); - try self.asmMemoryRegister( - .{ ._, .@"or" }, - limb_mem, - registerAlias(tmp_reg, limb_abi_size), - ); - } else return self.fail("TODO: implement packed store of {f}", .{src_ty.fmt(pt)}); - } -} - fn store( self: *CodeGen, ptr_ty: Type, @@ -174857,35 +174842,6 @@ fn store( } } -fn airStore(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { - const pt = self.pt; - const zcu = pt.zcu; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - - result: { - if (!safety and (try self.resolveInst(bin_op.rhs)) == .undef) break :result; - - try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); - const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); - defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); - - const ptr_ty = self.typeOf(bin_op.lhs); - const ptr_info = ptr_ty.ptrInfo(zcu); - const is_packed = ptr_info.flags.vector_index != .none or ptr_info.packed_offset.host_size > 0; - if (is_packed) try self.spillEflagsIfOccupied(); - - const src_mcv = try self.resolveInst(bin_op.rhs); - const ptr_mcv = try self.resolveInst(bin_op.lhs); - - if (is_packed) { - try self.packedStore(ptr_ty, ptr_mcv, src_mcv); - } else { - try self.store(ptr_ty, ptr_mcv, src_mcv, .{ .safety = safety }); - } - } - return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn genUnOp(self: *CodeGen, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue { const pt = self.pt; const zcu = pt.zcu; @@ -192171,6 +192127,8 @@ const Select = struct { exact_bool_vec: u16, ptr_any_bool_vec, ptr_bool_vec: Memory.Size, + ptr_any_bool_vec_elem, + ptr_bool_vec_elem: Memory.Size, remainder_bool_vec: OfIsSizes, exact_remainder_bool_vec: struct { of: Memory.Size, is: u16 }, signed_int_vec: Memory.Size, @@ -192273,6 +192231,22 @@ const Select = struct { .vector_type => |vector_type| vector_type.child == .bool_type and size.bitSize(cg.target) >= vector_type.len, else => false, }, + .ptr_any_bool_vec_elem => { + const ptr_info = ty.ptrInfo(zcu); + return switch (ptr_info.flags.vector_index) { + .none => false, + .runtime => unreachable, + else => ptr_info.child == .bool_type, + }; + }, + .ptr_bool_vec_elem => |size| { + const ptr_info = ty.ptrInfo(zcu); + return switch (ptr_info.flags.vector_index) { + .none => false, + .runtime => unreachable, + else => ptr_info.child == .bool_type and size.bitSize(cg.target) >= ptr_info.packed_offset.host_size, + }; + }, .remainder_bool_vec => |of_is| ty.isVector(zcu) and ty.scalarType(zcu).toIntern() == .bool_type and of_is.is.bitSize(cg.target) >= (ty.vectorLen(zcu) - 1) % of_is.of.bitSize(cg.target) + 1, .exact_remainder_bool_vec => |of_is| ty.isVector(zcu) and ty.scalarType(zcu).toIntern() == .bool_type and @@ -193266,7 +193240,7 @@ const Select = struct { ref: Ref, scale: Memory.Scale = .@"1", } = .{ .ref = .none }, - unused: u3 = 0, + unused: u2 = 0, }, imm: i32 = 0, @@ -193279,9 +193253,9 @@ const Select = struct { lea, mem, }; - const Adjust = packed struct(u10) { + const Adjust = packed struct(u11) { sign: enum(u1) { neg, pos }, - lhs: enum(u5) { + lhs: enum(u6) { none, ptr_size, ptr_bit_size, @@ -193303,6 +193277,7 @@ const Select = struct { src0_elem_size, dst0_elem_size, src0_elem_size_mul_src1, + vector_index, src1, src1_sub_bit_size, log2_src0_elem_size, @@ -193373,9 +193348,13 @@ const Select = struct { const sub_src0_elem_size: Adjust = .{ .sign = .neg, .lhs = .src0_elem_size, .op = .mul, .rhs = .@"1" }; const add_src0_elem_size_mul_src1: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size_mul_src1, .op = .mul, .rhs = .@"1" }; const sub_src0_elem_size_mul_src1: Adjust = .{ .sign = .neg, .lhs = .src0_elem_size_mul_src1, .op = .mul, .rhs = .@"1" }; + const add_vector_index: Adjust = .{ .sign = .pos, .lhs = .vector_index, .op = .mul, .rhs = .@"1" }; + const add_vector_index_rem_32: Adjust = .{ .sign = .pos, .lhs = .vector_index, .op = .rem_8_mul, .rhs = .@"4" }; + const add_vector_index_div_8_down_4: Adjust = .{ .sign = .pos, .lhs = .vector_index, .op = .div_8_down, .rhs = .@"4" }; const add_dst0_elem_size: Adjust = .{ .sign = .pos, .lhs = .dst0_elem_size, .op = .mul, .rhs = .@"1" }; const sub_dst0_elem_size: Adjust = .{ .sign = .neg, .lhs = .dst0_elem_size, .op = .mul, .rhs = .@"1" }; const add_src1_div_8_down_4: Adjust = .{ .sign = .pos, .lhs = .src1, .op = .div_8_down, .rhs = .@"4" }; + const add_src1: Adjust = .{ .sign = .pos, .lhs = .src1, .op = .mul, .rhs = .@"1" }; const add_src1_rem_32: Adjust = .{ .sign = .pos, .lhs = .src1, .op = .rem_8_mul, .rhs = .@"4" }; const add_src1_rem_64: Adjust = .{ .sign = .pos, .lhs = .src1, .op = .rem_8_mul, .rhs = .@"8" }; const add_src1_sub_bit_size: Adjust = .{ .sign = .pos, .lhs = .src1_sub_bit_size, .op = .mul, .rhs = .@"1" }; @@ -194258,6 +194237,10 @@ const Select = struct { .dst0_elem_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), .src0_elem_size_mul_src1 => @intCast(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * Select.Operand.Ref.src1.valueOf(s).immediate), + .vector_index => switch (op.flags.base.ref.typeOf(s).ptrInfo(s.cg.pt.zcu).flags.vector_index) { + .none, .runtime => unreachable, + else => |vector_index| @intFromEnum(vector_index), + }, .src1 => @intCast(Select.Operand.Ref.src1.valueOf(s).immediate), .src1_sub_bit_size => @as(SignedImm, @intCast(Select.Operand.Ref.src1.valueOf(s).immediate)) - @as(SignedImm, @intCast(s.cg.nonBoolScalarBitSize(op.flags.base.ref.typeOf(s)))), From d07b67a55cba683c7adb73f5e2f7b1bec803a361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 21 Sep 2025 21:42:39 +0200 Subject: [PATCH 066/112] Revert "x86_64: fix safety crashes in `storeRegs`" This reverts commit 37985613c760d1d20a1fbba292fd5c25129f400a. --- src/arch/x86_64/CodeGen.zig | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2fb24020b4..ad27db719f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -186975,13 +186975,7 @@ const Temp = struct { }, .struct_type => { assert(src_regs.len - part_index == std.math.divCeil(u32, src_abi_size, 8) catch unreachable); - break :part_ty switch (src_abi_size) { - 0, 3, 5...7 => unreachable, - 1 => .u8, - 2 => .u16, - 4 => .u32, - else => .u64, - }; + break :part_ty .u64; }, .tuple_type => |tuple_type| { assert(tuple_type.types.len == src_regs.len); @@ -186990,6 +186984,10 @@ const Temp = struct { }; const part_size: u31 = @intCast(part_ty.abiSize(zcu)); const src_rc = src_reg.class(); + const part_bit_size = switch (src_rc) { + else => 8 * part_size, + .x87 => part_ty.bitSize(zcu), + }; if (src_rc == .x87 or std.math.isPowerOfTwo(part_size)) { // hack around linker relocation bugs switch (ptr.tracking(cg).short) { @@ -186998,15 +186996,7 @@ const Temp = struct { } const strat = try cg.moveStrategy(part_ty, src_rc, false); try strat.write(cg, try ptr.tracking(cg).short.deref().mem(cg, .{ - .size = switch (src_rc) { - else => .fromBitSize(8 * part_size), - .x87 => switch (abi.classifySystemV(src_ty, zcu, cg.target, .other)[part_index]) { - else => unreachable, - .float => .dword, - .float_combine, .sse => .qword, - .x87 => .tbyte, - }, - }, + .size = .fromBitSize(part_bit_size), .disp = part_disp, }), registerAlias(src_reg, part_size)); } else { From 9694c83b954d1bd4a3f224ef69d75639e6da56c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 22 Sep 2025 04:56:46 +0200 Subject: [PATCH 067/112] Revert "frontend: another packedStructFieldPtrInfo fix" This reverts commit 3ab845e028f464666856e4eaaeff2e6ab1b3da5d. --- src/Type.zig | 5 ++++- test/behavior/union.zig | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Type.zig b/src/Type.zig index b9df7a5111..3ef95b285c 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3545,7 +3545,10 @@ pub fn packedStructFieldPtrInfo(struct_ty: Type, parent_ptr_ty: Type, field_idx: parent_ptr_info.packed_offset.host_size, parent_ptr_info.packed_offset.bit_offset + bit_offset, } else .{ - @intCast(struct_ty.abiSize(zcu)), + switch (zcu.comp.getZigBackend()) { + else => (running_bits + 7) / 8, + .stage2_x86_64 => @intCast(struct_ty.abiSize(zcu)), + }, bit_offset, }; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index ec562b17c8..f821097c27 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1564,9 +1564,13 @@ test "packed union field pointer has correct alignment" { const bp = &b.u.x; const cp = &c.u.x; - comptime assert(@TypeOf(ap) == *align(4:2:@sizeOf(S)) u20); - comptime assert(@TypeOf(bp) == *align(1:2:@sizeOf(S)) u20); - comptime assert(@TypeOf(cp) == *align(64:2:@sizeOf(S)) u20); + const host_size = switch (builtin.zig_backend) { + else => comptime std.math.divCeil(comptime_int, @bitSizeOf(S), 8) catch unreachable, + .stage2_x86_64 => @sizeOf(S), + }; + comptime assert(@TypeOf(ap) == *align(4:2:host_size) u20); + comptime assert(@TypeOf(bp) == *align(1:2:host_size) u20); + comptime assert(@TypeOf(cp) == *align(64:2:host_size) u20); a.u = .{ .x = 123 }; b.u = .{ .x = 456 }; From b8f2fec0f2d66427c8f947979fee8173099c690c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 22 Sep 2025 18:36:47 +0200 Subject: [PATCH 068/112] std.pie: fix register constraint in getDynamicSymbol() for s390x (#25327) If the compiler happens to pick `ret = r0`, then this will assemble to `ag r0, 0` which is obviously not what we want. Using `a` instead of `r` will ensure that we get an appropriate address register, i.e. `r1` through `r15`. Re-enable pie_linux for s390x-linux which was disabled in ed7ff0b693037078f451a7c6c1124611060f4892. --- lib/std/pie.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/pie.zig b/lib/std/pie.zig index 09b875ca15..403c1e8f1a 100644 --- a/lib/std/pie.zig +++ b/lib/std/pie.zig @@ -177,7 +177,7 @@ inline fn getDynamicSymbol() [*]const elf.Dyn { \\ jg 2f \\ 1: .quad _DYNAMIC - . \\ 2: - : [ret] "=r" (-> [*]const elf.Dyn), + : [ret] "=a" (-> [*]const elf.Dyn), ), // The compiler does not necessarily have any obligation to load the `l7` register (pointing // to the GOT), so do it ourselves just in case. From a569c7d66433c46efe8a36ee5191b033c98faeab Mon Sep 17 00:00:00 2001 From: alexrp Date: Mon, 22 Sep 2025 01:34:53 +0200 Subject: [PATCH 069/112] test: disable some stack trace tests on FreeBSD --- test/stack_traces.zig | 2 ++ test/standalone/stack_iterator/build.zig | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 40a6168297..0bd5a760cb 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -793,6 +793,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , .Debug = .{ .exclude_os = &.{ + .freebsd, .openbsd, // integer overflow .windows, // TODO intermittent failures }, @@ -837,6 +838,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { }, .ReleaseSafe = .{ .exclude_os = &.{ + .freebsd, .windows, // TODO .linux, // defeated by aggressive inlining .macos, // Broken in LLVM 20. diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index 8d2c448215..b6ac2a9aee 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -61,8 +61,12 @@ pub fn build(b: *std.Build) void { .use_llvm = true, }); - const run_cmd = b.addRunArtifact(exe); - test_step.dependOn(&run_cmd.step); + if (builtin.os.tag != .freebsd) { + const run_cmd = b.addRunArtifact(exe); + test_step.dependOn(&run_cmd.step); + } else { + test_step.dependOn(&exe.step); + } } // https://github.com/ziglang/zig/issues/24522 From d6d1fefae91b50cebd73501ff657709a945228f8 Mon Sep 17 00:00:00 2001 From: alexrp Date: Mon, 22 Sep 2025 01:33:21 +0200 Subject: [PATCH 070/112] test: disable test-link on FreeBSD https://github.com/ziglang/zig/issues/25323 --- test/link/elf.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/link/elf.zig b/test/link/elf.zig index 16ee824da7..161dcf2d32 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -2,6 +2,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { _ = build_opts; const elf_step = b.step("test-elf", "Run ELF tests"); + // https://github.com/ziglang/zig/issues/25323 + if (builtin.os.tag == .freebsd) return elf_step; + const default_target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs .os_tag = .linux, @@ -4285,6 +4288,7 @@ const addStaticLibrary = link.addStaticLibrary; const expectLinkErrors = link.expectLinkErrors; const link = @import("link.zig"); const std = @import("std"); +const builtin = @import("builtin"); const Build = std.Build; const BuildOptions = link.BuildOptions; From 0e673fdab22eafa5099b21ec66c50ba491811ce7 Mon Sep 17 00:00:00 2001 From: alexrp Date: Sun, 21 Sep 2025 08:46:37 +0200 Subject: [PATCH 071/112] std.posix: remove bogus assert that SIGRTMAX < NSIG --- lib/std/posix/test.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 55a53518d9..7577cd5400 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -883,7 +883,6 @@ test "sigrtmin/max" { try std.testing.expect(posix.sigrtmin() >= 32); try std.testing.expect(posix.sigrtmin() >= posix.system.sigrtmin()); try std.testing.expect(posix.sigrtmin() < posix.system.sigrtmax()); - try std.testing.expect(posix.sigrtmax() < posix.NSIG); } test "sigset empty/full" { From e526d65f5e11df3093cc2000611c9a6438f847fe Mon Sep 17 00:00:00 2001 From: alexrp Date: Sun, 21 Sep 2025 08:47:21 +0200 Subject: [PATCH 072/112] compiler: don't use self-hosted backend on any BSD yet There are some blocking bugs in the self-hosted ELF linker. --- src/target.zig | 2 +- test/src/StackTrace.zig | 2 +- test/tests.zig | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/target.zig b/src/target.zig index ee9d39fbf3..f8201d691b 100644 --- a/src/target.zig +++ b/src/target.zig @@ -236,7 +236,7 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool { if (target.cpu.arch.isSpirV()) return true; if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) { - if (target.os.tag == .netbsd or target.os.tag == .openbsd) { + if (target.os.tag.isBSD()) { // Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341 return false; } diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index d956e27cd7..87fa9a3ca1 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -44,7 +44,7 @@ fn addCaseInner(self: *StackTrace, config: Config, use_llvm: bool) void { fn shouldTestNonLlvm(target: *const std.Target) bool { return switch (target.cpu.arch) { .x86_64 => switch (target.ofmt) { - .elf => true, + .elf => !target.os.tag.isBSD(), else => false, }, else => false, diff --git a/test/tests.zig b/test/tests.zig index 1e5db67f07..6b14685a6f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2488,8 +2488,9 @@ pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: Opt else => return true, } const cpu_arch = query.cpu_arch orelse builtin.cpu.arch; + const os_tag = query.os_tag orelse builtin.os.tag; switch (cpu_arch) { - .x86_64 => if (std.Target.ptrBitWidth_arch_abi(cpu_arch, query.abi orelse .none) != 64) return true, + .x86_64 => if (os_tag.isBSD() or std.Target.ptrBitWidth_arch_abi(cpu_arch, query.abi orelse .none) != 64) return true, .spirv32, .spirv64 => return false, else => return true, } From 6069f908e9f1f8655af3faca3bd6de99de58af16 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Tue, 23 Sep 2025 20:12:28 -0500 Subject: [PATCH 073/112] std: always allow spawning processes when an env map is explicitly provided (#25092) In a library, the two `builtin.link_libc` and `builtin.output_mode == .Exe` checks could both be false. Thus, you would get a compile error even if you specified an `env_map` at runtime. This change turns the compile error into a runtime panic and updates the documentation to reflect the runtime requirement. --- lib/std/process/Child.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index ca5e61b243..39dab9f394 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -52,6 +52,8 @@ term: ?(SpawnError!Term), argv: []const []const u8, /// Leave as null to use the current env map using the supplied allocator. +/// Required if unable to access the current env map (e.g. building a library on +/// some platforms). env_map: ?*const EnvMap, stdin_behavior: StdIo, @@ -414,6 +416,8 @@ pub fn run(args: struct { argv: []const []const u8, cwd: ?[]const u8 = null, cwd_dir: ?fs.Dir = null, + /// Required if unable to access the current env map (e.g. building a + /// library on some platforms). env_map: ?*const EnvMap = null, max_output_bytes: usize = 50 * 1024, expand_arg0: Arg0Expand = .no_expand, @@ -614,7 +618,7 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { })).ptr; } else { // TODO come up with a solution for this. - @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process"); + @panic("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process"); } }; From bc567312bf58389ffed40f5fc3d0b3ee26271c3e Mon Sep 17 00:00:00 2001 From: rpkak Date: Sun, 31 Aug 2025 10:56:09 +0200 Subject: [PATCH 074/112] use copy_file_range syscall on linux --- lib/std/fs/File.zig | 2 +- lib/std/os/linux.zig | 4 +++- lib/std/posix.zig | 10 ++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 8da2112f8b..5413e89fa0 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1967,7 +1967,7 @@ pub const Writer = struct { const copy_file_range = switch (native_os) { .freebsd => std.os.freebsd.copy_file_range, - .linux => if (std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 34, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {}, + .linux => std.os.linux.wrapped.copy_file_range, else => {}, }; if (@TypeOf(copy_file_range) != void) cfr: { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 7db0634271..c2aa40f552 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -9802,7 +9802,9 @@ pub const wrapped = struct { }; pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: u32) CopyFileRangeError!usize { - const rc = system.copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); + const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 34, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 }); + const sys = if (use_c) std.c else std.os.linux; + const rc = sys.copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); switch (errno(rc)) { .SUCCESS => return @intCast(rc), .BADF => return error.BadFileFlags, diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 90ab157f0c..0b5a0e62b6 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -6387,14 +6387,16 @@ pub const CopyFileRangeError = error{ /// /// Maximum offsets on Linux and FreeBSD are `maxInt(i64)`. pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize { - if (builtin.os.tag == .freebsd or - (comptime builtin.os.tag == .linux and std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 34, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 }))) - { + if (builtin.os.tag == .freebsd or builtin.os.tag == .linux) { + const use_c = native_os != .linux or + std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 34, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 }); + const sys = if (use_c) std.c else linux; + var off_in_copy: i64 = @bitCast(off_in); var off_out_copy: i64 = @bitCast(off_out); while (true) { - const rc = system.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags); + const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags); if (native_os == .freebsd) { switch (errno(rc)) { .SUCCESS => return @intCast(rc), From 1f0cf7c5514587471a219c418c15b49413d0fd14 Mon Sep 17 00:00:00 2001 From: Kyle Schwarz Date: Sat, 30 Aug 2025 18:26:14 -0400 Subject: [PATCH 075/112] glibc: guard inet-fortified.h --- lib/libc/include/generic-glibc/arpa/inet.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/libc/include/generic-glibc/arpa/inet.h b/lib/libc/include/generic-glibc/arpa/inet.h index 84602130a9..07113deb0f 100644 --- a/lib/libc/include/generic-glibc/arpa/inet.h +++ b/lib/libc/include/generic-glibc/arpa/inet.h @@ -101,10 +101,13 @@ extern char *inet_nsap_ntoa (int __len, const unsigned char *__cp, char *__buf) __THROW; #endif +// zig patch: inet was fortified in glibc 2.42 +#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42) || __GLIBC__ > 2 #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function /* Include functions with security checks. */ # include #endif +#endif __END_DECLS From 427f0025dbea242b4cd760e7569eb625da5c0910 Mon Sep 17 00:00:00 2001 From: "taylor.fish" Date: Sat, 13 Sep 2025 17:17:18 -0700 Subject: [PATCH 076/112] Fix PowerPC syscalls causing invalid code from CBE Fixes #25209. On PowerPC, some registers are both inputs to syscalls and clobbered by them. An example is r0, which initially contains the syscall number, but may be overwritten during execution of the syscall. musl and glibc use a `+` (read-write) constraint to indicate this, which isn't supported in Zig. The current implementation of PowerPC syscalls in the Zig standard library instead lists these registers as both inputs and clobbers, but this results in the C backend generating code that is invalid for at least some C compilers, like GCC, which doesn't support the specifying the same register as both an input and a clobber. This PR changes the PowerPC syscall functions to list such registers as inputs and outputs rather than inputs and clobbers. Thanks to jacobly0 who pointed out that it's possible to have multiple outputs; I had gotten the wrong idea from the documentation. --- lib/std/os/linux/powerpc.zig | 68 ++++++++++++++++++++++++++++++---- lib/std/os/linux/powerpc64.zig | 68 ++++++++++++++++++++++++++++++---- 2 files changed, 122 insertions(+), 14 deletions(-) diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index c8e03a1b04..a77ecab16b 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -15,84 +15,125 @@ const sockaddr = linux.sockaddr; const timespec = linux.timespec; pub fn syscall0(number: SYS) usize { + // r0 is both an input register and a clobber. musl and glibc achieve this with + // a "+" constraint, which isn't supported in Zig, so instead we separately list + // r0 as both an input and an output. (Listing it as an input and a clobber would + // cause the C backend to emit invalid code; see #25209.) + var r0_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), : [number] "{r0}" (@intFromEnum(number)), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall1(number: SYS, arg1: usize) usize { + // r0 is both an input and a clobber. + var r0_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; + var r6_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), + [r6_out] "={r6}" (r6_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; + var r6_out: usize = undefined; + var r7_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), + [r6_out] "={r6}" (r6_out), + [r7_out] "={r7}" (r7_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall6( @@ -104,12 +145,25 @@ pub fn syscall6( arg5: usize, arg6: usize, ) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; + var r6_out: usize = undefined; + var r7_out: usize = undefined; + var r8_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), + [r6_out] "={r6}" (r6_out), + [r7_out] "={r7}" (r7_out), + [r8_out] "={r8}" (r8_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), @@ -117,7 +171,7 @@ pub fn syscall6( [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), [arg6] "{r8}" (arg6), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn clone() callconv(.naked) usize { diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index ff07ac9180..1e42dc88c8 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -15,84 +15,125 @@ const sockaddr = linux.sockaddr; const timespec = linux.timespec; pub fn syscall0(number: SYS) usize { + // r0 is both an input register and a clobber. musl and glibc achieve this with + // a "+" constraint, which isn't supported in Zig, so instead we separately list + // r0 as both an input and an output. (Listing it as an input and a clobber would + // cause the C backend to emit invalid code; see #25209.) + var r0_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), : [number] "{r0}" (@intFromEnum(number)), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall1(number: SYS, arg1: usize) usize { + // r0 is both an input and a clobber. + var r0_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; + var r6_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), + [r6_out] "={r6}" (r6_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; + var r6_out: usize = undefined; + var r7_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), + [r6_out] "={r6}" (r6_out), + [r7_out] "={r7}" (r7_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), [arg3] "{r5}" (arg3), [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn syscall6( @@ -104,12 +145,25 @@ pub fn syscall6( arg5: usize, arg6: usize, ) usize { + // These registers are both inputs and clobbers. + var r0_out: usize = undefined; + var r4_out: usize = undefined; + var r5_out: usize = undefined; + var r6_out: usize = undefined; + var r7_out: usize = undefined; + var r8_out: usize = undefined; return asm volatile ( \\ sc \\ bns+ 1f \\ neg 3, 3 \\ 1: : [ret] "={r3}" (-> usize), + [r0_out] "={r0}" (r0_out), + [r4_out] "={r4}" (r4_out), + [r5_out] "={r5}" (r5_out), + [r6_out] "={r6}" (r6_out), + [r7_out] "={r7}" (r7_out), + [r8_out] "={r8}" (r8_out), : [number] "{r0}" (@intFromEnum(number)), [arg1] "{r3}" (arg1), [arg2] "{r4}" (arg2), @@ -117,7 +171,7 @@ pub fn syscall6( [arg4] "{r6}" (arg4), [arg5] "{r7}" (arg5), [arg6] "{r8}" (arg6), - : .{ .memory = true, .cr0 = true, .r0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); + : .{ .memory = true, .cr0 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true }); } pub fn clone() callconv(.naked) usize { From a430be097be2d6ec9237d2e81d6e576c5926dc1a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 24 Sep 2025 13:09:33 -0400 Subject: [PATCH 077/112] x86_64: support more in/out forms Closes #25303 --- src/arch/x86_64/CodeGen.zig | 107 ++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index ad27db719f..c33300a006 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -180113,50 +180113,65 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { } var mnem_size: struct { + op_has_size: std.StaticBitSet(4), + size: Memory.Size, used: bool, - size: ?Memory.Size, - fn use(size: *@This()) ?Memory.Size { + fn init(size: ?Memory.Size) @This() { + return .{ + .op_has_size = if (size) |_| .initFull() else .initEmpty(), + .size = size orelse .none, + .used = false, + }; + } + fn use(size: *@This(), op_index: usize) ?Memory.Size { + if (!size.op_has_size.isSet(op_index)) return null; size.used = true; return size.size; } - } = .{ - .used = false, - .size = if (prefix == .directive) - null - else if (std.mem.endsWith(u8, mnem_str, "b")) - .byte - else if (std.mem.endsWith(u8, mnem_str, "w")) - .word - else if (std.mem.endsWith(u8, mnem_str, "l")) - .dword - else if (std.mem.endsWith(u8, mnem_str, "q") and - (std.mem.indexOfScalar(u8, "vp", mnem_str[0]) == null or !std.mem.endsWith(u8, mnem_str, "dq"))) - .qword - else if (std.mem.endsWith(u8, mnem_str, "t")) - .tbyte - else - null, - }; + } = .init(if (prefix == .directive) + null + else if (std.mem.endsWith(u8, mnem_str, "b")) + .byte + else if (std.mem.endsWith(u8, mnem_str, "w")) + .word + else if (std.mem.endsWith(u8, mnem_str, "l")) + .dword + else if (std.mem.endsWith(u8, mnem_str, "q") and + (std.mem.indexOfScalar(u8, "vp", mnem_str[0]) == null or !std.mem.endsWith(u8, mnem_str, "dq"))) + .qword + else if (std.mem.endsWith(u8, mnem_str, "t")) + .tbyte + else + null); var mnem_tag = while (true) break std.meta.stringToEnum( encoder.Instruction.Mnemonic, - mnem_str[0 .. mnem_str.len - @intFromBool(mnem_size.size != null)], - ) orelse if (mnem_size.size) |_| { - mnem_size.size = null; + mnem_str[0 .. mnem_str.len - @intFromBool(mnem_size.size != .none)], + ) orelse if (mnem_size.size != .none) { + mnem_size = .init(null); continue; } else return self.fail("invalid mnemonic: '{s}'", .{mnem_str}); - if (@as(?Memory.Size, switch (mnem_tag) { - .clflush => .byte, - .fldcw, .fnstcw, .fstcw, .fnstsw, .fstsw => .word, - .fldenv, .fnstenv, .fstenv => .none, - .frstor, .fsave, .fnsave, .fxrstor, .fxrstor64, .fxsave, .fxsave64 => .none, - .invlpg => .none, - .invpcid => .xword, - .ldmxcsr, .stmxcsr, .vldmxcsr, .vstmxcsr => .dword, - else => null, - })) |fixed_mnem_size| { - if (mnem_size.size) |size| if (size != fixed_mnem_size) + fixed_mnem_size: { + const fixed_mnem_size: Memory.Size = switch (mnem_tag) { + .clflush => .byte, + .fldcw, .fnstcw, .fstcw, .fnstsw, .fstsw => .word, + .fldenv, .fnstenv, .fstenv => .none, + .frstor, .fsave, .fnsave, .fxrstor, .fxrstor64, .fxsave, .fxsave64 => .none, + .in => { + mnem_size.op_has_size.unset(0); + break :fixed_mnem_size; + }, + .invlpg => .none, + .invpcid => .xword, + .ldmxcsr, .stmxcsr, .vldmxcsr, .vstmxcsr => .dword, + .out => { + mnem_size.op_has_size.unset(1); + break :fixed_mnem_size; + }, + else => break :fixed_mnem_size, + }; + if (mnem_size.size != .none and mnem_size.size != fixed_mnem_size) return self.fail("invalid size: '{s}'", .{mnem_str}); - mnem_size.size = fixed_mnem_size; + mnem_size = .init(fixed_mnem_size); } var ops: [4]Operand = @splat(.none); @@ -180164,7 +180179,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { var last_op = false; var op_it = std.mem.splitScalar(u8, mnem_it.rest(), ','); - next_op: for (&ops) |*op| { + next_op: for (&ops, 0..) |*op, op_index| { const op_str = while (!last_op) { const full_str = op_it.next() orelse break :next_op; const code_str = if (std.mem.indexOfScalar(u8, full_str, '#') orelse @@ -180186,13 +180201,13 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { op.* = .{ .mem = .{ .base = .{ .reg = reg }, .mod = .{ .rm = .{ - .size = mnem_size.use() orelse + .size = mnem_size.use(op_index) orelse return self.fail("unknown size: '{s}'", .{op_str}), .disp = disp, } }, } }; } else { - if (mnem_size.use()) |size| if (reg.size().bitSize(self.target) != size.bitSize(self.target)) + if (mnem_size.use(op_index)) |size| if (reg.size().bitSize(self.target) != size.bitSize(self.target)) return self.fail("invalid register size: '{s}'", .{op_str}); op.* = .{ .reg = reg }; } @@ -180211,14 +180226,14 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { else return self.fail("invalid modifier: '{s}'", .{modifier}), .register => |reg| if (std.mem.eql(u8, modifier, "")) - .{ .reg = if (mnem_size.use()) |size| reg.toSize(size, self.target) else reg } + .{ .reg = if (mnem_size.use(op_index)) |size| reg.toSize(size, self.target) else reg } else return self.fail("invalid modifier: '{s}'", .{modifier}), .memory => |addr| if (std.mem.eql(u8, modifier, "") or std.mem.eql(u8, modifier, "P")) .{ .mem = .{ .base = .{ .reg = .ds }, .mod = .{ .rm = .{ - .size = mnem_size.use() orelse + .size = mnem_size.use(op_index) orelse return self.fail("unknown size: '{s}'", .{op_str}), .disp = @intCast(@as(i64, @bitCast(addr))), } }, @@ -180229,7 +180244,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { .{ .mem = .{ .base = .{ .reg = reg_off.reg }, .mod = .{ .rm = .{ - .size = mnem_size.use() orelse + .size = mnem_size.use(op_index) orelse return self.fail("unknown size: '{s}'", .{op_str}), .disp = reg_off.off, } }, @@ -180240,7 +180255,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { .{ .mem = .{ .base = .{ .frame = frame_addr.index }, .mod = .{ .rm = .{ - .size = mnem_size.use() orelse + .size = mnem_size.use(op_index) orelse return self.fail("unknown size: '{s}'", .{op_str}), .disp = frame_addr.off, } }, @@ -180322,7 +180337,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { else .none, .mod = .{ .rm = .{ - .size = mnem_size.use() orelse return self.fail("unknown size: '{s}'", .{op_str}), + .size = mnem_size.use(op_index) orelse return self.fail("unknown size: '{s}'", .{op_str}), .index = if (index_str.len > 0) parseRegName(index_str["%%".len..]) orelse return self.fail("invalid index register: '{s}'", .{op_str}) @@ -180375,14 +180390,14 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { // convert from att syntax to intel syntax std.mem.reverse(Operand, ops[0..ops_len]); - if (!mnem_size.used) if (mnem_size.size) |size| { + if (mnem_size.size != .none and !mnem_size.used) { comptime var max_mnem_len: usize = 0; inline for (@typeInfo(encoder.Instruction.Mnemonic).@"enum".fields) |mnem| max_mnem_len = @max(mnem.name.len, max_mnem_len); var intel_mnem_buf: [max_mnem_len + 1]u8 = undefined; const intel_mnem_str = std.fmt.bufPrint(&intel_mnem_buf, "{s}{c}", .{ @tagName(mnem_tag), - @as(u8, switch (size) { + @as(u8, switch (mnem_size.size) { .byte => 'b', .word => 'w', .dword => 'd', @@ -180392,7 +180407,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { }), }) catch unreachable; if (std.meta.stringToEnum(encoder.Instruction.Mnemonic, intel_mnem_str)) |intel_mnem_tag| mnem_tag = intel_mnem_tag; - }; + } const mnem_name = @tagName(mnem_tag); const mnem_fixed_tag: Mir.Inst.FixedTag = if (prefix == .directive) .{ ._, .pseudo } From 9f2a200a3fce81d2091cba16a715acd3fdf1d70e Mon Sep 17 00:00:00 2001 From: "taylor.fish" Date: Sat, 13 Sep 2025 20:34:05 -0700 Subject: [PATCH 078/112] Fix PowerPC `restore_rt` Clang fails to compile the CBE translation of this code ("non-ASM statement in naked function"). Similar to the implementations of `restore_rt` on x86 and ARM, when the CBE is in use, this commit employs alternative inline assembly that avoids using non-immediate input operands. --- lib/std/os/linux/powerpc.zig | 18 +++++++++++++----- lib/std/os/linux/powerpc64.zig | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index a77ecab16b..ee1887143a 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -247,11 +247,19 @@ pub fn clone() callconv(.naked) usize { pub const restore = restore_rt; pub fn restore_rt() callconv(.naked) noreturn { - asm volatile ( - \\ sc - : - : [number] "{r0}" (@intFromEnum(SYS.rt_sigreturn)), - : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + switch (@import("builtin").zig_backend) { + .stage2_c => asm volatile ( + \\ li 0, %[number] + \\ sc + : + : [number] "i" (@intFromEnum(SYS.rt_sigreturn)), + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }), + else => _ = asm volatile ( + \\ sc + : + : [number] "{r0}" (@intFromEnum(SYS.rt_sigreturn)), + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }), + } } pub const F = struct { diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 1e42dc88c8..ffb17551e3 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -232,11 +232,19 @@ pub fn clone() callconv(.naked) usize { pub const restore = restore_rt; pub fn restore_rt() callconv(.naked) noreturn { - asm volatile ( - \\ sc - : - : [number] "{r0}" (@intFromEnum(SYS.rt_sigreturn)), - : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }); + switch (@import("builtin").zig_backend) { + .stage2_c => asm volatile ( + \\ li 0, %[number] + \\ sc + : + : [number] "i" (@intFromEnum(SYS.rt_sigreturn)), + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }), + else => _ = asm volatile ( + \\ sc + : + : [number] "{r0}" (@intFromEnum(SYS.rt_sigreturn)), + : .{ .memory = true, .cr0 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true }), + } } pub const F = struct { From 8520e4e286a82944a3902b10dad32886e0efd2fa Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 24 Sep 2025 15:56:07 -0400 Subject: [PATCH 079/112] x86_64: improve support for large enums Closes #25247 --- src/arch/x86_64/CodeGen.zig | 265 ++++++++++++++++++++++++++++++------ test/behavior/enum.zig | 19 +++ 2 files changed, 239 insertions(+), 45 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c33300a006..0a07a77d93 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -166722,6 +166722,33 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, .{ ._, ._, .@"test", .src0p, .src0p, ._, ._ }, } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .any_int, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_mem, .none, .none } }, + }, + .call_frame = .{ .alignment = .@"32" }, + .extra_temps = .{ + .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .zigcc, .after = 0, .at = 0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .cc = .nz }, .unused }, + .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, + .each = .{ .once = &.{ + .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, + .{ ._, ._, .call, .tmp1d, ._, ._, ._ }, + .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, + } }, }, .{ .required_features = .{ .sse, null, null, null }, .src_constraints = .{ .{ .int = .gpr }, .any, .any }, @@ -166748,6 +166775,33 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, .{ ._, ._, .@"test", .src0p, .src0p, ._, ._ }, } }, + }, .{ + .required_features = .{ .sse, null, null, null }, + .src_constraints = .{ .any_int, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_mem, .none, .none } }, + }, + .call_frame = .{ .alignment = .@"16" }, + .extra_temps = .{ + .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .zigcc, .after = 0, .at = 0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .cc = .nz }, .unused }, + .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, + .each = .{ .once = &.{ + .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, + .{ ._, ._, .call, .tmp1d, ._, ._, ._ }, + .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, + } }, }, .{ .src_constraints = .{ .{ .int = .gpr }, .any, .any }, .patterns = &.{ @@ -166773,6 +166827,32 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, .{ ._, ._, .@"test", .src0p, .src0p, ._, ._ }, } }, + }, .{ + .src_constraints = .{ .any_int, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_mem, .none, .none } }, + }, + .call_frame = .{ .alignment = .@"8" }, + .extra_temps = .{ + .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .zigcc, .after = 0, .at = 0 } } }, + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .cc = .nz }, .unused }, + .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, + .each = .{ .once = &.{ + .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, + .{ ._, ._, .call, .tmp1d, ._, ._, ._ }, + .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, + } }, } }) catch |err| switch (err) { error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{ @tagName(air_tag), @@ -166812,6 +166892,32 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .each = .{ .once = &.{ .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, } }, + }, .{ + .required_features = .{ .avx, null, null, null }, + .src_constraints = .{ .any_int, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_mem, .none, .none } }, + }, + .call_frame = .{ .alignment = .@"32" }, + .extra_temps = .{ + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .ret_gpr_pair = .{ .cc = .zigcc, .after = 0, .at = 0 } }, .unused }, + .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, + .each = .{ .once = &.{ + .{ ._, ._, .lea, .dst0p0, .mem(.src0), ._, ._ }, + .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, + } }, }, .{ .required_features = .{ .sse, null, null, null }, .src_constraints = .{ .{ .int = .gpr }, .any, .any }, @@ -166837,6 +166943,32 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .each = .{ .once = &.{ .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, } }, + }, .{ + .required_features = .{ .sse, null, null, null }, + .src_constraints = .{ .any_int, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_mem, .none, .none } }, + }, + .call_frame = .{ .alignment = .@"16" }, + .extra_temps = .{ + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .ret_gpr_pair = .{ .cc = .zigcc, .after = 0, .at = 0 } }, .unused }, + .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, + .each = .{ .once = &.{ + .{ ._, ._, .lea, .dst0p0, .mem(.src0), ._, ._ }, + .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, + } }, }, .{ .src_constraints = .{ .{ .int = .gpr }, .any, .any }, .patterns = &.{ @@ -166861,6 +166993,32 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .each = .{ .once = &.{ .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, } }, + }, .{ + .required_features = .{ .sse, null, null, null }, + .src_constraints = .{ .any_int, .any, .any }, + .patterns = &.{ + .{ .src = .{ .to_mem, .none, .none } }, + }, + .call_frame = .{ .alignment = .@"8" }, + .extra_temps = .{ + .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + .unused, + }, + .dst_temps = .{ .{ .ret_gpr_pair = .{ .cc = .zigcc, .after = 0, .at = 0 } }, .unused }, + .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, + .each = .{ .once = &.{ + .{ ._, ._, .lea, .dst0p0, .mem(.src0), ._, ._ }, + .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, + } }, } }) catch |err| switch (err) { error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{ @tagName(air_tag), @@ -166869,7 +167027,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }), else => |e| return e, }; - try ops[0].toPair(&res[0], cg); + if (res[0].tracking(cg).short != .register_pair) try ops[0].toPair(&res[0], cg); try res[0].finish(inst, &.{un_op}, &ops, cg); }, .error_name => |air_tag| { @@ -169240,32 +169398,43 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { const enum_ty: Type = .fromInterned(lazy_sym.ty); wip_mir_log.debug("{f}.@tagName:", .{enum_ty.fmt(pt)}); - const param_regs = abi.getCAbiIntParamRegs(.auto); - const param_locks = cg.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); - defer for (param_locks) |lock| cg.register_manager.unlockReg(lock); + const ret_regs = abi.getCAbiIntReturnRegs(.auto)[0..2].*; + const ret_locks = cg.register_manager.lockRegsAssumeUnused(2, ret_regs); + defer for (ret_locks) |lock| cg.register_manager.unlockReg(lock); - const ret_mcv: MCValue = .{ .register_pair = param_regs[0..2].* }; - var enum_temp = try cg.tempInit(enum_ty, .{ .register = param_regs[0] }); + const param_reg = abi.getCAbiIntParamRegs(.auto)[0]; + const param_lock = cg.register_manager.lockReg(param_reg); + defer if (param_lock) |lock| cg.register_manager.unlockReg(lock); + + const ret_mcv: MCValue = .{ .register_pair = ret_regs }; const data_reg = try cg.register_manager.allocReg(null, abi.RegisterClass.gp); const data_lock = cg.register_manager.lockRegAssumeUnused(data_reg); defer cg.register_manager.unlockReg(data_lock); - try cg.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = lazy_sym.ty }); + try cg.asmRegisterMemory(.{ ._, .lea }, data_reg.to64(), .{ + .base = .{ .lazy_sym = .{ .kind = .const_data, .ty = lazy_sym.ty } }, + }); var data_off: i32 = 0; const reset_index = cg.next_temp_index; const tag_names = ip.loadEnumType(lazy_sym.ty).names; for (0..tag_names.len) |tag_index| { + var enum_temp = try cg.tempInit(enum_ty, if (enum_ty.abiSize(zcu) <= @as(u4, switch (cg.target.cpu.arch) { + else => unreachable, + .x86 => 4, + .x86_64 => 8, + })) .{ .register = param_reg } else .{ .indirect = .{ .reg = param_reg } }); + const tag_name_len = tag_names.get(ip)[tag_index].length(ip); var tag_temp = try cg.tempFromValue(try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index))); const cc_temp = enum_temp.cmpInts(.neq, &tag_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; + try enum_temp.die(cg); try tag_temp.die(cg); const skip_reloc = try cg.asmJccReloc(cc_temp.tracking(cg).short.eflags, undefined); try cc_temp.die(cg); - try cg.resetTemps(reset_index); try cg.genSetReg( ret_mcv.register_pair[0], @@ -169279,8 +169448,9 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { cg.performReloc(skip_reloc); data_off += @intCast(tag_name_len + 1); + + try cg.resetTemps(reset_index); } - try enum_temp.die(cg); try cg.genSetReg(ret_mcv.register_pair[0], .usize, .{ .immediate = 0 }, .{}); try cg.asmOpOnly(.{ ._, .ret }); @@ -169289,12 +169459,16 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { const err_ty: Type = .fromInterned(lazy_sym.ty); wip_mir_log.debug("{f}.@errorCast:", .{err_ty.fmt(pt)}); - const param_regs = abi.getCAbiIntParamRegs(.auto); - const param_locks = cg.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); - defer for (param_locks) |lock| cg.register_manager.unlockReg(lock); + const ret_reg = abi.getCAbiIntReturnRegs(.auto)[0]; + const ret_lock = cg.register_manager.lockRegAssumeUnused(ret_reg); + defer cg.register_manager.unlockReg(ret_lock); - const ret_mcv: MCValue = .{ .register = param_regs[0] }; - const err_mcv: MCValue = .{ .register = param_regs[0] }; + const param_reg = abi.getCAbiIntParamRegs(.auto)[0]; + const param_lock = cg.register_manager.lockReg(param_reg); + defer if (param_lock) |lock| cg.register_manager.unlockReg(lock); + + const ret_mcv: MCValue = .{ .register = ret_reg }; + const err_mcv: MCValue = .{ .register = param_reg }; var err_temp = try cg.tempInit(err_ty, err_mcv); const ExpectedContents = [32]Mir.Inst.Index; @@ -182009,37 +182183,6 @@ fn genInlineMemset( try self.asmOpOnly(.{ .@"rep _sb", .sto }); } -fn genLazySymbolRef( - self: *CodeGen, - comptime tag: Mir.Inst.Tag, - reg: Register, - lazy_sym: link.File.LazySymbol, -) InnerError!void { - if (self.mod.pic) { - switch (tag) { - .lea, .call => try self.genSetReg(reg, .usize, .{ - .lea_lazy_sym = lazy_sym, - }, .{}), - .mov => try self.genSetReg(reg, .usize, .{ - .lea_lazy_sym = lazy_sym, - }, .{}), - else => unreachable, - } - switch (tag) { - .lea, .mov => {}, - .call => try self.asmRegister(.{ ._, .call }, reg), - else => unreachable, - } - } else switch (tag) { - .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ - .base = .{ .lazy_sym = lazy_sym }, - .mod = .{ .rm = .{ .size = .qword } }, - }), - .call => try self.asmImmediate(.{ ._, .call }, .{ .lazy_sym = lazy_sym }), - else => unreachable, - } -} - fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void { const pt = self.pt; const zcu = pt.zcu; @@ -193651,9 +193794,11 @@ const Select = struct { const tmp0y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0y } }; const tmp0w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0w }, .imm = 1 }; const tmp0d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0d }, .imm = 1 }; + const tmp0p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0p }, .imm = 1 }; const tmp0q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0q }, .imm = 1 }; const tmp0w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0w }, .imm = 2 }; const tmp0d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0d }, .imm = 2 }; + const tmp0p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0p }, .imm = 2 }; const tmp0q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0q }, .imm = 2 }; const tmp1l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1l } }; @@ -193669,9 +193814,11 @@ const Select = struct { const tmp1y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1y } }; const tmp1w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1w }, .imm = 1 }; const tmp1d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1d }, .imm = 1 }; + const tmp1p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1p }, .imm = 1 }; const tmp1q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1q }, .imm = 1 }; const tmp1w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1w }, .imm = 2 }; const tmp1d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1d }, .imm = 2 }; + const tmp1p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1p }, .imm = 2 }; const tmp1q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1q }, .imm = 2 }; const tmp2l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2l } }; @@ -193687,9 +193834,11 @@ const Select = struct { const tmp2y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2y } }; const tmp2w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2w }, .imm = 1 }; const tmp2d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2d }, .imm = 1 }; + const tmp2p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2p }, .imm = 1 }; const tmp2q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2q }, .imm = 1 }; const tmp2w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2w }, .imm = 2 }; const tmp2d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2d }, .imm = 2 }; + const tmp2p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2p }, .imm = 2 }; const tmp2q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2q }, .imm = 2 }; const tmp3l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3l } }; @@ -193705,9 +193854,11 @@ const Select = struct { const tmp3y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3y } }; const tmp3w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3w }, .imm = 1 }; const tmp3d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3d }, .imm = 1 }; + const tmp3p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3p }, .imm = 1 }; const tmp3q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3q }, .imm = 1 }; const tmp3w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3w }, .imm = 2 }; const tmp3d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3d }, .imm = 2 }; + const tmp3p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3p }, .imm = 2 }; const tmp3q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3q }, .imm = 2 }; const tmp4l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4l } }; @@ -193723,9 +193874,11 @@ const Select = struct { const tmp4y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4y } }; const tmp4w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4w }, .imm = 1 }; const tmp4d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4d }, .imm = 1 }; + const tmp4p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4p }, .imm = 1 }; const tmp4q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4q }, .imm = 1 }; const tmp4w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4w }, .imm = 2 }; const tmp4d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4d }, .imm = 2 }; + const tmp4p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4p }, .imm = 2 }; const tmp4q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4q }, .imm = 2 }; const tmp5l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5l } }; @@ -193741,9 +193894,11 @@ const Select = struct { const tmp5y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5y } }; const tmp5w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5w }, .imm = 1 }; const tmp5d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5d }, .imm = 1 }; + const tmp5p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5p }, .imm = 1 }; const tmp5q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5q }, .imm = 1 }; const tmp5w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5w }, .imm = 2 }; const tmp5d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5d }, .imm = 2 }; + const tmp5p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5p }, .imm = 2 }; const tmp5q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5q }, .imm = 2 }; const tmp6l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6l } }; @@ -193759,9 +193914,11 @@ const Select = struct { const tmp6y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6y } }; const tmp6w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6w }, .imm = 1 }; const tmp6d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6d }, .imm = 1 }; + const tmp6p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6p }, .imm = 1 }; const tmp6q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6q }, .imm = 1 }; const tmp6w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6w }, .imm = 2 }; const tmp6d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6d }, .imm = 2 }; + const tmp6p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6p }, .imm = 2 }; const tmp6q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6q }, .imm = 2 }; const tmp7l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7l } }; @@ -193777,9 +193934,11 @@ const Select = struct { const tmp7y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7y } }; const tmp7w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7w }, .imm = 1 }; const tmp7d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7d }, .imm = 1 }; + const tmp7p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7p }, .imm = 1 }; const tmp7q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7q }, .imm = 1 }; const tmp7w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7w }, .imm = 2 }; const tmp7d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7d }, .imm = 2 }; + const tmp7p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7p }, .imm = 2 }; const tmp7q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7q }, .imm = 2 }; const tmp8l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8l } }; @@ -193795,9 +193954,11 @@ const Select = struct { const tmp8y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8y } }; const tmp8w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8w }, .imm = 1 }; const tmp8d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8d }, .imm = 1 }; + const tmp8p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8p }, .imm = 1 }; const tmp8q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8q }, .imm = 1 }; const tmp8w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8w }, .imm = 2 }; const tmp8d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8d }, .imm = 2 }; + const tmp8p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8p }, .imm = 2 }; const tmp8q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8q }, .imm = 2 }; const tmp9l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9l } }; @@ -193813,9 +193974,11 @@ const Select = struct { const tmp9y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9y } }; const tmp9w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9w }, .imm = 1 }; const tmp9d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9d }, .imm = 1 }; + const tmp9p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9p }, .imm = 1 }; const tmp9q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9q }, .imm = 1 }; const tmp9w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9w }, .imm = 2 }; const tmp9d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9d }, .imm = 2 }; + const tmp9p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9p }, .imm = 2 }; const tmp9q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9q }, .imm = 2 }; const tmp10l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10l } }; @@ -193831,9 +193994,11 @@ const Select = struct { const tmp10y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10y } }; const tmp10w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10w }, .imm = 1 }; const tmp10d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10d }, .imm = 1 }; + const tmp10p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10p }, .imm = 1 }; const tmp10q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10q }, .imm = 1 }; const tmp10w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10w }, .imm = 2 }; const tmp10d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10d }, .imm = 2 }; + const tmp10p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10p }, .imm = 2 }; const tmp10q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10q }, .imm = 2 }; const dst0l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0l } }; @@ -193849,9 +194014,11 @@ const Select = struct { const dst0y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0y } }; const dst0w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0w }, .imm = 1 }; const dst0d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0d }, .imm = 1 }; + const dst0p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0p }, .imm = 1 }; const dst0q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0q }, .imm = 1 }; const dst0w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0w }, .imm = 2 }; const dst0d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0d }, .imm = 2 }; + const dst0p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0p }, .imm = 2 }; const dst0q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0q }, .imm = 2 }; const dst1l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1l } }; @@ -193867,9 +194034,11 @@ const Select = struct { const dst1y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1y } }; const dst1w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1w }, .imm = 1 }; const dst1d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1d }, .imm = 1 }; + const dst1p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1p }, .imm = 1 }; const dst1q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1q }, .imm = 1 }; const dst1w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1w }, .imm = 2 }; const dst1d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1d }, .imm = 2 }; + const dst1p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1p }, .imm = 2 }; const dst1q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1q }, .imm = 2 }; const src0l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0l } }; @@ -193885,9 +194054,11 @@ const Select = struct { const src0y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0y } }; const src0w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0w }, .imm = 1 }; const src0d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0d }, .imm = 1 }; + const src0p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0p }, .imm = 1 }; const src0q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0q }, .imm = 1 }; const src0w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0w }, .imm = 2 }; const src0d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0d }, .imm = 2 }; + const src0p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0p }, .imm = 2 }; const src0q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0q }, .imm = 2 }; const src1l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1l } }; @@ -193903,9 +194074,11 @@ const Select = struct { const src1y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1y } }; const src1w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1w }, .imm = 1 }; const src1d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1d }, .imm = 1 }; + const src1p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1p }, .imm = 1 }; const src1q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1q }, .imm = 1 }; const src1w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1w }, .imm = 2 }; const src1d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1d }, .imm = 2 }; + const src1p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1p }, .imm = 2 }; const src1q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1q }, .imm = 2 }; const src2l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2l } }; @@ -193921,9 +194094,11 @@ const Select = struct { const src2y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2y } }; const src2w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2w }, .imm = 1 }; const src2d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2d }, .imm = 1 }; + const src2p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2p }, .imm = 1 }; const src2q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2q }, .imm = 1 }; const src2w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2w }, .imm = 2 }; const src2d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2d }, .imm = 2 }; + const src2p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2p }, .imm = 2 }; const src2q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2q }, .imm = 2 }; fn si(imm: i32) Select.Operand { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 269fba13ba..8fb5d288e3 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1065,6 +1065,25 @@ test "tag name with signed enum values" { try expect(mem.eql(u8, @tagName(b), "bravo")); } +test "tag name with large enum values" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + + const Kdf = enum(u128) { + aes_kdf = 0xea4f8ac1080d74bf60448a629af3d9c9, + argon2d = 0x0c0ae303a4a9f7914b44298cdf6d63ef, + argon2id = 0xe6a1f0c63efc3db27347db56198b299e, + }; + var kdf: Kdf = .aes_kdf; + try expect(mem.eql(u8, @tagName(kdf), "aes_kdf")); + var argon2d_value: u128 = undefined; + argon2d_value = @intFromEnum(Kdf.argon2d); + kdf = @enumFromInt(argon2d_value); + try expect(mem.eql(u8, @tagName(kdf), "argon2d")); + kdf = .argon2id; + try expect(mem.eql(u8, @tagName(kdf), "argon2id")); +} + test "@tagName in callconv(.c) function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; From a1c410d512fe513ca7ae4f11d9cbd59273284efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 26 Sep 2025 00:19:47 +0200 Subject: [PATCH 080/112] Revert "x86_64: improve support for large enums" This reverts commit 8520e4e286a82944a3902b10dad32886e0efd2fa. --- src/arch/x86_64/CodeGen.zig | 265 ++++++------------------------------ test/behavior/enum.zig | 19 --- 2 files changed, 45 insertions(+), 239 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0a07a77d93..c33300a006 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -166722,33 +166722,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, .{ ._, ._, .@"test", .src0p, .src0p, ._, ._ }, } }, - }, .{ - .required_features = .{ .avx, null, null, null }, - .src_constraints = .{ .any_int, .any, .any }, - .patterns = &.{ - .{ .src = .{ .to_mem, .none, .none } }, - }, - .call_frame = .{ .alignment = .@"32" }, - .extra_temps = .{ - .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .zigcc, .after = 0, .at = 0 } } }, - .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - }, - .dst_temps = .{ .{ .cc = .nz }, .unused }, - .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, - .each = .{ .once = &.{ - .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, - .{ ._, ._, .call, .tmp1d, ._, ._, ._ }, - .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, - } }, }, .{ .required_features = .{ .sse, null, null, null }, .src_constraints = .{ .{ .int = .gpr }, .any, .any }, @@ -166775,33 +166748,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, .{ ._, ._, .@"test", .src0p, .src0p, ._, ._ }, } }, - }, .{ - .required_features = .{ .sse, null, null, null }, - .src_constraints = .{ .any_int, .any, .any }, - .patterns = &.{ - .{ .src = .{ .to_mem, .none, .none } }, - }, - .call_frame = .{ .alignment = .@"16" }, - .extra_temps = .{ - .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .zigcc, .after = 0, .at = 0 } } }, - .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - }, - .dst_temps = .{ .{ .cc = .nz }, .unused }, - .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, - .each = .{ .once = &.{ - .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, - .{ ._, ._, .call, .tmp1d, ._, ._, ._ }, - .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, - } }, }, .{ .src_constraints = .{ .{ .int = .gpr }, .any, .any }, .patterns = &.{ @@ -166827,32 +166773,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, .{ ._, ._, .@"test", .src0p, .src0p, ._, ._ }, } }, - }, .{ - .src_constraints = .{ .any_int, .any, .any }, - .patterns = &.{ - .{ .src = .{ .to_mem, .none, .none } }, - }, - .call_frame = .{ .alignment = .@"8" }, - .extra_temps = .{ - .{ .type = .usize, .kind = .{ .param_gpr = .{ .cc = .zigcc, .after = 0, .at = 0 } } }, - .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - }, - .dst_temps = .{ .{ .cc = .nz }, .unused }, - .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, - .each = .{ .once = &.{ - .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, - .{ ._, ._, .call, .tmp1d, ._, ._, ._ }, - .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, - } }, } }) catch |err| switch (err) { error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{ @tagName(air_tag), @@ -166892,32 +166812,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .each = .{ .once = &.{ .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, } }, - }, .{ - .required_features = .{ .avx, null, null, null }, - .src_constraints = .{ .any_int, .any, .any }, - .patterns = &.{ - .{ .src = .{ .to_mem, .none, .none } }, - }, - .call_frame = .{ .alignment = .@"32" }, - .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - }, - .dst_temps = .{ .{ .ret_gpr_pair = .{ .cc = .zigcc, .after = 0, .at = 0 } }, .unused }, - .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, - .each = .{ .once = &.{ - .{ ._, ._, .lea, .dst0p0, .mem(.src0), ._, ._ }, - .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, - } }, }, .{ .required_features = .{ .sse, null, null, null }, .src_constraints = .{ .{ .int = .gpr }, .any, .any }, @@ -166943,32 +166837,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .each = .{ .once = &.{ .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, } }, - }, .{ - .required_features = .{ .sse, null, null, null }, - .src_constraints = .{ .any_int, .any, .any }, - .patterns = &.{ - .{ .src = .{ .to_mem, .none, .none } }, - }, - .call_frame = .{ .alignment = .@"16" }, - .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - }, - .dst_temps = .{ .{ .ret_gpr_pair = .{ .cc = .zigcc, .after = 0, .at = 0 } }, .unused }, - .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, - .each = .{ .once = &.{ - .{ ._, ._, .lea, .dst0p0, .mem(.src0), ._, ._ }, - .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, - } }, }, .{ .src_constraints = .{ .{ .int = .gpr }, .any, .any }, .patterns = &.{ @@ -166993,32 +166861,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .each = .{ .once = &.{ .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, } }, - }, .{ - .required_features = .{ .sse, null, null, null }, - .src_constraints = .{ .any_int, .any, .any }, - .patterns = &.{ - .{ .src = .{ .to_mem, .none, .none } }, - }, - .call_frame = .{ .alignment = .@"8" }, - .extra_temps = .{ - .{ .type = .usize, .kind = .{ .lazy_sym = .{ .kind = .code, .ref = .src0 } } }, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - .unused, - }, - .dst_temps = .{ .{ .ret_gpr_pair = .{ .cc = .zigcc, .after = 0, .at = 0 } }, .unused }, - .clobbers = .{ .eflags = true, .caller_preserved = .zigcc }, - .each = .{ .once = &.{ - .{ ._, ._, .lea, .dst0p0, .mem(.src0), ._, ._ }, - .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, - } }, } }) catch |err| switch (err) { error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{ @tagName(air_tag), @@ -167027,7 +166869,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }), else => |e| return e, }; - if (res[0].tracking(cg).short != .register_pair) try ops[0].toPair(&res[0], cg); + try ops[0].toPair(&res[0], cg); try res[0].finish(inst, &.{un_op}, &ops, cg); }, .error_name => |air_tag| { @@ -169398,43 +169240,32 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { const enum_ty: Type = .fromInterned(lazy_sym.ty); wip_mir_log.debug("{f}.@tagName:", .{enum_ty.fmt(pt)}); - const ret_regs = abi.getCAbiIntReturnRegs(.auto)[0..2].*; - const ret_locks = cg.register_manager.lockRegsAssumeUnused(2, ret_regs); - defer for (ret_locks) |lock| cg.register_manager.unlockReg(lock); + const param_regs = abi.getCAbiIntParamRegs(.auto); + const param_locks = cg.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); + defer for (param_locks) |lock| cg.register_manager.unlockReg(lock); - const param_reg = abi.getCAbiIntParamRegs(.auto)[0]; - const param_lock = cg.register_manager.lockReg(param_reg); - defer if (param_lock) |lock| cg.register_manager.unlockReg(lock); - - const ret_mcv: MCValue = .{ .register_pair = ret_regs }; + const ret_mcv: MCValue = .{ .register_pair = param_regs[0..2].* }; + var enum_temp = try cg.tempInit(enum_ty, .{ .register = param_regs[0] }); const data_reg = try cg.register_manager.allocReg(null, abi.RegisterClass.gp); const data_lock = cg.register_manager.lockRegAssumeUnused(data_reg); defer cg.register_manager.unlockReg(data_lock); - try cg.asmRegisterMemory(.{ ._, .lea }, data_reg.to64(), .{ - .base = .{ .lazy_sym = .{ .kind = .const_data, .ty = lazy_sym.ty } }, - }); + try cg.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = lazy_sym.ty }); var data_off: i32 = 0; const reset_index = cg.next_temp_index; const tag_names = ip.loadEnumType(lazy_sym.ty).names; for (0..tag_names.len) |tag_index| { - var enum_temp = try cg.tempInit(enum_ty, if (enum_ty.abiSize(zcu) <= @as(u4, switch (cg.target.cpu.arch) { - else => unreachable, - .x86 => 4, - .x86_64 => 8, - })) .{ .register = param_reg } else .{ .indirect = .{ .reg = param_reg } }); - const tag_name_len = tag_names.get(ip)[tag_index].length(ip); var tag_temp = try cg.tempFromValue(try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index))); const cc_temp = enum_temp.cmpInts(.neq, &tag_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, }; - try enum_temp.die(cg); try tag_temp.die(cg); const skip_reloc = try cg.asmJccReloc(cc_temp.tracking(cg).short.eflags, undefined); try cc_temp.die(cg); + try cg.resetTemps(reset_index); try cg.genSetReg( ret_mcv.register_pair[0], @@ -169448,9 +169279,8 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { cg.performReloc(skip_reloc); data_off += @intCast(tag_name_len + 1); - - try cg.resetTemps(reset_index); } + try enum_temp.die(cg); try cg.genSetReg(ret_mcv.register_pair[0], .usize, .{ .immediate = 0 }, .{}); try cg.asmOpOnly(.{ ._, .ret }); @@ -169459,16 +169289,12 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { const err_ty: Type = .fromInterned(lazy_sym.ty); wip_mir_log.debug("{f}.@errorCast:", .{err_ty.fmt(pt)}); - const ret_reg = abi.getCAbiIntReturnRegs(.auto)[0]; - const ret_lock = cg.register_manager.lockRegAssumeUnused(ret_reg); - defer cg.register_manager.unlockReg(ret_lock); + const param_regs = abi.getCAbiIntParamRegs(.auto); + const param_locks = cg.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); + defer for (param_locks) |lock| cg.register_manager.unlockReg(lock); - const param_reg = abi.getCAbiIntParamRegs(.auto)[0]; - const param_lock = cg.register_manager.lockReg(param_reg); - defer if (param_lock) |lock| cg.register_manager.unlockReg(lock); - - const ret_mcv: MCValue = .{ .register = ret_reg }; - const err_mcv: MCValue = .{ .register = param_reg }; + const ret_mcv: MCValue = .{ .register = param_regs[0] }; + const err_mcv: MCValue = .{ .register = param_regs[0] }; var err_temp = try cg.tempInit(err_ty, err_mcv); const ExpectedContents = [32]Mir.Inst.Index; @@ -182183,6 +182009,37 @@ fn genInlineMemset( try self.asmOpOnly(.{ .@"rep _sb", .sto }); } +fn genLazySymbolRef( + self: *CodeGen, + comptime tag: Mir.Inst.Tag, + reg: Register, + lazy_sym: link.File.LazySymbol, +) InnerError!void { + if (self.mod.pic) { + switch (tag) { + .lea, .call => try self.genSetReg(reg, .usize, .{ + .lea_lazy_sym = lazy_sym, + }, .{}), + .mov => try self.genSetReg(reg, .usize, .{ + .lea_lazy_sym = lazy_sym, + }, .{}), + else => unreachable, + } + switch (tag) { + .lea, .mov => {}, + .call => try self.asmRegister(.{ ._, .call }, reg), + else => unreachable, + } + } else switch (tag) { + .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ + .base = .{ .lazy_sym = lazy_sym }, + .mod = .{ .rm = .{ .size = .qword } }, + }), + .call => try self.asmImmediate(.{ ._, .call }, .{ .lazy_sym = lazy_sym }), + else => unreachable, + } +} + fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void { const pt = self.pt; const zcu = pt.zcu; @@ -193794,11 +193651,9 @@ const Select = struct { const tmp0y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0y } }; const tmp0w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0w }, .imm = 1 }; const tmp0d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0d }, .imm = 1 }; - const tmp0p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0p }, .imm = 1 }; const tmp0q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0q }, .imm = 1 }; const tmp0w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0w }, .imm = 2 }; const tmp0d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0d }, .imm = 2 }; - const tmp0p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0p }, .imm = 2 }; const tmp0q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp0q }, .imm = 2 }; const tmp1l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1l } }; @@ -193814,11 +193669,9 @@ const Select = struct { const tmp1y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1y } }; const tmp1w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1w }, .imm = 1 }; const tmp1d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1d }, .imm = 1 }; - const tmp1p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1p }, .imm = 1 }; const tmp1q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1q }, .imm = 1 }; const tmp1w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1w }, .imm = 2 }; const tmp1d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1d }, .imm = 2 }; - const tmp1p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1p }, .imm = 2 }; const tmp1q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp1q }, .imm = 2 }; const tmp2l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2l } }; @@ -193834,11 +193687,9 @@ const Select = struct { const tmp2y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2y } }; const tmp2w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2w }, .imm = 1 }; const tmp2d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2d }, .imm = 1 }; - const tmp2p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2p }, .imm = 1 }; const tmp2q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2q }, .imm = 1 }; const tmp2w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2w }, .imm = 2 }; const tmp2d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2d }, .imm = 2 }; - const tmp2p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2p }, .imm = 2 }; const tmp2q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp2q }, .imm = 2 }; const tmp3l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3l } }; @@ -193854,11 +193705,9 @@ const Select = struct { const tmp3y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3y } }; const tmp3w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3w }, .imm = 1 }; const tmp3d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3d }, .imm = 1 }; - const tmp3p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3p }, .imm = 1 }; const tmp3q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3q }, .imm = 1 }; const tmp3w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3w }, .imm = 2 }; const tmp3d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3d }, .imm = 2 }; - const tmp3p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3p }, .imm = 2 }; const tmp3q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp3q }, .imm = 2 }; const tmp4l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4l } }; @@ -193874,11 +193723,9 @@ const Select = struct { const tmp4y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4y } }; const tmp4w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4w }, .imm = 1 }; const tmp4d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4d }, .imm = 1 }; - const tmp4p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4p }, .imm = 1 }; const tmp4q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4q }, .imm = 1 }; const tmp4w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4w }, .imm = 2 }; const tmp4d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4d }, .imm = 2 }; - const tmp4p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4p }, .imm = 2 }; const tmp4q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp4q }, .imm = 2 }; const tmp5l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5l } }; @@ -193894,11 +193741,9 @@ const Select = struct { const tmp5y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5y } }; const tmp5w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5w }, .imm = 1 }; const tmp5d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5d }, .imm = 1 }; - const tmp5p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5p }, .imm = 1 }; const tmp5q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5q }, .imm = 1 }; const tmp5w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5w }, .imm = 2 }; const tmp5d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5d }, .imm = 2 }; - const tmp5p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5p }, .imm = 2 }; const tmp5q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp5q }, .imm = 2 }; const tmp6l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6l } }; @@ -193914,11 +193759,9 @@ const Select = struct { const tmp6y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6y } }; const tmp6w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6w }, .imm = 1 }; const tmp6d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6d }, .imm = 1 }; - const tmp6p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6p }, .imm = 1 }; const tmp6q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6q }, .imm = 1 }; const tmp6w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6w }, .imm = 2 }; const tmp6d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6d }, .imm = 2 }; - const tmp6p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6p }, .imm = 2 }; const tmp6q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp6q }, .imm = 2 }; const tmp7l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7l } }; @@ -193934,11 +193777,9 @@ const Select = struct { const tmp7y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7y } }; const tmp7w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7w }, .imm = 1 }; const tmp7d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7d }, .imm = 1 }; - const tmp7p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7p }, .imm = 1 }; const tmp7q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7q }, .imm = 1 }; const tmp7w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7w }, .imm = 2 }; const tmp7d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7d }, .imm = 2 }; - const tmp7p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7p }, .imm = 2 }; const tmp7q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp7q }, .imm = 2 }; const tmp8l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8l } }; @@ -193954,11 +193795,9 @@ const Select = struct { const tmp8y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8y } }; const tmp8w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8w }, .imm = 1 }; const tmp8d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8d }, .imm = 1 }; - const tmp8p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8p }, .imm = 1 }; const tmp8q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8q }, .imm = 1 }; const tmp8w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8w }, .imm = 2 }; const tmp8d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8d }, .imm = 2 }; - const tmp8p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8p }, .imm = 2 }; const tmp8q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp8q }, .imm = 2 }; const tmp9l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9l } }; @@ -193974,11 +193813,9 @@ const Select = struct { const tmp9y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9y } }; const tmp9w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9w }, .imm = 1 }; const tmp9d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9d }, .imm = 1 }; - const tmp9p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9p }, .imm = 1 }; const tmp9q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9q }, .imm = 1 }; const tmp9w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9w }, .imm = 2 }; const tmp9d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9d }, .imm = 2 }; - const tmp9p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9p }, .imm = 2 }; const tmp9q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp9q }, .imm = 2 }; const tmp10l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10l } }; @@ -193994,11 +193831,9 @@ const Select = struct { const tmp10y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10y } }; const tmp10w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10w }, .imm = 1 }; const tmp10d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10d }, .imm = 1 }; - const tmp10p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10p }, .imm = 1 }; const tmp10q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10q }, .imm = 1 }; const tmp10w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10w }, .imm = 2 }; const tmp10d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10d }, .imm = 2 }; - const tmp10p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10p }, .imm = 2 }; const tmp10q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .tmp10q }, .imm = 2 }; const dst0l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0l } }; @@ -194014,11 +193849,9 @@ const Select = struct { const dst0y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0y } }; const dst0w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0w }, .imm = 1 }; const dst0d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0d }, .imm = 1 }; - const dst0p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0p }, .imm = 1 }; const dst0q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0q }, .imm = 1 }; const dst0w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0w }, .imm = 2 }; const dst0d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0d }, .imm = 2 }; - const dst0p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0p }, .imm = 2 }; const dst0q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst0q }, .imm = 2 }; const dst1l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1l } }; @@ -194034,11 +193867,9 @@ const Select = struct { const dst1y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1y } }; const dst1w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1w }, .imm = 1 }; const dst1d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1d }, .imm = 1 }; - const dst1p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1p }, .imm = 1 }; const dst1q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1q }, .imm = 1 }; const dst1w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1w }, .imm = 2 }; const dst1d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1d }, .imm = 2 }; - const dst1p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1p }, .imm = 2 }; const dst1q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .dst1q }, .imm = 2 }; const src0l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0l } }; @@ -194054,11 +193885,9 @@ const Select = struct { const src0y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0y } }; const src0w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0w }, .imm = 1 }; const src0d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0d }, .imm = 1 }; - const src0p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0p }, .imm = 1 }; const src0q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0q }, .imm = 1 }; const src0w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0w }, .imm = 2 }; const src0d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0d }, .imm = 2 }; - const src0p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0p }, .imm = 2 }; const src0q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src0q }, .imm = 2 }; const src1l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1l } }; @@ -194074,11 +193903,9 @@ const Select = struct { const src1y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1y } }; const src1w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1w }, .imm = 1 }; const src1d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1d }, .imm = 1 }; - const src1p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1p }, .imm = 1 }; const src1q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1q }, .imm = 1 }; const src1w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1w }, .imm = 2 }; const src1d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1d }, .imm = 2 }; - const src1p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1p }, .imm = 2 }; const src1q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src1q }, .imm = 2 }; const src2l: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2l } }; @@ -194094,11 +193921,9 @@ const Select = struct { const src2y: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2y } }; const src2w0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2w }, .imm = 1 }; const src2d0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2d }, .imm = 1 }; - const src2p0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2p }, .imm = 1 }; const src2q0: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2q }, .imm = 1 }; const src2w1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2w }, .imm = 2 }; const src2d1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2d }, .imm = 2 }; - const src2p1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2p }, .imm = 2 }; const src2q1: Select.Operand = .{ .flags = .{ .tag = .ref, .base = .src2q }, .imm = 2 }; fn si(imm: i32) Select.Operand { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 8fb5d288e3..269fba13ba 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1065,25 +1065,6 @@ test "tag name with signed enum values" { try expect(mem.eql(u8, @tagName(b), "bravo")); } -test "tag name with large enum values" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - - const Kdf = enum(u128) { - aes_kdf = 0xea4f8ac1080d74bf60448a629af3d9c9, - argon2d = 0x0c0ae303a4a9f7914b44298cdf6d63ef, - argon2id = 0xe6a1f0c63efc3db27347db56198b299e, - }; - var kdf: Kdf = .aes_kdf; - try expect(mem.eql(u8, @tagName(kdf), "aes_kdf")); - var argon2d_value: u128 = undefined; - argon2d_value = @intFromEnum(Kdf.argon2d); - kdf = @enumFromInt(argon2d_value); - try expect(mem.eql(u8, @tagName(kdf), "argon2d")); - kdf = .argon2id; - try expect(mem.eql(u8, @tagName(kdf), "argon2id")); -} - test "@tagName in callconv(.c) function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; From 6b1d94c5394e7e41389cb1cfef4f915965be1cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 27 Sep 2025 04:31:00 +0200 Subject: [PATCH 081/112] musl: add missing fenv C dummy functions for loongarch64-linux-muslsf https://www.openwall.com/lists/musl/2025/09/27/1 closes #25367 --- lib/libc/musl/src/fenv/loongarch64/fenv-sf.c | 3 +++ src/libs/musl.zig | 1 + 2 files changed, 4 insertions(+) create mode 100644 lib/libc/musl/src/fenv/loongarch64/fenv-sf.c diff --git a/lib/libc/musl/src/fenv/loongarch64/fenv-sf.c b/lib/libc/musl/src/fenv/loongarch64/fenv-sf.c new file mode 100644 index 0000000000..41154673ab --- /dev/null +++ b/lib/libc/musl/src/fenv/loongarch64/fenv-sf.c @@ -0,0 +1,3 @@ +#ifdef __loongarch_soft_float +#include "../fenv.c" +#endif diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 750252aa56..be7b0502f7 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -627,6 +627,7 @@ const src_files = [_][]const u8{ "musl/src/fenv/hexagon/fenv.S", "musl/src/fenv/i386/fenv.s", "musl/src/fenv/loongarch64/fenv.S", + "musl/src/fenv/loongarch64/fenv-sf.c", "musl/src/fenv/m68k/fenv.c", "musl/src/fenv/mips64/fenv.S", "musl/src/fenv/mips64/fenv-sf.c", From 8b5e4e032be23c945fb8d96e15249b3b29b51de1 Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Sat, 27 Sep 2025 09:53:47 +0200 Subject: [PATCH 082/112] lib/std/c: sync "struct stat" for DragonFly * Add missing functions like ISDIR() or ISREG(). This is required to build the zig compiler * Use octal notation for the S_ constants. This is how it is done for ".freebsd" and it is also the notation used by DragonFly in "sys/stat.h" * Reorder S_ constants in the same order as ".freebsd" does. Again, this follows the ordering within "sys/stat.h" --- lib/std/c.zig | 80 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 37f2790a51..cf7959fcc1 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -2235,40 +2235,70 @@ pub const S = switch (native_os) { } }, .dragonfly => struct { + pub const IFMT = 0o170000; + + pub const IFIFO = 0o010000; + pub const IFCHR = 0o020000; + pub const IFDIR = 0o040000; + pub const IFBLK = 0o060000; + pub const IFREG = 0o100000; + pub const IFLNK = 0o120000; + pub const IFSOCK = 0o140000; + pub const IFWHT = 0o160000; + + pub const ISUID = 0o4000; + pub const ISGID = 0o2000; + pub const ISVTX = 0o1000; + pub const IRWXU = 0o700; + pub const IRUSR = 0o400; + pub const IWUSR = 0o200; + pub const IXUSR = 0o100; + pub const IRWXG = 0o070; + pub const IRGRP = 0o040; + pub const IWGRP = 0o020; + pub const IXGRP = 0o010; + pub const IRWXO = 0o007; + pub const IROTH = 0o004; + pub const IWOTH = 0o002; + pub const IXOTH = 0o001; + pub const IREAD = IRUSR; pub const IEXEC = IXUSR; pub const IWRITE = IWUSR; - pub const IXOTH = 1; - pub const IWOTH = 2; - pub const IROTH = 4; - pub const IRWXO = 7; - pub const IXGRP = 8; - pub const IWGRP = 16; - pub const IRGRP = 32; - pub const IRWXG = 56; - pub const IXUSR = 64; - pub const IWUSR = 128; - pub const IRUSR = 256; - pub const IRWXU = 448; pub const ISTXT = 512; pub const BLKSIZE = 512; - pub const ISVTX = 512; - pub const ISGID = 1024; - pub const ISUID = 2048; - pub const IFIFO = 4096; - pub const IFCHR = 8192; - pub const IFDIR = 16384; - pub const IFBLK = 24576; - pub const IFREG = 32768; - pub const IFDB = 36864; - pub const IFLNK = 40960; - pub const IFSOCK = 49152; - pub const IFWHT = 57344; - pub const IFMT = 61440; + + pub fn ISFIFO(m: u32) bool { + return m & IFMT == IFIFO; + } pub fn ISCHR(m: u32) bool { return m & IFMT == IFCHR; } + + pub fn ISDIR(m: u32) bool { + return m & IFMT == IFDIR; + } + + pub fn ISBLK(m: u32) bool { + return m & IFMT == IFBLK; + } + + pub fn ISREG(m: u32) bool { + return m & IFMT == IFREG; + } + + pub fn ISLNK(m: u32) bool { + return m & IFMT == IFLNK; + } + + pub fn ISSOCK(m: u32) bool { + return m & IFMT == IFSOCK; + } + + pub fn IWHT(m: u32) bool { + return m & IFMT == IFWHT; + } }, .haiku => struct { pub const IFMT = 0o170000; From 65af34b1bf43c6354618dd95d16297f2be8f4a1b Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Sat, 27 Sep 2025 09:51:53 +0200 Subject: [PATCH 083/112] bootstrap: Add support for DragonFly --- bootstrap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bootstrap.c b/bootstrap.c index b6345987a9..1f17fc8455 100644 --- a/bootstrap.c +++ b/bootstrap.c @@ -64,6 +64,8 @@ static const char *get_host_os(void) { return "linux"; #elif defined(__FreeBSD__) return "freebsd"; +#elif defined(__DragonFly__) + return "dragonfly"; #elif defined(__HAIKU__) return "haiku"; #else From c40dbd6ff098e9df91cfe3f602419869293e889b Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 26 Sep 2025 02:14:07 -0700 Subject: [PATCH 084/112] Update descriptions of -f[no-]error-tracing to match the actual behavior Before https://github.com/ziglang/zig/pull/18160, error tracing defaulted to true in ReleaseSafe, but that is no longer the case. These option descriptions were never updating accordingly. --- src/main.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.zig b/src/main.zig index 8e6949e813..036adbf852 100644 --- a/src/main.zig +++ b/src/main.zig @@ -539,8 +539,8 @@ const usage_build_generic = \\ -funwind-tables Always produce unwind table entries for all functions \\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions \\ -fno-unwind-tables Never produce unwind table entries - \\ -ferror-tracing Enable error tracing in ReleaseFast mode - \\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode + \\ -ferror-tracing Enable error tracing in release builds + \\ -fno-error-tracing Disable error tracing in debug builds \\ -fsingle-threaded Code assumes there is only one thread \\ -fno-single-threaded Code may not assume there is only one thread \\ -fstrip Omit debug symbols From 135f1915daf8583a4dee81691da70cbeb14004f8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Sep 2025 17:14:26 -0700 Subject: [PATCH 085/112] Compilation: --debug-rt always Debug --debug-rt previously would make rt libs match the root module. Now they are always debug when --debug-rt is passed. This includes compiler-rt, fuzzer lib, and others. --- src/Compilation.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 44c0952d0a..4e06ad8d3e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -8093,7 +8093,7 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { /// compiler-rt, libcxx, libc, libunwind, etc. pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { if (comp.debug_compiler_runtime_libs) { - return comp.root_mod.optimize_mode; + return .Debug; } const target = &comp.root_mod.resolved_target.result; switch (comp.root_mod.optimize_mode) { From ebaec8e03f46f15fe194f6b794c34816a416dc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 26 Sep 2025 01:20:51 +0200 Subject: [PATCH 086/112] test: remove `pie` test case from test-standalone We already have test/cases/pie_linux.zig covering this. --- test/standalone/build.zig.zon | 3 --- test/standalone/pie/build.zig | 26 -------------------------- test/standalone/pie/main.zig | 15 --------------- 3 files changed, 44 deletions(-) delete mode 100644 test/standalone/pie/build.zig delete mode 100644 test/standalone/pie/main.zig diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index c57837c8e6..b48a389f40 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -136,9 +136,6 @@ .c_embed_path = .{ .path = "c_embed_path", }, - .pie = .{ - .path = "pie", - }, .issue_12706 = .{ .path = "issue_12706", }, diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig deleted file mode 100644 index 79d078a9eb..0000000000 --- a/test/standalone/pie/build.zig +++ /dev/null @@ -1,26 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - const optimize: std.builtin.OptimizeMode = .Debug; - const target = b.resolveTargetQuery(.{ - .os_tag = .linux, - .cpu_arch = .x86_64, - }); - - const main = b.addTest(.{ - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = optimize, - .target = target, - }), - }); - main.pie = true; - - const run = b.addRunArtifact(main); - run.skip_foreign_checks = true; - - test_step.dependOn(&run.step); -} diff --git a/test/standalone/pie/main.zig b/test/standalone/pie/main.zig deleted file mode 100644 index edf6a3fcaa..0000000000 --- a/test/standalone/pie/main.zig +++ /dev/null @@ -1,15 +0,0 @@ -const std = @import("std"); -const elf = std.elf; - -threadlocal var foo: u8 = 42; - -test "Check ELF header" { - // PIE executables are marked as ET_DYN, regular exes as ET_EXEC. - const header = @as(*elf.Ehdr, @ptrFromInt(std.process.getBaseAddress())); - try std.testing.expectEqual(elf.ET.DYN, header.e_type); -} - -test "TLS is initialized" { - // Ensure the TLS is initialized by the startup code. - try std.testing.expectEqual(@as(u8, 42), foo); -} From 4fd78f9c2639921739836c08f12949e2476ca2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 28 Sep 2025 08:00:56 +0200 Subject: [PATCH 087/112] libcxx: respond to some feature macro changes in LLVM 20 https://github.com/llvm/llvm-project/commit/ba87515fea90b5d55836a8e3be63a7e683ce299d closes #25376 --- src/libs/libcxx.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 40da4dd92f..3fc91aaa66 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -509,19 +509,19 @@ pub fn addCxxArgs( try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ abi_version, })); - try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_{s}THREADS", .{ - if (!comp.config.any_non_single_threaded) "NO_" else "", + try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_THREADS={d}", .{ + @as(u1, if (comp.config.any_non_single_threaded) 1 else 0), })); try cflags.append("-D_LIBCPP_HAS_MONOTONIC_CLOCK"); try cflags.append("-D_LIBCPP_HAS_TERMINAL"); - try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_{s}MUSL_LIBC", .{ - if (!target.abi.isMusl()) "NO_" else "", + try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_MUSL_LIBC={d}", .{ + @as(u1, if (target.abi.isMusl()) 1 else 0), })); try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - try cflags.append("-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS"); - try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_{s}FILESYSTEM", .{ - if (target.os.tag == .wasi) "NO_" else "", + try cflags.append("-D_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS=0"); + try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_FILESYSTEM={d}", .{ + @as(u1, if (target.os.tag == .wasi) 0 else 1), })); try cflags.append("-D_LIBCPP_HAS_RANDOM_DEVICE"); try cflags.append("-D_LIBCPP_HAS_LOCALIZATION"); @@ -545,7 +545,7 @@ pub fn addCxxArgs( if (target.isGnuLibC()) { // glibc 2.16 introduced aligned_alloc if (target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { - try cflags.append("-D_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION"); + try cflags.append("-D_LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION=0"); } } try cflags.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); From 0795e2b2ef96c75b8d5e68ed1b8ac23ced28bfce Mon Sep 17 00:00:00 2001 From: Timothy Bess Date: Sun, 9 Mar 2025 18:05:11 -0400 Subject: [PATCH 088/112] Fix zig build lazy -> eager dependency promotion Before, this had a subtle ordering bug where duplicate deps that are specified as both lazy and eager in different parts of the dependency tree end up not getting fetched depending on the ordering. I modified it to resubmit lazy deps that were promoted to eager for fetching so that it will be around for the builds that expect it to be eager downstream of this. --- src/Package/Fetch.zig | 48 ++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index bf77f88e93..3d2cc4cfb8 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -735,28 +735,34 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { // calling run(); no need to add it again. // // If we add a dep as lazy and then later try to add the same dep as eager, - // eagerness takes precedence and the existing entry is updated. + // eagerness takes precedence and the existing entry is updated and re-scheduled + // for fetching. for (dep_names, deps) |dep_name, dep| { + var promoted_existing_to_eager = false; const new_fetch = &new_fetches[new_fetch_index]; const location: Location = switch (dep.location) { - .url => |url| .{ .remote = .{ - .url = url, - .hash = h: { - const h = dep.hash orelse break :h null; - const pkg_hash: Package.Hash = .fromSlice(h); - if (h.len == 0) break :h pkg_hash; - const gop = f.job_queue.table.getOrPutAssumeCapacity(pkg_hash); - if (gop.found_existing) { - if (!dep.lazy) { - gop.value_ptr.*.lazy_status = .eager; + .url => |url| .{ + .remote = .{ + .url = url, + .hash = h: { + const h = dep.hash orelse break :h null; + const pkg_hash: Package.Hash = .fromSlice(h); + if (h.len == 0) break :h pkg_hash; + const gop = f.job_queue.table.getOrPutAssumeCapacity(pkg_hash); + if (gop.found_existing) { + if (!dep.lazy and gop.value_ptr.*.lazy_status != .eager) { + gop.value_ptr.*.lazy_status = .eager; + promoted_existing_to_eager = true; + } else { + continue; + } } - continue; - } - gop.value_ptr.* = new_fetch; - break :h pkg_hash; + gop.value_ptr.* = new_fetch; + break :h pkg_hash; + }, }, - } }, + }, .path => |rel_path| l: { // This might produce an invalid path, which is checked for // at the beginning of run(). @@ -764,10 +770,12 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { const pkg_hash = relativePathDigest(new_root, cache_root); const gop = f.job_queue.table.getOrPutAssumeCapacity(pkg_hash); if (gop.found_existing) { - if (!dep.lazy) { + if (!dep.lazy and gop.value_ptr.*.lazy_status != .eager) { gop.value_ptr.*.lazy_status = .eager; + promoted_existing_to_eager = true; + } else { + continue; } - continue; } gop.value_ptr.* = new_fetch; break :l .{ .relative_path = new_root }; @@ -775,7 +783,9 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { }; prog_names[new_fetch_index] = dep_name; new_fetch_index += 1; - f.job_queue.all_fetches.appendAssumeCapacity(new_fetch); + if (!promoted_existing_to_eager) { + f.job_queue.all_fetches.appendAssumeCapacity(new_fetch); + } new_fetch.* = .{ .arena = std.heap.ArenaAllocator.init(gpa), .location = location, From ab6dbfe1a314e7004fea4625c191d57d4c67f049 Mon Sep 17 00:00:00 2001 From: Jacob Date: Sat, 2 Aug 2025 16:39:07 +0800 Subject: [PATCH 089/112] translate_c: fix ternary operator output in C macros --- src/translate_c.zig | 7 ++++--- test/cases/translate_c/tenary_in_macro.c | 21 +++++++++++++++++++++ test/translate_c.zig | 4 ++-- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 test/cases/translate_c/tenary_in_macro.c diff --git a/src/translate_c.zig b/src/translate_c.zig index d481643e35..41a1a040a9 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -7,10 +7,10 @@ const meta = std.meta; const clang = @import("clang.zig"); const aro = @import("aro"); const CToken = aro.Tokenizer.Token; -const Node = ast.Node; -const Tag = Node.Tag; const common = @import("aro_translate_c"); const ast = common.ast; +const Node = ast.Node; +const Tag = Node.Tag; const Error = common.Error; const MacroProcessingError = common.MacroProcessingError; const TypeError = common.TypeError; @@ -5985,10 +5985,11 @@ fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { } _ = m.next(); + const cond_body = try macroIntToBool(c, node); const then_body = try parseCOrExpr(c, m, scope); try m.skip(c, .colon); const else_body = try parseCCondExpr(c, m, scope); - return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body }); + return Tag.@"if".create(c.arena, .{ .cond = cond_body, .then = then_body, .@"else" = else_body }); } fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { diff --git a/test/cases/translate_c/tenary_in_macro.c b/test/cases/translate_c/tenary_in_macro.c new file mode 100644 index 0000000000..a9cdb2724c --- /dev/null +++ b/test/cases/translate_c/tenary_in_macro.c @@ -0,0 +1,21 @@ +#define TERNARY_CHECK(i) check(i) +#define TERNARY_CALL(i) (TERNARY_CHECK(i) ? (i+1) : (i-1)) + +static inline int check(int obj) { + return obj % 2; +} +int target_func(int a) { + return TERNARY_CALL(a); +} + +// translate-c +// c_frontend=clang +// +// pub inline fn TERNARY_CHECK(i: anytype) @TypeOf(check(i)) { +// _ = &i; +// return check(i); +// } +// pub inline fn TERNARY_CALL(i: anytype) @TypeOf(if (TERNARY_CHECK(i) != 0) i + @as(c_int, 1) else i - @as(c_int, 1)) { +// _ = &i; +// return if (TERNARY_CHECK(i) != 0) i + @as(c_int, 1) else i - @as(c_int, 1); +// } \ No newline at end of file diff --git a/test/translate_c.zig b/test/translate_c.zig index 6d1a8916f2..49eb2f210c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3101,8 +3101,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ int a, b, c; \\#define FOO a ? b : c , &[_][]const u8{ - \\pub inline fn FOO() @TypeOf(if (a) b else c) { - \\ return if (a) b else c; + \\pub inline fn FOO() @TypeOf(if (a != 0) b else c) { + \\ return if (a != 0) b else c; \\} }); From 78012b4845319cbef27e656554c92b79e7cb8a3a Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Wed, 1 Oct 2025 01:57:01 -0700 Subject: [PATCH 090/112] resinator: fix an alignment problem --- lib/compiler/resinator/compile.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig index 60d91eeb73..6f050b0a00 100644 --- a/lib/compiler/resinator/compile.zig +++ b/lib/compiler/resinator/compile.zig @@ -674,7 +674,7 @@ pub const Compiler = struct { } try file_reader.seekTo(entry.data_offset_from_start_of_file); - var header_bytes = (file_reader.interface.takeArray(16) catch { + var header_bytes: [16]u8 align(@alignOf(ico.BitmapHeader)) = (file_reader.interface.takeArray(16) catch { return self.iconReadError( error.UnexpectedEOF, filename_utf8, From e6e93d82b0272ce34d12ee3c59428a3b83caf447 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 1 Oct 2025 13:18:00 +0100 Subject: [PATCH 091/112] Lld: fix implib emit path Resolves: https://github.com/ziglang/zig/issues/24993 --- src/link/Lld.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/link/Lld.zig b/src/link/Lld.zig index 48872f7077..1868270502 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -505,7 +505,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); if (comp.emit_implib) |raw_emit_path| { - const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path); + const path = try comp.resolveEmitPathFlush(arena, .artifact, raw_emit_path); try argv.append(try allocPrint(arena, "-IMPLIB:{f}", .{path})); } From 2700af2aeb804c332fe11c9c26116769e78c3dbd Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 3 Oct 2025 23:26:21 -0400 Subject: [PATCH 092/112] x86_64: fix bool vector init register clobber Closes #25439 --- src/arch/x86_64/CodeGen.zig | 2 ++ test/behavior/vector.zig | 46 ++++++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c33300a006..e1ad9e3d07 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -184685,6 +184685,8 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { if (result_ty.isVector(zcu) and elem_ty.toIntern() == .bool_type) { const result_size: u32 = @intCast(result_ty.abiSize(zcu)); const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); + const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_lock); try self.asmRegisterRegister( .{ ._, .xor }, registerAlias(dst_reg, @min(result_size, 4)), diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 48213e2178..0cd792b460 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -7,16 +7,50 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; test "implicit cast vector to array - bool" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .aarch64_be and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { - const a: @Vector(4, bool) = [_]bool{ true, false, true, false }; - const result_array: [4]bool = a; - try expect(mem.eql(bool, &result_array, &[4]bool{ true, false, true, false })); + { + var v: @Vector(4, bool) = undefined; + v = .{ true, false, true, false }; + const a: [4]bool = v; + try expect(mem.eql(bool, &a, &.{ true, false, true, false })); + } + { + var v: @Vector(25, bool) = undefined; + v = .{ false, false, false, false, true, true, false, false, false, true, false, true, false, false, true, false, false, true, false, false, true, true, true, false, false }; + const a: [25]bool = v; + try expect(mem.eql(bool, &a, &.{ false, false, false, false, true, true, false, false, false, true, false, true, false, false, true, false, false, true, false, false, true, true, true, false, false })); + } + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +} + +test "implicit cast array to vector - bool" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + { + var a: [4]bool = undefined; + a = .{ true, false, false, true }; + const v: @Vector(4, bool) = a; + try expect(mem.eql(bool, &@as([4]bool, v), &.{ true, false, false, true })); + } + { + var a: [25]bool = undefined; + a = .{ true, false, false, true, false, false, false, false, false, true, true, true, true, false, false, false, false, true, false, false, false, true, true, true, false }; + const v: @Vector(25, bool) = a; + try expect(mem.eql(bool, &@as([25]bool, v), &.{ true, false, false, true, false, false, false, false, false, true, true, true, true, false, false, false, false, true, false, false, false, true, true, true, false })); + } } }; try S.doTheTest(); From 7de67e6802d4cec114d5ede1892abe428e331cac Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 Oct 2025 05:05:31 -0400 Subject: [PATCH 093/112] InternPool: use sequential string indices instead of byte offsets This allows more bytes to be referenced by a smaller index range. Closes #22867 Closes #25297 Closes #25339 --- src/Compilation.zig | 9 ++- src/InternPool.zig | 132 ++++++++++++++++++++++++++---------------- src/Value.zig | 14 ++--- src/Zcu.zig | 4 +- src/Zcu/PerThread.zig | 8 +-- 5 files changed, 103 insertions(+), 64 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 4e06ad8d3e..3752e93a0d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3666,6 +3666,7 @@ const Header = extern struct { items_len: u32, extra_len: u32, limbs_len: u32, + strings_len: u32, string_bytes_len: u32, tracked_insts_len: u32, files_len: u32, @@ -3714,7 +3715,8 @@ pub fn saveState(comp: *Compilation) !void { .items_len = @intCast(local.mutate.items.len), .extra_len = @intCast(local.mutate.extra.len), .limbs_len = @intCast(local.mutate.limbs.len), - .string_bytes_len = @intCast(local.mutate.strings.len), + .strings_len = @intCast(local.mutate.strings.len), + .string_bytes_len = @intCast(local.mutate.string_bytes.len), .tracked_insts_len = @intCast(local.mutate.tracked_insts.len), .files_len = @intCast(local.mutate.files.len), }, @@ -3757,8 +3759,11 @@ pub fn saveState(comp: *Compilation) !void { addBuf(&bufs, @ptrCast(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len])); addBuf(&bufs, @ptrCast(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len])); } + if (pt_header.intern_pool.strings_len > 0) { + addBuf(&bufs, @ptrCast(local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.strings_len])); + } if (pt_header.intern_pool.string_bytes_len > 0) { - addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]); + addBuf(&bufs, local.shared.string_bytes.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]); } if (pt_header.intern_pool.tracked_insts_len > 0) { addBuf(&bufs, @ptrCast(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len])); diff --git a/src/InternPool.zig b/src/InternPool.zig index 5f108e3224..fc6634d925 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -182,7 +182,7 @@ pub const TrackedInst = extern struct { pub fn wrap(unwrapped: Unwrapped, ip: *const InternPool) TrackedInst.Index { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u32)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | unwrapped.index); } }; @@ -480,7 +480,7 @@ pub const ComptimeUnit = extern struct { fn wrap(unwrapped: Unwrapped, ip: *const InternPool) ComptimeUnit.Id { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u32)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | unwrapped.index); } }; @@ -699,7 +699,7 @@ pub const Nav = struct { fn wrap(unwrapped: Unwrapped, ip: *const InternPool) Nav.Index { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u32)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | unwrapped.index); } }; @@ -1047,6 +1047,7 @@ const Local = struct { extra: ListMutate, limbs: ListMutate, strings: ListMutate, + string_bytes: ListMutate, tracked_insts: ListMutate, files: ListMutate, maps: ListMutate, @@ -1061,6 +1062,7 @@ const Local = struct { extra: Extra, limbs: Limbs, strings: Strings, + string_bytes: StringBytes, tracked_insts: TrackedInsts, files: List(File), maps: Maps, @@ -1084,7 +1086,8 @@ const Local = struct { @sizeOf(u64) => List(struct { u64 }), else => @compileError("unsupported host"), }; - const Strings = List(struct { u8 }); + const Strings = List(struct { u32 }); + const StringBytes = List(struct { u8 }); const TrackedInsts = List(struct { TrackedInst.MaybeLost }); const Maps = List(struct { FieldMap }); const Navs = List(Nav.Repr); @@ -1414,11 +1417,7 @@ const Local = struct { }; } - /// In order to store references to strings in fewer bytes, we copy all - /// string bytes into here. String bytes can be null. It is up to whomever - /// is referencing the data here whether they want to store both index and length, - /// thus allowing null bytes, or store only index, and use null-termination. The - /// `strings` array is agnostic to either usage. + /// A list of offsets into `string_bytes` for each string. pub fn getMutableStrings(local: *Local, gpa: Allocator) Strings.Mutable { return .{ .gpa = gpa, @@ -1428,6 +1427,20 @@ const Local = struct { }; } + /// In order to store references to strings in fewer bytes, we copy all + /// string bytes into here. String bytes can be null. It is up to whomever + /// is referencing the data here whether they want to store both index and length, + /// thus allowing null bytes, or store only index, and use null-termination. The + /// `strings_bytes` array is agnostic to either usage. + pub fn getMutableStringBytes(local: *Local, gpa: Allocator) StringBytes.Mutable { + return .{ + .gpa = gpa, + .arena = &local.mutate.arena, + .mutate = &local.mutate.string_bytes, + .list = &local.shared.string_bytes, + }; + } + /// An index into `tracked_insts` gives a reference to a single ZIR instruction which /// persists across incremental updates. pub fn getMutableTrackedInsts(local: *Local, gpa: Allocator) TrackedInsts.Mutable { @@ -1597,7 +1610,7 @@ const Shard = struct { }; fn getTidMask(ip: *const InternPool) u32 { - return (@as(u32, 1) << ip.tid_width) - 1; + return @shlExact(@as(u32, 1), ip.tid_width) - 1; } fn getIndexMask(ip: *const InternPool, comptime BackingInt: type) u32 { @@ -1652,7 +1665,7 @@ pub const MapIndex = enum(u32) { fn wrap(unwrapped: Unwrapped, ip: *const InternPool) MapIndex { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u32)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | unwrapped.index); } }; @@ -1678,7 +1691,7 @@ pub const NamespaceIndex = enum(u32) { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.bucket_index <= ip.getIndexMask(u32) >> Local.namespaces_bucket_width); assert(unwrapped.index <= Local.namespaces_bucket_mask); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | unwrapped.bucket_index << Local.namespaces_bucket_width | unwrapped.index); } @@ -1721,7 +1734,7 @@ pub const FileIndex = enum(u32) { fn wrap(unwrapped: Unwrapped, ip: *const InternPool) FileIndex { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u32)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | unwrapped.index); } }; @@ -1780,7 +1793,8 @@ pub const String = enum(u32) { fn wrap(unwrapped: Unwrapped, ip: *const InternPool) String { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u32)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | unwrapped.index); + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_32) | + unwrapped.index); } }; fn unwrap(string: String, ip: *const InternPool) Unwrapped { @@ -1791,9 +1805,11 @@ pub const String = enum(u32) { } fn toOverlongSlice(string: String, ip: *const InternPool) []const u8 { - const unwrapped_string = string.unwrap(ip); - const strings = ip.getLocalShared(unwrapped_string.tid).strings.acquire(); - return strings.view().items(.@"0")[unwrapped_string.index..]; + const unwrapped = string.unwrap(ip); + const local_shared = ip.getLocalShared(unwrapped.tid); + const strings = local_shared.strings.acquire().view().items(.@"0"); + const string_bytes = local_shared.string_bytes.acquire().view().items(.@"0"); + return string_bytes[strings[unwrapped.index]..]; } const debug_state = InternPool.debug_state; @@ -1848,12 +1864,18 @@ pub const NullTerminatedString = enum(u32) { } pub fn toSlice(string: NullTerminatedString, ip: *const InternPool) [:0]const u8 { - const overlong_slice = string.toString().toOverlongSlice(ip); - return overlong_slice[0..std.mem.indexOfScalar(u8, overlong_slice, 0).? :0]; + const unwrapped = string.toString().unwrap(ip); + const local_shared = ip.getLocalShared(unwrapped.tid); + const strings = local_shared.strings.acquire().view().items(.@"0"); + const string_bytes = local_shared.string_bytes.acquire().view().items(.@"0"); + return string_bytes[strings[unwrapped.index] .. strings[unwrapped.index + 1] - 1 :0]; } pub fn length(string: NullTerminatedString, ip: *const InternPool) u32 { - return @intCast(string.toSlice(ip).len); + const unwrapped = string.toString().unwrap(ip); + const local_shared = ip.getLocalShared(unwrapped.tid); + const strings = local_shared.strings.acquire().view().items(.@"0"); + return strings[unwrapped.index + 1] - 1 - strings[unwrapped.index]; } pub fn eqlSlice(string: NullTerminatedString, slice: []const u8, ip: *const InternPool) bool { @@ -4767,7 +4789,8 @@ pub const Index = enum(u32) { fn wrap(unwrapped: Unwrapped, ip: *const InternPool) Index { assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); assert(unwrapped.index <= ip.getIndexMask(u30)); - return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_30 | unwrapped.index); + return @enumFromInt(@shlExact(@as(u32, @intFromEnum(unwrapped.tid)), ip.tid_shift_30) | + unwrapped.index); } pub fn getExtra(unwrapped: Unwrapped, ip: *const InternPool) Local.Extra { @@ -6784,6 +6807,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { .extra = .empty, .limbs = .empty, .strings = .empty, + .string_bytes = .empty, .tracked_insts = .empty, .files = .empty, .maps = .empty, @@ -6799,6 +6823,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { .extra = .empty, .limbs = .empty, .strings = .empty, + .string_bytes = .empty, .tracked_insts = .empty, .files = .empty, .maps = .empty, @@ -6808,6 +6833,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { .namespaces = .empty, }, }); + for (ip.locals) |*local| try local.getMutableStrings(gpa).append(.{0}); ip.tid_width = @intCast(std.math.log2_int_ceil(usize, used_threads)); ip.tid_shift_30 = if (single_threaded) 0 else 30 - ip.tid_width; @@ -8515,30 +8541,30 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All } if (child == .u8_type) bytes: { - const strings = ip.getLocal(tid).getMutableStrings(gpa); - const start = strings.mutate.len; - try strings.ensureUnusedCapacity(@intCast(len_including_sentinel + 1)); + const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa); + const start = string_bytes.mutate.len; + try string_bytes.ensureUnusedCapacity(@intCast(len_including_sentinel + 1)); try extra.ensureUnusedCapacity(@typeInfo(Bytes).@"struct".fields.len); switch (aggregate.storage) { - .bytes => |bytes| strings.appendSliceAssumeCapacity(.{bytes.toSlice(len, ip)}), + .bytes => |bytes| string_bytes.appendSliceAssumeCapacity(.{bytes.toSlice(len, ip)}), .elems => |elems| for (elems[0..@intCast(len)]) |elem| switch (ip.indexToKey(elem)) { .undef => { - strings.shrinkRetainingCapacity(start); + string_bytes.shrinkRetainingCapacity(start); break :bytes; }, - .int => |int| strings.appendAssumeCapacity(.{@intCast(int.storage.u64)}), + .int => |int| string_bytes.appendAssumeCapacity(.{@intCast(int.storage.u64)}), else => unreachable, }, .repeated_elem => |elem| switch (ip.indexToKey(elem)) { .undef => break :bytes, .int => |int| @memset( - strings.addManyAsSliceAssumeCapacity(@intCast(len))[0], + string_bytes.addManyAsSliceAssumeCapacity(@intCast(len))[0], @intCast(int.storage.u64), ), else => unreachable, }, } - if (sentinel != .none) strings.appendAssumeCapacity(.{ + if (sentinel != .none) string_bytes.appendAssumeCapacity(.{ @intCast(ip.indexToKey(sentinel).int.storage.u64), }); const string = try ip.getOrPutTrailingString( @@ -11754,10 +11780,10 @@ pub fn getOrPutString( slice: []const u8, comptime embedded_nulls: EmbeddedNulls, ) Allocator.Error!embedded_nulls.StringType() { - const strings = ip.getLocal(tid).getMutableStrings(gpa); - try strings.ensureUnusedCapacity(slice.len + 1); - strings.appendSliceAssumeCapacity(.{slice}); - strings.appendAssumeCapacity(.{0}); + const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa); + try string_bytes.ensureUnusedCapacity(slice.len + 1); + string_bytes.appendSliceAssumeCapacity(.{slice}); + string_bytes.appendAssumeCapacity(.{0}); return ip.getOrPutTrailingString(gpa, tid, @intCast(slice.len + 1), embedded_nulls); } @@ -11772,8 +11798,8 @@ pub fn getOrPutStringFmt( // ensure that references to strings in args do not get invalidated const format_z = format ++ .{0}; const len: u32 = @intCast(std.fmt.count(format_z, args)); - const strings = ip.getLocal(tid).getMutableStrings(gpa); - const slice = try strings.addManyAsSlice(len); + const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa); + const slice = try string_bytes.addManyAsSlice(len); assert((std.fmt.bufPrint(slice[0], format_z, args) catch unreachable).len == len); return ip.getOrPutTrailingString(gpa, tid, len, embedded_nulls); } @@ -11797,21 +11823,27 @@ pub fn getOrPutTrailingString( len: u32, comptime embedded_nulls: EmbeddedNulls, ) Allocator.Error!embedded_nulls.StringType() { - const strings = ip.getLocal(tid).getMutableStrings(gpa); - const start: u32 = @intCast(strings.mutate.len - len); - if (len > 0 and strings.view().items(.@"0")[strings.mutate.len - 1] == 0) { - strings.mutate.len -= 1; + const local = ip.getLocal(tid); + const strings = local.getMutableStrings(gpa); + try strings.ensureUnusedCapacity(1); + const string_bytes = local.getMutableStringBytes(gpa); + const start: u32 = @intCast(string_bytes.mutate.len - len); + if (len > 0 and string_bytes.view().items(.@"0")[string_bytes.mutate.len - 1] == 0) { + string_bytes.mutate.len -= 1; } else { - try strings.ensureUnusedCapacity(1); + try string_bytes.ensureUnusedCapacity(1); } - const key: []const u8 = strings.view().items(.@"0")[start..]; - const value: embedded_nulls.StringType() = - @enumFromInt(@intFromEnum((String.Unwrapped{ .tid = tid, .index = start }).wrap(ip))); + const key: []const u8 = string_bytes.view().items(.@"0")[start..]; + const value: embedded_nulls.StringType() = @enumFromInt(@intFromEnum((String.Unwrapped{ + .tid = tid, + .index = strings.mutate.len - 1, + }).wrap(ip))); const has_embedded_null = std.mem.indexOfScalar(u8, key, 0) != null; switch (embedded_nulls) { .no_embedded_nulls => assert(!has_embedded_null), .maybe_embedded_nulls => if (has_embedded_null) { - strings.appendAssumeCapacity(.{0}); + string_bytes.appendAssumeCapacity(.{0}); + strings.appendAssumeCapacity(.{string_bytes.mutate.len}); return value; }, } @@ -11829,7 +11861,7 @@ pub fn getOrPutTrailingString( const index = entry.acquire().unwrap() orelse break; if (entry.hash != hash) continue; if (!index.eqlSlice(key, ip)) continue; - strings.shrinkRetainingCapacity(start); + string_bytes.shrinkRetainingCapacity(start); return @enumFromInt(@intFromEnum(index)); } shard.mutate.string_map.mutex.lock(); @@ -11845,19 +11877,20 @@ pub fn getOrPutTrailingString( const index = entry.acquire().unwrap() orelse break; if (entry.hash != hash) continue; if (!index.eqlSlice(key, ip)) continue; - strings.shrinkRetainingCapacity(start); + string_bytes.shrinkRetainingCapacity(start); return @enumFromInt(@intFromEnum(index)); } defer shard.mutate.string_map.len += 1; const map_header = map.header().*; if (shard.mutate.string_map.len < map_header.capacity * 3 / 5) { - strings.appendAssumeCapacity(.{0}); + string_bytes.appendAssumeCapacity(.{0}); + strings.appendAssumeCapacity(.{string_bytes.mutate.len}); const entry = &map.entries[map_index]; entry.hash = hash; entry.release(@enumFromInt(@intFromEnum(value))); return value; } - const arena_state = &ip.getLocal(tid).mutate.arena; + const arena_state = &local.mutate.arena; var arena = arena_state.promote(gpa); defer arena_state.* = arena.state; const new_map_capacity = map_header.capacity * 2; @@ -11893,7 +11926,8 @@ pub fn getOrPutTrailingString( map_index &= new_map_mask; if (map.entries[map_index].value == .none) break; } - strings.appendAssumeCapacity(.{0}); + string_bytes.appendAssumeCapacity(.{0}); + strings.appendAssumeCapacity(.{string_bytes.mutate.len}); map.entries[map_index] = .{ .value = @enumFromInt(@intFromEnum(value)), .hash = hash, diff --git a/src/Value.zig b/src/Value.zig index 166e18519d..b4330705db 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -66,8 +66,8 @@ pub fn toIpString(val: Value, ty: Type, pt: Zcu.PerThread) !InternPool.NullTermi .repeated_elem => |elem| { const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(zcu)); const len: u32 = @intCast(ty.arrayLen(zcu)); - const strings = ip.getLocal(pt.tid).getMutableStrings(zcu.gpa); - try strings.appendNTimes(.{byte}, len); + const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(zcu.gpa); + try string_bytes.appendNTimes(.{byte}, len); return ip.getOrPutTrailingString(zcu.gpa, pt.tid, len, .no_embedded_nulls); }, } @@ -109,16 +109,16 @@ fn arrayToIpString(val: Value, len_u64: u64, pt: Zcu.PerThread) !InternPool.Null const gpa = zcu.gpa; const ip = &zcu.intern_pool; const len: u32 = @intCast(len_u64); - const strings = ip.getLocal(pt.tid).getMutableStrings(gpa); - try strings.ensureUnusedCapacity(len); + const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa); + try string_bytes.ensureUnusedCapacity(len); for (0..len) |i| { // I don't think elemValue has the possibility to affect ip.string_bytes. Let's // assert just to be sure. - const prev_len = strings.mutate.len; + const prev_len = string_bytes.mutate.len; const elem_val = try val.elemValue(pt, i); - assert(strings.mutate.len == prev_len); + assert(string_bytes.mutate.len == prev_len); const byte: u8 = @intCast(elem_val.toUnsignedInt(zcu)); - strings.appendAssumeCapacity(.{byte}); + string_bytes.appendAssumeCapacity(.{byte}); } return ip.getOrPutTrailingString(gpa, pt.tid, len, .no_embedded_nulls); } diff --git a/src/Zcu.zig b/src/Zcu.zig index 5f45e6395e..d26417c8f7 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -1116,8 +1116,8 @@ pub const File = struct { pub fn internFullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString { const gpa = pt.zcu.gpa; const ip = &pt.zcu.intern_pool; - const strings = ip.getLocal(pt.tid).getMutableStrings(gpa); - var w: Writer = .fixed((try strings.addManyAsSlice(file.fullyQualifiedNameLen()))[0]); + const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa); + var w: Writer = .fixed((try string_bytes.addManyAsSlice(file.fullyQualifiedNameLen()))[0]); file.renderFullyQualifiedName(&w) catch unreachable; assert(w.end == w.buffer.len); return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(w.end), .no_embedded_nulls); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 77cddb204e..a4024be07a 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -2459,10 +2459,10 @@ fn updateEmbedFileInner( // The loaded bytes of the file, including a sentinel 0 byte. const ip_str: InternPool.String = str: { - const strings = ip.getLocal(tid).getMutableStrings(gpa); - const old_len = strings.mutate.len; - errdefer strings.shrinkRetainingCapacity(old_len); - const bytes = (try strings.addManyAsSlice(size_plus_one))[0]; + const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa); + const old_len = string_bytes.mutate.len; + errdefer string_bytes.shrinkRetainingCapacity(old_len); + const bytes = (try string_bytes.addManyAsSlice(size_plus_one))[0]; var fr = file.reader(&.{}); fr.size = stat.size; fr.interface.readSliceAll(bytes[0..size]) catch |err| switch (err) { From cfb5350ed43779c099914940cfe16abedb16bce7 Mon Sep 17 00:00:00 2001 From: xdBronch <51252236+xdBronch@users.noreply.github.com> Date: Fri, 3 Oct 2025 19:37:51 -0400 Subject: [PATCH 094/112] don't pass zero-length `@memset` to the backend --- src/Sema.zig | 2 +- test/behavior/memset.zig | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 90e74f7555..2da8f18c31 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25299,7 +25299,6 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); const runtime_src = rs: { - const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), dest_src); const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; const len_u64 = try len_val.toUnsignedIntSema(pt); @@ -25309,6 +25308,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void return; } + const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src; const elem_val = try sema.resolveValue(elem) orelse break :rs value_src; const array_ty = try pt.arrayType(.{ diff --git a/test/behavior/memset.zig b/test/behavior/memset.zig index 0a15dc3cce..1efd90a2c6 100644 --- a/test/behavior/memset.zig +++ b/test/behavior/memset.zig @@ -175,3 +175,8 @@ test "zero keys with @memset" { try expect(!Keys.keys.left); try expect(!Keys.keys.right); } + +test "@memset with zero-length array" { + var array: [0]usize = undefined; + @memset(&array, 0); +} From 57f45cc87c28584b6fdfd46fbb7a3e2727d7d097 Mon Sep 17 00:00:00 2001 From: xdBronch <51252236+xdBronch@users.noreply.github.com> Date: Sat, 4 Oct 2025 19:33:26 -0400 Subject: [PATCH 095/112] fix read of undefined in http tests --- lib/std/http/test.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index 91a57dbbe2..fac241ede4 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -1105,8 +1105,8 @@ fn createTestServer(S: type) !*TestServer { const test_server = try std.testing.allocator.create(TestServer); test_server.* = .{ .net_server = try address.listen(.{ .reuse_address = true }), - .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}), .shutting_down = false, + .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}), }; return test_server; } From 9377cc6eef78705f7b835cb5a386ea0e80981fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 4 Oct 2025 21:37:29 +0200 Subject: [PATCH 096/112] std.zig.system: handle or1k in getExternalExecutor() --- lib/std/zig/system.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 14544bdb23..0c6de34d5a 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -102,6 +102,7 @@ pub fn getExternalExecutor( else => "qemu-mips64el", }, }, + .or1k => Executor{ .qemu = "qemu-or1k" }, .powerpc => Executor{ .qemu = "qemu-ppc" }, .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, @@ -121,7 +122,7 @@ pub fn getExternalExecutor( else => Executor{ .qemu = "qemu-x86_64" }, }, .xtensa => Executor{ .qemu = "qemu-xtensa" }, - else => return bad_result, + else => bad_result, }; } From b2591d50c7793e32cd6d842d32bd54237c3f21aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 4 Oct 2025 00:44:50 +0200 Subject: [PATCH 097/112] libcxxabi: define _LIBCPP_BUILDING_LIBRARY in addition to _LIBCXXABI_BUILDING_LIBRARY --- src/libs/libcxx.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 3fc91aaa66..66dbcfc906 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -399,6 +399,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr try addCxxArgs(comp, arena, &cflags); try cflags.append("-DNDEBUG"); + try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY"); if (!comp.config.any_non_single_threaded) { try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); From 03078bfa414577188c904f83439ab136d3d86361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 4 Oct 2025 00:43:20 +0200 Subject: [PATCH 098/112] libcxxabi: sort file list according to upstream CMakeLists.txt --- src/libs/libcxx.zig | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 66dbcfc906..868c884beb 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -9,25 +9,30 @@ const trace = @import("../tracy.zig").trace; const Module = @import("../Package/Module.zig"); const libcxxabi_files = [_][]const u8{ - "src/abort_message.cpp", "src/cxa_aux_runtime.cpp", "src/cxa_default_handlers.cpp", "src/cxa_demangle.cpp", - "src/cxa_exception.cpp", "src/cxa_exception_storage.cpp", "src/cxa_guard.cpp", "src/cxa_handlers.cpp", - "src/cxa_noexception.cpp", - "src/cxa_personality.cpp", - "src/cxa_thread_atexit.cpp", "src/cxa_vector.cpp", "src/cxa_virtual.cpp", - "src/fallback_malloc.cpp", - "src/private_typeinfo.cpp", + "src/stdlib_exception.cpp", - "src/stdlib_new_delete.cpp", "src/stdlib_stdexcept.cpp", "src/stdlib_typeinfo.cpp", + "src/stdlib_new_delete.cpp", + + "src/abort_message.cpp", + "src/fallback_malloc.cpp", + "src/private_typeinfo.cpp", + + "src/cxa_exception.cpp", + "src/cxa_personality.cpp", + + "src/cxa_noexception.cpp", + + "src/cxa_thread_atexit.cpp", }; const libcxx_base_files = [_][]const u8{ From bc7cdc2b6b08070e1983d6c38744d74c9dddf5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 4 Oct 2025 00:44:21 +0200 Subject: [PATCH 099/112] libcxxabi: don't build cxa_noexception.cpp if exceptions are enabled --- src/libs/libcxx.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 868c884beb..3c296d477f 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -393,11 +393,13 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len); for (libcxxabi_files) |cxxabi_src| { - if (!comp.config.any_non_single_threaded and std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) + if (!comp.config.any_non_single_threaded and std.mem.eql(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) continue; if (target.os.tag == .wasi and (std.mem.eql(u8, cxxabi_src, "src/cxa_exception.cpp") or std.mem.eql(u8, cxxabi_src, "src/cxa_personality.cpp"))) continue; + if (target.os.tag != .wasi and std.mem.eql(u8, cxxabi_src, "src/cxa_noexception.cpp")) + continue; var cflags = std.array_list.Managed([]const u8).init(arena); From 08b2fd46ab79e1f381e360c01a4f66abff8cb4b7 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Fri, 3 Oct 2025 22:15:38 +0100 Subject: [PATCH 100/112] std.c: Add missing SIG constants for serenity --- lib/std/c.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index cf7959fcc1..a3f98c7564 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -3121,8 +3121,17 @@ pub const SIG = switch (native_os) { pub const UNBLOCK = 2; pub const SETMASK = 3; }, + // https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal.h // https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal_numbers.h .serenity => struct { + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + + pub const BLOCK = 1; + pub const UNBLOCK = 2; + pub const SETMASK = 3; + pub const INVAL = 0; pub const HUP = 1; pub const INT = 2; From ff16a7c3fa29c95edefc8496114d577b4037fe6f Mon Sep 17 00:00:00 2001 From: tehlordvortex Date: Tue, 7 Oct 2025 16:12:16 +0100 Subject: [PATCH 101/112] std.Build: duplicate sub_path for LazyPath's dependency variant --- lib/std/Build.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 548f951991..9b394f60f3 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2524,7 +2524,10 @@ pub const LazyPath = union(enum) { .up = gen.up, .sub_path = dupePathInner(allocator, gen.sub_path), } }, - .dependency => |dep| .{ .dependency = dep }, + .dependency => |dep| .{ .dependency = .{ + .dependency = dep.dependency, + .sub_path = dupePathInner(allocator, dep.sub_path), + } }, }; } }; From acd6ffdf69332d61b51f64f4707b472c147b7034 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 3 Oct 2025 01:18:53 -0700 Subject: [PATCH 102/112] Reader.peekDelimiterInclusive: Fix handling of `stream` implementations that return 0 Previously, the logic in peekDelimiterInclusive (when the delimiter was not found in the existing buffer) used the `n` returned from `r.vtable.stream` as the length of the slice to check, but it's valid for `vtable.stream` implementations to return 0 if they wrote to the buffer instead of `w`. In that scenario, the `indexOfScalarPos` would be given a 0-length slice so it would never be able to find the delimiter. This commit changes the logic to assume that `r.vtable.stream` can both: - return 0, and - modify seek/end (i.e. it's also valid for a `vtable.stream` implementation to rebase) Also introduces `std.testing.ReaderIndirect` which helps in being able to test against Reader implementations that return 0 from `stream`/`readVec` Fixes #25428 --- lib/std/Io/Reader.zig | 41 ++++++++++++++++++++++++++--- lib/std/testing.zig | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index 8e6e96c9dc..b62e81cd13 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -763,13 +763,14 @@ pub fn takeDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { const buffer = r.buffer[0..r.end]; const seek = r.seek; - if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| { + if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |delimiter_index| { @branchHint(.likely); - return buffer[seek .. end + 1]; + return buffer[seek .. delimiter_index + 1]; } // TODO take a parameter for max search length rather than relying on buffer capacity try rebase(r, r.buffer.len); while (r.buffer.len - r.end != 0) { + const existing_buffered_len = r.end - r.seek; const end_cap = r.buffer[r.end..]; var writer: Writer = .fixed(end_cap); const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) { @@ -777,8 +778,8 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { else => |e| return e, }; r.end += n; - if (std.mem.indexOfScalarPos(u8, end_cap[0..n], 0, delimiter)) |end| { - return r.buffer[0 .. r.end - n + end + 1]; + if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], r.seek + existing_buffered_len, delimiter)) |delimiter_index| { + return r.buffer[r.seek .. delimiter_index + 1]; } } return error.StreamTooLong; @@ -1534,6 +1535,18 @@ test "readSliceShort with smaller buffer than Reader" { try testing.expectEqualStrings(str, &buf); } +test "readSliceShort with indirect reader" { + var r: Reader = .fixed("HelloFren"); + var ri_buf: [3]u8 = undefined; + var ri: std.testing.ReaderIndirect = .init(&r, &ri_buf); + var buf: [5]u8 = undefined; + try testing.expectEqual(5, try ri.interface.readSliceShort(&buf)); + try testing.expectEqualStrings("Hello", buf[0..5]); + try testing.expectEqual(4, try ri.interface.readSliceShort(&buf)); + try testing.expectEqualStrings("Fren", buf[0..4]); + try testing.expectEqual(0, try ri.interface.readSliceShort(&buf)); +} + test readVec { var r: Reader = .fixed(std.ascii.letters); var flat_buffer: [52]u8 = undefined; @@ -1643,6 +1656,26 @@ test "takeDelimiterInclusive when it rebases" { } } +test "takeDelimiterInclusive on an indirect reader when it rebases" { + const written_line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"; + var buffer: [128]u8 = undefined; + var tr: std.testing.Reader = .init(&buffer, &.{ + .{ .buffer = written_line[0..4] }, + .{ .buffer = written_line[4..] }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + .{ .buffer = written_line }, + }); + var indirect_buffer: [128]u8 = undefined; + var tri: std.testing.ReaderIndirect = .init(&tr.interface, &indirect_buffer); + const r = &tri.interface; + for (0..6) |_| { + try std.testing.expectEqualStrings(written_line, try r.takeDelimiterInclusive('\n')); + } +} + test "takeStruct and peekStruct packed" { var r: Reader = .fixed(&.{ 0b11110000, 0b00110011 }); const S = packed struct(u16) { a: u2, b: u6, c: u7, d: u1 }; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index e80e961b13..af2a0e3dc9 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -1249,3 +1249,63 @@ pub const Reader = struct { return n; } }; + +/// A `std.Io.Reader` that gets its data from another `std.Io.Reader`, and always +/// writes to its own buffer (and returns 0) during `stream` and `readVec`. +pub const ReaderIndirect = struct { + in: *std.Io.Reader, + interface: std.Io.Reader, + + pub fn init(in: *std.Io.Reader, buffer: []u8) ReaderIndirect { + return .{ + .in = in, + .interface = .{ + .vtable = &.{ + .stream = stream, + .readVec = readVec, + }, + .buffer = buffer, + .seek = 0, + .end = 0, + }, + }; + } + + fn readVec(r: *std.Io.Reader, _: [][]u8) std.Io.Reader.Error!usize { + try streamInner(r); + return 0; + } + + fn stream(r: *std.Io.Reader, _: *std.Io.Writer, _: std.Io.Limit) std.Io.Reader.StreamError!usize { + try streamInner(r); + return 0; + } + + fn streamInner(r: *std.Io.Reader) std.Io.Reader.Error!void { + const r_indirect: *ReaderIndirect = @alignCast(@fieldParentPtr("interface", r)); + + // If there's no room remaining in the buffer at all, make room. + if (r.buffer.len == r.end) { + try r.rebase(r.buffer.len); + } + + var writer: std.Io.Writer = .{ + .buffer = r.buffer, + .end = r.end, + .vtable = &.{ + .drain = std.Io.Writer.unreachableDrain, + .rebase = std.Io.Writer.unreachableRebase, + }, + }; + defer r.end = writer.end; + + r_indirect.in.streamExact(&writer, r.buffer.len - r.end) catch |err| switch (err) { + // Only forward EndOfStream if no new bytes were written to the buffer + error.EndOfStream => |e| if (r.end == writer.end) { + return e; + }, + error.WriteFailed => unreachable, + else => |e| return e, + }; + } +}; From 5c0ac90721ec45c6712535a99cb90df48e59e121 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Oct 2025 15:28:36 -0700 Subject: [PATCH 103/112] std: fix File.Writer sendfile with buffered contents * File.Writer.seekBy passed wrong offset to setPosAdjustingBuffer. * File.Writer.sendFile incorrectly used non-logical position. Related to 1d764c1fdf04829cec5974d82cec901825a80e49 Test case provided by: Co-authored-by: Kendall Condon --- lib/std/fs/File.zig | 18 +++++++++--------- lib/std/fs/test.zig | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 5413e89fa0..13293e94d7 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1242,7 +1242,7 @@ pub const Reader = struct { pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void { switch (r.mode) { .positional, .positional_reading => { - setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); + setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); }, .streaming, .streaming_reading => { if (posix.SEEK == void) { @@ -1251,7 +1251,7 @@ pub const Reader = struct { } const seek_err = r.seek_err orelse e: { if (posix.lseek_CUR(r.file.handle, offset)) |_| { - setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); + setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); return; } else |err| { r.seek_err = err; @@ -1275,16 +1275,16 @@ pub const Reader = struct { pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void { switch (r.mode) { .positional, .positional_reading => { - setPosAdjustingBuffer(r, offset); + setLogicalPos(r, offset); }, .streaming, .streaming_reading => { - if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos)); + if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - logicalPos(r))); if (r.seek_err) |err| return err; posix.lseek_SET(r.file.handle, offset) catch |err| { r.seek_err = err; return err; }; - setPosAdjustingBuffer(r, offset); + setLogicalPos(r, offset); }, .failure => return r.seek_err.?, } @@ -1294,7 +1294,7 @@ pub const Reader = struct { return r.pos - r.interface.bufferedLen(); } - fn setPosAdjustingBuffer(r: *Reader, offset: u64) void { + fn setLogicalPos(r: *Reader, offset: u64) void { const logical_pos = logicalPos(r); if (offset < logical_pos or offset >= r.pos) { r.interface.seek = 0; @@ -1855,7 +1855,7 @@ pub const Writer = struct { return error.EndOfStream; } const consumed = io_w.consume(@intCast(sbytes)); - file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; + file_reader.seekBy(@intCast(consumed)) catch return error.ReadFailed; return consumed; } @@ -1916,7 +1916,7 @@ pub const Writer = struct { return error.EndOfStream; } const consumed = io_w.consume(@bitCast(len)); - file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed; + file_reader.seekBy(@intCast(consumed)) catch return error.ReadFailed; return consumed; } @@ -2049,7 +2049,7 @@ pub const Writer = struct { reader_buffered: []const u8, ) std.Io.Writer.FileError!usize { const n = try drain(io_w, &.{reader_buffered}, 1); - file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed; + file_reader.seekBy(@intCast(n)) catch return error.ReadFailed; return n; } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 60879a5ead..d20462a701 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -2145,3 +2145,34 @@ test "seekBy" { try testing.expectEqual(15, n); try testing.expectEqualStrings("t's test seekBy", buffer[0..15]); } + +test "File.Writer sendfile with buffered contents" { + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + { + try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" }); + const in = try tmp_dir.dir.openFile("a", .{}); + defer in.close(); + const out = try tmp_dir.dir.createFile("b", .{}); + defer out.close(); + + var in_buf: [2]u8 = undefined; + var in_r = in.reader(&in_buf); + _ = try in_r.getSize(); // Catch seeks past end by populating size + try in_r.interface.fill(2); + + var out_buf: [1]u8 = undefined; + var out_w = out.writerStreaming(&out_buf); + try out_w.interface.writeByte('a'); + try testing.expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited)); + try out_w.interface.flush(); + } + + var check = try tmp_dir.dir.openFile("b", .{}); + defer check.close(); + var check_buf: [4]u8 = undefined; + var check_r = check.reader(&check_buf); + try testing.expectEqualStrings("abcd", try check_r.interface.take(4)); + try testing.expectError(error.EndOfStream, check_r.interface.takeByte()); +} From 9ea4d9aa3bc184a1be1199f3b519796d417c7765 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Oct 2025 16:35:32 -0700 Subject: [PATCH 104/112] std: fix sendFileReading not accounting for buffer Related to 1d764c1fdf04829cec5974d82cec901825a80e49 Test case provided by: Co-authored-by: Kendall Condon --- lib/std/Io/Writer.zig | 37 ++++++-- lib/std/fs/File.zig | 192 +++++++++++++++++++++--------------------- 2 files changed, 127 insertions(+), 102 deletions(-) diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 707ed9cb94..2bb86fd219 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -917,10 +917,12 @@ pub fn sendFileHeader( return n; } -/// Asserts nonzero buffer capacity. +/// Asserts nonzero buffer capacity and nonzero `limit`. pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) FileReadingError!usize { + assert(limit != .nothing); const dest = limit.slice(try w.writableSliceGreedy(1)); - const n = try file_reader.read(dest); + const n = try file_reader.interface.readSliceShort(dest); + if (n == 0) return error.EndOfStream; w.advance(n); return n; } @@ -2655,7 +2657,8 @@ pub const Allocating = struct { if (additional == 0) return error.EndOfStream; list.ensureUnusedCapacity(gpa, limit.minInt64(additional)) catch return error.WriteFailed; const dest = limit.slice(list.unusedCapacitySlice()); - const n = try file_reader.read(dest); + const n = try file_reader.interface.readSliceShort(dest); + if (n == 0) return error.EndOfStream; list.items.len += n; return n; } @@ -2714,18 +2717,40 @@ test "allocating sendFile" { const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); defer file.close(); - var r_buffer: [256]u8 = undefined; + var r_buffer: [2]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); - try file_writer.interface.writeByte('h'); + try file_writer.interface.writeAll("abcd"); try file_writer.interface.flush(); var file_reader = file_writer.moveToReader(); try file_reader.seekTo(0); + try file_reader.interface.fill(2); var allocating: Writer.Allocating = .init(testing.allocator); defer allocating.deinit(); + try allocating.ensureUnusedCapacity(1); + try testing.expectEqual(4, allocating.writer.sendFileAll(&file_reader, .unlimited)); + try testing.expectEqualStrings("abcd", allocating.writer.buffered()); +} - _ = try file_reader.interface.streamRemaining(&allocating.writer); +test sendFileReading { + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + defer file.close(); + var r_buffer: [2]u8 = undefined; + var file_writer: std.fs.File.Writer = .init(file, &r_buffer); + try file_writer.interface.writeAll("abcd"); + try file_writer.interface.flush(); + + var file_reader = file_writer.moveToReader(); + try file_reader.seekTo(0); + try file_reader.interface.fill(2); + + var w_buffer: [1]u8 = undefined; + var discarding: Writer.Discarding = .init(&w_buffer); + try testing.expectEqual(4, discarding.writer.sendFileReadingAll(&file_reader, .unlimited)); } test writeStruct { diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 13293e94d7..f59344e7f6 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1322,13 +1322,15 @@ pub const Reader = struct { }, .positional_reading => { const dest = limit.slice(try w.writableSliceGreedy(1)); - const n = try readPositional(r, dest); + var data: [1][]u8 = .{dest}; + const n = try readVecPositional(r, &data); w.advance(n); return n; }, .streaming_reading => { const dest = limit.slice(try w.writableSliceGreedy(1)); - const n = try readStreaming(r, dest); + var data: [1][]u8 = .{dest}; + const n = try readVecStreaming(r, &data); w.advance(n); return n; }, @@ -1339,94 +1341,100 @@ pub const Reader = struct { fn readVec(io_reader: *std.Io.Reader, data: [][]u8) std.Io.Reader.Error!usize { const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); switch (r.mode) { - .positional, .positional_reading => { - if (is_windows) { - // Unfortunately, `ReadFileScatter` cannot be used since it - // requires page alignment. - if (io_reader.seek == io_reader.end) { - io_reader.seek = 0; - io_reader.end = 0; - } - const first = data[0]; - if (first.len >= io_reader.buffer.len - io_reader.end) { - return readPositional(r, first); - } else { - io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]); - return 0; - } - } - var iovecs_buffer: [max_buffers_len]posix.iovec = undefined; - const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data); - const dest = iovecs_buffer[0..dest_n]; - assert(dest[0].len > 0); - const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) { - error.Unseekable => { - r.mode = r.mode.toStreaming(); - const pos = r.pos; - if (pos != 0) { - r.pos = 0; - r.seekBy(@intCast(pos)) catch { - r.mode = .failure; - return error.ReadFailed; - }; - } - return 0; - }, - else => |e| { - r.err = e; - return error.ReadFailed; - }, - }; - if (n == 0) { - r.size = r.pos; - return error.EndOfStream; - } - r.pos += n; - if (n > data_size) { - io_reader.end += n - data_size; - return data_size; - } - return n; - }, - .streaming, .streaming_reading => { - if (is_windows) { - // Unfortunately, `ReadFileScatter` cannot be used since it - // requires page alignment. - if (io_reader.seek == io_reader.end) { - io_reader.seek = 0; - io_reader.end = 0; - } - const first = data[0]; - if (first.len >= io_reader.buffer.len - io_reader.end) { - return readStreaming(r, first); - } else { - io_reader.end += try readStreaming(r, io_reader.buffer[io_reader.end..]); - return 0; - } - } - var iovecs_buffer: [max_buffers_len]posix.iovec = undefined; - const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data); - const dest = iovecs_buffer[0..dest_n]; - assert(dest[0].len > 0); - const n = posix.readv(r.file.handle, dest) catch |err| { - r.err = err; - return error.ReadFailed; - }; - if (n == 0) { - r.size = r.pos; - return error.EndOfStream; - } - r.pos += n; - if (n > data_size) { - io_reader.end += n - data_size; - return data_size; - } - return n; - }, + .positional, .positional_reading => return readVecPositional(r, data), + .streaming, .streaming_reading => return readVecStreaming(r, data), .failure => return error.ReadFailed, } } + fn readVecPositional(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize { + const io_reader = &r.interface; + if (is_windows) { + // Unfortunately, `ReadFileScatter` cannot be used since it + // requires page alignment. + if (io_reader.seek == io_reader.end) { + io_reader.seek = 0; + io_reader.end = 0; + } + const first = data[0]; + if (first.len >= io_reader.buffer.len - io_reader.end) { + return readPositional(r, first); + } else { + io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]); + return 0; + } + } + var iovecs_buffer: [max_buffers_len]posix.iovec = undefined; + const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data); + const dest = iovecs_buffer[0..dest_n]; + assert(dest[0].len > 0); + const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) { + error.Unseekable => { + r.mode = r.mode.toStreaming(); + const pos = r.pos; + if (pos != 0) { + r.pos = 0; + r.seekBy(@intCast(pos)) catch { + r.mode = .failure; + return error.ReadFailed; + }; + } + return 0; + }, + else => |e| { + r.err = e; + return error.ReadFailed; + }, + }; + if (n == 0) { + r.size = r.pos; + return error.EndOfStream; + } + r.pos += n; + if (n > data_size) { + io_reader.end += n - data_size; + return data_size; + } + return n; + } + + fn readVecStreaming(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize { + const io_reader = &r.interface; + if (is_windows) { + // Unfortunately, `ReadFileScatter` cannot be used since it + // requires page alignment. + if (io_reader.seek == io_reader.end) { + io_reader.seek = 0; + io_reader.end = 0; + } + const first = data[0]; + if (first.len >= io_reader.buffer.len - io_reader.end) { + return readStreaming(r, first); + } else { + io_reader.end += try readStreaming(r, io_reader.buffer[io_reader.end..]); + return 0; + } + } + var iovecs_buffer: [max_buffers_len]posix.iovec = undefined; + const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data); + const dest = iovecs_buffer[0..dest_n]; + assert(dest[0].len > 0); + const n = posix.readv(r.file.handle, dest) catch |err| { + r.err = err; + return error.ReadFailed; + }; + if (n == 0) { + r.size = r.pos; + return error.EndOfStream; + } + r.pos += n; + if (n > data_size) { + io_reader.end += n - data_size; + return data_size; + } + return n; + } + fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize { const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); const file = r.file; @@ -1493,7 +1501,7 @@ pub const Reader = struct { } } - pub fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize { + fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize { const n = r.file.pread(dest, r.pos) catch |err| switch (err) { error.Unseekable => { r.mode = r.mode.toStreaming(); @@ -1520,7 +1528,7 @@ pub const Reader = struct { return n; } - pub fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize { + fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize { const n = r.file.read(dest) catch |err| { r.err = err; return error.ReadFailed; @@ -1533,14 +1541,6 @@ pub const Reader = struct { return n; } - pub fn read(r: *Reader, dest: []u8) std.Io.Reader.Error!usize { - switch (r.mode) { - .positional, .positional_reading => return readPositional(r, dest), - .streaming, .streaming_reading => return readStreaming(r, dest), - .failure => return error.ReadFailed, - } - } - pub fn atEnd(r: *Reader) bool { // Even if stat fails, size is set when end is encountered. const size = r.size orelse return false; From f661ab6c36c0366331451986a29ee14ac05fd8eb Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 6 Sep 2025 10:23:41 +0100 Subject: [PATCH 105/112] std.Io.Reader: fix delimiter bugs Fix `takeDelimiter` and `takeDelimiterExclusive` tossing too many bytes (#25132) Also add/improve test coverage for all delimiter and sentinel methods, update usages of `takeDelimiterExclusive` to not rely on the fixed bug, tweak a handful of doc comments, and slightly simplify some logic. I have not fixed #24950 in this commit because I am a little less certain about the appropriate solution there. Resolves: #25132 Co-authored-by: Andrew Kelley --- lib/std/Io/Reader.zig | 75 +++++++++++++++++++++++++++++++----- lib/std/net.zig | 5 ++- lib/std/zig/system/linux.zig | 5 +-- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index b62e81cd13..68957a8902 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -449,7 +449,6 @@ pub fn readVecAll(r: *Reader, data: [][]u8) Error!void { /// is returned instead. /// /// See also: -/// * `peek` /// * `toss` pub fn peek(r: *Reader, n: usize) Error![]u8 { try r.fill(n); @@ -700,7 +699,7 @@ pub const DelimiterError = error{ }; /// Returns a slice of the next bytes of buffered data from the stream until -/// `sentinel` is found, advancing the seek position. +/// `sentinel` is found, advancing the seek position past the sentinel. /// /// Returned slice has a sentinel. /// @@ -733,7 +732,7 @@ pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel } /// Returns a slice of the next bytes of buffered data from the stream until -/// `delimiter` is found, advancing the seek position. +/// `delimiter` is found, advancing the seek position past the delimiter. /// /// Returned slice includes the delimiter as the last byte. /// @@ -786,7 +785,8 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { } /// Returns a slice of the next bytes of buffered data from the stream until -/// `delimiter` is found, advancing the seek position. +/// `delimiter` is found, advancing the seek position up to (but not past) +/// the delimiter. /// /// Returned slice excludes the delimiter. End-of-stream is treated equivalent /// to a delimiter, unless it would result in a length 0 return value, in which @@ -800,20 +800,44 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { /// Invalidates previously returned values from `peek`. /// /// See also: +/// * `takeDelimiter` /// * `takeDelimiterInclusive` /// * `peekDelimiterExclusive` pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { - const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) { + const result = try r.peekDelimiterExclusive(delimiter); + r.toss(result.len); + return result; +} + +/// Returns a slice of the next bytes of buffered data from the stream until +/// `delimiter` is found, advancing the seek position past the delimiter. +/// +/// Returned slice excludes the delimiter. End-of-stream is treated equivalent +/// to a delimiter, unless it would result in a length 0 return value, in which +/// case `null` is returned instead. +/// +/// If the delimiter is not found within a number of bytes matching the +/// capacity of this `Reader`, `error.StreamTooLong` is returned. In +/// such case, the stream state is unmodified as if this function was never +/// called. +/// +/// Invalidates previously returned values from `peek`. +/// +/// See also: +/// * `takeDelimiterInclusive` +/// * `takeDelimiterExclusive` +pub fn takeDelimiter(r: *Reader, delimiter: u8) error{ ReadFailed, StreamTooLong }!?[]u8 { + const inclusive = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) { error.EndOfStream => { const remaining = r.buffer[r.seek..r.end]; - if (remaining.len == 0) return error.EndOfStream; + if (remaining.len == 0) return null; r.toss(remaining.len); return remaining; }, else => |e| return e, }; - r.toss(result.len); - return result[0 .. result.len - 1]; + r.toss(inclusive.len); + return inclusive[0 .. inclusive.len - 1]; } /// Returns a slice of the next bytes of buffered data from the stream until @@ -1336,6 +1360,9 @@ test peekSentinel { var r: Reader = .fixed("ab\nc"); try testing.expectEqualStrings("ab", try r.peekSentinel('\n')); try testing.expectEqualStrings("ab", try r.peekSentinel('\n')); + r.toss(3); + try testing.expectError(error.EndOfStream, r.peekSentinel('\n')); + try testing.expectEqualStrings("c", try r.peek(1)); } test takeDelimiterInclusive { @@ -1350,22 +1377,52 @@ test peekDelimiterInclusive { try testing.expectEqualStrings("ab\n", try r.peekDelimiterInclusive('\n')); r.toss(3); try testing.expectError(error.EndOfStream, r.peekDelimiterInclusive('\n')); + try testing.expectEqualStrings("c", try r.peek(1)); } test takeDelimiterExclusive { var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab", try r.takeDelimiterExclusive('\n')); + try testing.expectEqualStrings("", try r.takeDelimiterExclusive('\n')); + try testing.expectEqualStrings("", try r.takeDelimiterExclusive('\n')); + try testing.expectEqualStrings("\n", try r.take(1)); + try testing.expectEqualStrings("c", try r.takeDelimiterExclusive('\n')); try testing.expectError(error.EndOfStream, r.takeDelimiterExclusive('\n')); } test peekDelimiterExclusive { var r: Reader = .fixed("ab\nc"); + try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n')); try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n')); - r.toss(3); + r.toss(2); + try testing.expectEqualStrings("", try r.peekDelimiterExclusive('\n')); + try testing.expectEqualStrings("\n", try r.take(1)); + try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n')); try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n')); + r.toss(1); + try testing.expectError(error.EndOfStream, r.peekDelimiterExclusive('\n')); +} + +test takeDelimiter { + var r: Reader = .fixed("ab\nc\n\nd"); + try testing.expectEqualStrings("ab", (try r.takeDelimiter('\n')).?); + try testing.expectEqualStrings("c", (try r.takeDelimiter('\n')).?); + try testing.expectEqualStrings("", (try r.takeDelimiter('\n')).?); + try testing.expectEqualStrings("d", (try r.takeDelimiter('\n')).?); + try testing.expectEqual(null, try r.takeDelimiter('\n')); + try testing.expectEqual(null, try r.takeDelimiter('\n')); + + r = .fixed("ab\nc\n\nd\n"); // one trailing newline does not affect behavior + try testing.expectEqualStrings("ab", (try r.takeDelimiter('\n')).?); + try testing.expectEqualStrings("c", (try r.takeDelimiter('\n')).?); + try testing.expectEqualStrings("", (try r.takeDelimiter('\n')).?); + try testing.expectEqualStrings("d", (try r.takeDelimiter('\n')).?); + try testing.expectEqual(null, try r.takeDelimiter('\n')); + try testing.expectEqual(null, try r.takeDelimiter('\n')); } test streamDelimiter { diff --git a/lib/std/net.zig b/lib/std/net.zig index 083bda85d5..03383b0f89 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1393,7 +1393,7 @@ fn parseHosts( br: *Io.Reader, ) error{ OutOfMemory, ReadFailed }!void { while (true) { - const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) { + const line = br.takeDelimiter('\n') catch |err| switch (err) { error.StreamTooLong => { // Skip lines that are too long. _ = br.discardDelimiterInclusive('\n') catch |e| switch (e) { @@ -1403,7 +1403,8 @@ fn parseHosts( continue; }, error.ReadFailed => return error.ReadFailed, - error.EndOfStream => break, + } orelse { + break; // end of stream }; var split_it = mem.splitScalar(u8, line, '#'); const no_comment_line = split_it.first(); diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index df70e71f5b..c0fe9a9ceb 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -358,14 +358,11 @@ fn CpuinfoParser(comptime impl: anytype) type { return struct { fn parse(arch: Target.Cpu.Arch, reader: *std.Io.Reader) !?Target.Cpu { var obj: impl = .{}; - while (reader.takeDelimiterExclusive('\n')) |line| { + while (try reader.takeDelimiter('\n')) |line| { const colon_pos = mem.indexOfScalar(u8, line, ':') orelse continue; const key = mem.trimEnd(u8, line[0..colon_pos], " \t"); const value = mem.trimStart(u8, line[colon_pos + 1 ..], " \t"); if (!try obj.line_hook(key, value)) break; - } else |err| switch (err) { - error.EndOfStream => {}, - else => |e| return e, } return obj.finalize(arch); } From 6682ff2e85dc528ad2552b23fd540423b0c456bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=27vesim=27=20Kuli=C5=84ski?= Date: Sun, 14 Sep 2025 17:26:55 +0200 Subject: [PATCH 106/112] std: std.fs.File fix sendFile with buffered data fixes #25196 Co-authored-by: Andrew Kelley --- lib/std/fs/File.zig | 2 +- lib/std/fs/test.zig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index f59344e7f6..44a137281d 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1783,7 +1783,7 @@ pub const Writer = struct { ) std.Io.Writer.FileError!usize { const reader_buffered = file_reader.interface.buffered(); if (reader_buffered.len >= @intFromEnum(limit)) - return sendFileBuffered(io_w, file_reader, reader_buffered); + return sendFileBuffered(io_w, file_reader, limit.slice(reader_buffered)); const writer_buffered = io_w.buffered(); const file_limit = @intFromEnum(limit) - reader_buffered.len; const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index d20462a701..bbfd98b1a0 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1515,6 +1515,41 @@ test "sendfile" { try testing.expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]); } +test "sendfile with buffered data" { + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + try tmp.dir.makePath("os_test_tmp"); + + var dir = try tmp.dir.openDir("os_test_tmp", .{}); + defer dir.close(); + + var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); + defer src_file.close(); + + try src_file.writeAll("AAAABBBB"); + + var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); + defer dest_file.close(); + + var src_buffer: [32]u8 = undefined; + var file_reader = src_file.reader(&src_buffer); + + try file_reader.seekTo(0); + try file_reader.interface.fill(8); + + var fallback_buffer: [32]u8 = undefined; + var file_writer = dest_file.writer(&fallback_buffer); + + try std.testing.expectEqual(4, try file_writer.interface.sendFileAll(&file_reader, .limited(4))); + + var written_buf: [8]u8 = undefined; + const amt = try dest_file.preadAll(&written_buf, 0); + + try std.testing.expectEqual(4, amt); + try std.testing.expectEqualSlices(u8, "AAAA", written_buf[0..amt]); +} + test "copyRangeAll" { var tmp = tmpDir(.{}); defer tmp.cleanup(); From 52730f3f2191aa242b030b9c5f3ca33a7f4fea88 Mon Sep 17 00:00:00 2001 From: whatisaphone Date: Mon, 1 Sep 2025 10:23:49 -0400 Subject: [PATCH 107/112] Fix Reader.Limited end of stream conditions --- lib/std/Io/Reader/Limited.zig | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/std/Io/Reader/Limited.zig b/lib/std/Io/Reader/Limited.zig index c2e9ee2f49..5d87200869 100644 --- a/lib/std/Io/Reader/Limited.zig +++ b/lib/std/Io/Reader/Limited.zig @@ -27,6 +27,7 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited { fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { const l: *Limited = @fieldParentPtr("interface", r); + if (l.remaining == .nothing) return error.EndOfStream; const combined_limit = limit.min(l.remaining); const n = try l.unlimited.stream(w, combined_limit); l.remaining = l.remaining.subtract(n).?; @@ -51,8 +52,51 @@ test stream { fn discard(r: *Reader, limit: Limit) Reader.Error!usize { const l: *Limited = @fieldParentPtr("interface", r); + if (l.remaining == .nothing) return error.EndOfStream; const combined_limit = limit.min(l.remaining); const n = try l.unlimited.discard(combined_limit); l.remaining = l.remaining.subtract(n).?; return n; } + +test "end of stream, read, hit limit exactly" { + var f: Reader = .fixed("i'm dying"); + var l = f.limited(.limited(4), &.{}); + const r = &l.interface; + + var buf: [2]u8 = undefined; + try r.readSliceAll(&buf); + try r.readSliceAll(&buf); + try std.testing.expectError(error.EndOfStream, l.interface.readSliceAll(&buf)); +} + +test "end of stream, read, hit limit after partial read" { + var f: Reader = .fixed("i'm dying"); + var l = f.limited(.limited(5), &.{}); + const r = &l.interface; + + var buf: [2]u8 = undefined; + try r.readSliceAll(&buf); + try r.readSliceAll(&buf); + try std.testing.expectError(error.EndOfStream, l.interface.readSliceAll(&buf)); +} + +test "end of stream, discard, hit limit exactly" { + var f: Reader = .fixed("i'm dying"); + var l = f.limited(.limited(4), &.{}); + const r = &l.interface; + + try r.discardAll(2); + try r.discardAll(2); + try std.testing.expectError(error.EndOfStream, l.interface.discardAll(2)); +} + +test "end of stream, discard, hit limit after partial read" { + var f: Reader = .fixed("i'm dying"); + var l = f.limited(.limited(5), &.{}); + const r = &l.interface; + + try r.discardAll(2); + try r.discardAll(2); + try std.testing.expectError(error.EndOfStream, l.interface.discardAll(2)); +} From 6a25bfc4751ab959f32390963d902bec731129d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Oct 2025 19:14:01 -0700 Subject: [PATCH 108/112] std.Io.Reader: rework peekDelimiterInclusive Now it's based on calling fillMore rather than an illegal aliased stream into the Reader buffer. This commit also includes a disambiguation block inspired by #25162. If `StreamTooLong` was added to `RebaseError` then this logic could be replaced by removing the exit condition from the while loop. That error code would represent when `buffer` capacity is too small for an operation, replacing the current use of asserts. --- lib/std/Io/Reader.zig | 49 +++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index 68957a8902..9ebfdebc87 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -760,28 +760,37 @@ pub fn takeDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { /// * `peekDelimiterExclusive` /// * `takeDelimiterInclusive` pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { - const buffer = r.buffer[0..r.end]; - const seek = r.seek; - if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |delimiter_index| { - @branchHint(.likely); - return buffer[seek .. delimiter_index + 1]; - } - // TODO take a parameter for max search length rather than relying on buffer capacity - try rebase(r, r.buffer.len); - while (r.buffer.len - r.end != 0) { - const existing_buffered_len = r.end - r.seek; - const end_cap = r.buffer[r.end..]; - var writer: Writer = .fixed(end_cap); - const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) { - error.WriteFailed => unreachable, - else => |e| return e, - }; - r.end += n; - if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], r.seek + existing_buffered_len, delimiter)) |delimiter_index| { - return r.buffer[r.seek .. delimiter_index + 1]; + { + const contents = r.buffer[0..r.end]; + const seek = r.seek; + if (std.mem.findScalarPos(u8, contents, seek, delimiter)) |end| { + @branchHint(.likely); + return contents[seek .. end + 1]; } } - return error.StreamTooLong; + while (true) { + const content_len = r.end - r.seek; + if (r.buffer.len - content_len == 0) break; + try fillMore(r); + const seek = r.seek; + const contents = r.buffer[0..r.end]; + if (std.mem.findScalarPos(u8, contents, seek + content_len, delimiter)) |end| { + return contents[seek .. end + 1]; + } + } + // It might or might not be end of stream. There is no more buffer space + // left to disambiguate. If `StreamTooLong` was added to `RebaseError` then + // this logic could be replaced by removing the exit condition from the + // above while loop. That error code would represent when `buffer` capacity + // is too small for an operation, replacing the current use of asserts. + var failing_writer = Writer.failing; + while (r.vtable.stream(r, &failing_writer, .limited(1))) |n| { + assert(n == 0); + } else |err| switch (err) { + error.WriteFailed => return error.StreamTooLong, + error.ReadFailed => |e| return e, + error.EndOfStream => |e| return e, + } } /// Returns a slice of the next bytes of buffered data from the stream until From 8ccca8472a06b7dfbe5478da86769056ee1604ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Oct 2025 11:58:44 -0700 Subject: [PATCH 109/112] std.fs.File.Reader.seekTo: fix one more logical position bug --- lib/std/fs/File.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 44a137281d..420cf43aa6 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1278,7 +1278,8 @@ pub const Reader = struct { setLogicalPos(r, offset); }, .streaming, .streaming_reading => { - if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - logicalPos(r))); + const logical_pos = logicalPos(r); + if (offset >= logical_pos) return Reader.seekBy(r, @intCast(offset - logical_pos)); if (r.seek_err) |err| return err; posix.lseek_SET(r.file.handle, offset) catch |err| { r.seek_err = err; From ca59ad658f25e0334e95a3044ca8813d0fff41c3 Mon Sep 17 00:00:00 2001 From: marximimus <40419155+marximimus@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:54:56 +0200 Subject: [PATCH 110/112] std.crypto.tls.Client: fix infinite loop in std.Io.Writer.writeAll --- lib/std/crypto/tls/Client.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index 8cfa942399..b697d624fa 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -942,7 +942,6 @@ fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize if (prepared.cleartext_len < buf.len) break :done; } for (data[0 .. data.len - 1]) |buf| { - if (buf.len < min_buffer_len) break :done; const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data); total_clear += prepared.cleartext_len; ciphertext_end += prepared.ciphertext_end; @@ -950,7 +949,6 @@ fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize } const buf = data[data.len - 1]; for (0..splat) |_| { - if (buf.len < min_buffer_len) break :done; const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data); total_clear += prepared.cleartext_len; ciphertext_end += prepared.ciphertext_end; From f559107fee1ff6a0bc8de484cb1e0ee1953466c1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Oct 2025 12:46:55 -0700 Subject: [PATCH 111/112] use older std.mem function names Cherry-picked bug fix was using renamed functions --- lib/std/Io/Reader.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index 9ebfdebc87..58e413bb9b 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -763,7 +763,7 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { { const contents = r.buffer[0..r.end]; const seek = r.seek; - if (std.mem.findScalarPos(u8, contents, seek, delimiter)) |end| { + if (std.mem.indexOfScalarPos(u8, contents, seek, delimiter)) |end| { @branchHint(.likely); return contents[seek .. end + 1]; } @@ -774,7 +774,7 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { try fillMore(r); const seek = r.seek; const contents = r.buffer[0..r.end]; - if (std.mem.findScalarPos(u8, contents, seek + content_len, delimiter)) |end| { + if (std.mem.indexOfScalarPos(u8, contents, seek + content_len, delimiter)) |end| { return contents[seek .. end + 1]; } } From e4cbd752c8c05f131051f8c873cff7823177d7d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Oct 2025 20:45:47 -0700 Subject: [PATCH 112/112] Release 0.15.2 --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index bb3e00e656..e231a4e945 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -317,7 +317,7 @@ 0.12.0 | 0.13.0 | 0.14.1 | - 0.15.1 | + 0.15.2 | master