commit 87561450904853c86bcbd2d487f1f221cfd7d954 (tree)
parent 53790d5aebd98bb5edf98770634c31818bb8c060
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Sat, 14 Feb 2026 01:33:08 +0200
Merge tag '0.15.2' into zig0-0.15.2
Release 0.15.2
Diffstat:
98 files changed, 2475 insertions(+), 1134 deletions(-)
diff --git a/.github/workflows/ci-pr-riscv64-linux.yaml b/.github/workflows/ci-pr-riscv64-linux.yaml
@@ -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
@@ -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:
diff --git 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/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
diff --git a/build.zig b/build.zig
@@ -33,7 +33,7 @@ const zig0_cflags = &[_][]const u8{
};
const zig0_compilers = &[_][]const u8{ "zig", "clang", "gcc", "tcc" };
-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 {
@@ -296,15 +296,15 @@ pub fn build(b: *std.Build) !void {
2 => {
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
var it = mem.splitScalar(u8, git_describe, '-');
- //const tagged_ancestor = it.first();
+ const tagged_ancestor = it.first();
const commit_height = it.next().?;
const commit_id = it.next().?;
- //const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
- //if (zig_version.order(ancestor_ver) != .gt) {
- // std.debug.print("Zig version '{f}' must be greater than tagged ancestor '{f}'\n", .{ zig_version, ancestor_ver });
- // std.process.exit(1);
- //}
+ const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
+ if (zig_version.order(ancestor_ver) != .gt) {
+ std.debug.print("Zig version '{f}' must be greater than tagged ancestor '{f}'\n", .{ zig_version, ancestor_ver });
+ std.process.exit(1);
+ }
// Check that the commit hash is prefixed with a 'g' (a Git convention).
if (commit_id.len < 1 or commit_id[0] != 'g') {
diff --git a/doc/langref.html.in b/doc/langref.html.in
@@ -317,7 +317,7 @@
<a href="https://ziglang.org/documentation/0.12.0/">0.12.0</a> |
<a href="https://ziglang.org/documentation/0.13.0/">0.13.0</a> |
<a href="https://ziglang.org/documentation/0.14.1/">0.14.1</a> |
- <a href="https://ziglang.org/documentation/0.15.1/">0.15.1</a> |
+ <a href="https://ziglang.org/documentation/0.15.2/">0.15.2</a> |
master
</nav>
<nav aria-labelledby="table-of-contents">
@@ -4888,8 +4888,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_open|@fieldParentPtr#}
<pre>{#syntax#}@fieldParentPtr(comptime field_name: []const u8, field_ptr: *T) anytype{#endsyntax#}</pre>
<p>
- 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.
</p>
<p>
If {#syntax#}field_ptr{#endsyntax#} does not point to the {#syntax#}field_name{#endsyntax#} field of an instance of
@@ -6278,10 +6278,6 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<li>Are you linking libc? In this case, {#syntax#}std.heap.c_allocator{#endsyntax#} is likely
the right choice, at least for your main allocator.</li>
<li>
- Need to use the same allocator in multiple threads? Use one of your choice
- wrapped around {#syntax#}std.heap.ThreadSafeAllocator{#endsyntax#}
- </li>
- <li>
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#}.
</li>
@@ -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
</li>
<li>
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.
</li>
<li>
- 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.
+ </li>
+ <li>
+ You can also consider implementing an allocator.
</li>
</ol>
{#header_close#}
@@ -6355,17 +6355,6 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<p>TODO: thread local variables</p>
{#header_close#}
- {#header_open|Implementing an Allocator#}
- <p>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#}.
- </p>
- <p>
- There are many example allocators to look at for inspiration. Look at std/heap.zig and
- {#syntax#}std.heap.GeneralPurposeAllocator{#endsyntax#}.
- </p>
- {#header_close#}
-
{#header_open|Heap Allocation Failure#}
<p>
Many programming languages choose to handle the possibility of heap allocation failure by
diff --git 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
diff --git 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("<code{s}>", .{
+ try out.print(gpa, "<code{s}>", .{
@as([]const u8, if (focused) " class=\"status-running\"" else ""),
});
try sli.appendPath(out);
- try out.writer(gpa).print(":{d}:{d} </code><button class=\"linkish\" onclick=\"wasm_exports.fuzzSelectSli({d});\">View</button>", .{
+ try out.print(gpa, ":{d}:{d} </code><button class=\"linkish\" onclick=\"wasm_exports.fuzzSelectSli({d});\">View</button>", .{
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, "<li>");
diff --git 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(
\\<tr>
\\ <th scope="row"><code>{f}</code></th>
\\ <td>{D}</td>
@@ -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(
\\<tr><td colspan="4">{d} more rows omitted</td></tr>
\\
, .{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(
\\<tr>
\\ <th scope="row"><code>{f}</code></th>
\\ <th scope="row"><code>{f}</code></th>
@@ -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(
\\<tr><td colspan="6">{d} more rows omitted</td></tr>
\\
, .{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,
);
}
diff --git 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
@@ -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()`).
diff --git 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,
diff --git 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 });
}
diff --git 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
diff --git 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 <bits/inet-fortified.h>
#endif
+#endif
__END_DECLS
diff --git a/lib/libc/musl/src/fenv/loongarch64/fenv-sf.c 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/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),
+ } },
};
}
};
diff --git 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, &.{
"@",
diff --git 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()) {
diff --git 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);
}
}
diff --git 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;
}
@@ -448,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);
@@ -699,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.
///
@@ -732,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.
///
@@ -760,31 +760,42 @@ 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)) |end| {
- @branchHint(.likely);
- return buffer[seek .. end + 1];
+ {
+ const contents = r.buffer[0..r.end];
+ const seek = r.seek;
+ if (std.mem.indexOfScalarPos(u8, contents, seek, delimiter)) |end| {
+ @branchHint(.likely);
+ return contents[seek .. end + 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 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, end_cap[0..n], 0, delimiter)) |end| {
- return r.buffer[0 .. r.end - n + end + 1];
+ 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.indexOfScalarPos(u8, contents, seek + content_len, delimiter)) |end| {
+ return contents[seek .. end + 1];
}
}
- return error.StreamTooLong;
+ // 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
-/// `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
@@ -798,20 +809,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
@@ -1334,6 +1369,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 {
@@ -1348,22 +1386,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 {
@@ -1533,6 +1601,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;
@@ -1642,6 +1722,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/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));
+}
diff --git 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());
+}
+
+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();
- _ = try file_reader.interface.streamRemaining(&allocating.writer);
+ 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/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,
diff --git 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,
}
}
diff --git 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;
@@ -3091,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;
@@ -5685,6 +5724,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) {
@@ -6664,7 +6720,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) {
diff --git 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);
+}
diff --git 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,
};
@@ -929,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;
@@ -937,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;
diff --git 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;
}
diff --git 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;
}
diff --git 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,17 @@ 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));
+ 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;
return err;
};
- setPosAdjustingBuffer(r, offset);
+ setLogicalPos(r, offset);
},
.failure => return r.seek_err.?,
}
@@ -1294,7 +1295,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;
@@ -1322,13 +1323,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,92 +1342,98 @@ 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;
+ .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;
- },
- };
- 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;
+ return 0;
},
- .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 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.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;
+ else => |e| {
+ r.err = e;
+ return error.ReadFailed;
},
- .failure => 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 {
@@ -1493,7 +1502,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 +1529,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 +1542,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;
@@ -1783,7 +1784,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));
@@ -1855,7 +1856,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 +1917,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;
}
@@ -1967,7 +1968,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: {
@@ -2049,7 +2050,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
@@ -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();
@@ -2145,3 +2180,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());
+}
diff --git 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;
@@ -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();
}
diff --git 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;
}
diff --git 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))));
}
diff --git 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/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
diff --git 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 {
@@ -4484,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),
diff --git 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.
///
diff --git 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/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);
}
@@ -9799,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/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)))),
};
}
diff --git 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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .memory = true, .cr0 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true });
}
pub fn clone() callconv(.naked) usize {
@@ -193,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
@@ -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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .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 });
+ : .{ .memory = true, .cr0 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .ctr = true, .xer = true });
}
pub fn clone() callconv(.naked) usize {
@@ -178,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 {
diff --git 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;
diff --git 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|
diff --git 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.
diff --git 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),
diff --git 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" {
diff --git 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;
@@ -1762,7 +1763,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{
diff --git 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");
}
};
diff --git 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]);
+ }
+}
diff --git 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,
+ };
+ }
+};
diff --git 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]));
diff --git 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" },
@@ -109,7 +110,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",
@@ -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,
};
}
diff --git 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);
}
diff --git 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;
}
diff --git 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 });
}
}
diff --git 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;
@@ -3659,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,
@@ -3707,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),
},
@@ -3750,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]));
@@ -6836,10 +6848,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 => {},
@@ -7129,6 +7137,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{
@@ -8086,7 +8098,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) {
diff --git 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,17 +1417,27 @@ const Local = struct {
};
}
+ /// A list of offsets into `string_bytes` for each string.
+ pub fn getMutableStrings(local: *Local, gpa: Allocator) Strings.Mutable {
+ return .{
+ .gpa = gpa,
+ .arena = &local.mutate.arena,
+ .mutate = &local.mutate.strings,
+ .list = &local.shared.strings,
+ };
+ }
+
/// 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.
- pub fn getMutableStrings(local: *Local, gpa: Allocator) Strings.Mutable {
+ /// `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.strings,
- .list = &local.shared.strings,
+ .mutate = &local.mutate.string_bytes,
+ .list = &local.shared.string_bytes,
};
}
@@ -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/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,
diff --git 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) {
@@ -25292,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);
@@ -25302,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(.{
@@ -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(
@@ -34958,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", .{});
}
@@ -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/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/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);
}
@@ -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;
@@ -2222,7 +2228,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.
diff --git 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);
@@ -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
@@ -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.?;
@@ -1571,7 +1585,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 +1595,17 @@ 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);
+ 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
const was_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
@@ -2443,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) {
@@ -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);
diff --git 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;
@@ -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 = .{ .reg = .xmm0 } },
.{ .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 } },
+ .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", ._, ._, ._ },
@@ -31490,7 +31489,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 +31813,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 +32174,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 +55061,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 +55360,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 +55689,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, ._, ._ },
@@ -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), ._, ._ },
@@ -86773,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 => {},
@@ -90325,7 +90587,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 +90609,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 +90624,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 = &.{
+ .{ ._, .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 = &.{
- .{ ._, .vp_b, .ackusw, .dst0y, .src0y, .dst0y, ._ },
+ .{ ._, .v_f128, .extract, .dst0x, .src0y, .ui(1), ._ },
+ .{ ._, .vp_b, .ackusw, .dst0x, .src0x, .dst0x, ._ },
} },
}, .{
.required_features = .{ .slow_incdec, null, null, null },
@@ -90449,7 +90737,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 +90761,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 +90777,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 = &.{
+ .{ ._, .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 = &.{
- .{ ._, .vp_w, .ackssd, .dst0y, .src0y, .dst0y, ._ },
- .{ ._, .vp_b, .ackssw, .dst0y, .dst0y, .dst0y, ._ },
+ .{ ._, .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 = &.{
+ .{ ._, .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 = &.{
- .{ ._, .vp_w, .ackusd, .dst0y, .src0y, .dst0y, ._ },
- .{ ._, .vp_b, .ackusw, .dst0y, .dst0y, .dst0y, ._ },
+ .{ ._, .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 +91039,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 +91061,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 +91076,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 = &.{
+ .{ ._, .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 = &.{
- .{ ._, .vp_w, .ackssd, .dst0y, .src0y, .dst0y, ._ },
+ .{ ._, .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 = &.{
+ .{ ._, .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 = &.{
- .{ ._, .vp_w, .ackusd, .dst0y, .src0y, .dst0y, ._ },
+ .{ ._, .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 +92756,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", ._, ._, ._ },
@@ -100782,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 },
@@ -100803,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 },
@@ -165240,7 +165582,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", ._, ._, ._ },
@@ -174400,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;
@@ -174555,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,
@@ -174776,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;
@@ -180076,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);
@@ -180127,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
@@ -180149,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 };
}
@@ -180174,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))),
} },
@@ -180192,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,
} },
@@ -180203,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,
} },
@@ -180285,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})
@@ -180338,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',
@@ -180355,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 }
@@ -184633,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)),
@@ -186940,6 +186994,10 @@ const Temp = struct {
assert(src_regs.len - part_index == std.math.divCeil(u32, src_abi_size, 8) catch unreachable);
break :part_ty .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();
@@ -192076,6 +192134,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,
@@ -192178,6 +192238,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
@@ -193171,7 +193247,7 @@ const Select = struct {
ref: Ref,
scale: Memory.Scale = .@"1",
} = .{ .ref = .none },
- unused: u3 = 0,
+ unused: u2 = 0,
},
imm: i32 = 0,
@@ -193184,9 +193260,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,
@@ -193208,6 +193284,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,
@@ -193278,9 +193355,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" };
@@ -194163,6 +194244,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)))),
diff --git 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| {
diff --git 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/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),
);
@@ -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/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;
diff --git 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{
@@ -388,17 +393,20 @@ 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);
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");
@@ -509,19 +517,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 +553,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");
diff --git 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",
diff --git 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/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}));
}
diff --git 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
@@ -538,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
@@ -1063,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,
@@ -1083,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,
@@ -3394,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,
diff --git 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/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/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);
-
- 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 }));
+ {
+ // 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 }));
+
+ {
+ // 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/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);
+}
diff --git 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);
+}
diff --git 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),
+ }
+}
diff --git 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
@@ -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;
diff --git 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();
+}
diff --git 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();
@@ -968,6 +1002,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 +1085,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();
diff --git a/test/cases/export_from_body_of_coerced_fn.zig 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
diff --git a/test/cases/translate_c/tenary_in_macro.c 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/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,
@@ -73,6 +76,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 +169,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 +2444,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);
@@ -4246,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;
diff --git 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/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/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
@@ -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
@@ -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);
-}
diff --git 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
diff --git 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);
diff --git 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,
}
diff --git 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;
\\}
});