authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-16 17:33:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-16 17:33:24-07:00
log1ed569e0b23c4432cd00604dcae89a17edc852a9
tree090e0b3817a0caa4f3e7b99ec1d4d965f2bc7438
parent778ca2ae6bf025edb6babeec08c957be1fbb37a5
parentb4d58e93ea4d0bbfe674f80d301279d302fe8fc8

Merge remote-tracking branch 'origin/master' into llvm16


399 files changed, 27595 insertions(+), 19646 deletions(-)

CMakeLists.txt+14-5
...@@ -296,14 +296,12 @@ set(ZIG_STAGE2_SOURCES...@@ -296,14 +296,12 @@ set(ZIG_STAGE2_SOURCES
296 "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"296 "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"
297 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"297 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
298 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"298 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
299 "${CMAKE_SOURCE_DIR}/lib/std/os/darwin.zig"
300 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"299 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
301 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig"300 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig"
302 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"301 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
303 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"302 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
304 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig"303 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig"
305 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"304 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
306 "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig"
307 "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig"305 "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig"
308 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig"306 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig"
309 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig"307 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig"
...@@ -507,7 +505,9 @@ set(ZIG_STAGE2_SOURCES...@@ -507,7 +505,9 @@ set(ZIG_STAGE2_SOURCES
507 "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig"505 "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig"
508 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig"506 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig"
509 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig"507 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig"
508 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Pool.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig"509 "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/Thread/WaitGroup.zig"
511 "${CMAKE_SOURCE_DIR}/lib/std/time.zig"511 "${CMAKE_SOURCE_DIR}/lib/std/time.zig"
512 "${CMAKE_SOURCE_DIR}/lib/std/treap.zig"512 "${CMAKE_SOURCE_DIR}/lib/std/treap.zig"
513 "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig"513 "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig"
...@@ -517,6 +517,7 @@ set(ZIG_STAGE2_SOURCES...@@ -517,6 +517,7 @@ set(ZIG_STAGE2_SOURCES
517 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"517 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
518 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"518 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
519 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"519 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
520 "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
520 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"521 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
521 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"522 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
522 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"523 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
...@@ -531,9 +532,7 @@ set(ZIG_STAGE2_SOURCES...@@ -531,9 +532,7 @@ set(ZIG_STAGE2_SOURCES
531 "${CMAKE_SOURCE_DIR}/src/Package.zig"532 "${CMAKE_SOURCE_DIR}/src/Package.zig"
532 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"533 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
533 "${CMAKE_SOURCE_DIR}/src/Sema.zig"534 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
534 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
535 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"535 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
536 "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"
537 "${CMAKE_SOURCE_DIR}/src/Zir.zig"536 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
538 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"537 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"
539 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"538 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"
...@@ -560,9 +559,12 @@ set(ZIG_STAGE2_SOURCES...@@ -560,9 +559,12 @@ set(ZIG_STAGE2_SOURCES
560 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"559 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"
561 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig"560 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig"
562 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig"561 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig"
562 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig"
563 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig"563 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig"
564 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig"
565 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig"564 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig"
565 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig"
566 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/encoder.zig"
567 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/encodings.zig"
566 "${CMAKE_SOURCE_DIR}/src/clang.zig"568 "${CMAKE_SOURCE_DIR}/src/clang.zig"
567 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"569 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
568 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"570 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
...@@ -827,6 +829,12 @@ else()...@@ -827,6 +829,12 @@ else()
827 set(ZIG_STATIC_ARG "")829 set(ZIG_STATIC_ARG "")
828endif()830endif()
829831
832if(CMAKE_POSITION_INDEPENDENT_CODE)
833 set(ZIG_PIE_ARG="-Dpie")
834else()
835 set(ZIG_PIE_ARG="")
836endif()
837
830set(ZIG_BUILD_ARGS838set(ZIG_BUILD_ARGS
831 --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"839 --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"
832 "-Dconfig_h=${ZIG_CONFIG_H_OUT}"840 "-Dconfig_h=${ZIG_CONFIG_H_OUT}"
...@@ -835,6 +843,7 @@ set(ZIG_BUILD_ARGS...@@ -835,6 +843,7 @@ set(ZIG_BUILD_ARGS
835 ${ZIG_STATIC_ARG}843 ${ZIG_STATIC_ARG}
836 ${ZIG_NO_LIB_ARG}844 ${ZIG_NO_LIB_ARG}
837 ${ZIG_SINGLE_THREADED_ARG}845 ${ZIG_SINGLE_THREADED_ARG}
846 ${ZIG_PIE_ARG}
838 "-Dtarget=${ZIG_TARGET_TRIPLE}"847 "-Dtarget=${ZIG_TARGET_TRIPLE}"
839 "-Dcpu=${ZIG_TARGET_MCPU}"848 "-Dcpu=${ZIG_TARGET_MCPU}"
840 "-Dversion-string=${RESOLVED_ZIG_VERSION}"849 "-Dversion-string=${RESOLVED_ZIG_VERSION}"
build.zig+114-108
...@@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void {...@@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void {
31 const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false;31 const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false;
3232
33 const test_step = b.step("test", "Run all the tests");33 const test_step = b.step("test", "Run all the tests");
34 const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false;
35 if (deprecated_skip_install_lib_files) {
36 std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{});
37 }
38 const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files and langref to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files;
3439
35 const docgen_exe = b.addExecutable(.{40 const docgen_exe = b.addExecutable(.{
36 .name = "docgen",41 .name = "docgen",
...@@ -40,28 +45,35 @@ pub fn build(b: *std.Build) !void {...@@ -40,28 +45,35 @@ pub fn build(b: *std.Build) !void {
40 });45 });
41 docgen_exe.single_threaded = single_threaded;46 docgen_exe.single_threaded = single_threaded;
4247
43 const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"});48 const docgen_cmd = b.addRunArtifact(docgen_exe);
44 const docgen_cmd = docgen_exe.run();49 docgen_cmd.addArgs(&.{ "--zig", b.zig_exe });
45 docgen_cmd.addArgs(&[_][]const u8{50 if (b.zig_lib_dir) |p| {
46 "--zig",51 docgen_cmd.addArgs(&.{ "--zig-lib-dir", p });
47 b.zig_exe,52 }
48 "doc" ++ fs.path.sep_str ++ "langref.html.in",53 docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" });
49 langref_out_path,54 const langref_file = docgen_cmd.addOutputFileArg("langref.html");
50 });55 const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
51 docgen_cmd.step.dependOn(&docgen_exe.step);56 if (!skip_install_lib_files) {
57 b.getInstallStep().dependOn(&install_langref.step);
58 }
5259
53 const docs_step = b.step("docs", "Build documentation");60 const docs_step = b.step("docs", "Build documentation");
54 docs_step.dependOn(&docgen_cmd.step);61 docs_step.dependOn(&docgen_cmd.step);
5562
56 const test_cases = b.addTest(.{63 // This is for legacy reasons, to be removed after our CI scripts are upgraded to use
57 .root_source_file = .{ .path = "src/test.zig" },64 // the file from the install prefix instead.
65 const legacy_write_to_cache = b.addWriteFiles();
66 legacy_write_to_cache.addCopyFileToSource(langref_file, "zig-cache/langref.html");
67 docs_step.dependOn(&legacy_write_to_cache.step);
68
69 const check_case_exe = b.addExecutable(.{
70 .name = "check-case",
71 .root_source_file = .{ .path = "test/src/Cases.zig" },
58 .optimize = optimize,72 .optimize = optimize,
59 });73 });
60 test_cases.main_pkg_path = ".";74 check_case_exe.main_pkg_path = ".";
61 test_cases.stack_size = stack_size;75 check_case_exe.stack_size = stack_size;
62 test_cases.single_threaded = single_threaded;76 check_case_exe.single_threaded = single_threaded;
63
64 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
6577
66 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;78 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
67 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;79 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
...@@ -74,11 +86,6 @@ pub fn build(b: *std.Build) !void {...@@ -74,11 +86,6 @@ pub fn build(b: *std.Build) !void {
74 const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false;86 const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false;
75 const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;87 const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
76 const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false;88 const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false;
77 const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false;
78 if (deprecated_skip_install_lib_files) {
79 std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{});
80 }
81 const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files;
8289
83 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;90 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
8491
...@@ -157,6 +164,7 @@ pub fn build(b: *std.Build) !void {...@@ -157,6 +164,7 @@ pub fn build(b: *std.Build) !void {
157 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);164 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
158 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;165 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
159 const strip = b.option(bool, "strip", "Omit debug information");166 const strip = b.option(bool, "strip", "Omit debug information");
167 const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
160 const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false;168 const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false;
161169
162 const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {170 const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {
...@@ -167,6 +175,7 @@ pub fn build(b: *std.Build) !void {...@@ -167,6 +175,7 @@ pub fn build(b: *std.Build) !void {
167175
168 const exe = addCompilerStep(b, optimize, target);176 const exe = addCompilerStep(b, optimize, target);
169 exe.strip = strip;177 exe.strip = strip;
178 exe.pie = pie;
170 exe.sanitize_thread = sanitize_thread;179 exe.sanitize_thread = sanitize_thread;
171 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;180 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
172 exe.install();181 exe.install();
...@@ -178,13 +187,12 @@ pub fn build(b: *std.Build) !void {...@@ -178,13 +187,12 @@ pub fn build(b: *std.Build) !void {
178 test_step.dependOn(&exe.step);187 test_step.dependOn(&exe.step);
179 }188 }
180189
181 b.default_step.dependOn(&exe.step);
182 exe.single_threaded = single_threaded;190 exe.single_threaded = single_threaded;
183191
184 if (target.isWindows() and target.getAbi() == .gnu) {192 if (target.isWindows() and target.getAbi() == .gnu) {
185 // LTO is currently broken on mingw, this can be removed when it's fixed.193 // LTO is currently broken on mingw, this can be removed when it's fixed.
186 exe.want_lto = false;194 exe.want_lto = false;
187 test_cases.want_lto = false;195 check_case_exe.want_lto = false;
188 }196 }
189197
190 const exe_options = b.addOptions();198 const exe_options = b.addOptions();
...@@ -199,11 +207,11 @@ pub fn build(b: *std.Build) !void {...@@ -199,11 +207,11 @@ pub fn build(b: *std.Build) !void {
199 exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);207 exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
200 exe_options.addOption(bool, "force_gpa", force_gpa);208 exe_options.addOption(bool, "force_gpa", force_gpa);
201 exe_options.addOption(bool, "only_c", only_c);209 exe_options.addOption(bool, "only_c", only_c);
202 exe_options.addOption(bool, "omit_pkg_fetching_code", false);210 exe_options.addOption(bool, "omit_pkg_fetching_code", only_c);
203211
204 if (link_libc) {212 if (link_libc) {
205 exe.linkLibC();213 exe.linkLibC();
206 test_cases.linkLibC();214 check_case_exe.linkLibC();
207 }215 }
208216
209 const is_debug = optimize == .Debug;217 const is_debug = optimize == .Debug;
...@@ -289,14 +297,14 @@ pub fn build(b: *std.Build) !void {...@@ -289,14 +297,14 @@ pub fn build(b: *std.Build) !void {
289 }297 }
290298
291 try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);299 try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
292 try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx);300 try addCmakeCfgOptionsToExe(b, cfg, check_case_exe, use_zig_libcxx);
293 } else {301 } else {
294 // Here we are -Denable-llvm but no cmake integration.302 // Here we are -Denable-llvm but no cmake integration.
295 try addStaticLlvmOptionsToExe(exe);303 try addStaticLlvmOptionsToExe(exe);
296 try addStaticLlvmOptionsToExe(test_cases);304 try addStaticLlvmOptionsToExe(check_case_exe);
297 }305 }
298 if (target.isWindows()) {306 if (target.isWindows()) {
299 inline for (.{ exe, test_cases }) |artifact| {307 inline for (.{ exe, check_case_exe }) |artifact| {
300 artifact.linkSystemLibrary("version");308 artifact.linkSystemLibrary("version");
301 artifact.linkSystemLibrary("uuid");309 artifact.linkSystemLibrary("uuid");
302 artifact.linkSystemLibrary("ole32");310 artifact.linkSystemLibrary("ole32");
...@@ -341,8 +349,9 @@ pub fn build(b: *std.Build) !void {...@@ -341,8 +349,9 @@ pub fn build(b: *std.Build) !void {
341 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");349 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
342350
343 const test_cases_options = b.addOptions();351 const test_cases_options = b.addOptions();
344 test_cases.addOptions("build_options", test_cases_options);352 check_case_exe.addOptions("build_options", test_cases_options);
345353
354 test_cases_options.addOption(bool, "enable_tracy", false);
346 test_cases_options.addOption(bool, "enable_logging", enable_logging);355 test_cases_options.addOption(bool, "enable_logging", enable_logging);
347 test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);356 test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
348 test_cases_options.addOption(bool, "skip_non_native", skip_non_native);357 test_cases_options.addOption(bool, "skip_non_native", skip_non_native);
...@@ -366,12 +375,6 @@ pub fn build(b: *std.Build) !void {...@@ -366,12 +375,6 @@ pub fn build(b: *std.Build) !void {
366 test_cases_options.addOption(std.SemanticVersion, "semver", semver);375 test_cases_options.addOption(std.SemanticVersion, "semver", semver);
367 test_cases_options.addOption(?[]const u8, "test_filter", test_filter);376 test_cases_options.addOption(?[]const u8, "test_filter", test_filter);
368377
369 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
370 test_cases_step.dependOn(&test_cases.step);
371 if (!skip_stage2_tests) {
372 test_step.dependOn(test_cases_step);
373 }
374
375 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;378 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
376 var chosen_mode_index: usize = 0;379 var chosen_mode_index: usize = 0;
377 if (!skip_debug) {380 if (!skip_debug) {
...@@ -392,96 +395,101 @@ pub fn build(b: *std.Build) !void {...@@ -392,96 +395,101 @@ pub fn build(b: *std.Build) !void {
392 }395 }
393 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];396 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
394397
395 // run stage1 `zig fmt` on this build.zig file just to make sure it works398 const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" };
396 test_step.dependOn(&fmt_build_zig.step);399 const fmt_exclude_paths = &.{"test/cases"};
397 const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works");400 const do_fmt = b.addFmt(.{
398 fmt_step.dependOn(&fmt_build_zig.step);401 .paths = fmt_include_paths,
399402 .exclude_paths = fmt_exclude_paths,
400 test_step.dependOn(tests.addPkgTests(403 });
401 b,
402 test_filter,
403 "test/behavior.zig",
404 "behavior",
405 "Run the behavior tests",
406 optimization_modes,
407 skip_single_threaded,
408 skip_non_native,
409 skip_libc,
410 skip_stage1,
411 skip_stage2_tests,
412 ));
413404
414 test_step.dependOn(tests.addPkgTests(405 b.step("test-fmt", "Check source files having conforming formatting").dependOn(&b.addFmt(.{
415 b,406 .paths = fmt_include_paths,
416 test_filter,407 .exclude_paths = fmt_exclude_paths,
417 "lib/compiler_rt.zig",408 .check = true,
418 "compiler-rt",409 }).step);
419 "Run the compiler_rt tests",
420 optimization_modes,
421 true, // skip_single_threaded
422 skip_non_native,
423 true, // skip_libc
424 skip_stage1,
425 skip_stage2_tests or true, // TODO get these all passing
426 ));
427410
428 test_step.dependOn(tests.addPkgTests(411 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
429 b,412 try tests.addCases(b, test_cases_step, test_filter, check_case_exe);
430 test_filter,413 if (!skip_stage2_tests) test_step.dependOn(test_cases_step);
431 "lib/c.zig",414
432 "universal-libc",415 test_step.dependOn(tests.addModuleTests(b, .{
433 "Run the universal libc tests",416 .test_filter = test_filter,
434 optimization_modes,417 .root_src = "test/behavior.zig",
435 true, // skip_single_threaded418 .name = "behavior",
436 skip_non_native,419 .desc = "Run the behavior tests",
437 true, // skip_libc420 .optimize_modes = optimization_modes,
438 skip_stage1,421 .skip_single_threaded = skip_single_threaded,
439 skip_stage2_tests or true, // TODO get these all passing422 .skip_non_native = skip_non_native,
440 ));423 .skip_libc = skip_libc,
424 .skip_stage1 = skip_stage1,
425 .skip_stage2 = skip_stage2_tests,
426 .max_rss = 1 * 1024 * 1024 * 1024,
427 }));
428
429 test_step.dependOn(tests.addModuleTests(b, .{
430 .test_filter = test_filter,
431 .root_src = "lib/compiler_rt.zig",
432 .name = "compiler-rt",
433 .desc = "Run the compiler_rt tests",
434 .optimize_modes = optimization_modes,
435 .skip_single_threaded = true,
436 .skip_non_native = skip_non_native,
437 .skip_libc = true,
438 .skip_stage1 = skip_stage1,
439 .skip_stage2 = true, // TODO get all these passing
440 }));
441
442 test_step.dependOn(tests.addModuleTests(b, .{
443 .test_filter = test_filter,
444 .root_src = "lib/c.zig",
445 .name = "universal-libc",
446 .desc = "Run the universal libc tests",
447 .optimize_modes = optimization_modes,
448 .skip_single_threaded = true,
449 .skip_non_native = skip_non_native,
450 .skip_libc = true,
451 .skip_stage1 = skip_stage1,
452 .skip_stage2 = true, // TODO get all these passing
453 }));
441454
442 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));455 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
443 test_step.dependOn(tests.addStandaloneTests(456 test_step.dependOn(tests.addStandaloneTests(
444 b,457 b,
445 test_filter,
446 optimization_modes,458 optimization_modes,
447 skip_non_native,
448 enable_macos_sdk,459 enable_macos_sdk,
449 target,
450 skip_stage2_tests,460 skip_stage2_tests,
451 b.enable_darling,
452 b.enable_qemu,
453 b.enable_rosetta,
454 b.enable_wasmtime,
455 b.enable_wine,
456 enable_symlinks_windows,461 enable_symlinks_windows,
457 ));462 ));
458 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));463 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
459 test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));464 test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
460 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));465 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
461 test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));466 test_step.dependOn(tests.addCliTests(b));
462 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));467 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
463 test_step.dependOn(tests.addTranslateCTests(b, test_filter));468 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
464 if (!skip_run_translated_c) {469 if (!skip_run_translated_c) {
465 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));470 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
466 }471 }
467 // tests for this feature are disabled until we have the self-hosted compiler available
468 // test_step.dependOn(tests.addGenHTests(b, test_filter));
469472
470 test_step.dependOn(tests.addPkgTests(473 test_step.dependOn(tests.addModuleTests(b, .{
471 b,474 .test_filter = test_filter,
472 test_filter,475 .root_src = "lib/std/std.zig",
473 "lib/std/std.zig",476 .name = "std",
474 "std",477 .desc = "Run the standard library tests",
475 "Run the standard library tests",478 .optimize_modes = optimization_modes,
476 optimization_modes,479 .skip_single_threaded = skip_single_threaded,
477 skip_single_threaded,480 .skip_non_native = skip_non_native,
478 skip_non_native,481 .skip_libc = skip_libc,
479 skip_libc,482 .skip_stage1 = skip_stage1,
480 skip_stage1,483 .skip_stage2 = true, // TODO get all these passing
481 true, // TODO get these all passing484 // I observed a value of 3398275072 on my M1, and multiplied by 1.1 to
482 ));485 // get this amount:
486 .max_rss = 3738102579,
487 }));
483488
484 try addWasiUpdateStep(b, version);489 try addWasiUpdateStep(b, version);
490
491 b.step("fmt", "Modify source files in place to have conforming formatting")
492 .dependOn(&do_fmt.step);
485}493}
486494
487fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {495fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
...@@ -510,6 +518,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {...@@ -510,6 +518,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
510 exe_options.addOption(bool, "enable_tracy_callstack", false);518 exe_options.addOption(bool, "enable_tracy_callstack", false);
511 exe_options.addOption(bool, "enable_tracy_allocation", false);519 exe_options.addOption(bool, "enable_tracy_allocation", false);
512 exe_options.addOption(bool, "value_tracing", false);520 exe_options.addOption(bool, "value_tracing", false);
521 exe_options.addOption(bool, "omit_pkg_fetching_code", true);
513522
514 const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" });523 const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" });
515 run_opt.addArtifactArg(exe);524 run_opt.addArtifactArg(exe);
...@@ -681,10 +690,7 @@ fn addCxxKnownPath(...@@ -681,10 +690,7 @@ fn addCxxKnownPath(
681) !void {690) !void {
682 if (!std.process.can_spawn)691 if (!std.process.can_spawn)
683 return error.RequiredLibraryNotFound;692 return error.RequiredLibraryNotFound;
684 const path_padded = try b.exec(&[_][]const u8{693 const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) });
685 ctx.cxx_compiler,
686 b.fmt("-print-file-name={s}", .{objname}),
687 });
688 var tokenizer = mem.tokenize(u8, path_padded, "\r\n");694 var tokenizer = mem.tokenize(u8, path_padded, "\r\n");
689 const path_unpadded = tokenizer.next().?;695 const path_unpadded = tokenizer.next().?;
690 if (mem.eql(u8, path_unpadded, objname)) {696 if (mem.eql(u8, path_unpadded, objname)) {
ci/aarch64-linux-debug.sh+7-1
...@@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \...@@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \
67 --zig-lib-dir "$(pwd)/../lib"67 --zig-lib-dir "$(pwd)/../lib"
6868
69# Look for HTML errors.69# Look for HTML errors.
70tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html"70tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html"
7171
72# Produce the experimental std lib documentation.72# Produce the experimental std lib documentation.
73stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib73stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
...@@ -98,3 +98,9 @@ unset CXX...@@ -98,3 +98,9 @@ unset CXX
98ninja install98ninja install
9999
100stage3/bin/zig test ../test/behavior.zig -I../test100stage3/bin/zig test ../test/behavior.zig -I../test
101stage3/bin/zig build -p stage4 \
102 -Dstatic-llvm \
103 -Dtarget=native-native-musl \
104 --search-prefix "$PREFIX" \
105 --zig-lib-dir "$(pwd)/../lib"
106stage4/bin/zig test ../test/behavior.zig -I../test
ci/aarch64-linux-release.sh+7-1
...@@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \...@@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \
67 --zig-lib-dir "$(pwd)/../lib"67 --zig-lib-dir "$(pwd)/../lib"
6868
69# Look for HTML errors.69# Look for HTML errors.
70tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html"70tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html"
7171
72# Produce the experimental std lib documentation.72# Produce the experimental std lib documentation.
73stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib73stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
...@@ -98,3 +98,9 @@ unset CXX...@@ -98,3 +98,9 @@ unset CXX
98ninja install98ninja install
9999
100stage3/bin/zig test ../test/behavior.zig -I../test100stage3/bin/zig test ../test/behavior.zig -I../test
101stage3/bin/zig build -p stage4 \
102 -Dstatic-llvm \
103 -Dtarget=native-native-musl \
104 --search-prefix "$PREFIX" \
105 --zig-lib-dir "$(pwd)/../lib"
106stage4/bin/zig test ../test/behavior.zig -I../test
ci/x86_64-linux-debug.sh+7-1
...@@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \...@@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \
66 --zig-lib-dir "$(pwd)/../lib"66 --zig-lib-dir "$(pwd)/../lib"
6767
68# Look for HTML errors.68# Look for HTML errors.
69tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html"69tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html"
7070
71# Produce the experimental std lib documentation.71# Produce the experimental std lib documentation.
72stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib72stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
...@@ -97,3 +97,9 @@ unset CXX...@@ -97,3 +97,9 @@ unset CXX
97ninja install97ninja install
9898
99stage3/bin/zig test ../test/behavior.zig -I../test99stage3/bin/zig test ../test/behavior.zig -I../test
100stage3/bin/zig build -p stage4 \
101 -Dstatic-llvm \
102 -Dtarget=native-native-musl \
103 --search-prefix "$PREFIX" \
104 --zig-lib-dir "$(pwd)/../lib"
105stage4/bin/zig test ../test/behavior.zig -I../test
ci/x86_64-linux-release.sh+7-1
...@@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \...@@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \
67 --zig-lib-dir "$(pwd)/../lib"67 --zig-lib-dir "$(pwd)/../lib"
6868
69# Look for HTML errors.69# Look for HTML errors.
70tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html"70tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html"
7171
72# Produce the experimental std lib documentation.72# Produce the experimental std lib documentation.
73stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib73stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
...@@ -114,3 +114,9 @@ unset CXX...@@ -114,3 +114,9 @@ unset CXX
114ninja install114ninja install
115115
116stage3/bin/zig test ../test/behavior.zig -I../test116stage3/bin/zig test ../test/behavior.zig -I../test
117stage3/bin/zig build -p stage4 \
118 -Dstatic-llvm \
119 -Dtarget=native-native-musl \
120 --search-prefix "$PREFIX" \
121 --zig-lib-dir "$(pwd)/../lib"
122stage4/bin/zig test ../test/behavior.zig -I../test
doc/docgen.zig+62-27
...@@ -28,10 +28,10 @@ const usage =...@@ -28,10 +28,10 @@ const usage =
28 \\28 \\
29;29;
3030
31fn errorf(comptime format: []const u8, args: anytype) noreturn {31fn fatal(comptime format: []const u8, args: anytype) noreturn {
32 const stderr = io.getStdErr().writer();32 const stderr = io.getStdErr().writer();
3333
34 stderr.print("error: " ++ format, args) catch {};34 stderr.print("error: " ++ format ++ "\n", args) catch {};
35 process.exit(1);35 process.exit(1);
36}36}
3737
...@@ -45,6 +45,7 @@ pub fn main() !void {...@@ -45,6 +45,7 @@ pub fn main() !void {
45 if (!args_it.skip()) @panic("expected self arg");45 if (!args_it.skip()) @panic("expected self arg");
4646
47 var zig_exe: []const u8 = "zig";47 var zig_exe: []const u8 = "zig";
48 var opt_zig_lib_dir: ?[]const u8 = null;
48 var do_code_tests = true;49 var do_code_tests = true;
49 var files = [_][]const u8{ "", "" };50 var files = [_][]const u8{ "", "" };
5051
...@@ -59,24 +60,29 @@ pub fn main() !void {...@@ -59,24 +60,29 @@ pub fn main() !void {
59 if (args_it.next()) |param| {60 if (args_it.next()) |param| {
60 zig_exe = param;61 zig_exe = param;
61 } else {62 } else {
62 errorf("expected parameter after --zig\n", .{});63 fatal("expected parameter after --zig", .{});
64 }
65 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
66 if (args_it.next()) |param| {
67 opt_zig_lib_dir = param;
68 } else {
69 fatal("expected parameter after --zig-lib-dir", .{});
63 }70 }
64 } else if (mem.eql(u8, arg, "--skip-code-tests")) {71 } else if (mem.eql(u8, arg, "--skip-code-tests")) {
65 do_code_tests = false;72 do_code_tests = false;
66 } else {73 } else {
67 errorf("unrecognized option: '{s}'\n", .{arg});74 fatal("unrecognized option: '{s}'", .{arg});
68 }75 }
69 } else {76 } else {
70 if (i > 1) {77 if (i > 1) {
71 errorf("too many arguments\n", .{});78 fatal("too many arguments", .{});
72 }79 }
73 files[i] = arg;80 files[i] = arg;
74 i += 1;81 i += 1;
75 }82 }
76 }83 }
77 if (i < 2) {84 if (i < 2) {
78 errorf("not enough arguments\n", .{});85 fatal("not enough arguments", .{});
79 process.exit(1);
80 }86 }
8187
82 var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only });88 var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only });
...@@ -95,7 +101,7 @@ pub fn main() !void {...@@ -95,7 +101,7 @@ pub fn main() !void {
95 try fs.cwd().makePath(tmp_dir_name);101 try fs.cwd().makePath(tmp_dir_name);
96 defer fs.cwd().deleteTree(tmp_dir_name) catch {};102 defer fs.cwd().deleteTree(tmp_dir_name) catch {};
97103
98 try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, do_code_tests);104 try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, opt_zig_lib_dir, do_code_tests);
99 try buffered_writer.flush();105 try buffered_writer.flush();
100}106}
101107
...@@ -1268,9 +1274,10 @@ fn genHtml(...@@ -1268,9 +1274,10 @@ fn genHtml(
1268 toc: *Toc,1274 toc: *Toc,
1269 out: anytype,1275 out: anytype,
1270 zig_exe: []const u8,1276 zig_exe: []const u8,
1277 opt_zig_lib_dir: ?[]const u8,
1271 do_code_tests: bool,1278 do_code_tests: bool,
1272) !void {1279) !void {
1273 var progress = Progress{};1280 var progress = Progress{ .dont_print_on_dumb = true };
1274 const root_node = progress.start("Generating docgen examples", toc.nodes.len);1281 const root_node = progress.start("Generating docgen examples", toc.nodes.len);
1275 defer root_node.end();1282 defer root_node.end();
12761283
...@@ -1278,7 +1285,7 @@ fn genHtml(...@@ -1278,7 +1285,7 @@ fn genHtml(
1278 try env_map.put("ZIG_DEBUG_COLOR", "1");1285 try env_map.put("ZIG_DEBUG_COLOR", "1");
12791286
1280 const host = try std.zig.system.NativeTargetInfo.detect(.{});1287 const host = try std.zig.system.NativeTargetInfo.detect(.{});
1281 const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe);1288 const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe, opt_zig_lib_dir);
12821289
1283 for (toc.nodes) |node| {1290 for (toc.nodes) |node| {
1284 defer root_node.completeOne();1291 defer root_node.completeOne();
...@@ -1370,6 +1377,9 @@ fn genHtml(...@@ -1370,6 +1377,9 @@ fn genHtml(
1370 "--color", "on",1377 "--color", "on",
1371 "--enable-cache", tmp_source_file_name,1378 "--enable-cache", tmp_source_file_name,
1372 });1379 });
1380 if (opt_zig_lib_dir) |zig_lib_dir| {
1381 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1382 }
13731383
1374 try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext});1384 try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext});
13751385
...@@ -1512,8 +1522,12 @@ fn genHtml(...@@ -1512,8 +1522,12 @@ fn genHtml(
1512 defer test_args.deinit();1522 defer test_args.deinit();
15131523
1514 try test_args.appendSlice(&[_][]const u8{1524 try test_args.appendSlice(&[_][]const u8{
1515 zig_exe, "test", tmp_source_file_name,1525 zig_exe, "test",
1526 tmp_source_file_name,
1516 });1527 });
1528 if (opt_zig_lib_dir) |zig_lib_dir| {
1529 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1530 }
1517 try shell_out.print("$ zig test {s}.zig ", .{code.name});1531 try shell_out.print("$ zig test {s}.zig ", .{code.name});
15181532
1519 switch (code.mode) {1533 switch (code.mode) {
...@@ -1564,12 +1578,13 @@ fn genHtml(...@@ -1564,12 +1578,13 @@ fn genHtml(
1564 defer test_args.deinit();1578 defer test_args.deinit();
15651579
1566 try test_args.appendSlice(&[_][]const u8{1580 try test_args.appendSlice(&[_][]const u8{
1567 zig_exe,1581 zig_exe, "test",
1568 "test",1582 "--color", "on",
1569 "--color",
1570 "on",
1571 tmp_source_file_name,1583 tmp_source_file_name,
1572 });1584 });
1585 if (opt_zig_lib_dir) |zig_lib_dir| {
1586 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1587 }
1573 try shell_out.print("$ zig test {s}.zig ", .{code.name});1588 try shell_out.print("$ zig test {s}.zig ", .{code.name});
15741589
1575 switch (code.mode) {1590 switch (code.mode) {
...@@ -1624,8 +1639,12 @@ fn genHtml(...@@ -1624,8 +1639,12 @@ fn genHtml(
1624 defer test_args.deinit();1639 defer test_args.deinit();
16251640
1626 try test_args.appendSlice(&[_][]const u8{1641 try test_args.appendSlice(&[_][]const u8{
1627 zig_exe, "test", tmp_source_file_name,1642 zig_exe, "test",
1643 tmp_source_file_name,
1628 });1644 });
1645 if (opt_zig_lib_dir) |zig_lib_dir| {
1646 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1647 }
1629 var mode_arg: []const u8 = "";1648 var mode_arg: []const u8 = "";
1630 switch (code.mode) {1649 switch (code.mode) {
1631 .Debug => {},1650 .Debug => {},
...@@ -1684,17 +1703,17 @@ fn genHtml(...@@ -1684,17 +1703,17 @@ fn genHtml(
1684 defer build_args.deinit();1703 defer build_args.deinit();
16851704
1686 try build_args.appendSlice(&[_][]const u8{1705 try build_args.appendSlice(&[_][]const u8{
1687 zig_exe,1706 zig_exe, "build-obj",
1688 "build-obj",1707 "--color", "on",
1708 "--name", code.name,
1689 tmp_source_file_name,1709 tmp_source_file_name,
1690 "--color",
1691 "on",
1692 "--name",
1693 code.name,
1694 try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{1710 try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{
1695 tmp_dir_name, fs.path.sep, name_plus_obj_ext,1711 tmp_dir_name, fs.path.sep, name_plus_obj_ext,
1696 }),1712 }),
1697 });1713 });
1714 if (opt_zig_lib_dir) |zig_lib_dir| {
1715 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1716 }
16981717
1699 try shell_out.print("$ zig build-obj {s}.zig ", .{code.name});1718 try shell_out.print("$ zig build-obj {s}.zig ", .{code.name});
17001719
...@@ -1758,13 +1777,15 @@ fn genHtml(...@@ -1758,13 +1777,15 @@ fn genHtml(
1758 defer test_args.deinit();1777 defer test_args.deinit();
17591778
1760 try test_args.appendSlice(&[_][]const u8{1779 try test_args.appendSlice(&[_][]const u8{
1761 zig_exe,1780 zig_exe, "build-lib",
1762 "build-lib",
1763 tmp_source_file_name,1781 tmp_source_file_name,
1764 try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{1782 try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{
1765 tmp_dir_name, fs.path.sep_str, bin_basename,1783 tmp_dir_name, fs.path.sep_str, bin_basename,
1766 }),1784 }),
1767 });1785 });
1786 if (opt_zig_lib_dir) |zig_lib_dir| {
1787 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1788 }
1768 try shell_out.print("$ zig build-lib {s}.zig ", .{code.name});1789 try shell_out.print("$ zig build-lib {s}.zig ", .{code.name});
17691790
1770 switch (code.mode) {1791 switch (code.mode) {
...@@ -1829,9 +1850,23 @@ fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8...@@ -1829,9 +1850,23 @@ fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8
1829 return result;1850 return result;
1830}1851}
18311852
1832fn getBuiltinCode(allocator: Allocator, env_map: *process.EnvMap, zig_exe: []const u8) ![]const u8 {1853fn getBuiltinCode(
1833 const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" });1854 allocator: Allocator,
1834 return result.stdout;1855 env_map: *process.EnvMap,
1856 zig_exe: []const u8,
1857 opt_zig_lib_dir: ?[]const u8,
1858) ![]const u8 {
1859 if (opt_zig_lib_dir) |zig_lib_dir| {
1860 const result = try exec(allocator, env_map, &.{
1861 zig_exe, "build-obj", "--show-builtin", "--zig-lib-dir", zig_lib_dir,
1862 });
1863 return result.stdout;
1864 } else {
1865 const result = try exec(allocator, env_map, &.{
1866 zig_exe, "build-obj", "--show-builtin",
1867 });
1868 return result.stdout;
1869 }
1835}1870}
18361871
1837fn dumpArgs(args: []const []const u8) void {1872fn dumpArgs(args: []const []const u8) void {
doc/langref.html.in+28-12
...@@ -7457,20 +7457,20 @@ pub const STDOUT_FILENO = 1;...@@ -7457,20 +7457,20 @@ pub const STDOUT_FILENO = 1;
74577457
7458pub fn syscall1(number: usize, arg1: usize) usize {7458pub fn syscall1(number: usize, arg1: usize) usize {
7459 return asm volatile ("syscall"7459 return asm volatile ("syscall"
7460 : [ret] "={rax}" (-> usize)7460 : [ret] "={rax}" (-> usize),
7461 : [number] "{rax}" (number),7461 : [number] "{rax}" (number),
7462 [arg1] "{rdi}" (arg1)7462 [arg1] "{rdi}" (arg1),
7463 : "rcx", "r11"7463 : "rcx", "r11"
7464 );7464 );
7465}7465}
74667466
7467pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {7467pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
7468 return asm volatile ("syscall"7468 return asm volatile ("syscall"
7469 : [ret] "={rax}" (-> usize)7469 : [ret] "={rax}" (-> usize),
7470 : [number] "{rax}" (number),7470 : [number] "{rax}" (number),
7471 [arg1] "{rdi}" (arg1),7471 [arg1] "{rdi}" (arg1),
7472 [arg2] "{rsi}" (arg2),7472 [arg2] "{rsi}" (arg2),
7473 [arg3] "{rdx}" (arg3)7473 [arg3] "{rdx}" (arg3),
7474 : "rcx", "r11"7474 : "rcx", "r11"
7475 );7475 );
7476}7476}
...@@ -7519,14 +7519,14 @@ pub fn syscall1(number: usize, arg1: usize) usize {...@@ -7519,14 +7519,14 @@ pub fn syscall1(number: usize, arg1: usize) usize {
7519 // type is the result type of the inline assembly expression.7519 // type is the result type of the inline assembly expression.
7520 // If it is a value binding, then `%[ret]` syntax would be used7520 // If it is a value binding, then `%[ret]` syntax would be used
7521 // to refer to the register bound to the value.7521 // to refer to the register bound to the value.
7522 (-> usize)7522 (-> usize),
7523 // Next is the list of inputs.7523 // Next is the list of inputs.
7524 // The constraint for these inputs means, "when the assembly code is7524 // The constraint for these inputs means, "when the assembly code is
7525 // executed, $rax shall have the value of `number` and $rdi shall have7525 // executed, $rax shall have the value of `number` and $rdi shall have
7526 // the value of `arg1`". Any number of input parameters is allowed,7526 // the value of `arg1`". Any number of input parameters is allowed,
7527 // including none.7527 // including none.
7528 : [number] "{rax}" (number),7528 : [number] "{rax}" (number),
7529 [arg1] "{rdi}" (arg1)7529 [arg1] "{rdi}" (arg1),
7530 // Next is the list of clobbers. These declare a set of registers whose7530 // Next is the list of clobbers. These declare a set of registers whose
7531 // values will not be preserved by the execution of this assembly code.7531 // values will not be preserved by the execution of this assembly code.
7532 // These do not include output or input registers. The special clobber7532 // These do not include output or input registers. The special clobber
...@@ -7818,12 +7818,14 @@ comptime {...@@ -7818,12 +7818,14 @@ comptime {
7818 <p>7818 <p>
7819 This function inserts a platform-specific debug trap instruction which causes7819 This function inserts a platform-specific debug trap instruction which causes
7820 debuggers to break there.7820 debuggers to break there.
7821 Unlike for {#syntax#}@trap(){#endsyntax#}, execution may continue after this point if the program is resumed.
7821 </p>7822 </p>
7822 <p>7823 <p>
7823 This function is only valid within function scope.7824 This function is only valid within function scope.
7824 </p>7825 </p>
78257826 {#see_also|@trap#}
7826 {#header_close#}7827 {#header_close#}
7828
7827 {#header_open|@mulAdd#}7829 {#header_open|@mulAdd#}
7828 <pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre>7830 <pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre>
7829 <p>7831 <p>
...@@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {...@@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {
9393 </p>9395 </p>
9394 {#header_close#}9396 {#header_close#}
93959397
9398 {#header_open|@trap#}
9399 <pre>{#syntax#}@trap() noreturn{#endsyntax#}</pre>
9400 <p>
9401 This function inserts a platform-specific trap/jam instruction which can be used to exit the program abnormally.
9402 This may be implemented by explicitly emitting an invalid instruction which may cause an illegal instruction exception of some sort.
9403 Unlike for {#syntax#}@breakpoint(){#endsyntax#}, execution does not continue after this point.
9404 </p>
9405 <p>
9406 Outside function scope, this builtin causes a compile error.
9407 </p>
9408 {#see_also|@breakpoint#}
9409 {#header_close#}
9410
9396 {#header_open|@truncate#}9411 {#header_open|@truncate#}
9397 <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>9412 <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
9398 <p>9413 <p>
...@@ -9565,9 +9580,10 @@ pub fn build(b: *std.Build) void {...@@ -9565,9 +9580,10 @@ pub fn build(b: *std.Build) void {
9565 This causes these options to be available:9580 This causes these options to be available:
9566 </p>9581 </p>
9567 <dl>9582 <dl>
9568 <dt><kbd>-Drelease-safe=[bool]</kbd></dt><dd>Optimizations on and safety on</dd>9583 <dt><kbd>-Doptimize=Debug</kbd></dt><dd>Optimizations off and safety on (default)</dd>
9569 <dt><kbd>-Drelease-fast=[bool]</kbd></dt><dd>Optimizations on and safety off</dd>9584 <dt><kbd>-Doptimize=ReleaseSafe</kbd></dt><dd>Optimizations on and safety on</dd>
9570 <dt><kbd>-Drelease-small=[bool]</kbd></dt><dd>Size optimizations on and safety off</dd>9585 <dt><kbd>-Doptimize=ReleaseFast</kbd></dt><dd>Optimizations on and safety off</dd>
9586 <dt><kbd>-Doptimize=ReleaseSmall</kbd></dt><dd>Size optimizations on and safety off</dd>
9571 </dl>9587 </dl>
9572 {#header_open|Debug#}9588 {#header_open|Debug#}
9573 {#shell_samp#}$ zig build-exe example.zig{#end_shell_samp#}9589 {#shell_samp#}$ zig build-exe example.zig{#end_shell_samp#}
...@@ -9788,8 +9804,8 @@ pub fn main() !void {...@@ -9788,8 +9804,8 @@ pub fn main() !void {
9788 {#header_close#}9804 {#header_close#}
9789 {#header_open|Builtin Overflow Functions#}9805 {#header_open|Builtin Overflow Functions#}
9790 <p>9806 <p>
9791 These builtins return a {#syntax#}bool{#endsyntax#} of whether or not overflow9807 These builtins return a tuple containing whether there was an overflow
9792 occurred, as well as returning the overflowed bits:9808 (as a {#syntax#}u1{#endsyntax#}) and the possibly overflowed bits of the operation:
9793 </p>9809 </p>
9794 <ul>9810 <ul>
9795 <li>{#link|@addWithOverflow#}</li>9811 <li>{#link|@addWithOverflow#}</li>
lib/build_runner.zig+702-53
...@@ -1,12 +1,14 @@...@@ -1,12 +1,14 @@
1const root = @import("@build");1const root = @import("@build");
2const std = @import("std");2const std = @import("std");
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const assert = std.debug.assert;
4const io = std.io;5const io = std.io;
5const fmt = std.fmt;6const fmt = std.fmt;
6const mem = std.mem;7const mem = std.mem;
7const process = std.process;8const process = std.process;
8const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
9const File = std.fs.File;10const File = std.fs.File;
11const Step = std.Build.Step;
1012
11pub const dependencies = @import("@dependencies");13pub const dependencies = @import("@dependencies");
1214
...@@ -14,12 +16,15 @@ pub fn main() !void {...@@ -14,12 +16,15 @@ pub fn main() !void {
14 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,16 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
15 // one shot program. We don't need to waste time freeing memory and finding places to squish17 // one shot program. We don't need to waste time freeing memory and finding places to squish
16 // bytes into. So we free everything all at once at the very end.18 // bytes into. So we free everything all at once at the very end.
17 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);19 var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
18 defer arena.deinit();20 defer single_threaded_arena.deinit();
1921
20 const allocator = arena.allocator();22 var thread_safe_arena: std.heap.ThreadSafeAllocator = .{
21 var args = try process.argsAlloc(allocator);23 .child_allocator = single_threaded_arena.allocator(),
22 defer process.argsFree(allocator, args);24 };
25 const arena = thread_safe_arena.allocator();
26
27 var args = try process.argsAlloc(arena);
2328
24 // skip my own exe name29 // skip my own exe name
25 var arg_idx: usize = 1;30 var arg_idx: usize = 1;
...@@ -59,18 +64,17 @@ pub fn main() !void {...@@ -59,18 +64,17 @@ pub fn main() !void {
59 };64 };
6065
61 var cache: std.Build.Cache = .{66 var cache: std.Build.Cache = .{
62 .gpa = allocator,67 .gpa = arena,
63 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),68 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
64 };69 };
65 cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });70 cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
66 cache.addPrefix(build_root_directory);71 cache.addPrefix(build_root_directory);
67 cache.addPrefix(local_cache_directory);72 cache.addPrefix(local_cache_directory);
68 cache.addPrefix(global_cache_directory);73 cache.addPrefix(global_cache_directory);
6974 cache.hash.addBytes(builtin.zig_version_string);
70 //cache.hash.addBytes(builtin.zig_version);
7175
72 const builder = try std.Build.create(76 const builder = try std.Build.create(
73 allocator,77 arena,
74 zig_exe,78 zig_exe,
75 build_root_directory,79 build_root_directory,
76 local_cache_directory,80 local_cache_directory,
...@@ -80,35 +84,34 @@ pub fn main() !void {...@@ -80,35 +84,34 @@ pub fn main() !void {
80 );84 );
81 defer builder.destroy();85 defer builder.destroy();
8286
83 var targets = ArrayList([]const u8).init(allocator);87 var targets = ArrayList([]const u8).init(arena);
84 var debug_log_scopes = ArrayList([]const u8).init(allocator);88 var debug_log_scopes = ArrayList([]const u8).init(arena);
8589 var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena };
86 const stderr_stream = io.getStdErr().writer();
87 const stdout_stream = io.getStdOut().writer();
8890
89 var install_prefix: ?[]const u8 = null;91 var install_prefix: ?[]const u8 = null;
90 var dir_list = std.Build.DirList{};92 var dir_list = std.Build.DirList{};
93 var enable_summary: ?bool = null;
94 var max_rss: usize = 0;
95 var color: Color = .auto;
9196
92 // before arg parsing, check for the NO_COLOR environment variable97 const stderr_stream = io.getStdErr().writer();
93 // if it exists, default the color setting to .off98 const stdout_stream = io.getStdOut().writer();
94 // explicit --color arguments will still override this setting.
95 builder.color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto;
9699
97 while (nextArg(args, &arg_idx)) |arg| {100 while (nextArg(args, &arg_idx)) |arg| {
98 if (mem.startsWith(u8, arg, "-D")) {101 if (mem.startsWith(u8, arg, "-D")) {
99 const option_contents = arg[2..];102 const option_contents = arg[2..];
100 if (option_contents.len == 0) {103 if (option_contents.len == 0) {
101 std.debug.print("Expected option name after '-D'\n\n", .{});104 std.debug.print("Expected option name after '-D'\n\n", .{});
102 return usageAndErr(builder, false, stderr_stream);105 usageAndErr(builder, false, stderr_stream);
103 }106 }
104 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {107 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
105 const option_name = option_contents[0..name_end];108 const option_name = option_contents[0..name_end];
106 const option_value = option_contents[name_end + 1 ..];109 const option_value = option_contents[name_end + 1 ..];
107 if (try builder.addUserInputOption(option_name, option_value))110 if (try builder.addUserInputOption(option_name, option_value))
108 return usageAndErr(builder, false, stderr_stream);111 usageAndErr(builder, false, stderr_stream);
109 } else {112 } else {
110 if (try builder.addUserInputFlag(option_contents))113 if (try builder.addUserInputFlag(option_contents))
111 return usageAndErr(builder, false, stderr_stream);114 usageAndErr(builder, false, stderr_stream);
112 }115 }
113 } else if (mem.startsWith(u8, arg, "-")) {116 } else if (mem.startsWith(u8, arg, "-")) {
114 if (mem.eql(u8, arg, "--verbose")) {117 if (mem.eql(u8, arg, "--verbose")) {
...@@ -118,69 +121,83 @@ pub fn main() !void {...@@ -118,69 +121,83 @@ pub fn main() !void {
118 } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) {121 } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) {
119 install_prefix = nextArg(args, &arg_idx) orelse {122 install_prefix = nextArg(args, &arg_idx) orelse {
120 std.debug.print("Expected argument after {s}\n\n", .{arg});123 std.debug.print("Expected argument after {s}\n\n", .{arg});
121 return usageAndErr(builder, false, stderr_stream);124 usageAndErr(builder, false, stderr_stream);
122 };125 };
123 } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {126 } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
124 return steps(builder, false, stdout_stream);127 return steps(builder, false, stdout_stream);
125 } else if (mem.eql(u8, arg, "--prefix-lib-dir")) {128 } else if (mem.eql(u8, arg, "--prefix-lib-dir")) {
126 dir_list.lib_dir = nextArg(args, &arg_idx) orelse {129 dir_list.lib_dir = nextArg(args, &arg_idx) orelse {
127 std.debug.print("Expected argument after {s}\n\n", .{arg});130 std.debug.print("Expected argument after {s}\n\n", .{arg});
128 return usageAndErr(builder, false, stderr_stream);131 usageAndErr(builder, false, stderr_stream);
129 };132 };
130 } else if (mem.eql(u8, arg, "--prefix-exe-dir")) {133 } else if (mem.eql(u8, arg, "--prefix-exe-dir")) {
131 dir_list.exe_dir = nextArg(args, &arg_idx) orelse {134 dir_list.exe_dir = nextArg(args, &arg_idx) orelse {
132 std.debug.print("Expected argument after {s}\n\n", .{arg});135 std.debug.print("Expected argument after {s}\n\n", .{arg});
133 return usageAndErr(builder, false, stderr_stream);136 usageAndErr(builder, false, stderr_stream);
134 };137 };
135 } else if (mem.eql(u8, arg, "--prefix-include-dir")) {138 } else if (mem.eql(u8, arg, "--prefix-include-dir")) {
136 dir_list.include_dir = nextArg(args, &arg_idx) orelse {139 dir_list.include_dir = nextArg(args, &arg_idx) orelse {
137 std.debug.print("Expected argument after {s}\n\n", .{arg});140 std.debug.print("Expected argument after {s}\n\n", .{arg});
138 return usageAndErr(builder, false, stderr_stream);141 usageAndErr(builder, false, stderr_stream);
139 };142 };
140 } else if (mem.eql(u8, arg, "--sysroot")) {143 } else if (mem.eql(u8, arg, "--sysroot")) {
141 const sysroot = nextArg(args, &arg_idx) orelse {144 const sysroot = nextArg(args, &arg_idx) orelse {
142 std.debug.print("Expected argument after --sysroot\n\n", .{});145 std.debug.print("Expected argument after --sysroot\n\n", .{});
143 return usageAndErr(builder, false, stderr_stream);146 usageAndErr(builder, false, stderr_stream);
144 };147 };
145 builder.sysroot = sysroot;148 builder.sysroot = sysroot;
149 } else if (mem.eql(u8, arg, "--maxrss")) {
150 const max_rss_text = nextArg(args, &arg_idx) orelse {
151 std.debug.print("Expected argument after --sysroot\n\n", .{});
152 usageAndErr(builder, false, stderr_stream);
153 };
154 // TODO: support shorthand such as "2GiB", "2GB", or "2G"
155 max_rss = std.fmt.parseInt(usize, max_rss_text, 10) catch |err| {
156 std.debug.print("invalid byte size: '{s}': {s}\n", .{
157 max_rss_text, @errorName(err),
158 });
159 process.exit(1);
160 };
146 } else if (mem.eql(u8, arg, "--search-prefix")) {161 } else if (mem.eql(u8, arg, "--search-prefix")) {
147 const search_prefix = nextArg(args, &arg_idx) orelse {162 const search_prefix = nextArg(args, &arg_idx) orelse {
148 std.debug.print("Expected argument after --search-prefix\n\n", .{});163 std.debug.print("Expected argument after --search-prefix\n\n", .{});
149 return usageAndErr(builder, false, stderr_stream);164 usageAndErr(builder, false, stderr_stream);
150 };165 };
151 builder.addSearchPrefix(search_prefix);166 builder.addSearchPrefix(search_prefix);
152 } else if (mem.eql(u8, arg, "--libc")) {167 } else if (mem.eql(u8, arg, "--libc")) {
153 const libc_file = nextArg(args, &arg_idx) orelse {168 const libc_file = nextArg(args, &arg_idx) orelse {
154 std.debug.print("Expected argument after --libc\n\n", .{});169 std.debug.print("Expected argument after --libc\n\n", .{});
155 return usageAndErr(builder, false, stderr_stream);170 usageAndErr(builder, false, stderr_stream);
156 };171 };
157 builder.libc_file = libc_file;172 builder.libc_file = libc_file;
158 } else if (mem.eql(u8, arg, "--color")) {173 } else if (mem.eql(u8, arg, "--color")) {
159 const next_arg = nextArg(args, &arg_idx) orelse {174 const next_arg = nextArg(args, &arg_idx) orelse {
160 std.debug.print("expected [auto|on|off] after --color", .{});175 std.debug.print("expected [auto|on|off] after --color", .{});
161 return usageAndErr(builder, false, stderr_stream);176 usageAndErr(builder, false, stderr_stream);
162 };177 };
163 builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse {178 color = std.meta.stringToEnum(Color, next_arg) orelse {
164 std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg});179 std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
165 return usageAndErr(builder, false, stderr_stream);180 usageAndErr(builder, false, stderr_stream);
166 };181 };
167 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {182 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
168 builder.zig_lib_dir = nextArg(args, &arg_idx) orelse {183 builder.zig_lib_dir = nextArg(args, &arg_idx) orelse {
169 std.debug.print("Expected argument after --zig-lib-dir\n\n", .{});184 std.debug.print("Expected argument after --zig-lib-dir\n\n", .{});
170 return usageAndErr(builder, false, stderr_stream);185 usageAndErr(builder, false, stderr_stream);
171 };186 };
172 } else if (mem.eql(u8, arg, "--debug-log")) {187 } else if (mem.eql(u8, arg, "--debug-log")) {
173 const next_arg = nextArg(args, &arg_idx) orelse {188 const next_arg = nextArg(args, &arg_idx) orelse {
174 std.debug.print("Expected argument after {s}\n\n", .{arg});189 std.debug.print("Expected argument after {s}\n\n", .{arg});
175 return usageAndErr(builder, false, stderr_stream);190 usageAndErr(builder, false, stderr_stream);
176 };191 };
177 try debug_log_scopes.append(next_arg);192 try debug_log_scopes.append(next_arg);
193 } else if (mem.eql(u8, arg, "--debug-pkg-config")) {
194 builder.debug_pkg_config = true;
178 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {195 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
179 builder.debug_compile_errors = true;196 builder.debug_compile_errors = true;
180 } else if (mem.eql(u8, arg, "--glibc-runtimes")) {197 } else if (mem.eql(u8, arg, "--glibc-runtimes")) {
181 builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse {198 builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse {
182 std.debug.print("Expected argument after --glibc-runtimes\n\n", .{});199 std.debug.print("Expected argument after --glibc-runtimes\n\n", .{});
183 return usageAndErr(builder, false, stderr_stream);200 usageAndErr(builder, false, stderr_stream);
184 };201 };
185 } else if (mem.eql(u8, arg, "--verbose-link")) {202 } else if (mem.eql(u8, arg, "--verbose-link")) {
186 builder.verbose_link = true;203 builder.verbose_link = true;
...@@ -194,8 +211,6 @@ pub fn main() !void {...@@ -194,8 +211,6 @@ pub fn main() !void {
194 builder.verbose_cc = true;211 builder.verbose_cc = true;
195 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {212 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
196 builder.verbose_llvm_cpu_features = true;213 builder.verbose_llvm_cpu_features = true;
197 } else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
198 builder.prominent_compile_errors = true;
199 } else if (mem.eql(u8, arg, "-fwine")) {214 } else if (mem.eql(u8, arg, "-fwine")) {
200 builder.enable_wine = true;215 builder.enable_wine = true;
201 } else if (mem.eql(u8, arg, "-fno-wine")) {216 } else if (mem.eql(u8, arg, "-fno-wine")) {
...@@ -216,6 +231,10 @@ pub fn main() !void {...@@ -216,6 +231,10 @@ pub fn main() !void {
216 builder.enable_darling = true;231 builder.enable_darling = true;
217 } else if (mem.eql(u8, arg, "-fno-darling")) {232 } else if (mem.eql(u8, arg, "-fno-darling")) {
218 builder.enable_darling = false;233 builder.enable_darling = false;
234 } else if (mem.eql(u8, arg, "-fsummary")) {
235 enable_summary = true;
236 } else if (mem.eql(u8, arg, "-fno-summary")) {
237 enable_summary = false;
219 } else if (mem.eql(u8, arg, "-freference-trace")) {238 } else if (mem.eql(u8, arg, "-freference-trace")) {
220 builder.reference_trace = 256;239 builder.reference_trace = 256;
221 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {240 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
...@@ -226,39 +245,639 @@ pub fn main() !void {...@@ -226,39 +245,639 @@ pub fn main() !void {
226 };245 };
227 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {246 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
228 builder.reference_trace = null;247 builder.reference_trace = null;
248 } else if (mem.startsWith(u8, arg, "-j")) {
249 const num = arg["-j".len..];
250 const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
251 std.debug.print("unable to parse jobs count '{s}': {s}", .{
252 num, @errorName(err),
253 });
254 process.exit(1);
255 };
256 if (n_jobs < 1) {
257 std.debug.print("number of jobs must be at least 1\n", .{});
258 process.exit(1);
259 }
260 thread_pool_options.n_jobs = n_jobs;
229 } else if (mem.eql(u8, arg, "--")) {261 } else if (mem.eql(u8, arg, "--")) {
230 builder.args = argsRest(args, arg_idx);262 builder.args = argsRest(args, arg_idx);
231 break;263 break;
232 } else {264 } else {
233 std.debug.print("Unrecognized argument: {s}\n\n", .{arg});265 std.debug.print("Unrecognized argument: {s}\n\n", .{arg});
234 return usageAndErr(builder, false, stderr_stream);266 usageAndErr(builder, false, stderr_stream);
235 }267 }
236 } else {268 } else {
237 try targets.append(arg);269 try targets.append(arg);
238 }270 }
239 }271 }
240272
273 const stderr = std.io.getStdErr();
274 const ttyconf = get_tty_conf(color, stderr);
275 switch (ttyconf) {
276 .no_color => try builder.env_map.put("NO_COLOR", "1"),
277 .escape_codes => try builder.env_map.put("ZIG_DEBUG_COLOR", "1"),
278 .windows_api => {},
279 }
280
281 var progress: std.Progress = .{ .dont_print_on_dumb = true };
282 const main_progress_node = progress.start("", 0);
283
241 builder.debug_log_scopes = debug_log_scopes.items;284 builder.debug_log_scopes = debug_log_scopes.items;
242 builder.resolveInstallPrefix(install_prefix, dir_list);285 builder.resolveInstallPrefix(install_prefix, dir_list);
243 try builder.runBuild(root);286 {
287 var prog_node = main_progress_node.start("user build.zig logic", 0);
288 defer prog_node.end();
289 try builder.runBuild(root);
290 }
244291
245 if (builder.validateUserInputDidItFail())292 if (builder.validateUserInputDidItFail())
246 return usageAndErr(builder, true, stderr_stream);293 usageAndErr(builder, true, stderr_stream);
247294
248 builder.make(targets.items) catch |err| {295 var run: Run = .{
249 switch (err) {296 .max_rss = max_rss,
250 error.InvalidStepName => {297 .max_rss_is_default = false,
251 return usageAndErr(builder, true, stderr_stream);298 .max_rss_mutex = .{},
299 .memory_blocked_steps = std.ArrayList(*Step).init(arena),
300
301 .claimed_rss = 0,
302 .enable_summary = enable_summary,
303 .ttyconf = ttyconf,
304 .stderr = stderr,
305 };
306
307 if (run.max_rss == 0) {
308 run.max_rss = process.totalSystemMemory() catch std.math.maxInt(usize);
309 run.max_rss_is_default = true;
310 }
311
312 runStepNames(
313 arena,
314 builder,
315 targets.items,
316 main_progress_node,
317 thread_pool_options,
318 &run,
319 ) catch |err| switch (err) {
320 error.UncleanExit => process.exit(1),
321 else => return err,
322 };
323}
324
325const Run = struct {
326 max_rss: usize,
327 max_rss_is_default: bool,
328 max_rss_mutex: std.Thread.Mutex,
329 memory_blocked_steps: std.ArrayList(*Step),
330
331 claimed_rss: usize,
332 enable_summary: ?bool,
333 ttyconf: std.debug.TTY.Config,
334 stderr: std.fs.File,
335};
336
337fn runStepNames(
338 arena: std.mem.Allocator,
339 b: *std.Build,
340 step_names: []const []const u8,
341 parent_prog_node: *std.Progress.Node,
342 thread_pool_options: std.Thread.Pool.Options,
343 run: *Run,
344) !void {
345 const gpa = b.allocator;
346 var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{};
347 defer step_stack.deinit(gpa);
348
349 if (step_names.len == 0) {
350 try step_stack.put(gpa, b.default_step, {});
351 } else {
352 try step_stack.ensureUnusedCapacity(gpa, step_names.len);
353 for (0..step_names.len) |i| {
354 const step_name = step_names[step_names.len - i - 1];
355 const s = b.top_level_steps.get(step_name) orelse {
356 std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name});
357 process.exit(1);
358 };
359 step_stack.putAssumeCapacity(&s.step, {});
360 }
361 }
362
363 const starting_steps = try arena.dupe(*Step, step_stack.keys());
364 for (starting_steps) |s| {
365 checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) {
366 error.DependencyLoopDetected => return error.UncleanExit,
367 else => |e| return e,
368 };
369 }
370
371 {
372 // Check that we have enough memory to complete the build.
373 var any_problems = false;
374 for (step_stack.keys()) |s| {
375 if (s.max_rss == 0) continue;
376 if (s.max_rss > run.max_rss) {
377 std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{
378 s.owner.dep_prefix, s.name, s.max_rss, run.max_rss,
379 });
380 any_problems = true;
381 }
382 }
383 if (any_problems) {
384 if (run.max_rss_is_default) {
385 std.debug.print("note: use --maxrss to override the default", .{});
386 }
387 return error.UncleanExit;
388 }
389 }
390
391 var thread_pool: std.Thread.Pool = undefined;
392 try thread_pool.init(thread_pool_options);
393 defer thread_pool.deinit();
394
395 {
396 defer parent_prog_node.end();
397
398 var step_prog = parent_prog_node.start("steps", step_stack.count());
399 defer step_prog.end();
400
401 var wait_group: std.Thread.WaitGroup = .{};
402 defer wait_group.wait();
403
404 // Here we spawn the initial set of tasks with a nice heuristic -
405 // dependency order. Each worker when it finishes a step will then
406 // check whether it should run any dependants.
407 const steps_slice = step_stack.keys();
408 for (0..steps_slice.len) |i| {
409 const step = steps_slice[steps_slice.len - i - 1];
410
411 wait_group.start();
412 thread_pool.spawn(workerMakeOneStep, .{
413 &wait_group, &thread_pool, b, step, &step_prog, run,
414 }) catch @panic("OOM");
415 }
416 }
417 assert(run.memory_blocked_steps.items.len == 0);
418
419 var test_skip_count: usize = 0;
420 var test_fail_count: usize = 0;
421 var test_pass_count: usize = 0;
422 var test_leak_count: usize = 0;
423 var test_count: usize = 0;
424
425 var success_count: usize = 0;
426 var skipped_count: usize = 0;
427 var failure_count: usize = 0;
428 var pending_count: usize = 0;
429 var total_compile_errors: usize = 0;
430 var compile_error_steps: std.ArrayListUnmanaged(*Step) = .{};
431 defer compile_error_steps.deinit(gpa);
432
433 for (step_stack.keys()) |s| {
434 test_fail_count += s.test_results.fail_count;
435 test_skip_count += s.test_results.skip_count;
436 test_leak_count += s.test_results.leak_count;
437 test_pass_count += s.test_results.passCount();
438 test_count += s.test_results.test_count;
439
440 switch (s.state) {
441 .precheck_unstarted => unreachable,
442 .precheck_started => unreachable,
443 .running => unreachable,
444 .precheck_done => {
445 // precheck_done is equivalent to dependency_failure in the case of
446 // transitive dependencies. For example:
447 // A -> B -> C (failure)
448 // B will be marked as dependency_failure, while A may never be queued, and thus
449 // remain in the initial state of precheck_done.
450 s.state = .dependency_failure;
451 pending_count += 1;
452 },
453 .dependency_failure => pending_count += 1,
454 .success => success_count += 1,
455 .skipped => skipped_count += 1,
456 .failure => {
457 failure_count += 1;
458 const compile_errors_len = s.result_error_bundle.errorMessageCount();
459 if (compile_errors_len > 0) {
460 total_compile_errors += compile_errors_len;
461 try compile_error_steps.append(gpa, s);
462 }
252 },463 },
253 error.UncleanExit => process.exit(1),
254 // This error is intended to indicate that the step has already
255 // logged an error message and so printing the error return trace
256 // here would be unwanted extra information, unless the user opts
257 // into it with a debug flag.
258 error.StepFailed => process.exit(1),
259 else => return err,
260 }464 }
261 };465 }
466
467 // A proper command line application defaults to silently succeeding.
468 // The user may request verbose mode if they have a different preference.
469 if (failure_count == 0 and run.enable_summary != true) return cleanExit();
470
471 const ttyconf = run.ttyconf;
472 const stderr = run.stderr;
473
474 if (run.enable_summary != false) {
475 const total_count = success_count + failure_count + pending_count + skipped_count;
476 ttyconf.setColor(stderr, .Cyan) catch {};
477 stderr.writeAll("Build Summary:") catch {};
478 ttyconf.setColor(stderr, .Reset) catch {};
479 stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
480 if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {};
481 if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {};
482
483 if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
484 if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {};
485 if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {};
486 if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {};
487
488 if (run.enable_summary == null) {
489 ttyconf.setColor(stderr, .Dim) catch {};
490 stderr.writeAll(" (disable with -fno-summary)") catch {};
491 ttyconf.setColor(stderr, .Reset) catch {};
492 }
493 stderr.writeAll("\n") catch {};
494
495 // Print a fancy tree with build results.
496 var print_node: PrintNode = .{ .parent = null };
497 if (step_names.len == 0) {
498 print_node.last = true;
499 printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {};
500 } else {
501 for (step_names, 0..) |step_name, i| {
502 const tls = b.top_level_steps.get(step_name).?;
503 print_node.last = i + 1 == b.top_level_steps.count();
504 printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {};
505 }
506 }
507 }
508
509 if (failure_count == 0) return cleanExit();
510
511 // Finally, render compile errors at the bottom of the terminal.
512 // We use a separate compile_error_steps array list because step_stack is destructively
513 // mutated in printTreeStep above.
514 if (total_compile_errors > 0) {
515 for (compile_error_steps.items) |s| {
516 if (s.result_error_bundle.errorMessageCount() > 0) {
517 s.result_error_bundle.renderToStdErr(renderOptions(ttyconf));
518 }
519 }
520
521 // Signal to parent process that we have printed compile errors. The
522 // parent process may choose to omit the "following command failed"
523 // line in this case.
524 process.exit(2);
525 }
526
527 process.exit(1);
528}
529
530const PrintNode = struct {
531 parent: ?*PrintNode,
532 last: bool = false,
533};
534
535fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.debug.TTY.Config) !void {
536 const parent = node.parent orelse return;
537 if (parent.parent == null) return;
538 try printPrefix(parent, stderr, ttyconf);
539 if (parent.last) {
540 try stderr.writeAll(" ");
541 } else {
542 try stderr.writeAll(switch (ttyconf) {
543 .no_color, .windows_api => "| ",
544 .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │
545 });
546 }
547}
548
549fn printTreeStep(
550 b: *std.Build,
551 s: *Step,
552 stderr: std.fs.File,
553 ttyconf: std.debug.TTY.Config,
554 parent_node: *PrintNode,
555 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
556) !void {
557 const first = step_stack.swapRemove(s);
558 try printPrefix(parent_node, stderr, ttyconf);
559
560 if (!first) try ttyconf.setColor(stderr, .Dim);
561 if (parent_node.parent != null) {
562 if (parent_node.last) {
563 try stderr.writeAll(switch (ttyconf) {
564 .no_color, .windows_api => "+- ",
565 .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─
566 });
567 } else {
568 try stderr.writeAll(switch (ttyconf) {
569 .no_color, .windows_api => "+- ",
570 .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─
571 });
572 }
573 }
574
575 // dep_prefix omitted here because it is redundant with the tree.
576 try stderr.writeAll(s.name);
577
578 if (first) {
579 switch (s.state) {
580 .precheck_unstarted => unreachable,
581 .precheck_started => unreachable,
582 .precheck_done => unreachable,
583 .running => unreachable,
584
585 .dependency_failure => {
586 try ttyconf.setColor(stderr, .Dim);
587 try stderr.writeAll(" transitive failure\n");
588 try ttyconf.setColor(stderr, .Reset);
589 },
590
591 .success => {
592 try ttyconf.setColor(stderr, .Green);
593 if (s.result_cached) {
594 try stderr.writeAll(" cached");
595 } else if (s.test_results.test_count > 0) {
596 const pass_count = s.test_results.passCount();
597 try stderr.writer().print(" {d} passed", .{pass_count});
598 if (s.test_results.skip_count > 0) {
599 try ttyconf.setColor(stderr, .Yellow);
600 try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count});
601 }
602 } else {
603 try stderr.writeAll(" success");
604 }
605 try ttyconf.setColor(stderr, .Reset);
606 if (s.result_duration_ns) |ns| {
607 try ttyconf.setColor(stderr, .Dim);
608 if (ns >= std.time.ns_per_min) {
609 try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min});
610 } else if (ns >= std.time.ns_per_s) {
611 try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s});
612 } else if (ns >= std.time.ns_per_ms) {
613 try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms});
614 } else if (ns >= std.time.ns_per_us) {
615 try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us});
616 } else {
617 try stderr.writer().print(" {d}ns", .{ns});
618 }
619 try ttyconf.setColor(stderr, .Reset);
620 }
621 if (s.result_peak_rss != 0) {
622 const rss = s.result_peak_rss;
623 try ttyconf.setColor(stderr, .Dim);
624 if (rss >= 1000_000_000) {
625 try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000});
626 } else if (rss >= 1000_000) {
627 try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000});
628 } else if (rss >= 1000) {
629 try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000});
630 } else {
631 try stderr.writer().print(" MaxRSS:{d}B", .{rss});
632 }
633 try ttyconf.setColor(stderr, .Reset);
634 }
635 try stderr.writeAll("\n");
636 },
637
638 .skipped => {
639 try ttyconf.setColor(stderr, .Yellow);
640 try stderr.writeAll(" skipped\n");
641 try ttyconf.setColor(stderr, .Reset);
642 },
643
644 .failure => {
645 if (s.result_error_bundle.errorMessageCount() > 0) {
646 try ttyconf.setColor(stderr, .Red);
647 try stderr.writer().print(" {d} errors\n", .{
648 s.result_error_bundle.errorMessageCount(),
649 });
650 try ttyconf.setColor(stderr, .Reset);
651 } else if (!s.test_results.isSuccess()) {
652 try stderr.writer().print(" {d}/{d} passed", .{
653 s.test_results.passCount(), s.test_results.test_count,
654 });
655 if (s.test_results.fail_count > 0) {
656 try stderr.writeAll(", ");
657 try ttyconf.setColor(stderr, .Red);
658 try stderr.writer().print("{d} failed", .{
659 s.test_results.fail_count,
660 });
661 try ttyconf.setColor(stderr, .Reset);
662 }
663 if (s.test_results.skip_count > 0) {
664 try stderr.writeAll(", ");
665 try ttyconf.setColor(stderr, .Yellow);
666 try stderr.writer().print("{d} skipped", .{
667 s.test_results.skip_count,
668 });
669 try ttyconf.setColor(stderr, .Reset);
670 }
671 if (s.test_results.leak_count > 0) {
672 try stderr.writeAll(", ");
673 try ttyconf.setColor(stderr, .Red);
674 try stderr.writer().print("{d} leaked", .{
675 s.test_results.leak_count,
676 });
677 try ttyconf.setColor(stderr, .Reset);
678 }
679 try stderr.writeAll("\n");
680 } else {
681 try ttyconf.setColor(stderr, .Red);
682 try stderr.writeAll(" failure\n");
683 try ttyconf.setColor(stderr, .Reset);
684 }
685 },
686 }
687
688 for (s.dependencies.items, 0..) |dep, i| {
689 var print_node: PrintNode = .{
690 .parent = parent_node,
691 .last = i == s.dependencies.items.len - 1,
692 };
693 try printTreeStep(b, dep, stderr, ttyconf, &print_node, step_stack);
694 }
695 } else {
696 if (s.dependencies.items.len == 0) {
697 try stderr.writeAll(" (reused)\n");
698 } else {
699 try stderr.writer().print(" (+{d} more reused dependencies)\n", .{
700 s.dependencies.items.len,
701 });
702 }
703 try ttyconf.setColor(stderr, .Reset);
704 }
705}
706
707fn checkForDependencyLoop(
708 b: *std.Build,
709 s: *Step,
710 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
711) !void {
712 switch (s.state) {
713 .precheck_started => {
714 std.debug.print("dependency loop detected:\n {s}\n", .{s.name});
715 return error.DependencyLoopDetected;
716 },
717 .precheck_unstarted => {
718 s.state = .precheck_started;
719
720 try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len);
721 for (s.dependencies.items) |dep| {
722 try step_stack.put(b.allocator, dep, {});
723 try dep.dependants.append(b.allocator, s);
724 checkForDependencyLoop(b, dep, step_stack) catch |err| {
725 if (err == error.DependencyLoopDetected) {
726 std.debug.print(" {s}\n", .{s.name});
727 }
728 return err;
729 };
730 }
731
732 s.state = .precheck_done;
733 },
734 .precheck_done => {},
735
736 // These don't happen until we actually run the step graph.
737 .dependency_failure => unreachable,
738 .running => unreachable,
739 .success => unreachable,
740 .failure => unreachable,
741 .skipped => unreachable,
742 }
743}
744
745fn workerMakeOneStep(
746 wg: *std.Thread.WaitGroup,
747 thread_pool: *std.Thread.Pool,
748 b: *std.Build,
749 s: *Step,
750 prog_node: *std.Progress.Node,
751 run: *Run,
752) void {
753 defer wg.finish();
754
755 // First, check the conditions for running this step. If they are not met,
756 // then we return without doing the step, relying on another worker to
757 // queue this step up again when dependencies are met.
758 for (s.dependencies.items) |dep| {
759 switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) {
760 .success, .skipped => continue,
761 .failure, .dependency_failure => {
762 @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst);
763 return;
764 },
765 .precheck_done, .running => {
766 // dependency is not finished yet.
767 return;
768 },
769 .precheck_unstarted => unreachable,
770 .precheck_started => unreachable,
771 }
772 }
773
774 if (s.max_rss != 0) {
775 run.max_rss_mutex.lock();
776 defer run.max_rss_mutex.unlock();
777
778 // Avoid running steps twice.
779 if (s.state != .precheck_done) {
780 // Another worker got the job.
781 return;
782 }
783
784 const new_claimed_rss = run.claimed_rss + s.max_rss;
785 if (new_claimed_rss > run.max_rss) {
786 // Running this step right now could possibly exceed the allotted RSS.
787 // Add this step to the queue of memory-blocked steps.
788 run.memory_blocked_steps.append(s) catch @panic("OOM");
789 return;
790 }
791
792 run.claimed_rss = new_claimed_rss;
793 s.state = .running;
794 } else {
795 // Avoid running steps twice.
796 if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) {
797 // Another worker got the job.
798 return;
799 }
800 }
801
802 var sub_prog_node = prog_node.start(s.name, 0);
803 sub_prog_node.activate();
804 defer sub_prog_node.end();
805
806 const make_result = s.make(&sub_prog_node);
807
808 // No matter the result, we want to display error/warning messages.
809 if (s.result_error_msgs.items.len > 0) {
810 sub_prog_node.context.lock_stderr();
811 defer sub_prog_node.context.unlock_stderr();
812
813 const stderr = run.stderr;
814 const ttyconf = run.ttyconf;
815
816 for (s.result_error_msgs.items) |msg| {
817 // Sometimes it feels like you just can't catch a break. Finally,
818 // with Zig, you can.
819 ttyconf.setColor(stderr, .Bold) catch break;
820 stderr.writeAll(s.owner.dep_prefix) catch break;
821 stderr.writeAll(s.name) catch break;
822 stderr.writeAll(": ") catch break;
823 ttyconf.setColor(stderr, .Red) catch break;
824 stderr.writeAll("error: ") catch break;
825 ttyconf.setColor(stderr, .Reset) catch break;
826 stderr.writeAll(msg) catch break;
827 stderr.writeAll("\n") catch break;
828 }
829 }
830
831 handle_result: {
832 if (make_result) |_| {
833 @atomicStore(Step.State, &s.state, .success, .SeqCst);
834 } else |err| switch (err) {
835 error.MakeFailed => {
836 @atomicStore(Step.State, &s.state, .failure, .SeqCst);
837 break :handle_result;
838 },
839 error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst),
840 }
841
842 // Successful completion of a step, so we queue up its dependants as well.
843 for (s.dependants.items) |dep| {
844 wg.start();
845 thread_pool.spawn(workerMakeOneStep, .{
846 wg, thread_pool, b, dep, prog_node, run,
847 }) catch @panic("OOM");
848 }
849 }
850
851 // If this is a step that claims resources, we must now queue up other
852 // steps that are waiting for resources.
853 if (s.max_rss != 0) {
854 run.max_rss_mutex.lock();
855 defer run.max_rss_mutex.unlock();
856
857 // Give the memory back to the scheduler.
858 run.claimed_rss -= s.max_rss;
859 // Avoid kicking off too many tasks that we already know will not have
860 // enough resources.
861 var remaining = run.max_rss - run.claimed_rss;
862 var i: usize = 0;
863 var j: usize = 0;
864 while (j < run.memory_blocked_steps.items.len) : (j += 1) {
865 const dep = run.memory_blocked_steps.items[j];
866 assert(dep.max_rss != 0);
867 if (dep.max_rss <= remaining) {
868 remaining -= dep.max_rss;
869
870 wg.start();
871 thread_pool.spawn(workerMakeOneStep, .{
872 wg, thread_pool, b, dep, prog_node, run,
873 }) catch @panic("OOM");
874 } else {
875 run.memory_blocked_steps.items[i] = dep;
876 i += 1;
877 }
878 }
879 run.memory_blocked_steps.shrinkRetainingCapacity(i);
880 }
262}881}
263882
264fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {883fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
...@@ -269,7 +888,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi...@@ -269,7 +888,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
269 }888 }
270889
271 const allocator = builder.allocator;890 const allocator = builder.allocator;
272 for (builder.top_level_steps.items) |top_level_step| {891 for (builder.top_level_steps.values()) |top_level_step| {
273 const name = if (&top_level_step.step == builder.default_step)892 const name = if (&top_level_step.step == builder.default_step)
274 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})893 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})
275 else894 else
...@@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi...@@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
327 \\ --verbose Print commands before executing them946 \\ --verbose Print commands before executing them
328 \\ --color [auto|off|on] Enable or disable colored error messages947 \\ --color [auto|off|on] Enable or disable colored error messages
329 \\ --prominent-compile-errors Output compile errors formatted for a human to read948 \\ --prominent-compile-errors Output compile errors formatted for a human to read
949 \\ -fsummary Print the build summary, even on success
950 \\ -fno-summary Omit the build summary, even on failure
951 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
952 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)
330 \\953 \\
331 \\Project-Specific Options:954 \\Project-Specific Options:
332 \\955 \\
...@@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi...@@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
364 \\ --zig-lib-dir [arg] Override path to Zig lib directory987 \\ --zig-lib-dir [arg] Override path to Zig lib directory
365 \\ --build-runner [file] Override path to build runner988 \\ --build-runner [file] Override path to build runner
366 \\ --debug-log [scope] Enable debugging the compiler989 \\ --debug-log [scope] Enable debugging the compiler
990 \\ --debug-pkg-config Fail if unknown pkg-config flags encountered
367 \\ --verbose-link Enable compiler debug output for linking991 \\ --verbose-link Enable compiler debug output for linking
368 \\ --verbose-air Enable compiler debug output for Zig AIR992 \\ --verbose-air Enable compiler debug output for Zig AIR
369 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR993 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
...@@ -374,7 +998,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi...@@ -374,7 +998,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
374 );998 );
375}999}
3761000
377fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void {1001fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) noreturn {
378 usage(builder, already_ran_build, out_stream) catch {};1002 usage(builder, already_ran_build, out_stream) catch {};
379 process.exit(1);1003 process.exit(1);
380}1004}
...@@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 {...@@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 {
389 if (idx >= args.len) return null;1013 if (idx >= args.len) return null;
390 return args[idx..];1014 return args[idx..];
391}1015}
1016
1017fn cleanExit() void {
1018 // Perhaps in the future there could be an Advanced Options flag such as
1019 // --debug-build-runner-leaks which would make this function return instead
1020 // of calling exit.
1021 process.exit(0);
1022}
1023
1024const Color = enum { auto, off, on };
1025
1026fn get_tty_conf(color: Color, stderr: std.fs.File) std.debug.TTY.Config {
1027 return switch (color) {
1028 .auto => std.debug.detectTTYConfig(stderr),
1029 .on => .escape_codes,
1030 .off => .no_color,
1031 };
1032}
1033
1034fn renderOptions(ttyconf: std.debug.TTY.Config) std.zig.ErrorBundle.RenderOptions {
1035 return .{
1036 .ttyconf = ttyconf,
1037 .include_source_line = ttyconf != .no_color,
1038 .include_reference_trace = ttyconf != .no_color,
1039 };
1040}
lib/compiler_rt/udivmodei4.zig+6-6
...@@ -79,16 +79,16 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {...@@ -79,16 +79,16 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {
79 }79 }
80 break;80 break;
81 }81 }
82 var carry: u64 = 0;82 var carry: i64 = 0;
83 i = 0;83 i = 0;
84 while (i <= n) : (i += 1) {84 while (i <= n) : (i += 1) {
85 const p = qhat * limb(&vn, i);85 const p = qhat * limb(&vn, i);
86 const t = limb(&un, i + j) - carry - @truncate(u32, p);86 const t = limb(&un, i + j) - carry - @truncate(u32, p);
87 limb_set(&un, i + j, @truncate(u32, t));87 limb_set(&un, i + j, @truncate(u32, @bitCast(u64, t)));
88 carry = @intCast(u64, p >> 32) - @intCast(u64, t >> 32);88 carry = @intCast(i64, p >> 32) - @intCast(i64, t >> 32);
89 }89 }
90 const t = limb(&un, j + n + 1) - carry;90 const t = limb(&un, j + n + 1) -% carry;
91 limb_set(&un, j + n + 1, @truncate(u32, t));91 limb_set(&un, j + n + 1, @truncate(u32, @bitCast(u64, t)));
92 if (q) |q_| limb_set(q_, j, @truncate(u32, qhat));92 if (q) |q_| limb_set(q_, j, @truncate(u32, qhat));
93 if (t < 0) {93 if (t < 0) {
94 if (q) |q_| limb_set(q_, j, limb(q_, j) - 1);94 if (q) |q_| limb_set(q_, j, limb(q_, j) - 1);
...@@ -99,7 +99,7 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {...@@ -99,7 +99,7 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {
99 limb_set(&un, i + j, @truncate(u32, t2));99 limb_set(&un, i + j, @truncate(u32, t2));
100 carry2 = t2 >> 32;100 carry2 = t2 >> 32;
101 }101 }
102 limb_set(un, j + n + 1, @truncate(u32, limb(&un, j + n + 1) + carry2));102 limb_set(&un, j + n + 1, @truncate(u32, limb(&un, j + n + 1) + carry2));
103 }103 }
104 if (j == 0) break;104 if (j == 0) break;
105 }105 }
lib/docs/main.js-4
...@@ -1187,10 +1187,6 @@ const NAV_MODES = {...@@ -1187,10 +1187,6 @@ const NAV_MODES = {
1187 payloadHtml += "panic";1187 payloadHtml += "panic";
1188 break;1188 break;
1189 }1189 }
1190 case "set_cold": {
1191 payloadHtml += "setCold";
1192 break;
1193 }
1194 case "set_runtime_safety": {1190 case "set_runtime_safety": {
1195 payloadHtml += "setRuntimeSafety";1191 payloadHtml += "setRuntimeSafety";
1196 break;1192 break;
lib/std/Build.zig+243-245
...@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");...@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");
32pub const CheckFileStep = @import("Build/CheckFileStep.zig");32pub const CheckFileStep = @import("Build/CheckFileStep.zig");
33pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");33pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
34pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");34pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
35pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
36pub const FmtStep = @import("Build/FmtStep.zig");35pub const FmtStep = @import("Build/FmtStep.zig");
37pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");36pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
38pub const InstallDirStep = @import("Build/InstallDirStep.zig");37pub const InstallDirStep = @import("Build/InstallDirStep.zig");
39pub const InstallFileStep = @import("Build/InstallFileStep.zig");38pub const InstallFileStep = @import("Build/InstallFileStep.zig");
40pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");39pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");
41pub const CompileStep = @import("Build/CompileStep.zig");40pub const CompileStep = @import("Build/CompileStep.zig");
42pub const LogStep = @import("Build/LogStep.zig");
43pub const OptionsStep = @import("Build/OptionsStep.zig");41pub const OptionsStep = @import("Build/OptionsStep.zig");
44pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");42pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
45pub const RunStep = @import("Build/RunStep.zig");43pub const RunStep = @import("Build/RunStep.zig");
...@@ -59,15 +57,12 @@ verbose_air: bool,...@@ -59,15 +57,12 @@ verbose_air: bool,
59verbose_llvm_ir: bool,57verbose_llvm_ir: bool,
60verbose_cimport: bool,58verbose_cimport: bool,
61verbose_llvm_cpu_features: bool,59verbose_llvm_cpu_features: bool,
62/// The purpose of executing the command is for a human to read compile errors from the terminal
63prominent_compile_errors: bool,
64color: enum { auto, on, off } = .auto,
65reference_trace: ?u32 = null,60reference_trace: ?u32 = null,
66invalid_user_input: bool,61invalid_user_input: bool,
67zig_exe: []const u8,62zig_exe: []const u8,
68default_step: *Step,63default_step: *Step,
69env_map: *EnvMap,64env_map: *EnvMap,
70top_level_steps: ArrayList(*TopLevelStep),65top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep),
71install_prefix: []const u8,66install_prefix: []const u8,
72dest_dir: ?[]const u8,67dest_dir: ?[]const u8,
73lib_dir: []const u8,68lib_dir: []const u8,
...@@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,...@@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
90args: ?[][]const u8 = null,85args: ?[][]const u8 = null,
91debug_log_scopes: []const []const u8 = &.{},86debug_log_scopes: []const []const u8 = &.{},
92debug_compile_errors: bool = false,87debug_compile_errors: bool = false,
88debug_pkg_config: bool = false,
9389
94/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.90/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
95enable_darling: bool = false,91enable_darling: bool = false,
...@@ -198,7 +194,7 @@ pub fn create(...@@ -198,7 +194,7 @@ pub fn create(
198 env_map.* = try process.getEnvMap(allocator);194 env_map.* = try process.getEnvMap(allocator);
199195
200 const self = try allocator.create(Build);196 const self = try allocator.create(Build);
201 self.* = Build{197 self.* = .{
202 .zig_exe = zig_exe,198 .zig_exe = zig_exe,
203 .build_root = build_root,199 .build_root = build_root,
204 .cache_root = cache_root,200 .cache_root = cache_root,
...@@ -211,13 +207,12 @@ pub fn create(...@@ -211,13 +207,12 @@ pub fn create(
211 .verbose_llvm_ir = false,207 .verbose_llvm_ir = false,
212 .verbose_cimport = false,208 .verbose_cimport = false,
213 .verbose_llvm_cpu_features = false,209 .verbose_llvm_cpu_features = false,
214 .prominent_compile_errors = false,
215 .invalid_user_input = false,210 .invalid_user_input = false,
216 .allocator = allocator,211 .allocator = allocator,
217 .user_input_options = UserInputOptionsMap.init(allocator),212 .user_input_options = UserInputOptionsMap.init(allocator),
218 .available_options_map = AvailableOptionsMap.init(allocator),213 .available_options_map = AvailableOptionsMap.init(allocator),
219 .available_options_list = ArrayList(AvailableOption).init(allocator),214 .available_options_list = ArrayList(AvailableOption).init(allocator),
220 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),215 .top_level_steps = .{},
221 .default_step = undefined,216 .default_step = undefined,
222 .env_map = env_map,217 .env_map = env_map,
223 .search_prefixes = ArrayList([]const u8).init(allocator),218 .search_prefixes = ArrayList([]const u8).init(allocator),
...@@ -227,12 +222,21 @@ pub fn create(...@@ -227,12 +222,21 @@ pub fn create(
227 .h_dir = undefined,222 .h_dir = undefined,
228 .dest_dir = env_map.get("DESTDIR"),223 .dest_dir = env_map.get("DESTDIR"),
229 .installed_files = ArrayList(InstalledFile).init(allocator),224 .installed_files = ArrayList(InstalledFile).init(allocator),
230 .install_tls = TopLevelStep{225 .install_tls = .{
231 .step = Step.initNoOp(.top_level, "install", allocator),226 .step = Step.init(.{
227 .id = .top_level,
228 .name = "install",
229 .owner = self,
230 }),
232 .description = "Copy build artifacts to prefix path",231 .description = "Copy build artifacts to prefix path",
233 },232 },
234 .uninstall_tls = TopLevelStep{233 .uninstall_tls = .{
235 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),234 .step = Step.init(.{
235 .id = .top_level,
236 .name = "uninstall",
237 .owner = self,
238 .makeFn = makeUninstall,
239 }),
236 .description = "Remove build artifacts from prefix path",240 .description = "Remove build artifacts from prefix path",
237 },241 },
238 .zig_lib_dir = null,242 .zig_lib_dir = null,
...@@ -241,8 +245,8 @@ pub fn create(...@@ -241,8 +245,8 @@ pub fn create(
241 .host = host,245 .host = host,
242 .modules = std.StringArrayHashMap(*Module).init(allocator),246 .modules = std.StringArrayHashMap(*Module).init(allocator),
243 };247 };
244 try self.top_level_steps.append(&self.install_tls);248 try self.top_level_steps.put(allocator, self.install_tls.step.name, &self.install_tls);
245 try self.top_level_steps.append(&self.uninstall_tls);249 try self.top_level_steps.put(allocator, self.uninstall_tls.step.name, &self.uninstall_tls);
246 self.default_step = &self.install_tls.step;250 self.default_step = &self.install_tls.step;
247 return self;251 return self;
248}252}
...@@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc...@@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
264 child.* = .{268 child.* = .{
265 .allocator = allocator,269 .allocator = allocator,
266 .install_tls = .{270 .install_tls = .{
267 .step = Step.initNoOp(.top_level, "install", allocator),271 .step = Step.init(.{
272 .id = .top_level,
273 .name = "install",
274 .owner = child,
275 }),
268 .description = "Copy build artifacts to prefix path",276 .description = "Copy build artifacts to prefix path",
269 },277 },
270 .uninstall_tls = .{278 .uninstall_tls = .{
271 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),279 .step = Step.init(.{
280 .id = .top_level,
281 .name = "uninstall",
282 .owner = child,
283 .makeFn = makeUninstall,
284 }),
272 .description = "Remove build artifacts from prefix path",285 .description = "Remove build artifacts from prefix path",
273 },286 },
274 .user_input_options = UserInputOptionsMap.init(allocator),287 .user_input_options = UserInputOptionsMap.init(allocator),
...@@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc...@@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
281 .verbose_llvm_ir = parent.verbose_llvm_ir,294 .verbose_llvm_ir = parent.verbose_llvm_ir,
282 .verbose_cimport = parent.verbose_cimport,295 .verbose_cimport = parent.verbose_cimport,
283 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,296 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
284 .prominent_compile_errors = parent.prominent_compile_errors,
285 .color = parent.color,
286 .reference_trace = parent.reference_trace,297 .reference_trace = parent.reference_trace,
287 .invalid_user_input = false,298 .invalid_user_input = false,
288 .zig_exe = parent.zig_exe,299 .zig_exe = parent.zig_exe,
289 .default_step = undefined,300 .default_step = undefined,
290 .env_map = parent.env_map,301 .env_map = parent.env_map,
291 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),302 .top_level_steps = .{},
292 .install_prefix = undefined,303 .install_prefix = undefined,
293 .dest_dir = parent.dest_dir,304 .dest_dir = parent.dest_dir,
294 .lib_dir = parent.lib_dir,305 .lib_dir = parent.lib_dir,
...@@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc...@@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
306 .zig_lib_dir = parent.zig_lib_dir,317 .zig_lib_dir = parent.zig_lib_dir,
307 .debug_log_scopes = parent.debug_log_scopes,318 .debug_log_scopes = parent.debug_log_scopes,
308 .debug_compile_errors = parent.debug_compile_errors,319 .debug_compile_errors = parent.debug_compile_errors,
320 .debug_pkg_config = parent.debug_pkg_config,
309 .enable_darling = parent.enable_darling,321 .enable_darling = parent.enable_darling,
310 .enable_qemu = parent.enable_qemu,322 .enable_qemu = parent.enable_qemu,
311 .enable_rosetta = parent.enable_rosetta,323 .enable_rosetta = parent.enable_rosetta,
...@@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc...@@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
316 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),328 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
317 .modules = std.StringArrayHashMap(*Module).init(allocator),329 .modules = std.StringArrayHashMap(*Module).init(allocator),
318 };330 };
319 try child.top_level_steps.append(&child.install_tls);331 try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls);
320 try child.top_level_steps.append(&child.uninstall_tls);332 try child.top_level_steps.put(allocator, child.uninstall_tls.step.name, &child.uninstall_tls);
321 child.default_step = &child.install_tls.step;333 child.default_step = &child.install_tls.step;
322 return child;334 return child;
323}335}
...@@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void {...@@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void {
372 },384 },
373 }385 }
374 }386 }
375 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);387
388 // Create an installation directory local to this package. This will be used when
389 // dependant packages require a standard prefix, such as include directories for C headers.
390 var hash = b.cache.hash;
376 // Random bytes to make unique. Refresh this with new random bytes when391 // Random bytes to make unique. Refresh this with new random bytes when
377 // implementation is modified in a non-backwards-compatible way.392 // implementation is modified in a non-backwards-compatible way.
378 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");393 hash.add(@as(u32, 0xd8cb0055));
379 hash.update(b.dep_prefix);394 hash.addBytes(b.dep_prefix);
380 // TODO additionally update the hash with `args`.395 // TODO additionally update the hash with `args`.
381396 const digest = hash.final();
382 var digest: [16]u8 = undefined;397 const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest });
383 hash.final(&digest);
384 var hash_basename: [digest.len * 2]u8 = undefined;
385 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
386 unreachable;
387
388 const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename });
389 b.resolveInstallPrefix(install_prefix, .{});398 b.resolveInstallPrefix(install_prefix, .{});
390}399}
391400
392pub fn destroy(self: *Build) void {401pub fn destroy(b: *Build) void {
393 self.env_map.deinit();402 b.env_map.deinit();
394 self.top_level_steps.deinit();403 b.top_level_steps.deinit(b.allocator);
395 self.allocator.destroy(self);404 b.allocator.destroy(b);
396}405}
397406
398/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.407/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
...@@ -441,6 +450,7 @@ pub const ExecutableOptions = struct {...@@ -441,6 +450,7 @@ pub const ExecutableOptions = struct {
441 target: CrossTarget = .{},450 target: CrossTarget = .{},
442 optimize: std.builtin.Mode = .Debug,451 optimize: std.builtin.Mode = .Debug,
443 linkage: ?CompileStep.Linkage = null,452 linkage: ?CompileStep.Linkage = null,
453 max_rss: usize = 0,
444};454};
445455
446pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {456pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
...@@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {...@@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
452 .optimize = options.optimize,462 .optimize = options.optimize,
453 .kind = .exe,463 .kind = .exe,
454 .linkage = options.linkage,464 .linkage = options.linkage,
465 .max_rss = options.max_rss,
455 });466 });
456}467}
457468
...@@ -460,6 +471,7 @@ pub const ObjectOptions = struct {...@@ -460,6 +471,7 @@ pub const ObjectOptions = struct {
460 root_source_file: ?FileSource = null,471 root_source_file: ?FileSource = null,
461 target: CrossTarget,472 target: CrossTarget,
462 optimize: std.builtin.Mode,473 optimize: std.builtin.Mode,
474 max_rss: usize = 0,
463};475};
464476
465pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {477pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
...@@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {...@@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
469 .target = options.target,481 .target = options.target,
470 .optimize = options.optimize,482 .optimize = options.optimize,
471 .kind = .obj,483 .kind = .obj,
484 .max_rss = options.max_rss,
472 });485 });
473}486}
474487
...@@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct {...@@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct {
478 version: ?std.builtin.Version = null,491 version: ?std.builtin.Version = null,
479 target: CrossTarget,492 target: CrossTarget,
480 optimize: std.builtin.Mode,493 optimize: std.builtin.Mode,
494 max_rss: usize = 0,
481};495};
482496
483pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {497pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
...@@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {...@@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
489 .version = options.version,503 .version = options.version,
490 .target = options.target,504 .target = options.target,
491 .optimize = options.optimize,505 .optimize = options.optimize,
506 .max_rss = options.max_rss,
492 });507 });
493}508}
494509
...@@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct {...@@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct {
498 target: CrossTarget,513 target: CrossTarget,
499 optimize: std.builtin.Mode,514 optimize: std.builtin.Mode,
500 version: ?std.builtin.Version = null,515 version: ?std.builtin.Version = null,
516 max_rss: usize = 0,
501};517};
502518
503pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {519pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
...@@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {...@@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
509 .version = options.version,525 .version = options.version,
510 .target = options.target,526 .target = options.target,
511 .optimize = options.optimize,527 .optimize = options.optimize,
528 .max_rss = options.max_rss,
512 });529 });
513}530}
514531
515pub const TestOptions = struct {532pub const TestOptions = struct {
516 name: []const u8 = "test",533 name: []const u8 = "test",
517 kind: CompileStep.Kind = .@"test",
518 root_source_file: FileSource,534 root_source_file: FileSource,
519 target: CrossTarget = .{},535 target: CrossTarget = .{},
520 optimize: std.builtin.Mode = .Debug,536 optimize: std.builtin.Mode = .Debug,
521 version: ?std.builtin.Version = null,537 version: ?std.builtin.Version = null,
538 max_rss: usize = 0,
522};539};
523540
524pub fn addTest(b: *Build, options: TestOptions) *CompileStep {541pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
525 return CompileStep.create(b, .{542 return CompileStep.create(b, .{
526 .name = options.name,543 .name = options.name,
527 .kind = options.kind,544 .kind = .@"test",
528 .root_source_file = options.root_source_file,545 .root_source_file = options.root_source_file,
529 .target = options.target,546 .target = options.target,
530 .optimize = options.optimize,547 .optimize = options.optimize,
548 .max_rss = options.max_rss,
531 });549 });
532}550}
533551
...@@ -536,6 +554,7 @@ pub const AssemblyOptions = struct {...@@ -536,6 +554,7 @@ pub const AssemblyOptions = struct {
536 source_file: FileSource,554 source_file: FileSource,
537 target: CrossTarget,555 target: CrossTarget,
538 optimize: std.builtin.Mode,556 optimize: std.builtin.Mode,
557 max_rss: usize = 0,
539};558};
540559
541pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {560pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
...@@ -545,22 +564,19 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {...@@ -545,22 +564,19 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
545 .root_source_file = null,564 .root_source_file = null,
546 .target = options.target,565 .target = options.target,
547 .optimize = options.optimize,566 .optimize = options.optimize,
567 .max_rss = options.max_rss,
548 });568 });
549 obj_step.addAssemblyFileSource(options.source_file.dupe(b));569 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
550 return obj_step;570 return obj_step;
551}571}
552572
553pub const AddModuleOptions = struct {573/// This function creates a module and adds it to the package's module set, making
554 name: []const u8,574/// it available to other packages which depend on this one.
555 source_file: FileSource,575/// `createModule` can be used instead to create a private module.
556 dependencies: []const ModuleDependency = &.{},576pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module {
557};577 const module = b.createModule(options);
558578 b.modules.put(b.dupe(name), module) catch @panic("OOM");
559pub fn addModule(b: *Build, options: AddModuleOptions) void {579 return module;
560 b.modules.put(b.dupe(options.name), b.createModule(.{
561 .source_file = options.source_file,
562 .dependencies = options.dependencies,
563 })) catch @panic("OOM");
564}580}
565581
566pub const ModuleDependency = struct {582pub const ModuleDependency = struct {
...@@ -573,8 +589,9 @@ pub const CreateModuleOptions = struct {...@@ -573,8 +589,9 @@ pub const CreateModuleOptions = struct {
573 dependencies: []const ModuleDependency = &.{},589 dependencies: []const ModuleDependency = &.{},
574};590};
575591
576/// Prefer to use `addModule` which will make the module available to other592/// This function creates a private module, to be used by the current package,
577/// packages which depend on this package.593/// but not exposed to other packages depending on this one.
594/// `addModule` can be used instead to create a public module.
578pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {595pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {
579 const module = b.allocator.create(Module) catch @panic("OOM");596 const module = b.allocator.create(Module) catch @panic("OOM");
580 module.* = .{597 module.* = .{
...@@ -608,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {...@@ -608,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
608/// Creates a `RunStep` with an executable built with `addExecutable`.625/// Creates a `RunStep` with an executable built with `addExecutable`.
609/// Add command line arguments with methods of `RunStep`.626/// Add command line arguments with methods of `RunStep`.
610pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {627pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
611 assert(exe.kind == .exe or exe.kind == .test_exe);
612
613 // It doesn't have to be native. We catch that if you actually try to run it.628 // It doesn't have to be native. We catch that if you actually try to run it.
614 // Consider that this is declarative; the run step may not be run unless a user629 // Consider that this is declarative; the run step may not be run unless a user
615 // option is supplied.630 // option is supplied.
616 const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name}));631 const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name}));
617 run_step.addArtifactArg(exe);632 run_step.addArtifactArg(exe);
618633
619 if (exe.kind == .test_exe) {634 if (exe.kind == .@"test") {
620 run_step.addArg(b.zig_exe);635 run_step.stdio = .zig_test;
636 run_step.addArgs(&.{"--listen=-"});
621 }637 }
622638
623 if (exe.vcpkg_bin_path) |path| {639 if (exe.vcpkg_bin_path) |path| {
...@@ -637,7 +653,11 @@ pub fn addConfigHeader(...@@ -637,7 +653,11 @@ pub fn addConfigHeader(
637 options: ConfigHeaderStep.Options,653 options: ConfigHeaderStep.Options,
638 values: anytype,654 values: anytype,
639) *ConfigHeaderStep {655) *ConfigHeaderStep {
640 const config_header_step = ConfigHeaderStep.create(b, options);656 var options_copy = options;
657 if (options_copy.first_ret_addr == null)
658 options_copy.first_ret_addr = @returnAddress();
659
660 const config_header_step = ConfigHeaderStep.create(b, options_copy);
641 config_header_step.addValues(values);661 config_header_step.addValues(values);
642 return config_header_step;662 return config_header_step;
643}663}
...@@ -674,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ...@@ -674,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ
674 return write_file_step;694 return write_file_step;
675}695}
676696
677pub fn addWriteFiles(self: *Build) *WriteFileStep {697pub fn addWriteFiles(b: *Build) *WriteFileStep {
678 const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");698 return WriteFileStep.create(b);
679 write_file_step.* = WriteFileStep.init(self);
680 return write_file_step;
681}
682
683pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
684 const data = self.fmt(format, args);
685 const log_step = self.allocator.create(LogStep) catch @panic("OOM");
686 log_step.* = LogStep.init(self, data);
687 return log_step;
688}699}
689700
690pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {701pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
...@@ -693,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {...@@ -693,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
693 return remove_dir_step;704 return remove_dir_step;
694}705}
695706
696pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {707pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep {
697 return FmtStep.create(self, paths);708 return FmtStep.create(b, options);
698}709}
699710
700pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {711pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
701 return TranslateCStep.create(self, options);712 return TranslateCStep.create(self, options);
702}713}
703714
704pub fn make(self: *Build, step_names: []const []const u8) !void {
705 var wanted_steps = ArrayList(*Step).init(self.allocator);
706 defer wanted_steps.deinit();
707
708 if (step_names.len == 0) {
709 try wanted_steps.append(self.default_step);
710 } else {
711 for (step_names) |step_name| {
712 const s = try self.getTopLevelStepByName(step_name);
713 try wanted_steps.append(s);
714 }
715 }
716
717 for (wanted_steps.items) |s| {
718 try self.makeOneStep(s);
719 }
720}
721
722pub fn getInstallStep(self: *Build) *Step {715pub fn getInstallStep(self: *Build) *Step {
723 return &self.install_tls.step;716 return &self.install_tls.step;
724}717}
...@@ -727,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step {...@@ -727,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step {
727 return &self.uninstall_tls.step;720 return &self.uninstall_tls.step;
728}721}
729722
730fn makeUninstall(uninstall_step: *Step) anyerror!void {723fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void {
724 _ = prog_node;
731 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);725 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
732 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);726 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
733727
...@@ -742,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void {...@@ -742,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void {
742 // TODO remove empty directories736 // TODO remove empty directories
743}737}
744738
745fn makeOneStep(self: *Build, s: *Step) anyerror!void {
746 if (s.loop_flag) {
747 log.err("Dependency loop detected:\n {s}", .{s.name});
748 return error.DependencyLoopDetected;
749 }
750 s.loop_flag = true;
751
752 for (s.dependencies.items) |dep| {
753 self.makeOneStep(dep) catch |err| {
754 if (err == error.DependencyLoopDetected) {
755 log.err(" {s}", .{s.name});
756 }
757 return err;
758 };
759 }
760
761 s.loop_flag = false;
762
763 try s.make();
764}
765
766fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
767 for (self.top_level_steps.items) |top_level_step| {
768 if (mem.eql(u8, top_level_step.step.name, name)) {
769 return &top_level_step.step;
770 }
771 }
772 log.err("Cannot run step '{s}' because it does not exist", .{name});
773 return error.InvalidStepName;
774}
775
776pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {739pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
777 const name = self.dupe(name_raw);740 const name = self.dupe(name_raw);
778 const description = self.dupe(description_raw);741 const description = self.dupe(description_raw);
...@@ -909,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -909,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
909872
910pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {873pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
911 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");874 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
912 step_info.* = TopLevelStep{875 step_info.* = .{
913 .step = Step.initNoOp(.top_level, name, self.allocator),876 .step = Step.init(.{
877 .id = .top_level,
878 .name = name,
879 .owner = self,
880 }),
914 .description = self.dupe(description),881 .description = self.dupe(description),
915 };882 };
916 self.top_level_steps.append(step_info) catch @panic("OOM");883 self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM");
917 return &step_info.step;884 return &step_info.step;
918}885}
919886
...@@ -1181,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool {...@@ -1181,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool {
1181 return self.invalid_user_input;1148 return self.invalid_user_input;
1182}1149}
11831150
1184pub fn spawnChild(self: *Build, argv: []const []const u8) !void {1151fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
1185 return self.spawnChildEnvMap(null, self.env_map, argv);1152 var buf = ArrayList(u8).init(ally);
1186}1153 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
1187
1188fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1189 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1190 for (argv) |arg| {1154 for (argv) |arg| {
1191 std.debug.print("{s} ", .{arg});1155 try buf.writer().print("{s} ", .{arg});
1192 }1156 }
1193 std.debug.print("\n", .{});1157 return buf.toOwnedSlice();
1194}1158}
11951159
1196pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {1160fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
1197 if (self.verbose) {1161 const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM");
1198 printCmd(cwd, argv);1162 std.debug.print("{s}\n", .{text});
1199 }
1200
1201 if (!std.process.can_spawn)
1202 return error.ExecNotSupported;
1203
1204 var child = std.ChildProcess.init(argv, self.allocator);
1205 child.cwd = cwd;
1206 child.env_map = env_map;
1207
1208 const term = child.spawnAndWait() catch |err| {
1209 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1210 return err;
1211 };
1212
1213 switch (term) {
1214 .Exited => |code| {
1215 if (code != 0) {
1216 log.err("The following command exited with error code {}:", .{code});
1217 printCmd(cwd, argv);
1218 return error.UncleanExit;
1219 }
1220 },
1221 else => {
1222 log.err("The following command terminated unexpectedly:", .{});
1223 printCmd(cwd, argv);
1224
1225 return error.UncleanExit;
1226 },
1227 }
1228}1163}
12291164
1230pub fn installArtifact(self: *Build, artifact: *CompileStep) void {1165pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
...@@ -1283,12 +1218,7 @@ pub fn addInstallFileWithDir(...@@ -1283,12 +1218,7 @@ pub fn addInstallFileWithDir(
1283 install_dir: InstallDir,1218 install_dir: InstallDir,
1284 dest_rel_path: []const u8,1219 dest_rel_path: []const u8,
1285) *InstallFileStep {1220) *InstallFileStep {
1286 if (dest_rel_path.len == 0) {1221 return InstallFileStep.create(self, source.dupe(self), install_dir, dest_rel_path);
1287 panic("dest_rel_path must be non-empty", .{});
1288 }
1289 const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM");
1290 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1291 return install_step;
1292}1222}
12931223
1294pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {1224pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
...@@ -1297,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst...@@ -1297,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst
1297 return install_step;1227 return install_step;
1298}1228}
12991229
1230pub fn addCheckFile(
1231 b: *Build,
1232 file_source: FileSource,
1233 options: CheckFileStep.Options,
1234) *CheckFileStep {
1235 return CheckFileStep.create(b, file_source, options);
1236}
1237
1300pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {1238pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
1301 const file = InstalledFile{1239 const file = InstalledFile{
1302 .dir = dir,1240 .dir = dir,
...@@ -1305,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u...@@ -1305,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u
1305 self.installed_files.append(file.dupe(self)) catch @panic("OOM");1243 self.installed_files.append(file.dupe(self)) catch @panic("OOM");
1306}1244}
13071245
1308pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
1309 if (self.verbose) {
1310 log.info("cp {s} {s} ", .{ source_path, dest_path });
1311 }
1312 const cwd = fs.cwd();
1313 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1314 if (self.verbose) switch (prev_status) {
1315 .stale => log.info("# installed", .{}),
1316 .fresh => log.info("# up-to-date", .{}),
1317 };
1318}
1319
1320pub fn truncateFile(self: *Build, dest_path: []const u8) !void {1246pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1321 if (self.verbose) {1247 if (self.verbose) {
1322 log.info("truncate {s}", .{dest_path});1248 log.info("truncate {s}", .{dest_path});
...@@ -1400,7 +1326,7 @@ pub fn execAllowFail(...@@ -1400,7 +1326,7 @@ pub fn execAllowFail(
1400) ExecError![]u8 {1326) ExecError![]u8 {
1401 assert(argv.len != 0);1327 assert(argv.len != 0);
14021328
1403 if (!std.process.can_spawn)1329 if (!process.can_spawn)
1404 return error.ExecNotSupported;1330 return error.ExecNotSupported;
14051331
1406 const max_output_size = 400 * 1024;1332 const max_output_size = 400 * 1024;
...@@ -1433,59 +1359,27 @@ pub fn execAllowFail(...@@ -1433,59 +1359,27 @@ pub fn execAllowFail(
1433 }1359 }
1434}1360}
14351361
1436pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {1362/// This is a helper function to be called from build.zig scripts, *not* from
1437 assert(argv.len != 0);1363/// inside step make() functions. If any errors occur, it fails the build with
14381364/// a helpful message.
1439 if (self.verbose) {1365pub fn exec(b: *Build, argv: []const []const u8) []u8 {
1440 printCmd(null, argv);1366 if (!process.can_spawn) {
1441 }1367 std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{
14421368 try allocPrintCmd(b.allocator, null, argv),
1443 if (!std.process.can_spawn) {1369 });
1444 if (src_step) |s| log.err("{s}...", .{s.name});1370 process.exit(1);
1445 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1446 printCmd(null, argv);
1447 std.os.abort();
1448 }1371 }
14491372
1450 var code: u8 = undefined;1373 var code: u8 = undefined;
1451 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {1374 return b.execAllowFail(argv, &code, .Inherit) catch |err| {
1452 error.ExecNotSupported => {1375 const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");
1453 if (src_step) |s| log.err("{s}...", .{s.name});1376 std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{
1454 log.err("Unable to spawn the following command: cannot spawn child process", .{});1377 @errorName(err), printed_cmd,
1455 printCmd(null, argv);1378 });
1456 std.os.abort();1379 process.exit(1);
1457 },
1458 error.FileNotFound => {
1459 if (src_step) |s| log.err("{s}...", .{s.name});
1460 log.err("Unable to spawn the following command: file not found", .{});
1461 printCmd(null, argv);
1462 std.os.exit(@truncate(u8, code));
1463 },
1464 error.ExitCodeFailure => {
1465 if (src_step) |s| log.err("{s}...", .{s.name});
1466 if (self.prominent_compile_errors) {
1467 log.err("The step exited with error code {d}", .{code});
1468 } else {
1469 log.err("The following command exited with error code {d}:", .{code});
1470 printCmd(null, argv);
1471 }
1472
1473 std.os.exit(@truncate(u8, code));
1474 },
1475 error.ProcessTerminated => {
1476 if (src_step) |s| log.err("{s}...", .{s.name});
1477 log.err("The following command terminated unexpectedly:", .{});
1478 printCmd(null, argv);
1479 std.os.exit(@truncate(u8, code));
1480 },
1481 else => |e| return e,
1482 };1380 };
1483}1381}
14841382
1485pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1486 return self.execFromStep(argv, null);
1487}
1488
1489pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {1383pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1490 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");1384 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
1491}1385}
...@@ -1550,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {...@@ -1550,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
15501444
1551 const full_path = b.pathFromRoot("build.zig.zon");1445 const full_path = b.pathFromRoot("build.zig.zon");
1552 std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path });1446 std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path });
1553 std.process.exit(1);1447 process.exit(1);
1448}
1449
1450pub fn anonymousDependency(
1451 b: *Build,
1452 /// The path to the directory containing the dependency's build.zig file,
1453 /// relative to the current package's build.zig.
1454 relative_build_root: []const u8,
1455 /// A direct `@import` of the build.zig of the dependency.
1456 comptime build_zig: type,
1457 args: anytype,
1458) *Dependency {
1459 const arena = b.allocator;
1460 const build_root = b.build_root.join(arena, &.{relative_build_root}) catch @panic("OOM");
1461 const name = arena.dupe(u8, relative_build_root) catch @panic("OOM");
1462 for (name) |*byte| switch (byte.*) {
1463 '/', '\\' => byte.* = '.',
1464 else => continue,
1465 };
1466 return dependencyInner(b, name, build_root, build_zig, args);
1554}1467}
15551468
1556fn dependencyInner(1469pub fn dependencyInner(
1557 b: *Build,1470 b: *Build,
1558 name: []const u8,1471 name: []const u8,
1559 build_root_string: []const u8,1472 build_root_string: []const u8,
...@@ -1566,7 +1479,7 @@ fn dependencyInner(...@@ -1566,7 +1479,7 @@ fn dependencyInner(
1566 std.debug.print("unable to open '{s}': {s}\n", .{1479 std.debug.print("unable to open '{s}': {s}\n", .{
1567 build_root_string, @errorName(err),1480 build_root_string, @errorName(err),
1568 });1481 });
1569 std.process.exit(1);1482 process.exit(1);
1570 },1483 },
1571 };1484 };
1572 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");1485 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
...@@ -1610,7 +1523,7 @@ pub const GeneratedFile = struct {...@@ -1610,7 +1523,7 @@ pub const GeneratedFile = struct {
16101523
1611 pub fn getPath(self: GeneratedFile) []const u8 {1524 pub fn getPath(self: GeneratedFile) []const u8 {
1612 return self.path orelse std.debug.panic(1525 return self.path orelse std.debug.panic(
1613 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",1526 "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?",
1614 .{self.step.name},1527 .{self.step.name},
1615 );1528 );
1616 }1529 }
...@@ -1650,12 +1563,23 @@ pub const FileSource = union(enum) {...@@ -1650,12 +1563,23 @@ pub const FileSource = union(enum) {
1650 }1563 }
16511564
1652 /// Should only be called during make(), returns a path relative to the build root or absolute.1565 /// Should only be called during make(), returns a path relative to the build root or absolute.
1653 pub fn getPath(self: FileSource, builder: *Build) []const u8 {1566 pub fn getPath(self: FileSource, src_builder: *Build) []const u8 {
1654 const path = switch (self) {1567 return getPath2(self, src_builder, null);
1655 .path => |p| builder.pathFromRoot(p),1568 }
1656 .generated => |gen| gen.getPath(),1569
1657 };1570 /// Should only be called during make(), returns a path relative to the build root or absolute.
1658 return path;1571 /// asking_step is only used for debugging purposes; it's the step being run that is asking for
1572 /// the path.
1573 pub fn getPath2(self: FileSource, src_builder: *Build, asking_step: ?*Step) []const u8 {
1574 switch (self) {
1575 .path => |p| return src_builder.pathFromRoot(p),
1576 .generated => |gen| return gen.path orelse {
1577 std.debug.getStderrMutex().lock();
1578 const stderr = std.io.getStdErr();
1579 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
1580 @panic("misconfigured build script");
1581 },
1582 }
1659 }1583 }
16601584
1661 /// Duplicates the file source for a given builder.1585 /// Duplicates the file source for a given builder.
...@@ -1667,6 +1591,54 @@ pub const FileSource = union(enum) {...@@ -1667,6 +1591,54 @@ pub const FileSource = union(enum) {
1667 }1591 }
1668};1592};
16691593
1594/// In this function the stderr mutex has already been locked.
1595fn dumpBadGetPathHelp(
1596 s: *Step,
1597 stderr: fs.File,
1598 src_builder: *Build,
1599 asking_step: ?*Step,
1600) anyerror!void {
1601 const w = stderr.writer();
1602 try w.print(
1603 \\getPath() was called on a GeneratedFile that wasn't built yet.
1604 \\ source package path: {s}
1605 \\ Is there a missing Step dependency on step '{s}'?
1606 \\
1607 , .{
1608 src_builder.build_root.path orelse ".",
1609 s.name,
1610 });
1611
1612 const tty_config = std.debug.detectTTYConfig(stderr);
1613 tty_config.setColor(w, .Red) catch {};
1614 try stderr.writeAll(" The step was created by this stack trace:\n");
1615 tty_config.setColor(w, .Reset) catch {};
1616
1617 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
1618 try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
1619 return;
1620 };
1621 const ally = debug_info.allocator;
1622 std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
1623 try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
1624 return;
1625 };
1626 if (asking_step) |as| {
1627 tty_config.setColor(w, .Red) catch {};
1628 try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n");
1629 tty_config.setColor(w, .Reset) catch {};
1630
1631 std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
1632 try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
1633 return;
1634 };
1635 }
1636
1637 tty_config.setColor(w, .Red) catch {};
1638 try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");
1639 tty_config.setColor(w, .Reset) catch {};
1640}
1641
1670/// Allocates a new string for assigning a value to a named macro.1642/// Allocates a new string for assigning a value to a named macro.
1671/// If the value is omitted, it is set to 1.1643/// If the value is omitted, it is set to 1.
1672/// `name` and `value` need not live longer than the function call.1644/// `name` and `value` need not live longer than the function call.
...@@ -1706,9 +1678,7 @@ pub const InstallDir = union(enum) {...@@ -1706,9 +1678,7 @@ pub const InstallDir = union(enum) {
1706 /// Duplicates the install directory including the path if set to custom.1678 /// Duplicates the install directory including the path if set to custom.
1707 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {1679 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
1708 if (self == .custom) {1680 if (self == .custom) {
1709 // Written with this temporary to avoid RLS problems1681 return .{ .custom = builder.dupe(self.custom) };
1710 const duped_path = builder.dupe(self.custom);
1711 return .{ .custom = duped_path };
1712 } else {1682 } else {
1713 return self;1683 return self;
1714 }1684 }
...@@ -1756,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {...@@ -1756,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1756 }1726 }
1757}1727}
17581728
1729/// This function is intended to be called in the `configure` phase only.
1730/// It returns an absolute directory path, which is potentially going to be a
1731/// source of API breakage in the future, so keep that in mind when using this
1732/// function.
1733pub fn makeTempPath(b: *Build) []const u8 {
1734 const rand_int = std.crypto.random.int(u64);
1735 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int);
1736 const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
1737 fs.cwd().makePath(result_path) catch |err| {
1738 std.debug.print("unable to make tmp path '{s}': {s}\n", .{
1739 result_path, @errorName(err),
1740 });
1741 };
1742 return result_path;
1743}
1744
1745/// There are a few copies of this function in miscellaneous places. Would be nice to find
1746/// a home for them.
1747fn hex64(x: u64) [16]u8 {
1748 const hex_charset = "0123456789abcdef";
1749 var result: [16]u8 = undefined;
1750 var i: usize = 0;
1751 while (i < 8) : (i += 1) {
1752 const byte = @truncate(u8, x >> @intCast(u6, 8 * i));
1753 result[i * 2 + 0] = hex_charset[byte >> 4];
1754 result[i * 2 + 1] = hex_charset[byte & 15];
1755 }
1756 return result;
1757}
1758
1759test {1759test {
1760 _ = CheckFileStep;1760 _ = CheckFileStep;
1761 _ = CheckObjectStep;1761 _ = CheckObjectStep;
1762 _ = EmulatableRunStep;
1763 _ = FmtStep;1762 _ = FmtStep;
1764 _ = InstallArtifactStep;1763 _ = InstallArtifactStep;
1765 _ = InstallDirStep;1764 _ = InstallDirStep;
1766 _ = InstallFileStep;1765 _ = InstallFileStep;
1767 _ = ObjCopyStep;1766 _ = ObjCopyStep;
1768 _ = CompileStep;1767 _ = CompileStep;
1769 _ = LogStep;
1770 _ = OptionsStep;1768 _ = OptionsStep;
1771 _ = RemoveDirStep;1769 _ = RemoveDirStep;
1772 _ = RunStep;1770 _ = RunStep;
lib/std/Build/Cache.zig+74-61
...@@ -7,27 +7,27 @@ pub const Directory = struct {...@@ -7,27 +7,27 @@ pub const Directory = struct {
7 /// directly, but it is needed when passing the directory to a child process.7 /// directly, but it is needed when passing the directory to a child process.
8 /// `null` means cwd.8 /// `null` means cwd.
9 path: ?[]const u8,9 path: ?[]const u8,
10 handle: std.fs.Dir,10 handle: fs.Dir,
1111
12 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {12 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
13 if (self.path) |p| {13 if (self.path) |p| {
14 // TODO clean way to do this with only 1 allocation14 // TODO clean way to do this with only 1 allocation
15 const part2 = try std.fs.path.join(allocator, paths);15 const part2 = try fs.path.join(allocator, paths);
16 defer allocator.free(part2);16 defer allocator.free(part2);
17 return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });17 return fs.path.join(allocator, &[_][]const u8{ p, part2 });
18 } else {18 } else {
19 return std.fs.path.join(allocator, paths);19 return fs.path.join(allocator, paths);
20 }20 }
21 }21 }
2222
23 pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {23 pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {
24 if (self.path) |p| {24 if (self.path) |p| {
25 // TODO clean way to do this with only 1 allocation25 // TODO clean way to do this with only 1 allocation
26 const part2 = try std.fs.path.join(allocator, paths);26 const part2 = try fs.path.join(allocator, paths);
27 defer allocator.free(part2);27 defer allocator.free(part2);
28 return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });28 return fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
29 } else {29 } else {
30 return std.fs.path.joinZ(allocator, paths);30 return fs.path.joinZ(allocator, paths);
31 }31 }
32 }32 }
3333
...@@ -39,6 +39,20 @@ pub const Directory = struct {...@@ -39,6 +39,20 @@ pub const Directory = struct {
39 if (self.path) |p| gpa.free(p);39 if (self.path) |p| gpa.free(p);
40 self.* = undefined;40 self.* = undefined;
41 }41 }
42
43 pub fn format(
44 self: Directory,
45 comptime fmt_string: []const u8,
46 options: fmt.FormatOptions,
47 writer: anytype,
48 ) !void {
49 _ = options;
50 if (fmt_string.len != 0) fmt.invalidFmtError(fmt, self);
51 if (self.path) |p| {
52 try writer.writeAll(p);
53 try writer.writeAll(fs.path.sep_str);
54 }
55 }
42};56};
4357
44gpa: Allocator,58gpa: Allocator,
...@@ -243,10 +257,10 @@ pub const HashHelper = struct {...@@ -243,10 +257,10 @@ pub const HashHelper = struct {
243 hh.hasher.final(&bin_digest);257 hh.hasher.final(&bin_digest);
244258
245 var out_digest: [hex_digest_len]u8 = undefined;259 var out_digest: [hex_digest_len]u8 = undefined;
246 _ = std.fmt.bufPrint(260 _ = fmt.bufPrint(
247 &out_digest,261 &out_digest,
248 "{s}",262 "{s}",
249 .{std.fmt.fmtSliceHexLower(&bin_digest)},263 .{fmt.fmtSliceHexLower(&bin_digest)},
250 ) catch unreachable;264 ) catch unreachable;
251 return out_digest;265 return out_digest;
252 }266 }
...@@ -365,10 +379,10 @@ pub const Manifest = struct {...@@ -365,10 +379,10 @@ pub const Manifest = struct {
365 var bin_digest: BinDigest = undefined;379 var bin_digest: BinDigest = undefined;
366 self.hash.hasher.final(&bin_digest);380 self.hash.hasher.final(&bin_digest);
367381
368 _ = std.fmt.bufPrint(382 _ = fmt.bufPrint(
369 &self.hex_digest,383 &self.hex_digest,
370 "{s}",384 "{s}",
371 .{std.fmt.fmtSliceHexLower(&bin_digest)},385 .{fmt.fmtSliceHexLower(&bin_digest)},
372 ) catch unreachable;386 ) catch unreachable;
373387
374 self.hash.hasher = hasher_init;388 self.hash.hasher = hasher_init;
...@@ -408,7 +422,11 @@ pub const Manifest = struct {...@@ -408,7 +422,11 @@ pub const Manifest = struct {
408 self.have_exclusive_lock = true;422 self.have_exclusive_lock = true;
409 return false; // cache miss; exclusive lock already held423 return false; // cache miss; exclusive lock already held
410 } else |err| switch (err) {424 } else |err| switch (err) {
411 error.WouldBlock => continue,425 // There are no dir components, so you would think
426 // that this was unreachable, however we have
427 // observed on macOS two processes racing to do
428 // openat() with O_CREAT manifest in ENOENT.
429 error.WouldBlock, error.FileNotFound => continue,
412 else => |e| return e,430 else => |e| return e,
413 }431 }
414 },432 },
...@@ -425,7 +443,10 @@ pub const Manifest = struct {...@@ -425,7 +443,10 @@ pub const Manifest = struct {
425 self.manifest_file = manifest_file;443 self.manifest_file = manifest_file;
426 self.have_exclusive_lock = true;444 self.have_exclusive_lock = true;
427 } else |err| switch (err) {445 } else |err| switch (err) {
428 error.WouldBlock => {446 // There are no dir components, so you would think that this was
447 // unreachable, however we have observed on macOS two processes racing
448 // to do openat() with O_CREAT manifest in ENOENT.
449 error.WouldBlock, error.FileNotFound => {
429 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{450 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{
430 .lock = .Shared,451 .lock = .Shared,
431 });452 });
...@@ -469,7 +490,7 @@ pub const Manifest = struct {...@@ -469,7 +490,7 @@ pub const Manifest = struct {
469 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;490 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
470 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;491 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
471 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;492 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
472 _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;493 _ = fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
473 const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat;494 const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat;
474 if (prefix >= self.cache.prefixes_len) return error.InvalidFormat;495 if (prefix >= self.cache.prefixes_len) return error.InvalidFormat;
475496
...@@ -806,10 +827,10 @@ pub const Manifest = struct {...@@ -806,10 +827,10 @@ pub const Manifest = struct {
806 self.hash.hasher.final(&bin_digest);827 self.hash.hasher.final(&bin_digest);
807828
808 var out_digest: [hex_digest_len]u8 = undefined;829 var out_digest: [hex_digest_len]u8 = undefined;
809 _ = std.fmt.bufPrint(830 _ = fmt.bufPrint(
810 &out_digest,831 &out_digest,
811 "{s}",832 "{s}",
812 .{std.fmt.fmtSliceHexLower(&bin_digest)},833 .{fmt.fmtSliceHexLower(&bin_digest)},
813 ) catch unreachable;834 ) catch unreachable;
814835
815 return out_digest;836 return out_digest;
...@@ -831,10 +852,10 @@ pub const Manifest = struct {...@@ -831,10 +852,10 @@ pub const Manifest = struct {
831 var encoded_digest: [hex_digest_len]u8 = undefined;852 var encoded_digest: [hex_digest_len]u8 = undefined;
832853
833 for (self.files.items) |file| {854 for (self.files.items) |file| {
834 _ = std.fmt.bufPrint(855 _ = fmt.bufPrint(
835 &encoded_digest,856 &encoded_digest,
836 "{s}",857 "{s}",
837 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},858 .{fmt.fmtSliceHexLower(&file.bin_digest)},
838 ) catch unreachable;859 ) catch unreachable;
839 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{860 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{
840 file.stat.size,861 file.stat.size,
...@@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {...@@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
955}976}
956977
957// Create/Write a file, close it, then grab its stat.mtime timestamp.978// Create/Write a file, close it, then grab its stat.mtime timestamp.
958fn testGetCurrentFileTimestamp() !i128 {979fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 {
959 const test_out_file = "test-filetimestamp.tmp";980 const test_out_file = "test-filetimestamp.tmp";
960981
961 var file = try fs.cwd().createFile(test_out_file, .{982 var file = try dir.createFile(test_out_file, .{
962 .read = true,983 .read = true,
963 .truncate = true,984 .truncate = true,
964 });985 });
965 defer {986 defer {
966 file.close();987 file.close();
967 fs.cwd().deleteFile(test_out_file) catch {};988 dir.deleteFile(test_out_file) catch {};
968 }989 }
969990
970 return (try file.stat()).mtime;991 return (try file.stat()).mtime;
...@@ -976,16 +997,17 @@ test "cache file and then recall it" {...@@ -976,16 +997,17 @@ test "cache file and then recall it" {
976 return error.SkipZigTest;997 return error.SkipZigTest;
977 }998 }
978999
979 const cwd = fs.cwd();1000 var tmp = testing.tmpDir(.{});
1001 defer tmp.cleanup();
9801002
981 const temp_file = "test.txt";1003 const temp_file = "test.txt";
982 const temp_manifest_dir = "temp_manifest_dir";1004 const temp_manifest_dir = "temp_manifest_dir";
9831005
984 try cwd.writeFile(temp_file, "Hello, world!\n");1006 try tmp.dir.writeFile(temp_file, "Hello, world!\n");
9851007
986 // Wait for file timestamps to tick1008 // Wait for file timestamps to tick
987 const initial_time = try testGetCurrentFileTimestamp();1009 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
988 while ((try testGetCurrentFileTimestamp()) == initial_time) {1010 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
989 std.time.sleep(1);1011 std.time.sleep(1);
990 }1012 }
9911013
...@@ -995,9 +1017,9 @@ test "cache file and then recall it" {...@@ -995,9 +1017,9 @@ test "cache file and then recall it" {
995 {1017 {
996 var cache = Cache{1018 var cache = Cache{
997 .gpa = testing.allocator,1019 .gpa = testing.allocator,
998 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1020 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
999 };1021 };
1000 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });1022 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
1001 defer cache.manifest_dir.close();1023 defer cache.manifest_dir.close();
10021024
1003 {1025 {
...@@ -1033,9 +1055,6 @@ test "cache file and then recall it" {...@@ -1033,9 +1055,6 @@ test "cache file and then recall it" {
10331055
1034 try testing.expectEqual(digest1, digest2);1056 try testing.expectEqual(digest1, digest2);
1035 }1057 }
1036
1037 try cwd.deleteTree(temp_manifest_dir);
1038 try cwd.deleteFile(temp_file);
1039}1058}
10401059
1041test "check that changing a file makes cache fail" {1060test "check that changing a file makes cache fail" {
...@@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" {...@@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" {
1043 // https://github.com/ziglang/zig/issues/54371062 // https://github.com/ziglang/zig/issues/5437
1044 return error.SkipZigTest;1063 return error.SkipZigTest;
1045 }1064 }
1046 const cwd = fs.cwd();1065 var tmp = testing.tmpDir(.{});
1066 defer tmp.cleanup();
10471067
1048 const temp_file = "cache_hash_change_file_test.txt";1068 const temp_file = "cache_hash_change_file_test.txt";
1049 const temp_manifest_dir = "cache_hash_change_file_manifest_dir";1069 const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
1050 const original_temp_file_contents = "Hello, world!\n";1070 const original_temp_file_contents = "Hello, world!\n";
1051 const updated_temp_file_contents = "Hello, world; but updated!\n";1071 const updated_temp_file_contents = "Hello, world; but updated!\n";
10521072
1053 try cwd.deleteTree(temp_manifest_dir);1073 try tmp.dir.writeFile(temp_file, original_temp_file_contents);
1054 try cwd.deleteTree(temp_file);
1055
1056 try cwd.writeFile(temp_file, original_temp_file_contents);
10571074
1058 // Wait for file timestamps to tick1075 // Wait for file timestamps to tick
1059 const initial_time = try testGetCurrentFileTimestamp();1076 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1060 while ((try testGetCurrentFileTimestamp()) == initial_time) {1077 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1061 std.time.sleep(1);1078 std.time.sleep(1);
1062 }1079 }
10631080
...@@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" {...@@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" {
1067 {1084 {
1068 var cache = Cache{1085 var cache = Cache{
1069 .gpa = testing.allocator,1086 .gpa = testing.allocator,
1070 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1087 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1071 };1088 };
1072 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });1089 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
1073 defer cache.manifest_dir.close();1090 defer cache.manifest_dir.close();
10741091
1075 {1092 {
...@@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" {...@@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" {
1089 try ch.writeManifest();1106 try ch.writeManifest();
1090 }1107 }
10911108
1092 try cwd.writeFile(temp_file, updated_temp_file_contents);1109 try tmp.dir.writeFile(temp_file, updated_temp_file_contents);
10931110
1094 {1111 {
1095 var ch = cache.obtain();1112 var ch = cache.obtain();
...@@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" {...@@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" {
11111128
1112 try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));1129 try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
1113 }1130 }
1114
1115 try cwd.deleteTree(temp_manifest_dir);
1116 try cwd.deleteTree(temp_file);
1117}1131}
11181132
1119test "no file inputs" {1133test "no file inputs" {
...@@ -1121,18 +1135,20 @@ test "no file inputs" {...@@ -1121,18 +1135,20 @@ test "no file inputs" {
1121 // https://github.com/ziglang/zig/issues/54371135 // https://github.com/ziglang/zig/issues/5437
1122 return error.SkipZigTest;1136 return error.SkipZigTest;
1123 }1137 }
1124 const cwd = fs.cwd();1138
1139 var tmp = testing.tmpDir(.{});
1140 defer tmp.cleanup();
1141
1125 const temp_manifest_dir = "no_file_inputs_manifest_dir";1142 const temp_manifest_dir = "no_file_inputs_manifest_dir";
1126 defer cwd.deleteTree(temp_manifest_dir) catch {};
11271143
1128 var digest1: [hex_digest_len]u8 = undefined;1144 var digest1: [hex_digest_len]u8 = undefined;
1129 var digest2: [hex_digest_len]u8 = undefined;1145 var digest2: [hex_digest_len]u8 = undefined;
11301146
1131 var cache = Cache{1147 var cache = Cache{
1132 .gpa = testing.allocator,1148 .gpa = testing.allocator,
1133 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1149 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1134 };1150 };
1135 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });1151 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
1136 defer cache.manifest_dir.close();1152 defer cache.manifest_dir.close();
11371153
1138 {1154 {
...@@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" {...@@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" {
1167 // https://github.com/ziglang/zig/issues/54371183 // https://github.com/ziglang/zig/issues/5437
1168 return error.SkipZigTest;1184 return error.SkipZigTest;
1169 }1185 }
1170 const cwd = fs.cwd();1186 var tmp = testing.tmpDir(.{});
1187 defer tmp.cleanup();
11711188
1172 const temp_file1 = "cache_hash_post_file_test1.txt";1189 const temp_file1 = "cache_hash_post_file_test1.txt";
1173 const temp_file2 = "cache_hash_post_file_test2.txt";1190 const temp_file2 = "cache_hash_post_file_test2.txt";
1174 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";1191 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
11751192
1176 try cwd.writeFile(temp_file1, "Hello, world!\n");1193 try tmp.dir.writeFile(temp_file1, "Hello, world!\n");
1177 try cwd.writeFile(temp_file2, "Hello world the second!\n");1194 try tmp.dir.writeFile(temp_file2, "Hello world the second!\n");
11781195
1179 // Wait for file timestamps to tick1196 // Wait for file timestamps to tick
1180 const initial_time = try testGetCurrentFileTimestamp();1197 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1181 while ((try testGetCurrentFileTimestamp()) == initial_time) {1198 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1182 std.time.sleep(1);1199 std.time.sleep(1);
1183 }1200 }
11841201
...@@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" {...@@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" {
1189 {1206 {
1190 var cache = Cache{1207 var cache = Cache{
1191 .gpa = testing.allocator,1208 .gpa = testing.allocator,
1192 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1209 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1193 };1210 };
1194 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });1211 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
1195 defer cache.manifest_dir.close();1212 defer cache.manifest_dir.close();
11961213
1197 {1214 {
...@@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" {...@@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" {
1224 try testing.expect(mem.eql(u8, &digest1, &digest2));1241 try testing.expect(mem.eql(u8, &digest1, &digest2));
12251242
1226 // Modify the file added after initial hash1243 // Modify the file added after initial hash
1227 try cwd.writeFile(temp_file2, "Hello world the second, updated\n");1244 try tmp.dir.writeFile(temp_file2, "Hello world the second, updated\n");
12281245
1229 // Wait for file timestamps to tick1246 // Wait for file timestamps to tick
1230 const initial_time2 = try testGetCurrentFileTimestamp();1247 const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir);
1231 while ((try testGetCurrentFileTimestamp()) == initial_time2) {1248 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) {
1232 std.time.sleep(1);1249 std.time.sleep(1);
1233 }1250 }
12341251
...@@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" {...@@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" {
12511268
1252 try testing.expect(!mem.eql(u8, &digest1, &digest3));1269 try testing.expect(!mem.eql(u8, &digest1, &digest3));
1253 }1270 }
1254
1255 try cwd.deleteTree(temp_manifest_dir);
1256 try cwd.deleteFile(temp_file1);
1257 try cwd.deleteFile(temp_file2);
1258}1271}
lib/std/Build/CheckFileStep.zig+62-25
...@@ -1,51 +1,88 @@...@@ -1,51 +1,88 @@
1const std = @import("../std.zig");1//! Fail the build step if a file does not match certain checks.
2const Step = std.Build.Step;2//! TODO: make this more flexible, supporting more kinds of checks.
3const fs = std.fs;3//! TODO: generalize the code in std.testing.expectEqualStrings and make this
4const mem = std.mem;4//! CheckFileStep produce those helpful diagnostics when there is not a match.
5
6const CheckFileStep = @This();
7
8pub const base_id = .check_file;
95
10step: Step,6step: Step,
11builder: *std.Build,
12expected_matches: []const []const u8,7expected_matches: []const []const u8,
8expected_exact: ?[]const u8,
13source: std.Build.FileSource,9source: std.Build.FileSource,
14max_bytes: usize = 20 * 1024 * 1024,10max_bytes: usize = 20 * 1024 * 1024,
1511
12pub const base_id = .check_file;
13
14pub const Options = struct {
15 expected_matches: []const []const u8 = &.{},
16 expected_exact: ?[]const u8 = null,
17};
18
16pub fn create(19pub fn create(
17 builder: *std.Build,20 owner: *std.Build,
18 source: std.Build.FileSource,21 source: std.Build.FileSource,
19 expected_matches: []const []const u8,22 options: Options,
20) *CheckFileStep {23) *CheckFileStep {
21 const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");24 const self = owner.allocator.create(CheckFileStep) catch @panic("OOM");
22 self.* = CheckFileStep{25 self.* = .{
23 .builder = builder,26 .step = Step.init(.{
24 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),27 .id = .check_file,
25 .source = source.dupe(builder),28 .name = "CheckFile",
26 .expected_matches = builder.dupeStrings(expected_matches),29 .owner = owner,
30 .makeFn = make,
31 }),
32 .source = source.dupe(owner),
33 .expected_matches = owner.dupeStrings(options.expected_matches),
34 .expected_exact = options.expected_exact,
27 };35 };
28 self.source.addStepDependencies(&self.step);36 self.source.addStepDependencies(&self.step);
29 return self;37 return self;
30}38}
3139
32fn make(step: *Step) !void {40pub fn setName(self: *CheckFileStep, name: []const u8) void {
41 self.step.name = name;
42}
43
44fn make(step: *Step, prog_node: *std.Progress.Node) !void {
45 _ = prog_node;
46 const b = step.owner;
33 const self = @fieldParentPtr(CheckFileStep, "step", step);47 const self = @fieldParentPtr(CheckFileStep, "step", step);
3448
35 const src_path = self.source.getPath(self.builder);49 const src_path = self.source.getPath(b);
36 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);50 const contents = fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes) catch |err| {
51 return step.fail("unable to read '{s}': {s}", .{
52 src_path, @errorName(err),
53 });
54 };
3755
38 for (self.expected_matches) |expected_match| {56 for (self.expected_matches) |expected_match| {
39 if (mem.indexOf(u8, contents, expected_match) == null) {57 if (mem.indexOf(u8, contents, expected_match) == null) {
40 std.debug.print(58 return step.fail(
41 \\59 \\
42 \\========= Expected to find: ===================60 \\========= expected to find: ===================
43 \\{s}61 \\{s}
44 \\========= But file does not contain it: =======62 \\========= but file does not contain it: =======
45 \\{s}63 \\{s}
46 \\64 \\===============================================
47 , .{ expected_match, contents });65 , .{ expected_match, contents });
48 return error.TestFailed;66 }
67 }
68
69 if (self.expected_exact) |expected_exact| {
70 if (!mem.eql(u8, expected_exact, contents)) {
71 return step.fail(
72 \\
73 \\========= expected: =====================
74 \\{s}
75 \\========= but found: ====================
76 \\{s}
77 \\========= from the following file: ======
78 \\{s}
79 , .{ expected_exact, contents, src_path });
49 }80 }
50 }81 }
51}82}
83
84const CheckFileStep = @This();
85const std = @import("../std.zig");
86const Step = std.Build.Step;
87const fs = std.fs;
88const mem = std.mem;
lib/std/Build/CheckObjectStep.zig+95-91
...@@ -10,25 +10,31 @@ const CheckObjectStep = @This();...@@ -10,25 +10,31 @@ const CheckObjectStep = @This();
1010
11const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
12const Step = std.Build.Step;12const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
1413
15pub const base_id = .check_object;14pub const base_id = .check_object;
1615
17step: Step,16step: Step,
18builder: *std.Build,
19source: std.Build.FileSource,17source: std.Build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,18max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),19checks: std.ArrayList(Check),
22dump_symtab: bool = false,20dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,21obj_format: std.Target.ObjectFormat,
2422
25pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {23pub fn create(
26 const gpa = builder.allocator;24 owner: *std.Build,
25 source: std.Build.FileSource,
26 obj_format: std.Target.ObjectFormat,
27) *CheckObjectStep {
28 const gpa = owner.allocator;
27 const self = gpa.create(CheckObjectStep) catch @panic("OOM");29 const self = gpa.create(CheckObjectStep) catch @panic("OOM");
28 self.* = .{30 self.* = .{
29 .builder = builder,31 .step = Step.init(.{
30 .step = Step.init(.check_file, "CheckObject", gpa, make),32 .id = .check_file,
31 .source = source.dupe(builder),33 .name = "CheckObject",
34 .owner = owner,
35 .makeFn = make,
36 }),
37 .source = source.dupe(owner),
32 .checks = std.ArrayList(Check).init(gpa),38 .checks = std.ArrayList(Check).init(gpa),
33 .obj_format = obj_format,39 .obj_format = obj_format,
34 };40 };
...@@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std...@@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std
3844
39/// Runs and (optionally) compares the output of a binary.45/// Runs and (optionally) compares the output of a binary.
40/// Asserts `self` was generated from an executable step.46/// Asserts `self` was generated from an executable step.
41pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {47/// TODO this doesn't actually compare, and there's no apparent reason for it
48/// to depend on the check object step. I don't see why this function should exist,
49/// the caller could just add the run step directly.
50pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
42 const dependencies_len = self.step.dependencies.items.len;51 const dependencies_len = self.step.dependencies.items.len;
43 assert(dependencies_len > 0);52 assert(dependencies_len > 0);
44 const exe_step = self.step.dependencies.items[dependencies_len - 1];53 const exe_step = self.step.dependencies.items[dependencies_len - 1];
45 const exe = exe_step.cast(std.Build.CompileStep).?;54 const exe = exe_step.cast(std.Build.CompileStep).?;
46 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);55 const run = self.step.owner.addRunArtifact(exe);
47 emulatable_step.step.dependOn(&self.step);56 run.skip_foreign_checks = true;
48 return emulatable_step;57 run.step.dependOn(&self.step);
58 return run;
49}59}
5060
51/// There two types of actions currently suported:61/// There two types of actions currently suported:
...@@ -123,7 +133,8 @@ const Action = struct {...@@ -123,7 +133,8 @@ const Action = struct {
123 /// Will return true if the `phrase` is correctly parsed into an RPN program and133 /// Will return true if the `phrase` is correctly parsed into an RPN program and
124 /// its reduced, computed value compares using `op` with the expected value, either134 /// its reduced, computed value compares using `op` with the expected value, either
125 /// a literal or another extracted variable.135 /// a literal or another extracted variable.
126 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {136 fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool {
137 const gpa = step.owner.allocator;
127 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);138 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
128 var values = std.ArrayList(u64).init(gpa);139 var values = std.ArrayList(u64).init(gpa);
129140
...@@ -140,11 +151,11 @@ const Action = struct {...@@ -140,11 +151,11 @@ const Action = struct {
140 } else {151 } else {
141 const val = std.fmt.parseInt(u64, next, 0) catch blk: {152 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142 break :blk global_vars.get(next) orelse {153 break :blk global_vars.get(next) orelse {
143 std.debug.print(154 try step.addError(
144 \\155 \\
145 \\========= Variable was not extracted: ===========156 \\========= variable was not extracted: ===========
146 \\{s}157 \\{s}
147 \\158 \\=================================================
148 , .{next});159 , .{next});
149 return error.UnknownVariable;160 return error.UnknownVariable;
150 };161 };
...@@ -176,11 +187,11 @@ const Action = struct {...@@ -176,11 +187,11 @@ const Action = struct {
176187
177 const exp_value = switch (act.expected.?.value) {188 const exp_value = switch (act.expected.?.value) {
178 .variable => |name| global_vars.get(name) orelse {189 .variable => |name| global_vars.get(name) orelse {
179 std.debug.print(190 try step.addError(
180 \\191 \\
181 \\========= Variable was not extracted: ===========192 \\========= variable was not extracted: ===========
182 \\{s}193 \\{s}
183 \\194 \\=================================================
184 , .{name});195 , .{name});
185 return error.UnknownVariable;196 return error.UnknownVariable;
186 },197 },
...@@ -249,7 +260,7 @@ const Check = struct {...@@ -249,7 +260,7 @@ const Check = struct {
249260
250/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.261/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
251pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {262pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
252 var new_check = Check.create(self.builder);263 var new_check = Check.create(self.step.owner);
253 new_check.match(phrase);264 new_check.match(phrase);
254 self.checks.append(new_check) catch @panic("OOM");265 self.checks.append(new_check) catch @panic("OOM");
255}266}
...@@ -291,34 +302,34 @@ pub fn checkComputeCompare(...@@ -291,34 +302,34 @@ pub fn checkComputeCompare(
291 program: []const u8,302 program: []const u8,
292 expected: ComputeCompareExpected,303 expected: ComputeCompareExpected,
293) void {304) void {
294 var new_check = Check.create(self.builder);305 var new_check = Check.create(self.step.owner);
295 new_check.computeCmp(program, expected);306 new_check.computeCmp(program, expected);
296 self.checks.append(new_check) catch @panic("OOM");307 self.checks.append(new_check) catch @panic("OOM");
297}308}
298309
299fn make(step: *Step) !void {310fn make(step: *Step, prog_node: *std.Progress.Node) !void {
311 _ = prog_node;
312 const b = step.owner;
313 const gpa = b.allocator;
300 const self = @fieldParentPtr(CheckObjectStep, "step", step);314 const self = @fieldParentPtr(CheckObjectStep, "step", step);
301315
302 const gpa = self.builder.allocator;316 const src_path = self.source.getPath(b);
303 const src_path = self.source.getPath(self.builder);317 const contents = fs.cwd().readFileAllocOptions(
304 const contents = try fs.cwd().readFileAllocOptions(
305 gpa,318 gpa,
306 src_path,319 src_path,
307 self.max_bytes,320 self.max_bytes,
308 null,321 null,
309 @alignOf(u64),322 @alignOf(u64),
310 null,323 null,
311 );324 ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) });
312325
313 const output = switch (self.obj_format) {326 const output = switch (self.obj_format) {
314 .macho => try MachODumper.parseAndDump(contents, .{327 .macho => try MachODumper.parseAndDump(step, contents, .{
315 .gpa = gpa,
316 .dump_symtab = self.dump_symtab,328 .dump_symtab = self.dump_symtab,
317 }),329 }),
318 .elf => @panic("TODO elf parser"),330 .elf => @panic("TODO elf parser"),
319 .coff => @panic("TODO coff parser"),331 .coff => @panic("TODO coff parser"),
320 .wasm => try WasmDumper.parseAndDump(contents, .{332 .wasm => try WasmDumper.parseAndDump(step, contents, .{
321 .gpa = gpa,
322 .dump_symtab = self.dump_symtab,333 .dump_symtab = self.dump_symtab,
323 }),334 }),
324 else => unreachable,335 else => unreachable,
...@@ -334,54 +345,50 @@ fn make(step: *Step) !void {...@@ -334,54 +345,50 @@ fn make(step: *Step) !void {
334 while (it.next()) |line| {345 while (it.next()) |line| {
335 if (try act.match(line, &vars)) break;346 if (try act.match(line, &vars)) break;
336 } else {347 } else {
337 std.debug.print(348 return step.fail(
338 \\349 \\
339 \\========= Expected to find: ==========================350 \\========= expected to find: ==========================
340 \\{s}351 \\{s}
341 \\========= But parsed file does not contain it: =======352 \\========= but parsed file does not contain it: =======
342 \\{s}353 \\{s}
343 \\354 \\======================================================
344 , .{ act.phrase, output });355 , .{ act.phrase, output });
345 return error.TestFailed;
346 }356 }
347 },357 },
348 .not_present => {358 .not_present => {
349 while (it.next()) |line| {359 while (it.next()) |line| {
350 if (try act.match(line, &vars)) {360 if (try act.match(line, &vars)) {
351 std.debug.print(361 return step.fail(
352 \\362 \\
353 \\========= Expected not to find: ===================363 \\========= expected not to find: ===================
354 \\{s}364 \\{s}
355 \\========= But parsed file does contain it: ========365 \\========= but parsed file does contain it: ========
356 \\{s}366 \\{s}
357 \\367 \\===================================================
358 , .{ act.phrase, output });368 , .{ act.phrase, output });
359 return error.TestFailed;
360 }369 }
361 }370 }
362 },371 },
363 .compute_cmp => {372 .compute_cmp => {
364 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {373 const res = act.computeCmp(step, vars) catch |err| switch (err) {
365 error.UnknownVariable => {374 error.UnknownVariable => {
366 std.debug.print(375 return step.fail(
367 \\========= From parsed file: =====================376 \\========= from parsed file: =====================
368 \\{s}377 \\{s}
369 \\378 \\=================================================
370 , .{output});379 , .{output});
371 return error.TestFailed;
372 },380 },
373 else => |e| return e,381 else => |e| return e,
374 };382 };
375 if (!res) {383 if (!res) {
376 std.debug.print(384 return step.fail(
377 \\385 \\
378 \\========= Comparison failed for action: ===========386 \\========= comparison failed for action: ===========
379 \\{s} {}387 \\{s} {}
380 \\========= From parsed file: =======================388 \\========= from parsed file: =======================
381 \\{s}389 \\{s}
382 \\390 \\===================================================
383 , .{ act.phrase, act.expected.?, output });391 , .{ act.phrase, act.expected.?, output });
384 return error.TestFailed;
385 }392 }
386 },393 },
387 }394 }
...@@ -390,7 +397,6 @@ fn make(step: *Step) !void {...@@ -390,7 +397,6 @@ fn make(step: *Step) !void {
390}397}
391398
392const Opts = struct {399const Opts = struct {
393 gpa: ?Allocator = null,
394 dump_symtab: bool = false,400 dump_symtab: bool = false,
395};401};
396402
...@@ -398,8 +404,8 @@ const MachODumper = struct {...@@ -398,8 +404,8 @@ const MachODumper = struct {
398 const LoadCommandIterator = macho.LoadCommandIterator;404 const LoadCommandIterator = macho.LoadCommandIterator;
399 const symtab_label = "symtab";405 const symtab_label = "symtab";
400406
401 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {407 fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
402 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator408 const gpa = step.owner.allocator;
403 var stream = std.io.fixedBufferStream(bytes);409 var stream = std.io.fixedBufferStream(bytes);
404 const reader = stream.reader();410 const reader = stream.reader();
405411
...@@ -681,8 +687,8 @@ const MachODumper = struct {...@@ -681,8 +687,8 @@ const MachODumper = struct {
681const WasmDumper = struct {687const WasmDumper = struct {
682 const symtab_label = "symbols";688 const symtab_label = "symbols";
683689
684 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {690 fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 {
685 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator691 const gpa = step.owner.allocator;
686 if (opts.dump_symtab) {692 if (opts.dump_symtab) {
687 @panic("TODO: Implement symbol table parsing and dumping");693 @panic("TODO: Implement symbol table parsing and dumping");
688 }694 }
...@@ -703,20 +709,24 @@ const WasmDumper = struct {...@@ -703,20 +709,24 @@ const WasmDumper = struct {
703 const writer = output.writer();709 const writer = output.writer();
704710
705 while (reader.readByte()) |current_byte| {711 while (reader.readByte()) |current_byte| {
706 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {712 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch {
707 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});713 return step.fail("Found invalid section id '{d}'", .{current_byte});
708 return err;
709 };714 };
710715
711 const section_length = try std.leb.readULEB128(u32, reader);716 const section_length = try std.leb.readULEB128(u32, reader);
712 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);717 try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer);
713 fbs.pos += section_length;718 fbs.pos += section_length;
714 } else |_| {} // reached end of stream719 } else |_| {} // reached end of stream
715720
716 return output.toOwnedSlice();721 return output.toOwnedSlice();
717 }722 }
718723
719 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {724 fn parseAndDumpSection(
725 step: *Step,
726 section: std.wasm.Section,
727 data: []const u8,
728 writer: anytype,
729 ) !void {
720 var fbs = std.io.fixedBufferStream(data);730 var fbs = std.io.fixedBufferStream(data);
721 const reader = fbs.reader();731 const reader = fbs.reader();
722732
...@@ -739,7 +749,7 @@ const WasmDumper = struct {...@@ -739,7 +749,7 @@ const WasmDumper = struct {
739 => {749 => {
740 const entries = try std.leb.readULEB128(u32, reader);750 const entries = try std.leb.readULEB128(u32, reader);
741 try writer.print("\nentries {d}\n", .{entries});751 try writer.print("\nentries {d}\n", .{entries});
742 try dumpSection(section, data[fbs.pos..], entries, writer);752 try dumpSection(step, section, data[fbs.pos..], entries, writer);
743 },753 },
744 .custom => {754 .custom => {
745 const name_length = try std.leb.readULEB128(u32, reader);755 const name_length = try std.leb.readULEB128(u32, reader);
...@@ -748,7 +758,7 @@ const WasmDumper = struct {...@@ -748,7 +758,7 @@ const WasmDumper = struct {
748 try writer.print("\nname {s}\n", .{name});758 try writer.print("\nname {s}\n", .{name});
749759
750 if (mem.eql(u8, name, "name")) {760 if (mem.eql(u8, name, "name")) {
751 try parseDumpNames(reader, writer, data);761 try parseDumpNames(step, reader, writer, data);
752 } else if (mem.eql(u8, name, "producers")) {762 } else if (mem.eql(u8, name, "producers")) {
753 try parseDumpProducers(reader, writer, data);763 try parseDumpProducers(reader, writer, data);
754 } else if (mem.eql(u8, name, "target_features")) {764 } else if (mem.eql(u8, name, "target_features")) {
...@@ -764,7 +774,7 @@ const WasmDumper = struct {...@@ -764,7 +774,7 @@ const WasmDumper = struct {
764 }774 }
765 }775 }
766776
767 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {777 fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
768 var fbs = std.io.fixedBufferStream(data);778 var fbs = std.io.fixedBufferStream(data);
769 const reader = fbs.reader();779 const reader = fbs.reader();
770780
...@@ -774,19 +784,18 @@ const WasmDumper = struct {...@@ -774,19 +784,18 @@ const WasmDumper = struct {
774 while (i < entries) : (i += 1) {784 while (i < entries) : (i += 1) {
775 const func_type = try reader.readByte();785 const func_type = try reader.readByte();
776 if (func_type != std.wasm.function_type) {786 if (func_type != std.wasm.function_type) {
777 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});787 return step.fail("expected function type, found byte '{d}'", .{func_type});
778 return error.UnexpectedByte;
779 }788 }
780 const params = try std.leb.readULEB128(u32, reader);789 const params = try std.leb.readULEB128(u32, reader);
781 try writer.print("params {d}\n", .{params});790 try writer.print("params {d}\n", .{params});
782 var index: u32 = 0;791 var index: u32 = 0;
783 while (index < params) : (index += 1) {792 while (index < params) : (index += 1) {
784 try parseDumpType(std.wasm.Valtype, reader, writer);793 try parseDumpType(step, std.wasm.Valtype, reader, writer);
785 } else index = 0;794 } else index = 0;
786 const returns = try std.leb.readULEB128(u32, reader);795 const returns = try std.leb.readULEB128(u32, reader);
787 try writer.print("returns {d}\n", .{returns});796 try writer.print("returns {d}\n", .{returns});
788 while (index < returns) : (index += 1) {797 while (index < returns) : (index += 1) {
789 try parseDumpType(std.wasm.Valtype, reader, writer);798 try parseDumpType(step, std.wasm.Valtype, reader, writer);
790 }799 }
791 }800 }
792 },801 },
...@@ -800,9 +809,8 @@ const WasmDumper = struct {...@@ -800,9 +809,8 @@ const WasmDumper = struct {
800 const name = data[fbs.pos..][0..name_len];809 const name = data[fbs.pos..][0..name_len];
801 fbs.pos += name_len;810 fbs.pos += name_len;
802811
803 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {812 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch {
804 std.debug.print("Invalid import kind\n", .{});813 return step.fail("invalid import kind", .{});
805 return err;
806 };814 };
807815
808 try writer.print(816 try writer.print(
...@@ -819,11 +827,11 @@ const WasmDumper = struct {...@@ -819,11 +827,11 @@ const WasmDumper = struct {
819 try parseDumpLimits(reader, writer);827 try parseDumpLimits(reader, writer);
820 },828 },
821 .global => {829 .global => {
822 try parseDumpType(std.wasm.Valtype, reader, writer);830 try parseDumpType(step, std.wasm.Valtype, reader, writer);
823 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});831 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
824 },832 },
825 .table => {833 .table => {
826 try parseDumpType(std.wasm.RefType, reader, writer);834 try parseDumpType(step, std.wasm.RefType, reader, writer);
827 try parseDumpLimits(reader, writer);835 try parseDumpLimits(reader, writer);
828 },836 },
829 }837 }
...@@ -838,7 +846,7 @@ const WasmDumper = struct {...@@ -838,7 +846,7 @@ const WasmDumper = struct {
838 .table => {846 .table => {
839 var i: u32 = 0;847 var i: u32 = 0;
840 while (i < entries) : (i += 1) {848 while (i < entries) : (i += 1) {
841 try parseDumpType(std.wasm.RefType, reader, writer);849 try parseDumpType(step, std.wasm.RefType, reader, writer);
842 try parseDumpLimits(reader, writer);850 try parseDumpLimits(reader, writer);
843 }851 }
844 },852 },
...@@ -851,9 +859,9 @@ const WasmDumper = struct {...@@ -851,9 +859,9 @@ const WasmDumper = struct {
851 .global => {859 .global => {
852 var i: u32 = 0;860 var i: u32 = 0;
853 while (i < entries) : (i += 1) {861 while (i < entries) : (i += 1) {
854 try parseDumpType(std.wasm.Valtype, reader, writer);862 try parseDumpType(step, std.wasm.Valtype, reader, writer);
855 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});863 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
856 try parseDumpInit(reader, writer);864 try parseDumpInit(step, reader, writer);
857 }865 }
858 },866 },
859 .@"export" => {867 .@"export" => {
...@@ -863,9 +871,8 @@ const WasmDumper = struct {...@@ -863,9 +871,8 @@ const WasmDumper = struct {
863 const name = data[fbs.pos..][0..name_len];871 const name = data[fbs.pos..][0..name_len];
864 fbs.pos += name_len;872 fbs.pos += name_len;
865 const kind_byte = try std.leb.readULEB128(u8, reader);873 const kind_byte = try std.leb.readULEB128(u8, reader);
866 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {874 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch {
867 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});875 return step.fail("invalid export kind value '{d}'", .{kind_byte});
868 return err;
869 };876 };
870 const index = try std.leb.readULEB128(u32, reader);877 const index = try std.leb.readULEB128(u32, reader);
871 try writer.print(878 try writer.print(
...@@ -880,7 +887,7 @@ const WasmDumper = struct {...@@ -880,7 +887,7 @@ const WasmDumper = struct {
880 var i: u32 = 0;887 var i: u32 = 0;
881 while (i < entries) : (i += 1) {888 while (i < entries) : (i += 1) {
882 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});889 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
883 try parseDumpInit(reader, writer);890 try parseDumpInit(step, reader, writer);
884891
885 const function_indexes = try std.leb.readULEB128(u32, reader);892 const function_indexes = try std.leb.readULEB128(u32, reader);
886 var function_index: u32 = 0;893 var function_index: u32 = 0;
...@@ -896,7 +903,7 @@ const WasmDumper = struct {...@@ -896,7 +903,7 @@ const WasmDumper = struct {
896 while (i < entries) : (i += 1) {903 while (i < entries) : (i += 1) {
897 const index = try std.leb.readULEB128(u32, reader);904 const index = try std.leb.readULEB128(u32, reader);
898 try writer.print("memory index 0x{x}\n", .{index});905 try writer.print("memory index 0x{x}\n", .{index});
899 try parseDumpInit(reader, writer);906 try parseDumpInit(step, reader, writer);
900 const size = try std.leb.readULEB128(u32, reader);907 const size = try std.leb.readULEB128(u32, reader);
901 try writer.print("size {d}\n", .{size});908 try writer.print("size {d}\n", .{size});
902 try reader.skipBytes(size, .{}); // we do not care about the content of the segments909 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
...@@ -906,11 +913,10 @@ const WasmDumper = struct {...@@ -906,11 +913,10 @@ const WasmDumper = struct {
906 }913 }
907 }914 }
908915
909 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {916 fn parseDumpType(step: *Step, comptime WasmType: type, reader: anytype, writer: anytype) !void {
910 const type_byte = try reader.readByte();917 const type_byte = try reader.readByte();
911 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {918 const valtype = std.meta.intToEnum(WasmType, type_byte) catch {
912 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});919 return step.fail("Invalid wasm type value '{d}'", .{type_byte});
913 return err;
914 };920 };
915 try writer.print("type {s}\n", .{@tagName(valtype)});921 try writer.print("type {s}\n", .{@tagName(valtype)});
916 }922 }
...@@ -925,11 +931,10 @@ const WasmDumper = struct {...@@ -925,11 +931,10 @@ const WasmDumper = struct {
925 }931 }
926 }932 }
927933
928 fn parseDumpInit(reader: anytype, writer: anytype) !void {934 fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void {
929 const byte = try std.leb.readULEB128(u8, reader);935 const byte = try std.leb.readULEB128(u8, reader);
930 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {936 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch {
931 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});937 return step.fail("invalid wasm opcode '{d}'", .{byte});
932 return err;
933 };938 };
934 switch (opcode) {939 switch (opcode) {
935 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),940 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
...@@ -941,14 +946,13 @@ const WasmDumper = struct {...@@ -941,14 +946,13 @@ const WasmDumper = struct {
941 }946 }
942 const end_opcode = try std.leb.readULEB128(u8, reader);947 const end_opcode = try std.leb.readULEB128(u8, reader);
943 if (end_opcode != std.wasm.opcode(.end)) {948 if (end_opcode != std.wasm.opcode(.end)) {
944 std.debug.print("expected 'end' opcode in init expression\n", .{});949 return step.fail("expected 'end' opcode in init expression", .{});
945 return error.MissingEndOpcode;
946 }950 }
947 }951 }
948952
949 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {953 fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void {
950 while (reader.context.pos < data.len) {954 while (reader.context.pos < data.len) {
951 try parseDumpType(std.wasm.NameSubsection, reader, writer);955 try parseDumpType(step, std.wasm.NameSubsection, reader, writer);
952 const size = try std.leb.readULEB128(u32, reader);956 const size = try std.leb.readULEB128(u32, reader);
953 const entries = try std.leb.readULEB128(u32, reader);957 const entries = try std.leb.readULEB128(u32, reader);
954 try writer.print(958 try writer.print(
lib/std/Build/CompileStep.zig+443-378
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("../std.zig");2const std = @import("../std.zig");
3const mem = std.mem;3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;4const fs = std.fs;
6const assert = std.debug.assert;5const assert = std.debug.assert;
7const panic = std.debug.panic;6const panic = std.debug.panic;
...@@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir;...@@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir;
22const InstallArtifactStep = std.Build.InstallArtifactStep;21const InstallArtifactStep = std.Build.InstallArtifactStep;
23const GeneratedFile = std.Build.GeneratedFile;22const GeneratedFile = std.Build.GeneratedFile;
24const ObjCopyStep = std.Build.ObjCopyStep;23const ObjCopyStep = std.Build.ObjCopyStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
26const CheckObjectStep = std.Build.CheckObjectStep;24const CheckObjectStep = std.Build.CheckObjectStep;
27const RunStep = std.Build.RunStep;25const RunStep = std.Build.RunStep;
28const OptionsStep = std.Build.OptionsStep;26const OptionsStep = std.Build.OptionsStep;
...@@ -32,7 +30,6 @@ const CompileStep = @This();...@@ -32,7 +30,6 @@ const CompileStep = @This();
32pub const base_id: Step.Id = .compile;30pub const base_id: Step.Id = .compile;
3331
34step: Step,32step: Step,
35builder: *std.Build,
36name: []const u8,33name: []const u8,
37target: CrossTarget,34target: CrossTarget,
38target_info: NativeTargetInfo,35target_info: NativeTargetInfo,
...@@ -49,9 +46,9 @@ strip: ?bool,...@@ -49,9 +46,9 @@ strip: ?bool,
49unwind_tables: ?bool,46unwind_tables: ?bool,
50// keep in sync with src/link.zig:CompressDebugSections47// keep in sync with src/link.zig:CompressDebugSections
51compress_debug_sections: enum { none, zlib } = .none,48compress_debug_sections: enum { none, zlib } = .none,
52lib_paths: ArrayList([]const u8),49lib_paths: ArrayList(FileSource),
53rpaths: ArrayList([]const u8),50rpaths: ArrayList(FileSource),
54framework_dirs: ArrayList([]const u8),51framework_dirs: ArrayList(FileSource),
55frameworks: StringHashMap(FrameworkLinkInfo),52frameworks: StringHashMap(FrameworkLinkInfo),
56verbose_link: bool,53verbose_link: bool,
57verbose_cc: bool,54verbose_cc: bool,
...@@ -86,7 +83,6 @@ c_std: std.Build.CStd,...@@ -86,7 +83,6 @@ c_std: std.Build.CStd,
86zig_lib_dir: ?[]const u8,83zig_lib_dir: ?[]const u8,
87main_pkg_path: ?[]const u8,84main_pkg_path: ?[]const u8,
88exec_cmd_args: ?[]const ?[]const u8,85exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,
90filter: ?[]const u8,86filter: ?[]const u8,
91test_evented_io: bool = false,87test_evented_io: bool = false,
92test_runner: ?[]const u8,88test_runner: ?[]const u8,
...@@ -210,10 +206,17 @@ want_lto: ?bool = null,...@@ -210,10 +206,17 @@ want_lto: ?bool = null,
210use_llvm: ?bool = null,206use_llvm: ?bool = null,
211use_lld: ?bool = null,207use_lld: ?bool = null,
212208
209/// This is an advanced setting that can change the intent of this CompileStep.
210/// If this slice has nonzero length, it means that this CompileStep exists to
211/// check for compile errors and return *success* if they match, and failure
212/// otherwise.
213expect_errors: []const []const u8 = &.{},
214
213output_path_source: GeneratedFile,215output_path_source: GeneratedFile,
214output_lib_path_source: GeneratedFile,216output_lib_path_source: GeneratedFile,
215output_h_path_source: GeneratedFile,217output_h_path_source: GeneratedFile,
216output_pdb_path_source: GeneratedFile,218output_pdb_path_source: GeneratedFile,
219output_dirname_source: GeneratedFile,
217220
218pub const CSourceFiles = struct {221pub const CSourceFiles = struct {
219 files: []const []const u8,222 files: []const []const u8,
...@@ -277,6 +280,7 @@ pub const Options = struct {...@@ -277,6 +280,7 @@ pub const Options = struct {
277 kind: Kind,280 kind: Kind,
278 linkage: ?Linkage = null,281 linkage: ?Linkage = null,
279 version: ?std.builtin.Version = null,282 version: ?std.builtin.Version = null,
283 max_rss: usize = 0,
280};284};
281285
282pub const Kind = enum {286pub const Kind = enum {
...@@ -284,7 +288,6 @@ pub const Kind = enum {...@@ -284,7 +288,6 @@ pub const Kind = enum {
284 lib,288 lib,
285 obj,289 obj,
286 @"test",290 @"test",
287 test_exe,
288};291};
289292
290pub const Linkage = enum { dynamic, static };293pub const Linkage = enum { dynamic, static };
...@@ -305,18 +308,35 @@ pub const EmitOption = union(enum) {...@@ -305,18 +308,35 @@ pub const EmitOption = union(enum) {
305 }308 }
306};309};
307310
308pub fn create(builder: *std.Build, options: Options) *CompileStep {311pub fn create(owner: *std.Build, options: Options) *CompileStep {
309 const name = builder.dupe(options.name);312 const name = owner.dupe(options.name);
310 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;313 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null;
311 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {314 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
312 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});315 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
313 }316 }
314317
315 const self = builder.allocator.create(CompileStep) catch @panic("OOM");318 // Avoid the common case of the step name looking like "zig test test".
319 const name_adjusted = if (options.kind == .@"test" and mem.eql(u8, name, "test"))
320 ""
321 else
322 owner.fmt("{s} ", .{name});
323
324 const step_name = owner.fmt("{s} {s}{s} {s}", .{
325 switch (options.kind) {
326 .exe => "zig build-exe",
327 .lib => "zig build-lib",
328 .obj => "zig build-obj",
329 .@"test" => "zig test",
330 },
331 name_adjusted,
332 @tagName(options.optimize),
333 options.target.zigTriple(owner.allocator) catch @panic("OOM"),
334 });
335
336 const self = owner.allocator.create(CompileStep) catch @panic("OOM");
316 self.* = CompileStep{337 self.* = CompileStep{
317 .strip = null,338 .strip = null,
318 .unwind_tables = null,339 .unwind_tables = null,
319 .builder = builder,
320 .verbose_link = false,340 .verbose_link = false,
321 .verbose_cc = false,341 .verbose_cc = false,
322 .optimize = options.optimize,342 .optimize = options.optimize,
...@@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {...@@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
325 .kind = options.kind,345 .kind = options.kind,
326 .root_src = root_src,346 .root_src = root_src,
327 .name = name,347 .name = name,
328 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),348 .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator),
329 .step = Step.init(base_id, name, builder.allocator, make),349 .step = Step.init(.{
350 .id = base_id,
351 .name = step_name,
352 .owner = owner,
353 .makeFn = make,
354 .max_rss = options.max_rss,
355 }),
330 .version = options.version,356 .version = options.version,
331 .out_filename = undefined,357 .out_filename = undefined,
332 .out_h_filename = builder.fmt("{s}.h", .{name}),358 .out_h_filename = owner.fmt("{s}.h", .{name}),
333 .out_lib_filename = undefined,359 .out_lib_filename = undefined,
334 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),360 .out_pdb_filename = owner.fmt("{s}.pdb", .{name}),
335 .major_only_filename = null,361 .major_only_filename = null,
336 .name_only_filename = null,362 .name_only_filename = null,
337 .modules = std.StringArrayHashMap(*Module).init(builder.allocator),363 .modules = std.StringArrayHashMap(*Module).init(owner.allocator),
338 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),364 .include_dirs = ArrayList(IncludeDir).init(owner.allocator),
339 .link_objects = ArrayList(LinkObject).init(builder.allocator),365 .link_objects = ArrayList(LinkObject).init(owner.allocator),
340 .c_macros = ArrayList([]const u8).init(builder.allocator),366 .c_macros = ArrayList([]const u8).init(owner.allocator),
341 .lib_paths = ArrayList([]const u8).init(builder.allocator),367 .lib_paths = ArrayList(FileSource).init(owner.allocator),
342 .rpaths = ArrayList([]const u8).init(builder.allocator),368 .rpaths = ArrayList(FileSource).init(owner.allocator),
343 .framework_dirs = ArrayList([]const u8).init(builder.allocator),369 .framework_dirs = ArrayList(FileSource).init(owner.allocator),
344 .installed_headers = ArrayList(*Step).init(builder.allocator),370 .installed_headers = ArrayList(*Step).init(owner.allocator),
345 .object_src = undefined,371 .object_src = undefined,
346 .c_std = std.Build.CStd.C99,372 .c_std = std.Build.CStd.C99,
347 .zig_lib_dir = null,373 .zig_lib_dir = null,
348 .main_pkg_path = null,374 .main_pkg_path = null,
349 .exec_cmd_args = null,375 .exec_cmd_args = null,
350 .name_prefix = "",
351 .filter = null,376 .filter = null,
352 .test_runner = null,377 .test_runner = null,
353 .disable_stack_probing = false,378 .disable_stack_probing = false,
...@@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {...@@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
363 .output_lib_path_source = GeneratedFile{ .step = &self.step },388 .output_lib_path_source = GeneratedFile{ .step = &self.step },
364 .output_h_path_source = GeneratedFile{ .step = &self.step },389 .output_h_path_source = GeneratedFile{ .step = &self.step },
365 .output_pdb_path_source = GeneratedFile{ .step = &self.step },390 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
391 .output_dirname_source = GeneratedFile{ .step = &self.step },
366392
367 .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"),393 .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"),
368 };394 };
...@@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {...@@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
372}398}
373399
374fn computeOutFileNames(self: *CompileStep) void {400fn computeOutFileNames(self: *CompileStep) void {
401 const b = self.step.owner;
375 const target = self.target_info.target;402 const target = self.target_info.target;
376403
377 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{404 self.out_filename = std.zig.binNameAlloc(b.allocator, .{
378 .root_name = self.name,405 .root_name = self.name,
379 .target = target,406 .target = target,
380 .output_mode = switch (self.kind) {407 .output_mode = switch (self.kind) {
381 .lib => .Lib,408 .lib => .Lib,
382 .obj => .Obj,409 .obj => .Obj,
383 .exe, .@"test", .test_exe => .Exe,410 .exe, .@"test" => .Exe,
384 },411 },
385 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {412 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
386 .dynamic => .Dynamic,413 .dynamic => .Dynamic,
...@@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void {...@@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void {
394 self.out_lib_filename = self.out_filename;421 self.out_lib_filename = self.out_filename;
395 } else if (self.version) |version| {422 } else if (self.version) |version| {
396 if (target.isDarwin()) {423 if (target.isDarwin()) {
397 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{424 self.major_only_filename = b.fmt("lib{s}.{d}.dylib", .{
398 self.name,425 self.name,
399 version.major,426 version.major,
400 });427 });
401 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});428 self.name_only_filename = b.fmt("lib{s}.dylib", .{self.name});
402 self.out_lib_filename = self.out_filename;429 self.out_lib_filename = self.out_filename;
403 } else if (target.os.tag == .windows) {430 } else if (target.os.tag == .windows) {
404 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});431 self.out_lib_filename = b.fmt("{s}.lib", .{self.name});
405 } else {432 } else {
406 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });433 self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major });
407 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});434 self.name_only_filename = b.fmt("lib{s}.so", .{self.name});
408 self.out_lib_filename = self.out_filename;435 self.out_lib_filename = self.out_filename;
409 }436 }
410 } else {437 } else {
411 if (target.isDarwin()) {438 if (target.isDarwin()) {
412 self.out_lib_filename = self.out_filename;439 self.out_lib_filename = self.out_filename;
413 } else if (target.os.tag == .windows) {440 } else if (target.os.tag == .windows) {
414 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});441 self.out_lib_filename = b.fmt("{s}.lib", .{self.name});
415 } else {442 } else {
416 self.out_lib_filename = self.out_filename;443 self.out_lib_filename = self.out_filename;
417 }444 }
418 }445 }
419 if (self.output_dir != null) {446 if (self.output_dir != null) {
420 self.output_lib_path_source.path = self.builder.pathJoin(447 self.output_lib_path_source.path = b.pathJoin(
421 &.{ self.output_dir.?, self.out_lib_filename },448 &.{ self.output_dir.?, self.out_lib_filename },
422 );449 );
423 }450 }
...@@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void {...@@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void {
425}452}
426453
427pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {454pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
428 self.output_dir = self.builder.dupePath(dir);455 const b = self.step.owner;
456 self.output_dir = b.dupePath(dir);
429}457}
430458
431pub fn install(self: *CompileStep) void {459pub fn install(self: *CompileStep) void {
432 self.builder.installArtifact(self);460 const b = self.step.owner;
461 b.installArtifact(self);
433}462}
434463
435pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {464pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
436 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);465 const b = cs.step.owner;
437 a.builder.getInstallStep().dependOn(&install_file.step);466 const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);
438 a.installed_headers.append(&install_file.step) catch @panic("OOM");467 b.getInstallStep().dependOn(&install_file.step);
468 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
439}469}
440470
441pub const InstallConfigHeaderOptions = struct {471pub const InstallConfigHeaderOptions = struct {
...@@ -449,13 +479,14 @@ pub fn installConfigHeader(...@@ -449,13 +479,14 @@ pub fn installConfigHeader(
449 options: InstallConfigHeaderOptions,479 options: InstallConfigHeaderOptions,
450) void {480) void {
451 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;481 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
452 const install_file = cs.builder.addInstallFileWithDir(482 const b = cs.step.owner;
483 const install_file = b.addInstallFileWithDir(
453 .{ .generated = &config_header.output_file },484 .{ .generated = &config_header.output_file },
454 options.install_dir,485 options.install_dir,
455 dest_rel_path,486 dest_rel_path,
456 );487 );
457 install_file.step.dependOn(&config_header.step);488 install_file.step.dependOn(&config_header.step);
458 cs.builder.getInstallStep().dependOn(&install_file.step);489 b.getInstallStep().dependOn(&install_file.step);
459 cs.installed_headers.append(&install_file.step) catch @panic("OOM");490 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
460}491}
461492
...@@ -472,91 +503,83 @@ pub fn installHeadersDirectory(...@@ -472,91 +503,83 @@ pub fn installHeadersDirectory(
472}503}
473504
474pub fn installHeadersDirectoryOptions(505pub fn installHeadersDirectoryOptions(
475 a: *CompileStep,506 cs: *CompileStep,
476 options: std.Build.InstallDirStep.Options,507 options: std.Build.InstallDirStep.Options,
477) void {508) void {
478 const install_dir = a.builder.addInstallDirectory(options);509 const b = cs.step.owner;
479 a.builder.getInstallStep().dependOn(&install_dir.step);510 const install_dir = b.addInstallDirectory(options);
480 a.installed_headers.append(&install_dir.step) catch @panic("OOM");511 b.getInstallStep().dependOn(&install_dir.step);
512 cs.installed_headers.append(&install_dir.step) catch @panic("OOM");
481}513}
482514
483pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {515pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void {
484 assert(l.kind == .lib);516 assert(l.kind == .lib);
485 const install_step = a.builder.getInstallStep();517 const b = cs.step.owner;
518 const install_step = b.getInstallStep();
486 // Copy each element from installed_headers, modifying the builder519 // Copy each element from installed_headers, modifying the builder
487 // to be the new parent's builder.520 // to be the new parent's builder.
488 for (l.installed_headers.items) |step| {521 for (l.installed_headers.items) |step| {
489 const step_copy = switch (step.id) {522 const step_copy = switch (step.id) {
490 inline .install_file, .install_dir => |id| blk: {523 inline .install_file, .install_dir => |id| blk: {
491 const T = id.Type();524 const T = id.Type();
492 const ptr = a.builder.allocator.create(T) catch @panic("OOM");525 const ptr = b.allocator.create(T) catch @panic("OOM");
493 ptr.* = step.cast(T).?.*;526 ptr.* = step.cast(T).?.*;
494 ptr.override_source_builder = ptr.builder;527 ptr.dest_builder = b;
495 ptr.builder = a.builder;
496 break :blk &ptr.step;528 break :blk &ptr.step;
497 },529 },
498 else => unreachable,530 else => unreachable,
499 };531 };
500 a.installed_headers.append(step_copy) catch @panic("OOM");532 cs.installed_headers.append(step_copy) catch @panic("OOM");
501 install_step.dependOn(step_copy);533 install_step.dependOn(step_copy);
502 }534 }
503 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");535 cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
504}536}
505537
506pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep {538pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep {
539 const b = cs.step.owner;
507 var copy = options;540 var copy = options;
508 if (copy.basename == null) {541 if (copy.basename == null) {
509 if (options.format) |f| {542 if (options.format) |f| {
510 copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) });543 copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) });
511 } else {544 } else {
512 copy.basename = cs.name;545 copy.basename = cs.name;
513 }546 }
514 }547 }
515 return cs.builder.addObjCopy(cs.getOutputSource(), copy);548 return b.addObjCopy(cs.getOutputSource(), copy);
516}549}
517550
518/// Deprecated: use `std.Build.addRunArtifact`551/// Deprecated: use `std.Build.addRunArtifact`
519/// This function will run in the context of the package that created the executable,552/// This function will run in the context of the package that created the executable,
520/// which is undesirable when running an executable provided by a dependency package.553/// which is undesirable when running an executable provided by a dependency package.
521pub fn run(exe: *CompileStep) *RunStep {554pub fn run(cs: *CompileStep) *RunStep {
522 return exe.builder.addRunArtifact(exe);555 return cs.step.owner.addRunArtifact(cs);
523}
524
525/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
526/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
527/// When a binary cannot be ran through emulation or the option is disabled, a warning
528/// will be printed and the binary will *NOT* be ran.
529pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
530 assert(exe.kind == .exe or exe.kind == .test_exe);
531
532 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
533 if (exe.vcpkg_bin_path) |path| {
534 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
535 }
536 return run_step;
537}556}
538557
539pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {558pub fn checkObject(self: *CompileStep) *CheckObjectStep {
540 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);559 return CheckObjectStep.create(self.step.owner, self.getOutputSource(), self.target_info.target.ofmt);
541}560}
542561
543pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {562pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
544 self.linker_script = source.dupe(self.builder);563 const b = self.step.owner;
564 self.linker_script = source.dupe(b);
545 source.addStepDependencies(&self.step);565 source.addStepDependencies(&self.step);
546}566}
547567
548pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {568pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
549 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");569 const b = self.step.owner;
570 self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
550}571}
551572
552pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {573pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
553 self.frameworks.put(self.builder.dupe(framework_name), .{574 const b = self.step.owner;
575 self.frameworks.put(b.dupe(framework_name), .{
554 .needed = true,576 .needed = true,
555 }) catch @panic("OOM");577 }) catch @panic("OOM");
556}578}
557579
558pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {580pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
559 self.frameworks.put(self.builder.dupe(framework_name), .{581 const b = self.step.owner;
582 self.frameworks.put(b.dupe(framework_name), .{
560 .weak = true,583 .weak = true,
561 }) catch @panic("OOM");584 }) catch @panic("OOM");
562}585}
...@@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool {...@@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool {
595 if (!self.target.isWindows() and !self.target.isUefi()) return false;618 if (!self.target.isWindows() and !self.target.isUefi()) return false;
596 if (self.target.getObjectFormat() == .c) return false;619 if (self.target.getObjectFormat() == .c) return false;
597 if (self.strip == true) return false;620 if (self.strip == true) return false;
598 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;621 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test";
599}622}
600623
601pub fn linkLibC(self: *CompileStep) void {624pub fn linkLibC(self: *CompileStep) void {
...@@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void {...@@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void {
609/// If the value is omitted, it is set to 1.632/// If the value is omitted, it is set to 1.
610/// `name` and `value` need not live longer than the function call.633/// `name` and `value` need not live longer than the function call.
611pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {634pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
612 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);635 const b = self.step.owner;
636 const macro = std.Build.constructCMacro(b.allocator, name, value);
613 self.c_macros.append(macro) catch @panic("OOM");637 self.c_macros.append(macro) catch @panic("OOM");
614}638}
615639
616/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.640/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
617pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {641pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
618 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");642 const b = self.step.owner;
643 self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM");
619}644}
620645
621/// This one has no integration with anything, it just puts -lname on the command line.646/// This one has no integration with anything, it just puts -lname on the command line.
622/// Prefer to use `linkSystemLibrary` instead.647/// Prefer to use `linkSystemLibrary` instead.
623pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {648pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
649 const b = self.step.owner;
624 self.link_objects.append(.{650 self.link_objects.append(.{
625 .system_lib = .{651 .system_lib = .{
626 .name = self.builder.dupe(name),652 .name = b.dupe(name),
627 .needed = false,653 .needed = false,
628 .weak = false,654 .weak = false,
629 .use_pkg_config = .no,655 .use_pkg_config = .no,
...@@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {...@@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
634/// This one has no integration with anything, it just puts -needed-lname on the command line.660/// This one has no integration with anything, it just puts -needed-lname on the command line.
635/// Prefer to use `linkSystemLibraryNeeded` instead.661/// Prefer to use `linkSystemLibraryNeeded` instead.
636pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {662pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
663 const b = self.step.owner;
637 self.link_objects.append(.{664 self.link_objects.append(.{
638 .system_lib = .{665 .system_lib = .{
639 .name = self.builder.dupe(name),666 .name = b.dupe(name),
640 .needed = true,667 .needed = true,
641 .weak = false,668 .weak = false,
642 .use_pkg_config = .no,669 .use_pkg_config = .no,
...@@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {...@@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
647/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the674/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
648/// command line. Prefer to use `linkSystemLibraryWeak` instead.675/// command line. Prefer to use `linkSystemLibraryWeak` instead.
649pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {676pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
677 const b = self.step.owner;
650 self.link_objects.append(.{678 self.link_objects.append(.{
651 .system_lib = .{679 .system_lib = .{
652 .name = self.builder.dupe(name),680 .name = b.dupe(name),
653 .needed = false,681 .needed = false,
654 .weak = true,682 .weak = true,
655 .use_pkg_config = .no,683 .use_pkg_config = .no,
...@@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {...@@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
660/// This links against a system library, exclusively using pkg-config to find the library.688/// This links against a system library, exclusively using pkg-config to find the library.
661/// Prefer to use `linkSystemLibrary` instead.689/// Prefer to use `linkSystemLibrary` instead.
662pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {690pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
691 const b = self.step.owner;
663 self.link_objects.append(.{692 self.link_objects.append(.{
664 .system_lib = .{693 .system_lib = .{
665 .name = self.builder.dupe(lib_name),694 .name = b.dupe(lib_name),
666 .needed = false,695 .needed = false,
667 .weak = false,696 .weak = false,
668 .use_pkg_config = .force,697 .use_pkg_config = .force,
...@@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8)...@@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8)
673/// This links against a system library, exclusively using pkg-config to find the library.702/// This links against a system library, exclusively using pkg-config to find the library.
674/// Prefer to use `linkSystemLibraryNeeded` instead.703/// Prefer to use `linkSystemLibraryNeeded` instead.
675pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {704pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
705 const b = self.step.owner;
676 self.link_objects.append(.{706 self.link_objects.append(.{
677 .system_lib = .{707 .system_lib = .{
678 .name = self.builder.dupe(lib_name),708 .name = b.dupe(lib_name),
679 .needed = true,709 .needed = true,
680 .weak = false,710 .weak = false,
681 .use_pkg_config = .force,711 .use_pkg_config = .force,
...@@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons...@@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons
685715
686/// Run pkg-config for the given library name and parse the output, returning the arguments716/// Run pkg-config for the given library name and parse the output, returning the arguments
687/// that should be passed to zig to link the given library.717/// that should be passed to zig to link the given library.
688pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {718fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
719 const b = self.step.owner;
689 const pkg_name = match: {720 const pkg_name = match: {
690 // First we have to map the library name to pkg config name. Unfortunately,721 // First we have to map the library name to pkg config name. Unfortunately,
691 // there are several examples where this is not straightforward:722 // there are several examples where this is not straightforward:
692 // -lSDL2 -> pkg-config sdl2723 // -lSDL2 -> pkg-config sdl2
693 // -lgdk-3 -> pkg-config gdk-3.0724 // -lgdk-3 -> pkg-config gdk-3.0
694 // -latk-1.0 -> pkg-config atk725 // -latk-1.0 -> pkg-config atk
695 const pkgs = try getPkgConfigList(self.builder);726 const pkgs = try getPkgConfigList(b);
696727
697 // Exact match means instant winner.728 // Exact match means instant winner.
698 for (pkgs) |pkg| {729 for (pkgs) |pkg| {
...@@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u...@@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
732 };763 };
733764
734 var code: u8 = undefined;765 var code: u8 = undefined;
735 const stdout = if (self.builder.execAllowFail(&[_][]const u8{766 const stdout = if (b.execAllowFail(&[_][]const u8{
736 "pkg-config",767 "pkg-config",
737 pkg_name,768 pkg_name,
738 "--cflags",769 "--cflags",
...@@ -742,11 +773,10 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u...@@ -742,11 +773,10 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
742 error.ExecNotSupported => return error.PkgConfigFailed,773 error.ExecNotSupported => return error.PkgConfigFailed,
743 error.ExitCodeFailure => return error.PkgConfigFailed,774 error.ExitCodeFailure => return error.PkgConfigFailed,
744 error.FileNotFound => return error.PkgConfigNotInstalled,775 error.FileNotFound => return error.PkgConfigNotInstalled,
745 error.ChildExecFailed => return error.PkgConfigFailed,
746 else => return err,776 else => return err,
747 };777 };
748778
749 var zig_args = ArrayList([]const u8).init(self.builder.allocator);779 var zig_args = ArrayList([]const u8).init(b.allocator);
750 defer zig_args.deinit();780 defer zig_args.deinit();
751781
752 var it = mem.tokenize(u8, stdout, " \r\n\t");782 var it = mem.tokenize(u8, stdout, " \r\n\t");
...@@ -771,8 +801,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u...@@ -771,8 +801,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
771 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });801 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
772 } else if (mem.startsWith(u8, tok, "-D")) {802 } else if (mem.startsWith(u8, tok, "-D")) {
773 try zig_args.append(tok);803 try zig_args.append(tok);
774 } else if (self.builder.verbose) {804 } else if (b.debug_pkg_config) {
775 log.warn("Ignoring pkg-config flag '{s}'", .{tok});805 return self.step.fail("unknown pkg-config flag '{s}'", .{tok});
776 }806 }
777 }807 }
778808
...@@ -795,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {...@@ -795,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
795 needed: bool = false,825 needed: bool = false,
796 weak: bool = false,826 weak: bool = false,
797}) void {827}) void {
828 const b = self.step.owner;
798 if (isLibCLibrary(name)) {829 if (isLibCLibrary(name)) {
799 self.linkLibC();830 self.linkLibC();
800 return;831 return;
...@@ -806,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {...@@ -806,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
806837
807 self.link_objects.append(.{838 self.link_objects.append(.{
808 .system_lib = .{839 .system_lib = .{
809 .name = self.builder.dupe(name),840 .name = b.dupe(name),
810 .needed = opts.needed,841 .needed = opts.needed,
811 .weak = opts.weak,842 .weak = opts.weak,
812 .use_pkg_config = .yes,843 .use_pkg_config = .yes,
...@@ -814,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {...@@ -814,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
814 }) catch @panic("OOM");845 }) catch @panic("OOM");
815}846}
816847
817pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {848pub fn setName(self: *CompileStep, text: []const u8) void {
818 assert(self.kind == .@"test" or self.kind == .test_exe);849 const b = self.step.owner;
819 self.name_prefix = self.builder.dupe(text);850 assert(self.kind == .@"test");
851 self.name = b.dupe(text);
820}852}
821853
822pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {854pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
823 assert(self.kind == .@"test" or self.kind == .test_exe);855 const b = self.step.owner;
824 self.filter = if (text) |t| self.builder.dupe(t) else null;856 assert(self.kind == .@"test");
857 self.filter = if (text) |t| b.dupe(t) else null;
825}858}
826859
827pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {860pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
828 assert(self.kind == .@"test" or self.kind == .test_exe);861 const b = self.step.owner;
829 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;862 assert(self.kind == .@"test");
863 self.test_runner = if (path) |p| b.dupePath(p) else null;
830}864}
831865
832/// Handy when you have many C/C++ source files and want them all to have the same flags.866/// Handy when you have many C/C++ source files and want them all to have the same flags.
833pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {867pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
834 const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");868 const b = self.step.owner;
869 const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM");
835870
836 const files_copy = self.builder.dupeStrings(files);871 const files_copy = b.dupeStrings(files);
837 const flags_copy = self.builder.dupeStrings(flags);872 const flags_copy = b.dupeStrings(flags);
838873
839 c_source_files.* = .{874 c_source_files.* = .{
840 .files = files_copy,875 .files = files_copy,
...@@ -851,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con...@@ -851,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con
851}886}
852887
853pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {888pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
854 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");889 const b = self.step.owner;
855 c_source_file.* = source.dupe(self.builder);890 const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM");
891 c_source_file.* = source.dupe(b);
856 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");892 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
857 source.source.addStepDependencies(&self.step);893 source.source.addStepDependencies(&self.step);
858}894}
...@@ -866,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {...@@ -866,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {
866}902}
867903
868pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {904pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
869 self.zig_lib_dir = self.builder.dupePath(dir_path);905 const b = self.step.owner;
906 self.zig_lib_dir = b.dupePath(dir_path);
870}907}
871908
872pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {909pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
873 self.main_pkg_path = self.builder.dupePath(dir_path);910 const b = self.step.owner;
911 self.main_pkg_path = b.dupePath(dir_path);
874}912}
875913
876pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {914pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
877 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;915 const b = self.step.owner;
916 self.libc_file = if (libc_file) |f| f.dupe(b) else null;
878}917}
879918
880/// Returns the generated executable, library or object file.919/// Returns the generated executable, library or object file.
881/// To run an executable built with zig build, use `run`, or create an install step and invoke it.920/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
882pub fn getOutputSource(self: *CompileStep) FileSource {921pub fn getOutputSource(self: *CompileStep) FileSource {
883 return FileSource{ .generated = &self.output_path_source };922 return .{ .generated = &self.output_path_source };
923}
924
925pub fn getOutputDirectorySource(self: *CompileStep) FileSource {
926 return .{ .generated = &self.output_dirname_source };
884}927}
885928
886/// Returns the generated import library. This function can only be called for libraries.929/// Returns the generated import library. This function can only be called for libraries.
887pub fn getOutputLibSource(self: *CompileStep) FileSource {930pub fn getOutputLibSource(self: *CompileStep) FileSource {
888 assert(self.kind == .lib);931 assert(self.kind == .lib);
889 return FileSource{ .generated = &self.output_lib_path_source };932 return .{ .generated = &self.output_lib_path_source };
890}933}
891934
892/// Returns the generated header file.935/// Returns the generated header file.
893/// This function can only be called for libraries or object files which have `emit_h` set.936/// This function can only be called for libraries or object files which have `emit_h` set.
894pub fn getOutputHSource(self: *CompileStep) FileSource {937pub fn getOutputHSource(self: *CompileStep) FileSource {
895 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");938 assert(self.kind != .exe and self.kind != .@"test");
896 assert(self.emit_h);939 assert(self.emit_h);
897 return FileSource{ .generated = &self.output_h_path_source };940 return .{ .generated = &self.output_h_path_source };
898}941}
899942
900/// Returns the generated PDB file. This function can only be called for Windows and UEFI.943/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
901pub fn getOutputPdbSource(self: *CompileStep) FileSource {944pub fn getOutputPdbSource(self: *CompileStep) FileSource {
902 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?945 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
903 assert(self.target.isWindows() or self.target.isUefi());946 assert(self.target.isWindows() or self.target.isUefi());
904 return FileSource{ .generated = &self.output_pdb_path_source };947 return .{ .generated = &self.output_pdb_path_source };
905}948}
906949
907pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {950pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
951 const b = self.step.owner;
908 self.link_objects.append(.{952 self.link_objects.append(.{
909 .assembly_file = .{ .path = self.builder.dupe(path) },953 .assembly_file = .{ .path = b.dupe(path) },
910 }) catch @panic("OOM");954 }) catch @panic("OOM");
911}955}
912956
913pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {957pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
914 const source_duped = source.dupe(self.builder);958 const b = self.step.owner;
959 const source_duped = source.dupe(b);
915 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");960 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
916 source_duped.addStepDependencies(&self.step);961 source_duped.addStepDependencies(&self.step);
917}962}
...@@ -921,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {...@@ -921,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
921}966}
922967
923pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {968pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
924 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");969 const b = self.step.owner;
970 self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM");
925 source.addStepDependencies(&self.step);971 source.addStepDependencies(&self.step);
926}972}
927973
...@@ -936,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath");...@@ -936,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath");
936pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");982pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
937983
938pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {984pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
939 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");985 const b = self.step.owner;
986 self.include_dirs.append(IncludeDir{ .raw_path_system = b.dupe(path) }) catch @panic("OOM");
940}987}
941988
942pub fn addIncludePath(self: *CompileStep, path: []const u8) void {989pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
943 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");990 const b = self.step.owner;
991 self.include_dirs.append(IncludeDir{ .raw_path = b.dupe(path) }) catch @panic("OOM");
944}992}
945993
946pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {994pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
...@@ -949,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi...@@ -949,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi
949}997}
950998
951pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {999pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
952 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");1000 const b = self.step.owner;
1001 self.lib_paths.append(.{ .path = b.dupe(path) }) catch @panic("OOM");
1002}
1003
1004pub fn addLibraryPathDirectorySource(self: *CompileStep, directory_source: FileSource) void {
1005 self.lib_paths.append(directory_source) catch @panic("OOM");
1006 directory_source.addStepDependencies(&self.step);
953}1007}
9541008
955pub fn addRPath(self: *CompileStep, path: []const u8) void {1009pub fn addRPath(self: *CompileStep, path: []const u8) void {
956 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");1010 const b = self.step.owner;
1011 self.rpaths.append(.{ .path = b.dupe(path) }) catch @panic("OOM");
1012}
1013
1014pub fn addRPathDirectorySource(self: *CompileStep, directory_source: FileSource) void {
1015 self.rpaths.append(directory_source) catch @panic("OOM");
1016 directory_source.addStepDependencies(&self.step);
957}1017}
9581018
959pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {1019pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
960 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");1020 const b = self.step.owner;
1021 self.framework_dirs.append(.{ .path = b.dupe(dir_path) }) catch @panic("OOM");
1022}
1023
1024pub fn addFrameworkPathDirectorySource(self: *CompileStep, directory_source: FileSource) void {
1025 self.framework_dirs.append(directory_source) catch @panic("OOM");
1026 directory_source.addStepDependencies(&self.step);
961}1027}
9621028
963/// Adds a module to be used with `@import` and exposing it in the current1029/// Adds a module to be used with `@import` and exposing it in the current
964/// package's module table using `name`.1030/// package's module table using `name`.
965pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {1031pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
966 cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");1032 const b = cs.step.owner;
1033 cs.modules.put(b.dupe(name), module) catch @panic("OOM");
9671034
968 var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator);1035 var done = std.AutoHashMap(*Module, void).init(b.allocator);
969 defer done.deinit();1036 defer done.deinit();
970 cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");1037 cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");
971}1038}
...@@ -973,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {...@@ -973,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
973/// Adds a module to be used with `@import` without exposing it in the current1040/// Adds a module to be used with `@import` without exposing it in the current
974/// package's module table.1041/// package's module table.
975pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {1042pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {
976 const module = cs.builder.createModule(options);1043 const b = cs.step.owner;
1044 const module = b.createModule(options);
977 return addModule(cs, name, module);1045 return addModule(cs, name, module);
978}1046}
9791047
...@@ -993,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM...@@ -993,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM
993/// If Vcpkg was found on the system, it will be added to include and lib1061/// If Vcpkg was found on the system, it will be added to include and lib
994/// paths for the specified target.1062/// paths for the specified target.
995pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {1063pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
1064 const b = self.step.owner;
996 // Ideally in the Unattempted case we would call the function recursively1065 // Ideally in the Unattempted case we would call the function recursively
997 // after findVcpkgRoot and have only one switch statement, but the compiler1066 // after findVcpkgRoot and have only one switch statement, but the compiler
998 // cannot resolve the error set.1067 // cannot resolve the error set.
999 switch (self.builder.vcpkg_root) {1068 switch (b.vcpkg_root) {
1000 .unattempted => {1069 .unattempted => {
1001 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|1070 b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root|
1002 VcpkgRoot{ .found = root }1071 VcpkgRoot{ .found = root }
1003 else1072 else
1004 .not_found;1073 .not_found;
...@@ -1007,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {...@@ -1007,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
1007 .found => {},1076 .found => {},
1008 }1077 }
10091078
1010 switch (self.builder.vcpkg_root) {1079 switch (b.vcpkg_root) {
1011 .unattempted => unreachable,1080 .unattempted => unreachable,
1012 .not_found => return error.VcpkgNotFound,1081 .not_found => return error.VcpkgNotFound,
1013 .found => |root| {1082 .found => |root| {
1014 const allocator = self.builder.allocator;1083 const allocator = b.allocator;
1015 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);1084 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1016 defer self.builder.allocator.free(triplet);1085 defer b.allocator.free(triplet);
10171086
1018 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });1087 const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" });
1019 errdefer allocator.free(include_path);1088 errdefer allocator.free(include_path);
1020 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });1089 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
10211090
1022 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });1091 const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" });
1023 try self.lib_paths.append(lib_path);1092 try self.lib_paths.append(.{ .path = lib_path });
10241093
1025 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });1094 self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" });
1026 },1095 },
1027 }1096 }
1028}1097}
10291098
1030pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {1099pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1100 const b = self.step.owner;
1031 assert(self.kind == .@"test");1101 assert(self.kind == .@"test");
1032 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");1102 const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1033 for (args, 0..) |arg, i| {1103 for (args, 0..) |arg, i| {
1034 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;1104 duped_args[i] = if (arg) |a| b.dupe(a) else null;
1035 }1105 }
1036 self.exec_cmd_args = duped_args;1106 self.exec_cmd_args = duped_args;
1037}1107}
...@@ -1040,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {...@@ -1040,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
1040 self.step.dependOn(&other.step);1110 self.step.dependOn(&other.step);
1041 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");1111 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
1042 self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");1112 self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
1113
1114 for (other.installed_headers.items) |install_step| {
1115 self.step.dependOn(install_step);
1116 }
1043}1117}
10441118
1045fn appendModuleArgs(1119fn appendModuleArgs(
1046 cs: *CompileStep,1120 cs: *CompileStep,
1047 zig_args: *ArrayList([]const u8),1121 zig_args: *ArrayList([]const u8),
1048) error{OutOfMemory}!void {1122) error{OutOfMemory}!void {
1123 const b = cs.step.owner;
1049 // First, traverse the whole dependency graph and give every module a unique name, ideally one1124 // First, traverse the whole dependency graph and give every module a unique name, ideally one
1050 // named after what it's called somewhere in the graph. It will help here to have both a mapping1125 // named after what it's called somewhere in the graph. It will help here to have both a mapping
1051 // from module to name and a set of all the currently-used names.1126 // from module to name and a set of all the currently-used names.
1052 var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator);1127 var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator);
1053 var names = std.StringHashMap(void).init(cs.builder.allocator);1128 var names = std.StringHashMap(void).init(b.allocator);
10541129
1055 var to_name = std.ArrayList(struct {1130 var to_name = std.ArrayList(struct {
1056 name: []const u8,1131 name: []const u8,
1057 mod: *Module,1132 mod: *Module,
1058 }).init(cs.builder.allocator);1133 }).init(b.allocator);
1059 {1134 {
1060 var it = cs.modules.iterator();1135 var it = cs.modules.iterator();
1061 while (it.next()) |kv| {1136 while (it.next()) |kv| {
...@@ -1076,7 +1151,7 @@ fn appendModuleArgs(...@@ -1076,7 +1151,7 @@ fn appendModuleArgs(
1076 if (mod_names.contains(dep.mod)) continue;1151 if (mod_names.contains(dep.mod)) continue;
10771152
1078 // We'll use this buffer to store the name we decide on1153 // We'll use this buffer to store the name we decide on
1079 var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32);1154 var buf = try b.allocator.alloc(u8, dep.name.len + 32);
1080 // First, try just the exposed dependency name1155 // First, try just the exposed dependency name
1081 std.mem.copy(u8, buf, dep.name);1156 std.mem.copy(u8, buf, dep.name);
1082 var name = buf[0..dep.name.len];1157 var name = buf[0..dep.name.len];
...@@ -1113,15 +1188,15 @@ fn appendModuleArgs(...@@ -1113,15 +1188,15 @@ fn appendModuleArgs(
1113 const mod = kv.key_ptr.*;1188 const mod = kv.key_ptr.*;
1114 const name = kv.value_ptr.*;1189 const name = kv.value_ptr.*;
11151190
1116 const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies);1191 const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies);
1117 const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));1192 const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));
1118 try zig_args.append("--mod");1193 try zig_args.append("--mod");
1119 try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));1194 try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));
1120 }1195 }
1121 }1196 }
11221197
1123 // Lastly, output the root dependencies1198 // Lastly, output the root dependencies
1124 const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules);1199 const deps_str = try constructDepString(b.allocator, mod_names, cs.modules);
1125 if (deps_str.len > 0) {1200 if (deps_str.len > 0) {
1126 try zig_args.append("--deps");1201 try zig_args.append("--deps");
1127 try zig_args.append(deps_str);1202 try zig_args.append(deps_str);
...@@ -1151,43 +1226,36 @@ fn constructDepString(...@@ -1151,43 +1226,36 @@ fn constructDepString(
1151 }1226 }
1152}1227}
11531228
1154fn make(step: *Step) !void {1229fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1230 const b = step.owner;
1155 const self = @fieldParentPtr(CompileStep, "step", step);1231 const self = @fieldParentPtr(CompileStep, "step", step);
1156 const builder = self.builder;
11571232
1158 if (self.root_src == null and self.link_objects.items.len == 0) {1233 if (self.root_src == null and self.link_objects.items.len == 0) {
1159 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});1234 return step.fail("the linker needs one or more objects to link", .{});
1160 return error.NeedAnObject;
1161 }1235 }
11621236
1163 var zig_args = ArrayList([]const u8).init(builder.allocator);1237 var zig_args = ArrayList([]const u8).init(b.allocator);
1164 defer zig_args.deinit();1238 defer zig_args.deinit();
11651239
1166 try zig_args.append(builder.zig_exe);1240 try zig_args.append(b.zig_exe);
11671241
1168 const cmd = switch (self.kind) {1242 const cmd = switch (self.kind) {
1169 .lib => "build-lib",1243 .lib => "build-lib",
1170 .exe => "build-exe",1244 .exe => "build-exe",
1171 .obj => "build-obj",1245 .obj => "build-obj",
1172 .@"test" => "test",1246 .@"test" => "test",
1173 .test_exe => "test",
1174 };1247 };
1175 try zig_args.append(cmd);1248 try zig_args.append(cmd);
11761249
1177 if (builder.color != .auto) {1250 if (b.reference_trace) |some| {
1178 try zig_args.append("--color");1251 try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some}));
1179 try zig_args.append(@tagName(builder.color));
1180 }
1181
1182 if (builder.reference_trace) |some| {
1183 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1184 }1252 }
11851253
1186 try addFlag(&zig_args, "LLVM", self.use_llvm);1254 try addFlag(&zig_args, "LLVM", self.use_llvm);
1187 try addFlag(&zig_args, "LLD", self.use_lld);1255 try addFlag(&zig_args, "LLD", self.use_lld);
11881256
1189 if (self.target.ofmt) |ofmt| {1257 if (self.target.ofmt) |ofmt| {
1190 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));1258 try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1191 }1259 }
11921260
1193 if (self.entry_symbol_name) |entry| {1261 if (self.entry_symbol_name) |entry| {
...@@ -1197,18 +1265,18 @@ fn make(step: *Step) !void {...@@ -1197,18 +1265,18 @@ fn make(step: *Step) !void {
11971265
1198 if (self.stack_size) |stack_size| {1266 if (self.stack_size) |stack_size| {
1199 try zig_args.append("--stack");1267 try zig_args.append("--stack");
1200 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));1268 try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size}));
1201 }1269 }
12021270
1203 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));1271 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b));
12041272
1205 // We will add link objects from transitive dependencies, but we want to keep1273 // We will add link objects from transitive dependencies, but we want to keep
1206 // all link objects in the same order provided.1274 // all link objects in the same order provided.
1207 // This array is used to keep self.link_objects immutable.1275 // This array is used to keep self.link_objects immutable.
1208 var transitive_deps: TransitiveDeps = .{1276 var transitive_deps: TransitiveDeps = .{
1209 .link_objects = ArrayList(LinkObject).init(builder.allocator),1277 .link_objects = ArrayList(LinkObject).init(b.allocator),
1210 .seen_system_libs = StringHashMap(void).init(builder.allocator),1278 .seen_system_libs = StringHashMap(void).init(b.allocator),
1211 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),1279 .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator),
1212 .is_linking_libcpp = self.is_linking_libcpp,1280 .is_linking_libcpp = self.is_linking_libcpp,
1213 .is_linking_libc = self.is_linking_libc,1281 .is_linking_libc = self.is_linking_libc,
1214 .frameworks = &self.frameworks,1282 .frameworks = &self.frameworks,
...@@ -1221,14 +1289,13 @@ fn make(step: *Step) !void {...@@ -1221,14 +1289,13 @@ fn make(step: *Step) !void {
12211289
1222 for (transitive_deps.link_objects.items) |link_object| {1290 for (transitive_deps.link_objects.items) |link_object| {
1223 switch (link_object) {1291 switch (link_object) {
1224 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),1292 .static_path => |static_path| try zig_args.append(static_path.getPath(b)),
12251293
1226 .other_step => |other| switch (other.kind) {1294 .other_step => |other| switch (other.kind) {
1227 .exe => @panic("Cannot link with an executable build artifact"),1295 .exe => @panic("Cannot link with an executable build artifact"),
1228 .test_exe => @panic("Cannot link with an executable build artifact"),
1229 .@"test" => @panic("Cannot link with a test"),1296 .@"test" => @panic("Cannot link with a test"),
1230 .obj => {1297 .obj => {
1231 try zig_args.append(other.getOutputSource().getPath(builder));1298 try zig_args.append(other.getOutputSource().getPath(b));
1232 },1299 },
1233 .lib => l: {1300 .lib => l: {
1234 if (self.isStaticLibrary() and other.isStaticLibrary()) {1301 if (self.isStaticLibrary() and other.isStaticLibrary()) {
...@@ -1236,7 +1303,7 @@ fn make(step: *Step) !void {...@@ -1236,7 +1303,7 @@ fn make(step: *Step) !void {
1236 break :l;1303 break :l;
1237 }1304 }
12381305
1239 const full_path_lib = other.getOutputLibSource().getPath(builder);1306 const full_path_lib = other.getOutputLibSource().getPath(b);
1240 try zig_args.append(full_path_lib);1307 try zig_args.append(full_path_lib);
12411308
1242 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {1309 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
...@@ -1251,14 +1318,11 @@ fn make(step: *Step) !void {...@@ -1251,14 +1318,11 @@ fn make(step: *Step) !void {
1251 .system_lib => |system_lib| {1318 .system_lib => |system_lib| {
1252 const prefix: []const u8 = prefix: {1319 const prefix: []const u8 = prefix: {
1253 if (system_lib.needed) break :prefix "-needed-l";1320 if (system_lib.needed) break :prefix "-needed-l";
1254 if (system_lib.weak) {1321 if (system_lib.weak) break :prefix "-weak-l";
1255 if (self.target.isDarwin()) break :prefix "-weak-l";
1256 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1257 }
1258 break :prefix "-l";1322 break :prefix "-l";
1259 };1323 };
1260 switch (system_lib.use_pkg_config) {1324 switch (system_lib.use_pkg_config) {
1261 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),1325 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
1262 .yes, .force => {1326 .yes, .force => {
1263 if (self.runPkgConfig(system_lib.name)) |args| {1327 if (self.runPkgConfig(system_lib.name)) |args| {
1264 try zig_args.appendSlice(args);1328 try zig_args.appendSlice(args);
...@@ -1272,7 +1336,7 @@ fn make(step: *Step) !void {...@@ -1272,7 +1336,7 @@ fn make(step: *Step) !void {
1272 .yes => {1336 .yes => {
1273 // pkg-config failed, so fall back to linking the library1337 // pkg-config failed, so fall back to linking the library
1274 // by name directly.1338 // by name directly.
1275 try zig_args.append(builder.fmt("{s}{s}", .{1339 try zig_args.append(b.fmt("{s}{s}", .{
1276 prefix,1340 prefix,
1277 system_lib.name,1341 system_lib.name,
1278 }));1342 }));
...@@ -1295,7 +1359,7 @@ fn make(step: *Step) !void {...@@ -1295,7 +1359,7 @@ fn make(step: *Step) !void {
1295 try zig_args.append("--");1359 try zig_args.append("--");
1296 prev_has_extra_flags = false;1360 prev_has_extra_flags = false;
1297 }1361 }
1298 try zig_args.append(asm_file.getPath(builder));1362 try zig_args.append(asm_file.getPath(b));
1299 },1363 },
13001364
1301 .c_source_file => |c_source_file| {1365 .c_source_file => |c_source_file| {
...@@ -1312,7 +1376,7 @@ fn make(step: *Step) !void {...@@ -1312,7 +1376,7 @@ fn make(step: *Step) !void {
1312 }1376 }
1313 try zig_args.append("--");1377 try zig_args.append("--");
1314 }1378 }
1315 try zig_args.append(c_source_file.source.getPath(builder));1379 try zig_args.append(c_source_file.source.getPath(b));
1316 },1380 },
13171381
1318 .c_source_files => |c_source_files| {1382 .c_source_files => |c_source_files| {
...@@ -1330,7 +1394,7 @@ fn make(step: *Step) !void {...@@ -1330,7 +1394,7 @@ fn make(step: *Step) !void {
1330 try zig_args.append("--");1394 try zig_args.append("--");
1331 }1395 }
1332 for (c_source_files.files) |file| {1396 for (c_source_files.files) |file| {
1333 try zig_args.append(builder.pathFromRoot(file));1397 try zig_args.append(b.pathFromRoot(file));
1334 }1398 }
1335 },1399 },
1336 }1400 }
...@@ -1346,7 +1410,7 @@ fn make(step: *Step) !void {...@@ -1346,7 +1410,7 @@ fn make(step: *Step) !void {
13461410
1347 if (self.image_base) |image_base| {1411 if (self.image_base) |image_base| {
1348 try zig_args.append("--image-base");1412 try zig_args.append("--image-base");
1349 try zig_args.append(builder.fmt("0x{x}", .{image_base}));1413 try zig_args.append(b.fmt("0x{x}", .{image_base}));
1350 }1414 }
13511415
1352 if (self.filter) |filter| {1416 if (self.filter) |filter| {
...@@ -1358,39 +1422,34 @@ fn make(step: *Step) !void {...@@ -1358,39 +1422,34 @@ fn make(step: *Step) !void {
1358 try zig_args.append("--test-evented-io");1422 try zig_args.append("--test-evented-io");
1359 }1423 }
13601424
1361 if (self.name_prefix.len != 0) {
1362 try zig_args.append("--test-name-prefix");
1363 try zig_args.append(self.name_prefix);
1364 }
1365
1366 if (self.test_runner) |test_runner| {1425 if (self.test_runner) |test_runner| {
1367 try zig_args.append("--test-runner");1426 try zig_args.append("--test-runner");
1368 try zig_args.append(builder.pathFromRoot(test_runner));1427 try zig_args.append(b.pathFromRoot(test_runner));
1369 }1428 }
13701429
1371 for (builder.debug_log_scopes) |log_scope| {1430 for (b.debug_log_scopes) |log_scope| {
1372 try zig_args.append("--debug-log");1431 try zig_args.append("--debug-log");
1373 try zig_args.append(log_scope);1432 try zig_args.append(log_scope);
1374 }1433 }
13751434
1376 if (builder.debug_compile_errors) {1435 if (b.debug_compile_errors) {
1377 try zig_args.append("--debug-compile-errors");1436 try zig_args.append("--debug-compile-errors");
1378 }1437 }
13791438
1380 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");1439 if (b.verbose_cimport) try zig_args.append("--verbose-cimport");
1381 if (builder.verbose_air) try zig_args.append("--verbose-air");1440 if (b.verbose_air) try zig_args.append("--verbose-air");
1382 if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");1441 if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1383 if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");1442 if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1384 if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");1443 if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
1385 if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");1444 if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
13861445
1387 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);1446 if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg);
1388 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);1447 if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg);
1389 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);1448 if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg);
1390 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);1449 if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg);
1391 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);1450 if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg);
1392 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);1451 if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1393 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);1452 if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg);
13941453
1395 if (self.emit_h) try zig_args.append("-femit-h");1454 if (self.emit_h) try zig_args.append("-femit-h");
13961455
...@@ -1431,31 +1490,31 @@ fn make(step: *Step) !void {...@@ -1431,31 +1490,31 @@ fn make(step: *Step) !void {
1431 }1490 }
1432 if (self.link_z_common_page_size) |size| {1491 if (self.link_z_common_page_size) |size| {
1433 try zig_args.append("-z");1492 try zig_args.append("-z");
1434 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));1493 try zig_args.append(b.fmt("common-page-size={d}", .{size}));
1435 }1494 }
1436 if (self.link_z_max_page_size) |size| {1495 if (self.link_z_max_page_size) |size| {
1437 try zig_args.append("-z");1496 try zig_args.append("-z");
1438 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));1497 try zig_args.append(b.fmt("max-page-size={d}", .{size}));
1439 }1498 }
14401499
1441 if (self.libc_file) |libc_file| {1500 if (self.libc_file) |libc_file| {
1442 try zig_args.append("--libc");1501 try zig_args.append("--libc");
1443 try zig_args.append(libc_file.getPath(builder));1502 try zig_args.append(libc_file.getPath(b));
1444 } else if (builder.libc_file) |libc_file| {1503 } else if (b.libc_file) |libc_file| {
1445 try zig_args.append("--libc");1504 try zig_args.append("--libc");
1446 try zig_args.append(libc_file);1505 try zig_args.append(libc_file);
1447 }1506 }
14481507
1449 switch (self.optimize) {1508 switch (self.optimize) {
1450 .Debug => {}, // Skip since it's the default.1509 .Debug => {}, // Skip since it's the default.
1451 else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),1510 else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})),
1452 }1511 }
14531512
1454 try zig_args.append("--cache-dir");1513 try zig_args.append("--cache-dir");
1455 try zig_args.append(builder.cache_root.path orelse ".");1514 try zig_args.append(b.cache_root.path orelse ".");
14561515
1457 try zig_args.append("--global-cache-dir");1516 try zig_args.append("--global-cache-dir");
1458 try zig_args.append(builder.global_cache_root.path orelse ".");1517 try zig_args.append(b.global_cache_root.path orelse ".");
14591518
1460 try zig_args.append("--name");1519 try zig_args.append("--name");
1461 try zig_args.append(self.name);1520 try zig_args.append(self.name);
...@@ -1467,11 +1526,11 @@ fn make(step: *Step) !void {...@@ -1467,11 +1526,11 @@ fn make(step: *Step) !void {
1467 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {1526 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1468 if (self.version) |version| {1527 if (self.version) |version| {
1469 try zig_args.append("--version");1528 try zig_args.append("--version");
1470 try zig_args.append(builder.fmt("{}", .{version}));1529 try zig_args.append(b.fmt("{}", .{version}));
1471 }1530 }
14721531
1473 if (self.target.isDarwin()) {1532 if (self.target.isDarwin()) {
1474 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{1533 const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{
1475 self.target.libPrefix(),1534 self.target.libPrefix(),
1476 self.name,1535 self.name,
1477 self.target.dynamicLibSuffix(),1536 self.target.dynamicLibSuffix(),
...@@ -1485,7 +1544,7 @@ fn make(step: *Step) !void {...@@ -1485,7 +1544,7 @@ fn make(step: *Step) !void {
1485 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });1544 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1486 }1545 }
1487 if (self.pagezero_size) |pagezero_size| {1546 if (self.pagezero_size) |pagezero_size| {
1488 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});1547 const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size});
1489 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });1548 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1490 }1549 }
1491 if (self.search_strategy) |strat| switch (strat) {1550 if (self.search_strategy) |strat| switch (strat) {
...@@ -1493,7 +1552,7 @@ fn make(step: *Step) !void {...@@ -1493,7 +1552,7 @@ fn make(step: *Step) !void {
1493 .dylibs_first => try zig_args.append("-search_dylibs_first"),1552 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1494 };1553 };
1495 if (self.headerpad_size) |headerpad_size| {1554 if (self.headerpad_size) |headerpad_size| {
1496 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});1555 const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size});
1497 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });1556 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1498 }1557 }
1499 if (self.headerpad_max_install_names) {1558 if (self.headerpad_max_install_names) {
...@@ -1541,16 +1600,16 @@ fn make(step: *Step) !void {...@@ -1541,16 +1600,16 @@ fn make(step: *Step) !void {
1541 try zig_args.append("--export-table");1600 try zig_args.append("--export-table");
1542 }1601 }
1543 if (self.initial_memory) |initial_memory| {1602 if (self.initial_memory) |initial_memory| {
1544 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));1603 try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory}));
1545 }1604 }
1546 if (self.max_memory) |max_memory| {1605 if (self.max_memory) |max_memory| {
1547 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));1606 try zig_args.append(b.fmt("--max-memory={d}", .{max_memory}));
1548 }1607 }
1549 if (self.shared_memory) {1608 if (self.shared_memory) {
1550 try zig_args.append("--shared-memory");1609 try zig_args.append("--shared-memory");
1551 }1610 }
1552 if (self.global_base) |global_base| {1611 if (self.global_base) |global_base| {
1553 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));1612 try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
1554 }1613 }
15551614
1556 if (self.code_model != .default) {1615 if (self.code_model != .default) {
...@@ -1558,16 +1617,16 @@ fn make(step: *Step) !void {...@@ -1558,16 +1617,16 @@ fn make(step: *Step) !void {
1558 try zig_args.append(@tagName(self.code_model));1617 try zig_args.append(@tagName(self.code_model));
1559 }1618 }
1560 if (self.wasi_exec_model) |model| {1619 if (self.wasi_exec_model) |model| {
1561 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));1620 try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)}));
1562 }1621 }
1563 for (self.export_symbol_names) |symbol_name| {1622 for (self.export_symbol_names) |symbol_name| {
1564 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));1623 try zig_args.append(b.fmt("--export={s}", .{symbol_name}));
1565 }1624 }
15661625
1567 if (!self.target.isNative()) {1626 if (!self.target.isNative()) {
1568 try zig_args.appendSlice(&.{1627 try zig_args.appendSlice(&.{
1569 "-target", try self.target.zigTriple(builder.allocator),1628 "-target", try self.target.zigTriple(b.allocator),
1570 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),1629 "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()),
1571 });1630 });
15721631
1573 if (self.target.dynamic_linker.get()) |dynamic_linker| {1632 if (self.target.dynamic_linker.get()) |dynamic_linker| {
...@@ -1578,12 +1637,12 @@ fn make(step: *Step) !void {...@@ -1578,12 +1637,12 @@ fn make(step: *Step) !void {
15781637
1579 if (self.linker_script) |linker_script| {1638 if (self.linker_script) |linker_script| {
1580 try zig_args.append("--script");1639 try zig_args.append("--script");
1581 try zig_args.append(linker_script.getPath(builder));1640 try zig_args.append(linker_script.getPath(b));
1582 }1641 }
15831642
1584 if (self.version_script) |version_script| {1643 if (self.version_script) |version_script| {
1585 try zig_args.append("--version-script");1644 try zig_args.append("--version-script");
1586 try zig_args.append(builder.pathFromRoot(version_script));1645 try zig_args.append(b.pathFromRoot(version_script));
1587 }1646 }
15881647
1589 if (self.kind == .@"test") {1648 if (self.kind == .@"test") {
...@@ -1596,83 +1655,7 @@ fn make(step: *Step) !void {...@@ -1596,83 +1655,7 @@ fn make(step: *Step) !void {
1596 try zig_args.append("--test-cmd-bin");1655 try zig_args.append("--test-cmd-bin");
1597 }1656 }
1598 }1657 }
1599 } else {
1600 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1601
1602 switch (builder.host.getExternalExecutor(self.target_info, .{
1603 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1604 .link_libc = transitive_deps.is_linking_libc,
1605 })) {
1606 .native => {},
1607 .bad_dl, .bad_os_or_cpu => {
1608 try zig_args.append("--test-no-exec");
1609 },
1610 .rosetta => if (builder.enable_rosetta) {
1611 try zig_args.append("--test-cmd-bin");
1612 } else {
1613 try zig_args.append("--test-no-exec");
1614 },
1615 .qemu => |bin_name| ok: {
1616 if (builder.enable_qemu) qemu: {
1617 const glibc_dir_arg = if (need_cross_glibc)
1618 builder.glibc_runtimes_dir orelse break :qemu
1619 else
1620 null;
1621 try zig_args.append("--test-cmd");
1622 try zig_args.append(bin_name);
1623 if (glibc_dir_arg) |dir| {
1624 // TODO look into making this a call to `linuxTriple`. This
1625 // needs the directory to be called "i686" rather than
1626 // "x86" which is why we do it manually here.
1627 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1628 const cpu_arch = self.target.getCpuArch();
1629 const os_tag = self.target.getOsTag();
1630 const abi = self.target.getAbi();
1631 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1632 "i686"
1633 else
1634 @tagName(cpu_arch);
1635 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1636 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1637 });
1638
1639 try zig_args.append("--test-cmd");
1640 try zig_args.append("-L");
1641 try zig_args.append("--test-cmd");
1642 try zig_args.append(full_dir);
1643 }
1644 try zig_args.append("--test-cmd-bin");
1645 break :ok;
1646 }
1647 try zig_args.append("--test-no-exec");
1648 },
1649 .wine => |bin_name| if (builder.enable_wine) {
1650 try zig_args.append("--test-cmd");
1651 try zig_args.append(bin_name);
1652 try zig_args.append("--test-cmd-bin");
1653 } else {
1654 try zig_args.append("--test-no-exec");
1655 },
1656 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1657 try zig_args.append("--test-cmd");
1658 try zig_args.append(bin_name);
1659 try zig_args.append("--test-cmd");
1660 try zig_args.append("--dir=.");
1661 try zig_args.append("--test-cmd-bin");
1662 } else {
1663 try zig_args.append("--test-no-exec");
1664 },
1665 .darling => |bin_name| if (builder.enable_darling) {
1666 try zig_args.append("--test-cmd");
1667 try zig_args.append(bin_name);
1668 try zig_args.append("--test-cmd-bin");
1669 } else {
1670 try zig_args.append("--test-no-exec");
1671 },
1672 }
1673 }1658 }
1674 } else if (self.kind == .test_exe) {
1675 try zig_args.append("--test-no-exec");
1676 }1659 }
16771660
1678 try self.appendModuleArgs(&zig_args);1661 try self.appendModuleArgs(&zig_args);
...@@ -1681,18 +1664,18 @@ fn make(step: *Step) !void {...@@ -1681,18 +1664,18 @@ fn make(step: *Step) !void {
1681 switch (include_dir) {1664 switch (include_dir) {
1682 .raw_path => |include_path| {1665 .raw_path => |include_path| {
1683 try zig_args.append("-I");1666 try zig_args.append("-I");
1684 try zig_args.append(builder.pathFromRoot(include_path));1667 try zig_args.append(b.pathFromRoot(include_path));
1685 },1668 },
1686 .raw_path_system => |include_path| {1669 .raw_path_system => |include_path| {
1687 if (builder.sysroot != null) {1670 if (b.sysroot != null) {
1688 try zig_args.append("-iwithsysroot");1671 try zig_args.append("-iwithsysroot");
1689 } else {1672 } else {
1690 try zig_args.append("-isystem");1673 try zig_args.append("-isystem");
1691 }1674 }
16921675
1693 const resolved_include_path = builder.pathFromRoot(include_path);1676 const resolved_include_path = b.pathFromRoot(include_path);
16941677
1695 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {1678 const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1696 // We need to check for disk designator and strip it out from dir path so1679 // We need to check for disk designator and strip it out from dir path so
1697 // that zig/clang can concat resolved_include_path with sysroot.1680 // that zig/clang can concat resolved_include_path with sysroot.
1698 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);1681 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
...@@ -1708,17 +1691,14 @@ fn make(step: *Step) !void {...@@ -1708,17 +1691,14 @@ fn make(step: *Step) !void {
1708 },1691 },
1709 .other_step => |other| {1692 .other_step => |other| {
1710 if (other.emit_h) {1693 if (other.emit_h) {
1711 const h_path = other.getOutputHSource().getPath(builder);1694 const h_path = other.getOutputHSource().getPath(b);
1712 try zig_args.append("-isystem");1695 try zig_args.append("-isystem");
1713 try zig_args.append(fs.path.dirname(h_path).?);1696 try zig_args.append(fs.path.dirname(h_path).?);
1714 }1697 }
1715 if (other.installed_headers.items.len > 0) {1698 if (other.installed_headers.items.len > 0) {
1716 for (other.installed_headers.items) |install_step| {
1717 try install_step.make();
1718 }
1719 try zig_args.append("-I");1699 try zig_args.append("-I");
1720 try zig_args.append(builder.pathJoin(&.{1700 try zig_args.append(b.pathJoin(&.{
1721 other.builder.install_prefix, "include",1701 other.step.owner.install_prefix, "include",
1722 }));1702 }));
1723 }1703 }
1724 },1704 },
...@@ -1730,33 +1710,35 @@ fn make(step: *Step) !void {...@@ -1730,33 +1710,35 @@ fn make(step: *Step) !void {
1730 }1710 }
1731 }1711 }
17321712
1733 for (self.lib_paths.items) |lib_path| {1713 for (self.c_macros.items) |c_macro| {
1734 try zig_args.append("-L");1714 try zig_args.append("-D");
1735 try zig_args.append(lib_path);1715 try zig_args.append(c_macro);
1736 }1716 }
17371717
1738 for (self.rpaths.items) |rpath| {1718 try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len);
1739 try zig_args.append("-rpath");1719 for (self.lib_paths.items) |lib_path| {
1740 try zig_args.append(rpath);1720 zig_args.appendAssumeCapacity("-L");
1721 zig_args.appendAssumeCapacity(lib_path.getPath2(b, step));
1741 }1722 }
17421723
1743 for (self.c_macros.items) |c_macro| {1724 try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len);
1744 try zig_args.append("-D");1725 for (self.rpaths.items) |rpath| {
1745 try zig_args.append(c_macro);1726 zig_args.appendAssumeCapacity("-rpath");
1727 zig_args.appendAssumeCapacity(rpath.getPath2(b, step));
1746 }1728 }
17471729
1748 if (self.target.isDarwin()) {1730 for (self.framework_dirs.items) |directory_source| {
1749 for (self.framework_dirs.items) |dir| {1731 if (b.sysroot != null) {
1750 if (builder.sysroot != null) {1732 try zig_args.append("-iframeworkwithsysroot");
1751 try zig_args.append("-iframeworkwithsysroot");1733 } else {
1752 } else {1734 try zig_args.append("-iframework");
1753 try zig_args.append("-iframework");
1754 }
1755 try zig_args.append(dir);
1756 try zig_args.append("-F");
1757 try zig_args.append(dir);
1758 }1735 }
1736 try zig_args.append(directory_source.getPath2(b, step));
1737 try zig_args.append("-F");
1738 try zig_args.append(directory_source.getPath2(b, step));
1739 }
17591740
1741 {
1760 var it = self.frameworks.iterator();1742 var it = self.frameworks.iterator();
1761 while (it.next()) |entry| {1743 while (it.next()) |entry| {
1762 const name = entry.key_ptr.*;1744 const name = entry.key_ptr.*;
...@@ -1770,29 +1752,45 @@ fn make(step: *Step) !void {...@@ -1770,29 +1752,45 @@ fn make(step: *Step) !void {
1770 }1752 }
1771 try zig_args.append(name);1753 try zig_args.append(name);
1772 }1754 }
1773 } else {
1774 if (self.framework_dirs.items.len > 0) {
1775 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1776 }
1777
1778 if (self.frameworks.count() > 0) {
1779 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1780 }
1781 }1755 }
17821756
1783 if (builder.sysroot) |sysroot| {1757 if (b.sysroot) |sysroot| {
1784 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });1758 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1785 }1759 }
17861760
1787 for (builder.search_prefixes.items) |search_prefix| {1761 for (b.search_prefixes.items) |search_prefix| {
1788 try zig_args.append("-L");1762 var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| {
1789 try zig_args.append(builder.pathJoin(&.{1763 return step.fail("unable to open prefix directory '{s}': {s}", .{
1790 search_prefix, "lib",1764 search_prefix, @errorName(err),
1791 }));1765 });
1792 try zig_args.append("-I");1766 };
1793 try zig_args.append(builder.pathJoin(&.{1767 defer prefix_dir.close();
1794 search_prefix, "include",1768
1795 }));1769 // Avoid passing -L and -I flags for nonexistent directories.
1770 // This prevents a warning, that should probably be upgraded to an error in Zig's
1771 // CLI parsing code, when the linker sees an -L directory that does not exist.
1772
1773 if (prefix_dir.accessZ("lib", .{})) |_| {
1774 try zig_args.appendSlice(&.{
1775 "-L", try fs.path.join(b.allocator, &.{ search_prefix, "lib" }),
1776 });
1777 } else |err| switch (err) {
1778 error.FileNotFound => {},
1779 else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{
1780 search_prefix, @errorName(e),
1781 }),
1782 }
1783
1784 if (prefix_dir.accessZ("include", .{})) |_| {
1785 try zig_args.appendSlice(&.{
1786 "-I", try fs.path.join(b.allocator, &.{ search_prefix, "include" }),
1787 });
1788 } else |err| switch (err) {
1789 error.FileNotFound => {},
1790 else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{
1791 search_prefix, @errorName(e),
1792 }),
1793 }
1796 }1794 }
17971795
1798 try addFlag(&zig_args, "valgrind", self.valgrind_support);1796 try addFlag(&zig_args, "valgrind", self.valgrind_support);
...@@ -1801,15 +1799,15 @@ fn make(step: *Step) !void {...@@ -1801,15 +1799,15 @@ fn make(step: *Step) !void {
18011799
1802 if (self.zig_lib_dir) |dir| {1800 if (self.zig_lib_dir) |dir| {
1803 try zig_args.append("--zig-lib-dir");1801 try zig_args.append("--zig-lib-dir");
1804 try zig_args.append(builder.pathFromRoot(dir));1802 try zig_args.append(b.pathFromRoot(dir));
1805 } else if (builder.zig_lib_dir) |dir| {1803 } else if (b.zig_lib_dir) |dir| {
1806 try zig_args.append("--zig-lib-dir");1804 try zig_args.append("--zig-lib-dir");
1807 try zig_args.append(dir);1805 try zig_args.append(dir);
1808 }1806 }
18091807
1810 if (self.main_pkg_path) |dir| {1808 if (self.main_pkg_path) |dir| {
1811 try zig_args.append("--main-pkg-path");1809 try zig_args.append("--main-pkg-path");
1812 try zig_args.append(builder.pathFromRoot(dir));1810 try zig_args.append(b.pathFromRoot(dir));
1813 }1811 }
18141812
1815 try addFlag(&zig_args, "PIC", self.force_pic);1813 try addFlag(&zig_args, "PIC", self.force_pic);
...@@ -1831,6 +1829,7 @@ fn make(step: *Step) !void {...@@ -1831,6 +1829,7 @@ fn make(step: *Step) !void {
1831 }1829 }
18321830
1833 try zig_args.append("--enable-cache");1831 try zig_args.append("--enable-cache");
1832 try zig_args.append("--listen=-");
18341833
1835 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux1834 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1836 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and1835 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
...@@ -1841,15 +1840,15 @@ fn make(step: *Step) !void {...@@ -1841,15 +1840,15 @@ fn make(step: *Step) !void {
1841 args_length += arg.len + 1; // +1 to account for null terminator1840 args_length += arg.len + 1; // +1 to account for null terminator
1842 }1841 }
1843 if (args_length >= 30 * 1024) {1842 if (args_length >= 30 * 1024) {
1844 try builder.cache_root.handle.makePath("args");1843 try b.cache_root.handle.makePath("args");
18451844
1846 const args_to_escape = zig_args.items[2..];1845 const args_to_escape = zig_args.items[2..];
1847 var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len);1846 var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len);
1848 arg_blk: for (args_to_escape) |arg| {1847 arg_blk: for (args_to_escape) |arg| {
1849 for (arg, 0..) |c, arg_idx| {1848 for (arg, 0..) |c, arg_idx| {
1850 if (c == '\\' or c == '"') {1849 if (c == '\\' or c == '"') {
1851 // Slow path for arguments that need to be escaped. We'll need to allocate and copy1850 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1852 var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1);1851 var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1);
1853 const writer = escaped.writer();1852 const writer = escaped.writer();
1854 try writer.writeAll(arg[0..arg_idx]);1853 try writer.writeAll(arg[0..arg_idx]);
1855 for (arg[arg_idx..]) |to_escape| {1854 for (arg[arg_idx..]) |to_escape| {
...@@ -1865,8 +1864,8 @@ fn make(step: *Step) !void {...@@ -1865,8 +1864,8 @@ fn make(step: *Step) !void {
18651864
1866 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with1865 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1867 // other zig build commands running in parallel.1866 // other zig build commands running in parallel.
1868 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);1867 const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items);
1869 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });1868 const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
18701869
1871 var args_hash: [Sha256.digest_length]u8 = undefined;1870 var args_hash: [Sha256.digest_length]u8 = undefined;
1872 Sha256.hash(args, &args_hash, .{});1871 Sha256.hash(args, &args_hash, .{});
...@@ -1878,28 +1877,35 @@ fn make(step: *Step) !void {...@@ -1878,28 +1877,35 @@ fn make(step: *Step) !void {
1878 );1877 );
18791878
1880 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;1879 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
1881 try builder.cache_root.handle.writeFile(args_file, args);1880 try b.cache_root.handle.writeFile(args_file, args);
18821881
1883 const resolved_args_file = try mem.concat(builder.allocator, u8, &.{1882 const resolved_args_file = try mem.concat(b.allocator, u8, &.{
1884 "@",1883 "@",
1885 try builder.cache_root.join(builder.allocator, &.{args_file}),1884 try b.cache_root.join(b.allocator, &.{args_file}),
1886 });1885 });
18871886
1888 zig_args.shrinkRetainingCapacity(2);1887 zig_args.shrinkRetainingCapacity(2);
1889 try zig_args.append(resolved_args_file);1888 try zig_args.append(resolved_args_file);
1890 }1889 }
18911890
1892 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);1891 const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
1893 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");1892 error.NeedCompileErrorCheck => {
1893 assert(self.expect_errors.len != 0);
1894 try checkCompileErrors(self);
1895 return;
1896 },
1897 else => |e| return e,
1898 };
1899 const build_output_dir = fs.path.dirname(output_bin_path).?;
18941900
1895 if (self.output_dir) |output_dir| {1901 if (self.output_dir) |output_dir| {
1896 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});1902 var src_dir = try fs.cwd().openIterableDir(build_output_dir, .{});
1897 defer src_dir.close();1903 defer src_dir.close();
18981904
1899 // Create the output directory if it doesn't exist.1905 // Create the output directory if it doesn't exist.
1900 try std.fs.cwd().makePath(output_dir);1906 try fs.cwd().makePath(output_dir);
19011907
1902 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});1908 var dest_dir = try fs.cwd().openDir(output_dir, .{});
1903 defer dest_dir.close();1909 defer dest_dir.close();
19041910
1905 var it = src_dir.iterate();1911 var it = src_dir.iterate();
...@@ -1923,25 +1929,34 @@ fn make(step: *Step) !void {...@@ -1923,25 +1929,34 @@ fn make(step: *Step) !void {
19231929
1924 // Update generated files1930 // Update generated files
1925 if (self.output_dir != null) {1931 if (self.output_dir != null) {
1926 self.output_path_source.path = builder.pathJoin(1932 self.output_dirname_source.path = self.output_dir.?;
1933
1934 self.output_path_source.path = b.pathJoin(
1927 &.{ self.output_dir.?, self.out_filename },1935 &.{ self.output_dir.?, self.out_filename },
1928 );1936 );
19291937
1930 if (self.emit_h) {1938 if (self.emit_h) {
1931 self.output_h_path_source.path = builder.pathJoin(1939 self.output_h_path_source.path = b.pathJoin(
1932 &.{ self.output_dir.?, self.out_h_filename },1940 &.{ self.output_dir.?, self.out_h_filename },
1933 );1941 );
1934 }1942 }
19351943
1936 if (self.target.isWindows() or self.target.isUefi()) {1944 if (self.target.isWindows() or self.target.isUefi()) {
1937 self.output_pdb_path_source.path = builder.pathJoin(1945 self.output_pdb_path_source.path = b.pathJoin(
1938 &.{ self.output_dir.?, self.out_pdb_filename },1946 &.{ self.output_dir.?, self.out_pdb_filename },
1939 );1947 );
1940 }1948 }
1941 }1949 }
19421950
1943 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {1951 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and
1944 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);1952 self.version != null and self.target.wantSharedLibSymLinks())
1953 {
1954 try doAtomicSymLinks(
1955 step,
1956 self.getOutputSource().getPath(b),
1957 self.major_only_filename.?,
1958 self.name_only_filename.?,
1959 );
1945 }1960 }
1946}1961}
19471962
...@@ -1983,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {...@@ -1983,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1983}1998}
19841999
1985pub fn doAtomicSymLinks(2000pub fn doAtomicSymLinks(
1986 allocator: Allocator,2001 step: *Step,
1987 output_path: []const u8,2002 output_path: []const u8,
1988 filename_major_only: []const u8,2003 filename_major_only: []const u8,
1989 filename_name_only: []const u8,2004 filename_name_only: []const u8,
1990) !void {2005) !void {
2006 const arena = step.owner.allocator;
1991 const out_dir = fs.path.dirname(output_path) orelse ".";2007 const out_dir = fs.path.dirname(output_path) orelse ".";
1992 const out_basename = fs.path.basename(output_path);2008 const out_basename = fs.path.basename(output_path);
1993 // sym link for libfoo.so.1 to libfoo.so.1.2.32009 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1994 const major_only_path = try fs.path.join(2010 const major_only_path = try fs.path.join(arena, &.{ out_dir, filename_major_only });
1995 allocator,2011 fs.atomicSymLink(arena, out_basename, major_only_path) catch |err| {
1996 &[_][]const u8{ out_dir, filename_major_only },2012 return step.fail("unable to symlink {s} -> {s}: {s}", .{
1997 );2013 major_only_path, out_basename, @errorName(err),
1998 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {2014 });
1999 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
2000 return err;
2001 };2015 };
2002 // sym link for libfoo.so to libfoo.so.12016 // sym link for libfoo.so to libfoo.so.1
2003 const name_only_path = try fs.path.join(2017 const name_only_path = try fs.path.join(arena, &.{ out_dir, filename_name_only });
2004 allocator,2018 fs.atomicSymLink(arena, filename_major_only, name_only_path) catch |err| {
2005 &[_][]const u8{ out_dir, filename_name_only },2019 return step.fail("Unable to symlink {s} -> {s}: {s}", .{
2006 );2020 name_only_path, filename_major_only, @errorName(err),
2007 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {2021 });
2008 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
2009 return err;
2010 };2022 };
2011}2023}
20122024
...@@ -2042,7 +2054,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {...@@ -2042,7 +2054,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
2042 error.FileNotFound => error.PkgConfigNotInstalled,2054 error.FileNotFound => error.PkgConfigNotInstalled,
2043 error.InvalidName => error.PkgConfigNotInstalled,2055 error.InvalidName => error.PkgConfigNotInstalled,
2044 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,2056 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2045 error.ChildExecFailed => error.PkgConfigFailed,
2046 else => return err,2057 else => return err,
2047 };2058 };
2048 self.pkg_config_pkg_list = result;2059 self.pkg_config_pkg_list = result;
...@@ -2119,3 +2130,57 @@ const TransitiveDeps = struct {...@@ -2119,3 +2130,57 @@ const TransitiveDeps = struct {
2119 }2130 }
2120 }2131 }
2121};2132};
2133
2134fn checkCompileErrors(self: *CompileStep) !void {
2135 // Clear this field so that it does not get printed by the build runner.
2136 const actual_eb = self.step.result_error_bundle;
2137 self.step.result_error_bundle = std.zig.ErrorBundle.empty;
2138
2139 const arena = self.step.owner.allocator;
2140
2141 var actual_stderr_list = std.ArrayList(u8).init(arena);
2142 try actual_eb.renderToWriter(.{
2143 .ttyconf = .no_color,
2144 .include_reference_trace = false,
2145 .include_source_line = false,
2146 }, actual_stderr_list.writer());
2147 const actual_stderr = try actual_stderr_list.toOwnedSlice();
2148
2149 // Render the expected lines into a string that we can compare verbatim.
2150 var expected_generated = std.ArrayList(u8).init(arena);
2151
2152 var actual_line_it = mem.split(u8, actual_stderr, "\n");
2153 for (self.expect_errors) |expect_line| {
2154 const actual_line = actual_line_it.next() orelse {
2155 try expected_generated.appendSlice(expect_line);
2156 try expected_generated.append('\n');
2157 continue;
2158 };
2159 if (mem.endsWith(u8, actual_line, expect_line)) {
2160 try expected_generated.appendSlice(actual_line);
2161 try expected_generated.append('\n');
2162 continue;
2163 }
2164 if (mem.startsWith(u8, expect_line, ":?:?: ")) {
2165 if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) {
2166 try expected_generated.appendSlice(actual_line);
2167 try expected_generated.append('\n');
2168 continue;
2169 }
2170 }
2171 try expected_generated.appendSlice(expect_line);
2172 try expected_generated.append('\n');
2173 }
2174
2175 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
2176
2177 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
2178 return self.step.fail(
2179 \\
2180 \\========= expected: =====================
2181 \\{s}
2182 \\========= but found: ====================
2183 \\{s}
2184 \\=========================================
2185 , .{ expected_generated.items, actual_stderr });
2186}
lib/std/Build/ConfigHeaderStep.zig+82-73
...@@ -1,9 +1,3 @@...@@ -1,9 +1,3 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.Build.Step;
4
5pub const base_id: Step.Id = .config_header;
6
7pub const Style = union(enum) {1pub const Style = union(enum) {
8 /// The configure format supported by autotools. It uses `#undef foo` to2 /// The configure format supported by autotools. It uses `#undef foo` to
9 /// mark lines that can be substituted with different values.3 /// mark lines that can be substituted with different values.
...@@ -34,7 +28,6 @@ pub const Value = union(enum) {...@@ -34,7 +28,6 @@ pub const Value = union(enum) {
34};28};
3529
36step: Step,30step: Step,
37builder: *std.Build,
38values: std.StringArrayHashMap(Value),31values: std.StringArrayHashMap(Value),
39output_file: std.Build.GeneratedFile,32output_file: std.Build.GeneratedFile,
4033
...@@ -42,43 +35,57 @@ style: Style,...@@ -42,43 +35,57 @@ style: Style,
42max_bytes: usize,35max_bytes: usize,
43include_path: []const u8,36include_path: []const u8,
4437
38pub const base_id: Step.Id = .config_header;
39
45pub const Options = struct {40pub const Options = struct {
46 style: Style = .blank,41 style: Style = .blank,
47 max_bytes: usize = 2 * 1024 * 1024,42 max_bytes: usize = 2 * 1024 * 1024,
48 include_path: ?[]const u8 = null,43 include_path: ?[]const u8 = null,
44 first_ret_addr: ?usize = null,
49};45};
5046
51pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {47pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep {
52 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");48 const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM");
53 const name = if (options.style.getFileSource()) |s|
54 builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() })
55 else
56 builder.fmt("configure {s} header", .{@tagName(options.style)});
57 self.* = .{
58 .builder = builder,
59 .step = Step.init(base_id, name, builder.allocator, make),
60 .style = options.style,
61 .values = std.StringArrayHashMap(Value).init(builder.allocator),
6249
63 .max_bytes = options.max_bytes,50 var include_path: []const u8 = "config.h";
64 .include_path = "config.h",
65 .output_file = .{ .step = &self.step },
66 };
6751
68 if (options.style.getFileSource()) |s| switch (s) {52 if (options.style.getFileSource()) |s| switch (s) {
69 .path => |p| {53 .path => |p| {
70 const basename = std.fs.path.basename(p);54 const basename = std.fs.path.basename(p);
71 if (std.mem.endsWith(u8, basename, ".h.in")) {55 if (std.mem.endsWith(u8, basename, ".h.in")) {
72 self.include_path = basename[0 .. basename.len - 3];56 include_path = basename[0 .. basename.len - 3];
73 }57 }
74 },58 },
75 else => {},59 else => {},
76 };60 };
7761
78 if (options.include_path) |include_path| {62 if (options.include_path) |p| {
79 self.include_path = include_path;63 include_path = p;
80 }64 }
8165
66 const name = if (options.style.getFileSource()) |s|
67 owner.fmt("configure {s} header {s} to {s}", .{
68 @tagName(options.style), s.getDisplayName(), include_path,
69 })
70 else
71 owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path });
72
73 self.* = .{
74 .step = Step.init(.{
75 .id = base_id,
76 .name = name,
77 .owner = owner,
78 .makeFn = make,
79 .first_ret_addr = options.first_ret_addr orelse @returnAddress(),
80 }),
81 .style = options.style,
82 .values = std.StringArrayHashMap(Value).init(owner.allocator),
83
84 .max_bytes = options.max_bytes,
85 .include_path = include_path,
86 .output_file = .{ .step = &self.step },
87 };
88
82 return self;89 return self;
83}90}
8491
...@@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v...@@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
146 }153 }
147}154}
148155
149fn make(step: *Step) !void {156fn make(step: *Step, prog_node: *std.Progress.Node) !void {
157 _ = prog_node;
158 const b = step.owner;
150 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);159 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
151 const gpa = self.builder.allocator;160 const gpa = b.allocator;
152161 const arena = b.allocator;
153 // The cache is used here not really as a way to speed things up - because writing
154 // the data to a file would probably be very fast - but as a way to find a canonical
155 // location to put build artifacts.
156162
157 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep163 var man = b.cache.obtain();
158 // files, then two ConfigHeaderStep executing in parallel might clobber each other.164 defer man.deinit();
159165
160 // TODO port the cache system from the compiler to zig std lib. Until then
161 // we construct the path directly, and no "cache hit" detection happens;
162 // the files are always written.
163 // Note there is very similar code over in WriteFileStep
164 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
165 // Random bytes to make ConfigHeaderStep unique. Refresh this with new166 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
166 // random bytes when ConfigHeaderStep implementation is modified in a167 // random bytes when ConfigHeaderStep implementation is modified in a
167 // non-backwards-compatible way.168 // non-backwards-compatible way.
168 var hash = Hasher.init("PGuDTpidxyMqnkGM");169 man.hash.add(@as(u32, 0xdef08d23));
169170
170 var output = std.ArrayList(u8).init(gpa);171 var output = std.ArrayList(u8).init(gpa);
171 defer output.deinit();172 defer output.deinit();
...@@ -177,15 +178,15 @@ fn make(step: *Step) !void {...@@ -177,15 +178,15 @@ fn make(step: *Step) !void {
177 switch (self.style) {178 switch (self.style) {
178 .autoconf => |file_source| {179 .autoconf => |file_source| {
179 try output.appendSlice(c_generated_line);180 try output.appendSlice(c_generated_line);
180 const src_path = file_source.getPath(self.builder);181 const src_path = file_source.getPath(b);
181 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);182 const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes);
182 try render_autoconf(contents, &output, self.values, src_path);183 try render_autoconf(step, contents, &output, self.values, src_path);
183 },184 },
184 .cmake => |file_source| {185 .cmake => |file_source| {
185 try output.appendSlice(c_generated_line);186 try output.appendSlice(c_generated_line);
186 const src_path = file_source.getPath(self.builder);187 const src_path = file_source.getPath(b);
187 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);188 const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes);
188 try render_cmake(contents, &output, self.values, src_path);189 try render_cmake(step, contents, &output, self.values, src_path);
189 },190 },
190 .blank => {191 .blank => {
191 try output.appendSlice(c_generated_line);192 try output.appendSlice(c_generated_line);
...@@ -197,43 +198,44 @@ fn make(step: *Step) !void {...@@ -197,43 +198,44 @@ fn make(step: *Step) !void {
197 },198 },
198 }199 }
199200
200 hash.update(output.items);201 man.hash.addBytes(output.items);
201202
202 var digest: [16]u8 = undefined;203 if (try step.cacheHit(&man)) {
203 hash.final(&digest);204 const digest = man.final();
204 var hash_basename: [digest.len * 2]u8 = undefined;205 self.output_file.path = try b.cache_root.join(arena, &.{
205 _ = std.fmt.bufPrint(206 "o", &digest, self.include_path,
206 &hash_basename,207 });
207 "{s}",208 return;
208 .{std.fmt.fmtSliceHexLower(&digest)},209 }
209 ) catch unreachable;
210210
211 const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename });211 const digest = man.final();
212212
213 // If output_path has directory parts, deal with them. Example:213 // If output_path has directory parts, deal with them. Example:
214 // output_dir is zig-cache/o/HASH214 // output_dir is zig-cache/o/HASH
215 // output_path is libavutil/avconfig.h215 // output_path is libavutil/avconfig.h
216 // We want to open directory zig-cache/o/HASH/libavutil/216 // We want to open directory zig-cache/o/HASH/libavutil/
217 // but keep output_dir as zig-cache/o/HASH for -I include217 // but keep output_dir as zig-cache/o/HASH for -I include
218 const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d|218 const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, self.include_path });
219 try std.fs.path.join(gpa, &.{ output_dir, d })219 const sub_path_dirname = std.fs.path.dirname(sub_path).?;
220 else
221 output_dir;
222220
223 var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {221 b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
224 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });222 return step.fail("unable to make path '{}{s}': {s}", .{
225 return err;223 b.cache_root, sub_path_dirname, @errorName(err),
224 });
226 };225 };
227 defer dir.close();
228226
229 try dir.writeFile(std.fs.path.basename(self.include_path), output.items);227 b.cache_root.handle.writeFile(sub_path, output.items) catch |err| {
228 return step.fail("unable to write file '{}{s}': {s}", .{
229 b.cache_root, sub_path, @errorName(err),
230 });
231 };
230232
231 self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{233 self.output_file.path = try b.cache_root.join(arena, &.{sub_path});
232 output_dir, self.include_path,234 try man.writeManifest();
233 });
234}235}
235236
236fn render_autoconf(237fn render_autoconf(
238 step: *Step,
237 contents: []const u8,239 contents: []const u8,
238 output: *std.ArrayList(u8),240 output: *std.ArrayList(u8),
239 values: std.StringArrayHashMap(Value),241 values: std.StringArrayHashMap(Value),
...@@ -260,7 +262,7 @@ fn render_autoconf(...@@ -260,7 +262,7 @@ fn render_autoconf(
260 }262 }
261 const name = it.rest();263 const name = it.rest();
262 const kv = values_copy.fetchSwapRemove(name) orelse {264 const kv = values_copy.fetchSwapRemove(name) orelse {
263 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{265 try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{
264 src_path, line_index + 1, name,266 src_path, line_index + 1, name,
265 });267 });
266 any_errors = true;268 any_errors = true;
...@@ -270,15 +272,17 @@ fn render_autoconf(...@@ -270,15 +272,17 @@ fn render_autoconf(
270 }272 }
271273
272 for (values_copy.keys()) |name| {274 for (values_copy.keys()) |name| {
273 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });275 try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name });
276 any_errors = true;
274 }277 }
275278
276 if (any_errors) {279 if (any_errors) {
277 return error.HeaderConfigFailed;280 return error.MakeFailed;
278 }281 }
279}282}
280283
281fn render_cmake(284fn render_cmake(
285 step: *Step,
282 contents: []const u8,286 contents: []const u8,
283 output: *std.ArrayList(u8),287 output: *std.ArrayList(u8),
284 values: std.StringArrayHashMap(Value),288 values: std.StringArrayHashMap(Value),
...@@ -304,14 +308,14 @@ fn render_cmake(...@@ -304,14 +308,14 @@ fn render_cmake(
304 continue;308 continue;
305 }309 }
306 const name = it.next() orelse {310 const name = it.next() orelse {
307 std.debug.print("{s}:{d}: error: missing define name\n", .{311 try step.addError("{s}:{d}: error: missing define name", .{
308 src_path, line_index + 1,312 src_path, line_index + 1,
309 });313 });
310 any_errors = true;314 any_errors = true;
311 continue;315 continue;
312 };316 };
313 const kv = values_copy.fetchSwapRemove(name) orelse {317 const kv = values_copy.fetchSwapRemove(name) orelse {
314 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{318 try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{
315 src_path, line_index + 1, name,319 src_path, line_index + 1, name,
316 });320 });
317 any_errors = true;321 any_errors = true;
...@@ -321,7 +325,8 @@ fn render_cmake(...@@ -321,7 +325,8 @@ fn render_cmake(
321 }325 }
322326
323 for (values_copy.keys()) |name| {327 for (values_copy.keys()) |name| {
324 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });328 try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name });
329 any_errors = true;
325 }330 }
326331
327 if (any_errors) {332 if (any_errors) {
...@@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !...@@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !
426 },431 },
427 }432 }
428}433}
434
435const std = @import("../std.zig");
436const ConfigHeaderStep = @This();
437const Step = std.Build.Step;
lib/std/Build/EmulatableRunStep.zig deleted-213
...@@ -1,213 +0,0 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const Step = std.Build.Step;
9const CompileStep = std.Build.CompileStep;
10const RunStep = std.Build.RunStep;
11
12const fs = std.fs;
13const process = std.process;
14const EnvMap = process.EnvMap;
15
16const EmulatableRunStep = @This();
17
18pub const base_id = .emulatable_run;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22step: Step,
23builder: *std.Build,
24
25/// The artifact (executable) to be run by this step
26exe: *CompileStep,
27
28/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
29expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },
30
31/// Override this field to modify the environment
32env_map: ?*EnvMap,
33
34/// Set this to modify the current working directory
35cwd: ?[]const u8,
36
37stdout_action: RunStep.StdIoAction = .inherit,
38stderr_action: RunStep.StdIoAction = .inherit,
39
40/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
41/// or through emulation.
42hide_foreign_binaries_warning: bool,
43
44/// Creates a step that will execute the given artifact. This step will allow running the
45/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
46/// When set to false, and the binary is foreign, running the executable is skipped.
47/// Asserts given artifact is an executable.
48pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
49 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
50 const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM");
51
52 const option_name = "hide-foreign-warnings";
53 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
54 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
55 } else false;
56
57 self.* = .{
58 .builder = builder,
59 .step = Step.init(.emulatable_run, name, builder.allocator, make),
60 .exe = artifact,
61 .env_map = null,
62 .cwd = null,
63 .hide_foreign_binaries_warning = hide_warnings,
64 };
65 self.step.dependOn(&artifact.step);
66
67 return self;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
72 const host_info = self.builder.host;
73
74 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
75 defer argv_list.deinit();
76
77 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
78 switch (host_info.getExternalExecutor(self.exe.target_info, .{
79 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
80 .link_libc = self.exe.is_linking_libc,
81 })) {
82 .native => {},
83 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
84 .wine => |bin_name| if (self.builder.enable_wine) {
85 try argv_list.append(bin_name);
86 } else return,
87 .qemu => |bin_name| if (self.builder.enable_qemu) {
88 const glibc_dir_arg = if (need_cross_glibc)
89 self.builder.glibc_runtimes_dir orelse return
90 else
91 null;
92 try argv_list.append(bin_name);
93 if (glibc_dir_arg) |dir| {
94 // TODO look into making this a call to `linuxTriple`. This
95 // needs the directory to be called "i686" rather than
96 // "x86" which is why we do it manually here.
97 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
98 const cpu_arch = self.exe.target.getCpuArch();
99 const os_tag = self.exe.target.getOsTag();
100 const abi = self.exe.target.getAbi();
101 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
102 "i686"
103 else
104 @tagName(cpu_arch);
105 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
106 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
107 });
108
109 try argv_list.append("-L");
110 try argv_list.append(full_dir);
111 }
112 } else return warnAboutForeignBinaries(self),
113 .darling => |bin_name| if (self.builder.enable_darling) {
114 try argv_list.append(bin_name);
115 } else return warnAboutForeignBinaries(self),
116 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
117 try argv_list.append(bin_name);
118 try argv_list.append("--dir=.");
119 } else return warnAboutForeignBinaries(self),
120 else => return warnAboutForeignBinaries(self),
121 }
122
123 if (self.exe.target.isWindows()) {
124 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
125 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
126 }
127
128 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
129 try argv_list.append(executable_path);
130
131 try RunStep.runCommand(
132 argv_list.items,
133 self.builder,
134 self.expected_term,
135 self.stdout_action,
136 self.stderr_action,
137 .Inherit,
138 self.env_map,
139 self.cwd,
140 false,
141 );
142}
143
144pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
145 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
146}
147
148pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
149 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
153 if (step.hide_foreign_binaries_warning) return;
154 const builder = step.builder;
155 const artifact = step.exe;
156
157 const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error");
158 const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error");
159 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error");
160 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
161 switch (builder.host.getExternalExecutor(target_info, .{
162 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
163 .link_libc = artifact.is_linking_libc,
164 })) {
165 .native => unreachable,
166 .bad_dl => |foreign_dl| {
167 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
168 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
169 host_dl, foreign_dl, host_dl,
170 });
171 },
172 .bad_os_or_cpu => {
173 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
174 host_name, foreign_name,
175 });
176 },
177 .darling => if (!builder.enable_darling) {
178 std.debug.print(
179 "the host system ({s}) does not appear to be capable of executing binaries " ++
180 "from the target ({s}). Consider enabling darling.\n",
181 .{ host_name, foreign_name },
182 );
183 },
184 .rosetta => if (!builder.enable_rosetta) {
185 std.debug.print(
186 "the host system ({s}) does not appear to be capable of executing binaries " ++
187 "from the target ({s}). Consider enabling rosetta.\n",
188 .{ host_name, foreign_name },
189 );
190 },
191 .wine => if (!builder.enable_wine) {
192 std.debug.print(
193 "the host system ({s}) does not appear to be capable of executing binaries " ++
194 "from the target ({s}). Consider enabling wine.\n",
195 .{ host_name, foreign_name },
196 );
197 },
198 .qemu => if (!builder.enable_qemu) {
199 std.debug.print(
200 "the host system ({s}) does not appear to be capable of executing binaries " ++
201 "from the target ({s}). Consider enabling qemu.\n",
202 .{ host_name, foreign_name },
203 );
204 },
205 .wasmtime => {
206 std.debug.print(
207 "the host system ({s}) does not appear to be capable of executing binaries " ++
208 "from the target ({s}). Consider enabling wasmtime.\n",
209 .{ host_name, foreign_name },
210 );
211 },
212 }
213}
lib/std/Build/FmtStep.zig+63-22
...@@ -1,32 +1,73 @@...@@ -1,32 +1,73 @@
1const std = @import("../std.zig");1//! This step has two modes:
2const Step = std.Build.Step;2//! * Modify mode: directly modify source files, formatting them in place.
3const FmtStep = @This();3//! * Check mode: fail the step if a non-conforming file is found.
4
5step: Step,
6paths: []const []const u8,
7exclude_paths: []const []const u8,
8check: bool,
49
5pub const base_id = .fmt;10pub const base_id = .fmt;
611
7step: Step,12pub const Options = struct {
8builder: *std.Build,13 paths: []const []const u8 = &.{},
9argv: [][]const u8,14 exclude_paths: []const []const u8 = &.{},
1015 /// If true, fails the build step when any non-conforming files are encountered.
11pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {16 check: bool = false,
12 const self = builder.allocator.create(FmtStep) catch @panic("OOM");17};
13 const name = "zig fmt";
14 self.* = FmtStep{
15 .step = Step.init(.fmt, name, builder.allocator, make),
16 .builder = builder,
17 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"),
18 };
1918
20 self.argv[0] = builder.zig_exe;19pub fn create(owner: *std.Build, options: Options) *FmtStep {
21 self.argv[1] = "fmt";20 const self = owner.allocator.create(FmtStep) catch @panic("OOM");
22 for (paths, 0..) |path, i| {21 const name = if (options.check) "zig fmt --check" else "zig fmt";
23 self.argv[2 + i] = builder.pathFromRoot(path);22 self.* = .{
24 }23 .step = Step.init(.{
24 .id = base_id,
25 .name = name,
26 .owner = owner,
27 .makeFn = make,
28 }),
29 .paths = options.paths,
30 .exclude_paths = options.exclude_paths,
31 .check = options.check,
32 };
25 return self;33 return self;
26}34}
2735
28fn make(step: *Step) !void {36fn make(step: *Step, prog_node: *std.Progress.Node) !void {
37 // zig fmt is fast enough that no progress is needed.
38 _ = prog_node;
39
40 // TODO: if check=false, this means we are modifying source files in place, which
41 // is an operation that could race against other operations also modifying source files
42 // in place. In this case, this step should obtain a write lock while making those
43 // modifications.
44
45 const b = step.owner;
46 const arena = b.allocator;
29 const self = @fieldParentPtr(FmtStep, "step", step);47 const self = @fieldParentPtr(FmtStep, "step", step);
3048
31 return self.builder.spawnChild(self.argv);49 var argv: std.ArrayListUnmanaged([]const u8) = .{};
50 try argv.ensureUnusedCapacity(arena, 2 + 1 + self.paths.len + 2 * self.exclude_paths.len);
51
52 argv.appendAssumeCapacity(b.zig_exe);
53 argv.appendAssumeCapacity("fmt");
54
55 if (self.check) {
56 argv.appendAssumeCapacity("--check");
57 }
58
59 for (self.paths) |p| {
60 argv.appendAssumeCapacity(b.pathFromRoot(p));
61 }
62
63 for (self.exclude_paths) |p| {
64 argv.appendAssumeCapacity("--exclude");
65 argv.appendAssumeCapacity(b.pathFromRoot(p));
66 }
67
68 return step.evalChildProcess(argv.items);
32}69}
70
71const std = @import("../std.zig");
72const Step = std.Build.Step;
73const FmtStep = @This();
lib/std/Build/InstallArtifactStep.zig+77-27
...@@ -3,83 +3,133 @@ const Step = std.Build.Step;...@@ -3,83 +3,133 @@ const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;3const CompileStep = std.Build.CompileStep;
4const InstallDir = std.Build.InstallDir;4const InstallDir = std.Build.InstallDir;
5const InstallArtifactStep = @This();5const InstallArtifactStep = @This();
6const fs = std.fs;
67
7pub const base_id = .install_artifact;8pub const base_id = .install_artifact;
89
9step: Step,10step: Step,
10builder: *std.Build,11dest_builder: *std.Build,
11artifact: *CompileStep,12artifact: *CompileStep,
12dest_dir: InstallDir,13dest_dir: InstallDir,
13pdb_dir: ?InstallDir,14pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,15h_dir: ?InstallDir,
16/// If non-null, adds additional path components relative to dest_dir, and
17/// overrides the basename of the CompileStep.
18dest_sub_path: ?[]const u8,
1519
16pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {20pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
17 if (artifact.install_step) |s| return s;21 if (artifact.install_step) |s| return s;
1822
19 const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM");23 const self = owner.allocator.create(InstallArtifactStep) catch @panic("OOM");
20 self.* = InstallArtifactStep{24 self.* = InstallArtifactStep{
21 .builder = builder,25 .step = Step.init(.{
22 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),26 .id = base_id,
27 .name = owner.fmt("install {s}", .{artifact.name}),
28 .owner = owner,
29 .makeFn = make,
30 }),
31 .dest_builder = owner,
23 .artifact = artifact,32 .artifact = artifact,
24 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {33 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
25 .obj => @panic("Cannot install a .obj build artifact."),34 .obj => @panic("Cannot install a .obj build artifact."),
26 .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."),35 .exe, .@"test" => InstallDir{ .bin = {} },
27 .exe, .test_exe => InstallDir{ .bin = {} },
28 .lib => InstallDir{ .lib = {} },36 .lib => InstallDir{ .lib = {} },
29 },37 },
30 .pdb_dir = if (artifact.producesPdbFile()) blk: {38 .pdb_dir = if (artifact.producesPdbFile()) blk: {
31 if (artifact.kind == .exe or artifact.kind == .test_exe) {39 if (artifact.kind == .exe or artifact.kind == .@"test") {
32 break :blk InstallDir{ .bin = {} };40 break :blk InstallDir{ .bin = {} };
33 } else {41 } else {
34 break :blk InstallDir{ .lib = {} };42 break :blk InstallDir{ .lib = {} };
35 }43 }
36 } else null,44 } else null,
37 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,45 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
46 .dest_sub_path = null,
38 };47 };
39 self.step.dependOn(&artifact.step);48 self.step.dependOn(&artifact.step);
40 artifact.install_step = self;49 artifact.install_step = self;
4150
42 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);51 owner.pushInstalledFile(self.dest_dir, artifact.out_filename);
43 if (self.artifact.isDynamicLibrary()) {52 if (self.artifact.isDynamicLibrary()) {
44 if (artifact.major_only_filename) |name| {53 if (artifact.major_only_filename) |name| {
45 builder.pushInstalledFile(.lib, name);54 owner.pushInstalledFile(.lib, name);
46 }55 }
47 if (artifact.name_only_filename) |name| {56 if (artifact.name_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);57 owner.pushInstalledFile(.lib, name);
49 }58 }
50 if (self.artifact.target.isWindows()) {59 if (self.artifact.target.isWindows()) {
51 builder.pushInstalledFile(.lib, artifact.out_lib_filename);60 owner.pushInstalledFile(.lib, artifact.out_lib_filename);
52 }61 }
53 }62 }
54 if (self.pdb_dir) |pdb_dir| {63 if (self.pdb_dir) |pdb_dir| {
55 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);64 owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
56 }65 }
57 if (self.h_dir) |h_dir| {66 if (self.h_dir) |h_dir| {
58 builder.pushInstalledFile(h_dir, artifact.out_h_filename);67 owner.pushInstalledFile(h_dir, artifact.out_h_filename);
59 }68 }
60 return self;69 return self;
61}70}
6271
63fn make(step: *Step) !void {72fn make(step: *Step, prog_node: *std.Progress.Node) !void {
73 _ = prog_node;
74 const src_builder = step.owner;
64 const self = @fieldParentPtr(InstallArtifactStep, "step", step);75 const self = @fieldParentPtr(InstallArtifactStep, "step", step);
65 const builder = self.builder;76 const dest_builder = self.dest_builder;
6677
67 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);78 const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename;
68 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);79 const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path);
69 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {80 const cwd = fs.cwd();
70 try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);81
82 var all_cached = true;
83
84 {
85 const full_src_path = self.artifact.getOutputSource().getPath(src_builder);
86 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {
87 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
88 full_src_path, full_dest_path, @errorName(err),
89 });
90 };
91 all_cached = all_cached and p == .fresh;
92 }
93
94 if (self.artifact.isDynamicLibrary() and
95 self.artifact.version != null and
96 self.artifact.target.wantSharedLibSymLinks())
97 {
98 try CompileStep.doAtomicSymLinks(step, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
71 }99 }
72 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {100 if (self.artifact.isDynamicLibrary() and
73 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);101 self.artifact.target.isWindows() and
74 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);102 self.artifact.emit_implib != .no_emit)
103 {
104 const full_src_path = self.artifact.getOutputLibSource().getPath(src_builder);
105 const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
106 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| {
107 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
108 full_src_path, full_implib_path, @errorName(err),
109 });
110 };
111 all_cached = all_cached and p == .fresh;
75 }112 }
76 if (self.pdb_dir) |pdb_dir| {113 if (self.pdb_dir) |pdb_dir| {
77 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);114 const full_src_path = self.artifact.getOutputPdbSource().getPath(src_builder);
78 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);115 const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
116 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| {
117 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
118 full_src_path, full_pdb_path, @errorName(err),
119 });
120 };
121 all_cached = all_cached and p == .fresh;
79 }122 }
80 if (self.h_dir) |h_dir| {123 if (self.h_dir) |h_dir| {
81 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);124 const full_src_path = self.artifact.getOutputHSource().getPath(src_builder);
82 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);125 const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename);
126 const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| {
127 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
128 full_src_path, full_h_path, @errorName(err),
129 });
130 };
131 all_cached = all_cached and p == .fresh;
83 }132 }
84 self.artifact.installed_path = full_dest_path;133 self.artifact.installed_path = full_dest_path;
134 step.result_cached = all_cached;
85}135}
lib/std/Build/InstallDirStep.zig+43-26
...@@ -4,14 +4,12 @@ const fs = std.fs;...@@ -4,14 +4,12 @@ const fs = std.fs;
4const Step = std.Build.Step;4const Step = std.Build.Step;
5const InstallDir = std.Build.InstallDir;5const InstallDir = std.Build.InstallDir;
6const InstallDirStep = @This();6const InstallDirStep = @This();
7const log = std.log;
87
9step: Step,8step: Step,
10builder: *std.Build,
11options: Options,9options: Options,
12/// This is used by the build system when a file being installed comes from one10/// This is used by the build system when a file being installed comes from one
13/// package but is being installed by another.11/// package but is being installed by another.
14override_source_builder: ?*std.Build = null,12dest_builder: *std.Build,
1513
16pub const base_id = .install_dir;14pub const base_id = .install_dir;
1715
...@@ -40,31 +38,35 @@ pub const Options = struct {...@@ -40,31 +38,35 @@ pub const Options = struct {
40 }38 }
41};39};
4240
43pub fn init(41pub fn init(owner: *std.Build, options: Options) InstallDirStep {
44 builder: *std.Build,42 owner.pushInstalledFile(options.install_dir, options.install_subdir);
45 options: Options,43 return .{
46) InstallDirStep {44 .step = Step.init(.{
47 builder.pushInstalledFile(options.install_dir, options.install_subdir);45 .id = .install_dir,
48 return InstallDirStep{46 .name = owner.fmt("install {s}/", .{options.source_dir}),
49 .builder = builder,47 .owner = owner,
50 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),48 .makeFn = make,
51 .options = options.dupe(builder),49 }),
50 .options = options.dupe(owner),
51 .dest_builder = owner,
52 };52 };
53}53}
5454
55fn make(step: *Step) !void {55fn make(step: *Step, prog_node: *std.Progress.Node) !void {
56 _ = prog_node;
56 const self = @fieldParentPtr(InstallDirStep, "step", step);57 const self = @fieldParentPtr(InstallDirStep, "step", step);
57 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);58 const dest_builder = self.dest_builder;
58 const src_builder = self.override_source_builder orelse self.builder;59 const arena = dest_builder.allocator;
59 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);60 const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
60 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {61 const src_builder = self.step.owner;
61 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{62 var src_dir = src_builder.build_root.handle.openIterableDir(self.options.source_dir, .{}) catch |err| {
62 full_src_dir, @errorName(err),63 return step.fail("unable to open source directory '{}{s}': {s}", .{
64 src_builder.build_root, self.options.source_dir, @errorName(err),
63 });65 });
64 return error.StepFailed;
65 };66 };
66 defer src_dir.close();67 defer src_dir.close();
67 var it = try src_dir.walk(self.builder.allocator);68 var it = try src_dir.walk(arena);
69 var all_cached = true;
68 next_entry: while (try it.next()) |entry| {70 next_entry: while (try it.next()) |entry| {
69 for (self.options.exclude_extensions) |ext| {71 for (self.options.exclude_extensions) |ext| {
70 if (mem.endsWith(u8, entry.path, ext)) {72 if (mem.endsWith(u8, entry.path, ext)) {
...@@ -72,22 +74,37 @@ fn make(step: *Step) !void {...@@ -72,22 +74,37 @@ fn make(step: *Step) !void {
72 }74 }
73 }75 }
7476
75 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });77 // relative to src build root
76 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });78 const src_sub_path = try fs.path.join(arena, &.{ self.options.source_dir, entry.path });
79 const dest_path = try fs.path.join(arena, &.{ dest_prefix, entry.path });
80 const cwd = fs.cwd();
7781
78 switch (entry.kind) {82 switch (entry.kind) {
79 .Directory => try fs.cwd().makePath(dest_path),83 .Directory => try cwd.makePath(dest_path),
80 .File => {84 .File => {
81 for (self.options.blank_extensions) |ext| {85 for (self.options.blank_extensions) |ext| {
82 if (mem.endsWith(u8, entry.path, ext)) {86 if (mem.endsWith(u8, entry.path, ext)) {
83 try self.builder.truncateFile(dest_path);87 try dest_builder.truncateFile(dest_path);
84 continue :next_entry;88 continue :next_entry;
85 }89 }
86 }90 }
8791
88 try self.builder.updateFile(full_path, dest_path);92 const prev_status = fs.Dir.updateFile(
93 src_builder.build_root.handle,
94 src_sub_path,
95 cwd,
96 dest_path,
97 .{},
98 ) catch |err| {
99 return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{
100 src_builder.build_root, src_sub_path, dest_path, @errorName(err),
101 });
102 };
103 all_cached = all_cached and prev_status == .fresh;
89 },104 },
90 else => continue,105 else => continue,
91 }106 }
92 }107 }
108
109 step.result_cached = all_cached;
93}110}
lib/std/Build/InstallFileStep.zig+34-17
...@@ -3,38 +3,55 @@ const Step = std.Build.Step;...@@ -3,38 +3,55 @@ const Step = std.Build.Step;
3const FileSource = std.Build.FileSource;3const FileSource = std.Build.FileSource;
4const InstallDir = std.Build.InstallDir;4const InstallDir = std.Build.InstallDir;
5const InstallFileStep = @This();5const InstallFileStep = @This();
6const assert = std.debug.assert;
67
7pub const base_id = .install_file;8pub const base_id = .install_file;
89
9step: Step,10step: Step,
10builder: *std.Build,
11source: FileSource,11source: FileSource,
12dir: InstallDir,12dir: InstallDir,
13dest_rel_path: []const u8,13dest_rel_path: []const u8,
14/// This is used by the build system when a file being installed comes from one14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.15/// package but is being installed by another.
16override_source_builder: ?*std.Build = null,16dest_builder: *std.Build,
1717
18pub fn init(18pub fn create(
19 builder: *std.Build,19 owner: *std.Build,
20 source: FileSource,20 source: FileSource,
21 dir: InstallDir,21 dir: InstallDir,
22 dest_rel_path: []const u8,22 dest_rel_path: []const u8,
23) InstallFileStep {23) *InstallFileStep {
24 builder.pushInstalledFile(dir, dest_rel_path);24 assert(dest_rel_path.len != 0);
25 return InstallFileStep{25 owner.pushInstalledFile(dir, dest_rel_path);
26 .builder = builder,26 const self = owner.allocator.create(InstallFileStep) catch @panic("OOM");
27 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),27 self.* = .{
28 .source = source.dupe(builder),28 .step = Step.init(.{
29 .dir = dir.dupe(builder),29 .id = base_id,
30 .dest_rel_path = builder.dupePath(dest_rel_path),30 .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }),
31 .owner = owner,
32 .makeFn = make,
33 }),
34 .source = source.dupe(owner),
35 .dir = dir.dupe(owner),
36 .dest_rel_path = owner.dupePath(dest_rel_path),
37 .dest_builder = owner,
31 };38 };
39 source.addStepDependencies(&self.step);
40 return self;
32}41}
3342
34fn make(step: *Step) !void {43fn make(step: *Step, prog_node: *std.Progress.Node) !void {
44 _ = prog_node;
45 const src_builder = step.owner;
35 const self = @fieldParentPtr(InstallFileStep, "step", step);46 const self = @fieldParentPtr(InstallFileStep, "step", step);
36 const src_builder = self.override_source_builder orelse self.builder;47 const dest_builder = self.dest_builder;
37 const full_src_path = self.source.getPath(src_builder);48 const full_src_path = self.source.getPath2(src_builder, step);
38 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);49 const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path);
39 try self.builder.updateFile(full_src_path, full_dest_path);50 const cwd = std.fs.cwd();
51 const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| {
52 return step.fail("unable to update file from '{s}' to '{s}': {s}", .{
53 full_src_path, full_dest_path, @errorName(err),
54 });
55 };
56 step.result_cached = prev == .fresh;
40}57}
lib/std/Build/LogStep.zig deleted-23
...@@ -1,23 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const Step = std.Build.Step;
4const LogStep = @This();
5
6pub const base_id = .log;
7
8step: Step,
9builder: *std.Build,
10data: []const u8,
11
12pub fn init(builder: *std.Build, data: []const u8) LogStep {
13 return LogStep{
14 .builder = builder,
15 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
16 .data = builder.dupe(data),
17 };
18}
19
20fn make(step: *Step) anyerror!void {
21 const self = @fieldParentPtr(LogStep, "step", step);
22 log.info("{s}", .{self.data});
23}
lib/std/Build/ObjCopyStep.zig+15-31
...@@ -21,7 +21,6 @@ pub const RawFormat = enum {...@@ -21,7 +21,6 @@ pub const RawFormat = enum {
21};21};
2222
23step: Step,23step: Step,
24builder: *std.Build,
25file_source: std.Build.FileSource,24file_source: std.Build.FileSource,
26basename: []const u8,25basename: []const u8,
27output_file: std.Build.GeneratedFile,26output_file: std.Build.GeneratedFile,
...@@ -38,19 +37,18 @@ pub const Options = struct {...@@ -38,19 +37,18 @@ pub const Options = struct {
38};37};
3938
40pub fn create(39pub fn create(
41 builder: *std.Build,40 owner: *std.Build,
42 file_source: std.Build.FileSource,41 file_source: std.Build.FileSource,
43 options: Options,42 options: Options,
44) *ObjCopyStep {43) *ObjCopyStep {
45 const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM");44 const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM");
46 self.* = ObjCopyStep{45 self.* = ObjCopyStep{
47 .step = Step.init(46 .step = Step.init(.{
48 base_id,47 .id = base_id,
49 builder.fmt("objcopy {s}", .{file_source.getDisplayName()}),48 .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}),
50 builder.allocator,49 .owner = owner,
51 make,50 .makeFn = make,
52 ),51 }),
53 .builder = builder,
54 .file_source = file_source,52 .file_source = file_source,
55 .basename = options.basename orelse file_source.getDisplayName(),53 .basename = options.basename orelse file_source.getDisplayName(),
56 .output_file = std.Build.GeneratedFile{ .step = &self.step },54 .output_file = std.Build.GeneratedFile{ .step = &self.step },
...@@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {...@@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {
67 return .{ .generated = &self.output_file };65 return .{ .generated = &self.output_file };
68}66}
6967
70fn make(step: *Step) !void {68fn make(step: *Step, prog_node: *std.Progress.Node) !void {
69 const b = step.owner;
71 const self = @fieldParentPtr(ObjCopyStep, "step", step);70 const self = @fieldParentPtr(ObjCopyStep, "step", step);
72 const b = self.builder;
7371
74 var man = b.cache.obtain();72 var man = b.cache.obtain();
75 defer man.deinit();73 defer man.deinit();
...@@ -84,7 +82,7 @@ fn make(step: *Step) !void {...@@ -84,7 +82,7 @@ fn make(step: *Step) !void {
84 man.hash.addOptional(self.pad_to);82 man.hash.addOptional(self.pad_to);
85 man.hash.addOptional(self.format);83 man.hash.addOptional(self.format);
8684
87 if (man.hit() catch |err| failWithCacheError(man, err)) {85 if (try step.cacheHit(&man)) {
88 // Cache hit, skip subprocess execution.86 // Cache hit, skip subprocess execution.
89 const digest = man.final();87 const digest = man.final();
90 self.output_file.path = try b.cache_root.join(b.allocator, &.{88 self.output_file.path = try b.cache_root.join(b.allocator, &.{
...@@ -97,8 +95,7 @@ fn make(step: *Step) !void {...@@ -97,8 +95,7 @@ fn make(step: *Step) !void {
97 const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename });95 const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename });
98 const cache_path = "o" ++ fs.path.sep_str ++ digest;96 const cache_path = "o" ++ fs.path.sep_str ++ digest;
99 b.cache_root.handle.makePath(cache_path) catch |err| {97 b.cache_root.handle.makePath(cache_path) catch |err| {
100 std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });98 return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) });
101 return err;
102 };99 };
103100
104 var argv = std.ArrayList([]const u8).init(b.allocator);101 var argv = std.ArrayList([]const u8).init(b.allocator);
...@@ -116,23 +113,10 @@ fn make(step: *Step) !void {...@@ -116,23 +113,10 @@ fn make(step: *Step) !void {
116 };113 };
117114
118 try argv.appendSlice(&.{ full_src_path, full_dest_path });115 try argv.appendSlice(&.{ full_src_path, full_dest_path });
119 _ = try self.builder.execFromStep(argv.items, &self.step);116
117 try argv.append("--listen=-");
118 _ = try step.evalZigProcess(argv.items, prog_node);
120119
121 self.output_file.path = full_dest_path;120 self.output_file.path = full_dest_path;
122 try man.writeManifest();121 try man.writeManifest();
123}122}
124
125/// TODO consolidate this with the same function in RunStep?
126/// Also properly deal with concurrency (see open PR)
127fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
128 const i = man.failed_file_index orelse failWithSimpleError(err);
129 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
130 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
131 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
132 std.process.exit(1);
133}
134
135fn failWithSimpleError(err: anyerror) noreturn {
136 std.debug.print("{s}\n", .{@errorName(err)});
137 std.process.exit(1);
138}
lib/std/Build/OptionsStep.zig+23-18
...@@ -12,21 +12,24 @@ pub const base_id = .options;...@@ -12,21 +12,24 @@ pub const base_id = .options;
1212
13step: Step,13step: Step,
14generated_file: GeneratedFile,14generated_file: GeneratedFile,
15builder: *std.Build,
1615
17contents: std.ArrayList(u8),16contents: std.ArrayList(u8),
18artifact_args: std.ArrayList(OptionArtifactArg),17artifact_args: std.ArrayList(OptionArtifactArg),
19file_source_args: std.ArrayList(OptionFileSourceArg),18file_source_args: std.ArrayList(OptionFileSourceArg),
2019
21pub fn create(builder: *std.Build) *OptionsStep {20pub fn create(owner: *std.Build) *OptionsStep {
22 const self = builder.allocator.create(OptionsStep) catch @panic("OOM");21 const self = owner.allocator.create(OptionsStep) catch @panic("OOM");
23 self.* = .{22 self.* = .{
24 .builder = builder,23 .step = Step.init(.{
25 .step = Step.init(.options, "options", builder.allocator, make),24 .id = base_id,
25 .name = "options",
26 .owner = owner,
27 .makeFn = make,
28 }),
26 .generated_file = undefined,29 .generated_file = undefined,
27 .contents = std.ArrayList(u8).init(builder.allocator),30 .contents = std.ArrayList(u8).init(owner.allocator),
28 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),31 .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator),
29 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),32 .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator),
30 };33 };
31 self.generated_file = .{ .step = &self.step };34 self.generated_file = .{ .step = &self.step };
3235
...@@ -192,7 +195,7 @@ pub fn addOptionFileSource(...@@ -192,7 +195,7 @@ pub fn addOptionFileSource(
192) void {195) void {
193 self.file_source_args.append(.{196 self.file_source_args.append(.{
194 .name = name,197 .name = name,
195 .source = source.dupe(self.builder),198 .source = source.dupe(self.step.owner),
196 }) catch @panic("OOM");199 }) catch @panic("OOM");
197 source.addStepDependencies(&self.step);200 source.addStepDependencies(&self.step);
198}201}
...@@ -200,12 +203,12 @@ pub fn addOptionFileSource(...@@ -200,12 +203,12 @@ pub fn addOptionFileSource(
200/// The value is the path in the cache dir.203/// The value is the path in the cache dir.
201/// Adds a dependency automatically.204/// Adds a dependency automatically.
202pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {205pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
203 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM");206 self.artifact_args.append(.{ .name = self.step.owner.dupe(name), .artifact = artifact }) catch @panic("OOM");
204 self.step.dependOn(&artifact.step);207 self.step.dependOn(&artifact.step);
205}208}
206209
207pub fn createModule(self: *OptionsStep) *std.Build.Module {210pub fn createModule(self: *OptionsStep) *std.Build.Module {
208 return self.builder.createModule(.{211 return self.step.owner.createModule(.{
209 .source_file = self.getSource(),212 .source_file = self.getSource(),
210 .dependencies = &.{},213 .dependencies = &.{},
211 });214 });
...@@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource {...@@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource {
215 return .{ .generated = &self.generated_file };218 return .{ .generated = &self.generated_file };
216}219}
217220
218fn make(step: *Step) !void {221fn make(step: *Step, prog_node: *std.Progress.Node) !void {
222 // This step completes so quickly that no progress is necessary.
223 _ = prog_node;
224
225 const b = step.owner;
219 const self = @fieldParentPtr(OptionsStep, "step", step);226 const self = @fieldParentPtr(OptionsStep, "step", step);
220227
221 for (self.artifact_args.items) |item| {228 for (self.artifact_args.items) |item| {
222 self.addOption(229 self.addOption(
223 []const u8,230 []const u8,
224 item.name,231 item.name,
225 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),232 b.pathFromRoot(item.artifact.getOutputSource().getPath(b)),
226 );233 );
227 }234 }
228235
...@@ -230,20 +237,18 @@ fn make(step: *Step) !void {...@@ -230,20 +237,18 @@ fn make(step: *Step) !void {
230 self.addOption(237 self.addOption(
231 []const u8,238 []const u8,
232 item.name,239 item.name,
233 item.source.getPath(self.builder),240 item.source.getPath(b),
234 );241 );
235 }242 }
236243
237 var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{});244 var options_dir = try b.cache_root.handle.makeOpenPath("options", .{});
238 defer options_dir.close();245 defer options_dir.close();
239246
240 const basename = self.hashContentsToFileName();247 const basename = self.hashContentsToFileName();
241248
242 try options_dir.writeFile(&basename, self.contents.items);249 try options_dir.writeFile(&basename, self.contents.items);
243250
244 self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{251 self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename });
245 "options", &basename,
246 });
247}252}
248253
249fn hashContentsToFileName(self: *OptionsStep) [64]u8 {254fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
lib/std/Build/RemoveDirStep.zig+24-11
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;2const fs = std.fs;
4const Step = std.Build.Step;3const Step = std.Build.Step;
5const RemoveDirStep = @This();4const RemoveDirStep = @This();
...@@ -7,23 +6,37 @@ const RemoveDirStep = @This();...@@ -7,23 +6,37 @@ const RemoveDirStep = @This();
7pub const base_id = .remove_dir;6pub const base_id = .remove_dir;
87
9step: Step,8step: Step,
10builder: *std.Build,
11dir_path: []const u8,9dir_path: []const u8,
1210
13pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {11pub fn init(owner: *std.Build, dir_path: []const u8) RemoveDirStep {
14 return RemoveDirStep{12 return RemoveDirStep{
15 .builder = builder,13 .step = Step.init(.{
16 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),14 .id = .remove_dir,
17 .dir_path = builder.dupePath(dir_path),15 .name = owner.fmt("RemoveDir {s}", .{dir_path}),
16 .owner = owner,
17 .makeFn = make,
18 }),
19 .dir_path = owner.dupePath(dir_path),
18 };20 };
19}21}
2022
21fn make(step: *Step) !void {23fn make(step: *Step, prog_node: *std.Progress.Node) !void {
24 // TODO update progress node while walking file system.
25 // Should the standard library support this use case??
26 _ = prog_node;
27
28 const b = step.owner;
22 const self = @fieldParentPtr(RemoveDirStep, "step", step);29 const self = @fieldParentPtr(RemoveDirStep, "step", step);
2330
24 const full_path = self.builder.pathFromRoot(self.dir_path);31 b.build_root.handle.deleteTree(self.dir_path) catch |err| {
25 fs.cwd().deleteTree(full_path) catch |err| {32 if (b.build_root.path) |base| {
26 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });33 return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{
27 return err;34 base, self.dir_path, @errorName(err),
35 });
36 } else {
37 return step.fail("unable to recursively delete path '{s}': {s}", .{
38 self.dir_path, @errorName(err),
39 });
40 }
28 };41 };
29}42}
lib/std/Build/RunStep.zig+975-271
...@@ -10,76 +10,136 @@ const ArrayList = std.ArrayList;...@@ -10,76 +10,136 @@ const ArrayList = std.ArrayList;
10const EnvMap = process.EnvMap;10const EnvMap = process.EnvMap;
11const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
12const ExecError = std.Build.ExecError;12const ExecError = std.Build.ExecError;
1313const assert = std.debug.assert;
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
1514
16const RunStep = @This();15const RunStep = @This();
1716
18pub const base_id: Step.Id = .run;17pub const base_id: Step.Id = .run;
1918
20step: Step,19step: Step,
21builder: *std.Build,
2220
23/// See also addArg and addArgs to modifying this directly21/// See also addArg and addArgs to modifying this directly
24argv: ArrayList(Arg),22argv: ArrayList(Arg),
2523
26/// Set this to modify the current working directory24/// Set this to modify the current working directory
25/// TODO change this to a Build.Cache.Directory to better integrate with
26/// future child process cwd API.
27cwd: ?[]const u8,27cwd: ?[]const u8,
2828
29/// Override this field to modify the environment, or use setEnvironmentVariable29/// Override this field to modify the environment, or use setEnvironmentVariable
30env_map: ?*EnvMap,30env_map: ?*EnvMap,
3131
32stdout_action: StdIoAction = .inherit,32/// Configures whether the RunStep is considered to have side-effects, and also
33stderr_action: StdIoAction = .inherit,33/// whether the RunStep will inherit stdio streams, forwarding them to the
3434/// parent process, in which case will require a global lock to prevent other
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,35/// steps from interfering with stdio while the subprocess associated with this
3636/// RunStep is running.
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution37/// If the RunStep is determined to not have side-effects, then execution will
38expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },38/// be skipped if all output files are up-to-date and input files are
3939/// unchanged.
40/// Print the command before running it40stdio: StdIo = .infer_from_args,
41print: bool,41/// This field must be `null` if stdio is `inherit`.
42/// Controls whether execution is skipped if the output file is up-to-date.42stdin: ?[]const u8 = null,
43/// The default is to always run if there is no output file, and to skip
44/// running if all output files are up-to-date.
45condition: enum { output_outdated, always } = .output_outdated,
4643
47/// Additional file paths relative to build.zig that, when modified, indicate44/// Additional file paths relative to build.zig that, when modified, indicate
48/// that the RunStep should be re-executed.45/// that the RunStep should be re-executed.
46/// If the RunStep is determined to have side-effects, this field is ignored
47/// and the RunStep is always executed when it appears in the build graph.
49extra_file_dependencies: []const []const u8 = &.{},48extra_file_dependencies: []const []const u8 = &.{},
5049
51pub const StdIoAction = union(enum) {50/// After adding an output argument, this step will by default rename itself
51/// for a better display name in the build summary.
52/// This can be disabled by setting this to false.
53rename_step_with_output_arg: bool = true,
54
55/// If this is true, a RunStep which is configured to check the output of the
56/// executed binary will not fail the build if the binary cannot be executed
57/// due to being for a foreign binary to the host system which is running the
58/// build graph.
59/// Command-line arguments such as -fqemu and -fwasmtime may affect whether a
60/// binary is detected as foreign, as well as system configuration such as
61/// Rosetta (macOS) and binfmt_misc (Linux).
62/// If this RunStep is considered to have side-effects, then this flag does
63/// nothing.
64skip_foreign_checks: bool = false,
65
66/// If stderr or stdout exceeds this amount, the child process is killed and
67/// the step fails.
68max_stdio_size: usize = 10 * 1024 * 1024,
69
70captured_stdout: ?*Output = null,
71captured_stderr: ?*Output = null,
72
73has_side_effects: bool = false,
74
75pub const StdIo = union(enum) {
76 /// Whether the RunStep has side-effects will be determined by whether or not one
77 /// of the args is an output file (added with `addOutputFileArg`).
78 /// If the RunStep is determined to have side-effects, this is the same as `inherit`.
79 /// The step will fail if the subprocess crashes or returns a non-zero exit code.
80 infer_from_args,
81 /// Causes the RunStep to be considered to have side-effects, and therefore
82 /// always execute when it appears in the build graph.
83 /// It also means that this step will obtain a global lock to prevent other
84 /// steps from running in the meantime.
85 /// The step will fail if the subprocess crashes or returns a non-zero exit code.
52 inherit,86 inherit,
53 ignore,87 /// Causes the RunStep to be considered to *not* have side-effects. The
54 expect_exact: []const u8,88 /// process will be re-executed if any of the input dependencies are
55 expect_matches: []const []const u8,89 /// modified. The exit code and standard I/O streams will be checked for
90 /// certain conditions, and the step will succeed or fail based on these
91 /// conditions.
92 /// Note that an explicit check for exit code 0 needs to be added to this
93 /// list if such a check is desireable.
94 check: std.ArrayList(Check),
95 /// This RunStep is running a zig unit test binary and will communicate
96 /// extra metadata over the IPC protocol.
97 zig_test,
98
99 pub const Check = union(enum) {
100 expect_stderr_exact: []const u8,
101 expect_stderr_match: []const u8,
102 expect_stdout_exact: []const u8,
103 expect_stdout_match: []const u8,
104 expect_term: std.process.Child.Term,
105 };
56};106};
57107
58pub const Arg = union(enum) {108pub const Arg = union(enum) {
59 artifact: *CompileStep,109 artifact: *CompileStep,
60 file_source: std.Build.FileSource,110 file_source: std.Build.FileSource,
111 directory_source: std.Build.FileSource,
61 bytes: []u8,112 bytes: []u8,
62 output: Output,113 output: *Output,
114};
63115
64 pub const Output = struct {116pub const Output = struct {
65 generated_file: *std.Build.GeneratedFile,117 generated_file: std.Build.GeneratedFile,
66 basename: []const u8,118 prefix: []const u8,
67 };119 basename: []const u8,
68};120};
69121
70pub fn create(builder: *std.Build, name: []const u8) *RunStep {122pub fn create(owner: *std.Build, name: []const u8) *RunStep {
71 const self = builder.allocator.create(RunStep) catch @panic("OOM");123 const self = owner.allocator.create(RunStep) catch @panic("OOM");
72 self.* = RunStep{124 self.* = .{
73 .builder = builder,125 .step = Step.init(.{
74 .step = Step.init(base_id, name, builder.allocator, make),126 .id = base_id,
75 .argv = ArrayList(Arg).init(builder.allocator),127 .name = name,
128 .owner = owner,
129 .makeFn = make,
130 }),
131 .argv = ArrayList(Arg).init(owner.allocator),
76 .cwd = null,132 .cwd = null,
77 .env_map = null,133 .env_map = null,
78 .print = builder.verbose,
79 };134 };
80 return self;135 return self;
81}136}
82137
138pub fn setName(self: *RunStep, name: []const u8) void {
139 self.step.name = name;
140 self.rename_step_with_output_arg = false;
141}
142
83pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {143pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
84 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");144 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
85 self.step.dependOn(&artifact.step);145 self.step.dependOn(&artifact.step);
...@@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {...@@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
89/// run, and returns a FileSource which can be used as inputs to other APIs149/// run, and returns a FileSource which can be used as inputs to other APIs
90/// throughout the build system.150/// throughout the build system.
91pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {151pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
92 const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");152 return addPrefixedOutputFileArg(rs, "", basename);
93 generated_file.* = .{ .step = &rs.step };153}
94 rs.argv.append(.{ .output = .{
95 .generated_file = generated_file,
96 .basename = rs.builder.dupe(basename),
97 } }) catch @panic("OOM");
98154
99 return .{ .generated = generated_file };155pub fn addPrefixedOutputFileArg(
156 rs: *RunStep,
157 prefix: []const u8,
158 basename: []const u8,
159) std.Build.FileSource {
160 const b = rs.step.owner;
161
162 const output = b.allocator.create(Output) catch @panic("OOM");
163 output.* = .{
164 .prefix = prefix,
165 .basename = basename,
166 .generated_file = .{ .step = &rs.step },
167 };
168 rs.argv.append(.{ .output = output }) catch @panic("OOM");
169
170 if (rs.rename_step_with_output_arg) {
171 rs.setName(b.fmt("{s} ({s})", .{ rs.step.name, basename }));
172 }
173
174 return .{ .generated = &output.generated_file };
100}175}
101176
102pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {177pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
103 self.argv.append(Arg{178 self.argv.append(.{
104 .file_source = file_source.dupe(self.builder),179 .file_source = file_source.dupe(self.step.owner),
105 }) catch @panic("OOM");180 }) catch @panic("OOM");
106 file_source.addStepDependencies(&self.step);181 file_source.addStepDependencies(&self.step);
107}182}
108183
184pub fn addDirectorySourceArg(self: *RunStep, directory_source: std.Build.FileSource) void {
185 self.argv.append(.{
186 .directory_source = directory_source.dupe(self.step.owner),
187 }) catch @panic("OOM");
188 directory_source.addStepDependencies(&self.step);
189}
190
109pub fn addArg(self: *RunStep, arg: []const u8) void {191pub fn addArg(self: *RunStep, arg: []const u8) void {
110 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");192 self.argv.append(.{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM");
111}193}
112194
113pub fn addArgs(self: *RunStep, args: []const []const u8) void {195pub fn addArgs(self: *RunStep, args: []const []const u8) void {
...@@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void {...@@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void {
117}199}
118200
119pub fn clearEnvironment(self: *RunStep) void {201pub fn clearEnvironment(self: *RunStep) void {
120 const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");202 const b = self.step.owner;
121 new_env_map.* = EnvMap.init(self.builder.allocator);203 const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM");
204 new_env_map.* = EnvMap.init(b.allocator);
122 self.env_map = new_env_map;205 self.env_map = new_env_map;
123}206}
124207
125pub fn addPathDir(self: *RunStep, search_path: []const u8) void {208pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
126 addPathDirInternal(&self.step, self.builder, search_path);209 const b = self.step.owner;
127}210 const env_map = getEnvMapInternal(self);
128
129/// For internal use only, users of `RunStep` should use `addPathDir` directly.
130pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void {
131 const env_map = getEnvMapInternal(step, builder.allocator);
132211
133 const key = "PATH";212 const key = "PATH";
134 var prev_path = env_map.get(key);213 var prev_path = env_map.get(key);
135214
136 if (prev_path) |pp| {215 if (prev_path) |pp| {
137 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });216 const new_path = b.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
138 env_map.put(key, new_path) catch @panic("OOM");217 env_map.put(key, new_path) catch @panic("OOM");
139 } else {218 } else {
140 env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM");219 env_map.put(key, b.dupePath(search_path)) catch @panic("OOM");
141 }220 }
142}221}
143222
144pub fn getEnvMap(self: *RunStep) *EnvMap {223pub fn getEnvMap(self: *RunStep) *EnvMap {
145 return getEnvMapInternal(&self.step, self.builder.allocator);224 return getEnvMapInternal(self);
146}225}
147226
148fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {227fn getEnvMapInternal(self: *RunStep) *EnvMap {
149 const maybe_env_map = switch (step.id) {228 const arena = self.step.owner.allocator;
150 .run => step.cast(RunStep).?.env_map,229 return self.env_map orelse {
151 .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,230 const env_map = arena.create(EnvMap) catch @panic("OOM");
152 else => unreachable,231 env_map.* = process.getEnvMap(arena) catch @panic("unhandled error");
153 };232 self.env_map = env_map;
154 return maybe_env_map orelse {
155 const env_map = allocator.create(EnvMap) catch @panic("OOM");
156 env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");
157 switch (step.id) {
158 .run => step.cast(RunStep).?.env_map = env_map,
159 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
160 else => unreachable,
161 }
162 return env_map;233 return env_map;
163 };234 };
164}235}
165236
166pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {237pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
238 const b = self.step.owner;
167 const env_map = self.getEnvMap();239 const env_map = self.getEnvMap();
168 env_map.put(240 env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error");
169 self.builder.dupe(key),241}
170 self.builder.dupe(value),242
171 ) catch @panic("unhandled error");243pub fn removeEnvironmentVariable(self: *RunStep, key: []const u8) void {
244 self.getEnvMap().remove(key);
172}245}
173246
247/// Adds a check for exact stderr match. Does not add any other checks.
174pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {248pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
175 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };249 const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) };
250 self.addCheck(new_check);
176}251}
177252
253/// Adds a check for exact stdout match as well as a check for exit code 0, if
254/// there is not already an expected termination check.
178pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {255pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
179 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };256 const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) };
257 self.addCheck(new_check);
258 if (!self.hasTermCheck()) {
259 self.expectExitCode(0);
260 }
180}261}
181262
182fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {263pub fn expectExitCode(self: *RunStep, code: u8) void {
183 return switch (action) {264 const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } };
184 .ignore => .Ignore,265 self.addCheck(new_check);
185 .inherit => .Inherit,266}
186 .expect_exact, .expect_matches => .Pipe,267
268pub fn hasTermCheck(self: RunStep) bool {
269 for (self.stdio.check.items) |check| switch (check) {
270 .expect_term => return true,
271 else => continue,
187 };272 };
273 return false;
188}274}
189275
190fn needOutputCheck(self: RunStep) bool {276pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void {
191 switch (self.condition) {277 switch (self.stdio) {
192 .always => return false,278 .infer_from_args => {
193 .output_outdated => {},279 self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) };
280 self.stdio.check.append(new_check) catch @panic("OOM");
281 },
282 .check => |*checks| checks.append(new_check) catch @panic("OOM"),
283 else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"),
194 }284 }
195 if (self.extra_file_dependencies.len > 0) return true;285}
286
287pub fn captureStdErr(self: *RunStep) std.Build.FileSource {
288 assert(self.stdio != .inherit);
289
290 if (self.captured_stderr) |output| return .{ .generated = &output.generated_file };
291
292 const output = self.step.owner.allocator.create(Output) catch @panic("OOM");
293 output.* = .{
294 .prefix = "",
295 .basename = "stderr",
296 .generated_file = .{ .step = &self.step },
297 };
298 self.captured_stderr = output;
299 return .{ .generated = &output.generated_file };
300}
301
302pub fn captureStdOut(self: *RunStep) *std.Build.GeneratedFile {
303 assert(self.stdio != .inherit);
196304
305 if (self.captured_stdout) |output| return .{ .generated = &output.generated_file };
306
307 const output = self.step.owner.allocator.create(Output) catch @panic("OOM");
308 output.* = .{
309 .prefix = "",
310 .basename = "stdout",
311 .generated_file = .{ .step = &self.step },
312 };
313 self.captured_stdout = output;
314 return .{ .generated = &output.generated_file };
315}
316
317/// Returns whether the RunStep has side effects *other than* updating the output arguments.
318fn hasSideEffects(self: RunStep) bool {
319 if (self.has_side_effects) return true;
320 return switch (self.stdio) {
321 .infer_from_args => !self.hasAnyOutputArgs(),
322 .inherit => true,
323 .check => false,
324 .zig_test => false,
325 };
326}
327
328fn hasAnyOutputArgs(self: RunStep) bool {
329 if (self.captured_stdout != null) return true;
330 if (self.captured_stderr != null) return true;
197 for (self.argv.items) |arg| switch (arg) {331 for (self.argv.items) |arg| switch (arg) {
198 .output => return true,332 .output => return true,
199 else => continue,333 else => continue,
200 };334 };
335 return false;
336}
337
338fn checksContainStdout(checks: []const StdIo.Check) bool {
339 for (checks) |check| switch (check) {
340 .expect_stderr_exact,
341 .expect_stderr_match,
342 .expect_term,
343 => continue,
344
345 .expect_stdout_exact,
346 .expect_stdout_match,
347 => return true,
348 };
349 return false;
350}
201351
352fn checksContainStderr(checks: []const StdIo.Check) bool {
353 for (checks) |check| switch (check) {
354 .expect_stdout_exact,
355 .expect_stdout_match,
356 .expect_term,
357 => continue,
358
359 .expect_stderr_exact,
360 .expect_stderr_match,
361 => return true,
362 };
202 return false;363 return false;
203}364}
204365
205fn make(step: *Step) !void {366fn make(step: *Step, prog_node: *std.Progress.Node) !void {
367 const b = step.owner;
368 const arena = b.allocator;
206 const self = @fieldParentPtr(RunStep, "step", step);369 const self = @fieldParentPtr(RunStep, "step", step);
207 const need_output_check = self.needOutputCheck();370 const has_side_effects = self.hasSideEffects();
208371
209 var argv_list = ArrayList([]const u8).init(self.builder.allocator);372 var argv_list = ArrayList([]const u8).init(arena);
210 var output_placeholders = ArrayList(struct {373 var output_placeholders = ArrayList(struct {
211 index: usize,374 index: usize,
212 output: Arg.Output,375 output: *Output,
213 }).init(self.builder.allocator);376 }).init(arena);
214377
215 var man = self.builder.cache.obtain();378 var man = b.cache.obtain();
216 defer man.deinit();379 defer man.deinit();
217380
218 for (self.argv.items) |arg| {381 for (self.argv.items) |arg| {
...@@ -222,23 +385,29 @@ fn make(step: *Step) !void {...@@ -222,23 +385,29 @@ fn make(step: *Step) !void {
222 man.hash.addBytes(bytes);385 man.hash.addBytes(bytes);
223 },386 },
224 .file_source => |file| {387 .file_source => |file| {
225 const file_path = file.getPath(self.builder);388 const file_path = file.getPath(b);
226 try argv_list.append(file_path);389 try argv_list.append(file_path);
227 _ = try man.addFile(file_path, null);390 _ = try man.addFile(file_path, null);
228 },391 },
392 .directory_source => |file| {
393 const file_path = file.getPath(b);
394 try argv_list.append(file_path);
395 man.hash.addBytes(file_path);
396 },
229 .artifact => |artifact| {397 .artifact => |artifact| {
230 if (artifact.target.isWindows()) {398 if (artifact.target.isWindows()) {
231 // On Windows we don't have rpaths so we have to add .dll search paths to PATH399 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
232 self.addPathForDynLibs(artifact);400 self.addPathForDynLibs(artifact);
233 }401 }
234 const file_path = artifact.installed_path orelse402 const file_path = artifact.installed_path orelse
235 artifact.getOutputSource().getPath(self.builder);403 artifact.getOutputSource().getPath(b);
236404
237 try argv_list.append(file_path);405 try argv_list.append(file_path);
238406
239 _ = try man.addFile(file_path, null);407 _ = try man.addFile(file_path, null);
240 },408 },
241 .output => |output| {409 .output => |output| {
410 man.hash.addBytes(output.prefix);
242 man.hash.addBytes(output.basename);411 man.hash.addBytes(output.basename);
243 // Add a placeholder into the argument list because we need the412 // Add a placeholder into the argument list because we need the
244 // manifest hash to be updated with all arguments before the413 // manifest hash to be updated with all arguments before the
...@@ -252,60 +421,77 @@ fn make(step: *Step) !void {...@@ -252,60 +421,77 @@ fn make(step: *Step) !void {
252 }421 }
253 }422 }
254423
255 if (need_output_check) {424 if (self.captured_stdout) |output| {
256 for (self.extra_file_dependencies) |file_path| {425 man.hash.addBytes(output.basename);
257 _ = try man.addFile(self.builder.pathFromRoot(file_path), null);426 }
258 }
259427
260 if (man.hit() catch |err| failWithCacheError(man, err)) {428 if (self.captured_stderr) |output| {
261 // cache hit, skip running command429 man.hash.addBytes(output.basename);
262 const digest = man.final();430 }
263 for (output_placeholders.items) |placeholder| {
264 placeholder.output.generated_file.path = try self.builder.cache_root.join(
265 self.builder.allocator,
266 &.{ "o", &digest, placeholder.output.basename },
267 );
268 }
269 return;
270 }
271431
272 const digest = man.final();432 hashStdIo(&man.hash, self.stdio);
273433
434 if (has_side_effects) {
435 try runCommand(self, argv_list.items, has_side_effects, null, prog_node);
436 return;
437 }
438
439 for (self.extra_file_dependencies) |file_path| {
440 _ = try man.addFile(b.pathFromRoot(file_path), null);
441 }
442
443 if (try step.cacheHit(&man)) {
444 // cache hit, skip running command
445 const digest = man.final();
274 for (output_placeholders.items) |placeholder| {446 for (output_placeholders.items) |placeholder| {
275 const output_path = try self.builder.cache_root.join(447 placeholder.output.generated_file.path = try b.cache_root.join(arena, &.{
276 self.builder.allocator,448 "o", &digest, placeholder.output.basename,
277 &.{ "o", &digest, placeholder.output.basename },449 });
278 );450 }
279 const output_dir = fs.path.dirname(output_path).?;451
280 fs.cwd().makePath(output_dir) catch |err| {452 if (self.captured_stdout) |output| {
281 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });453 output.generated_file.path = try b.cache_root.join(arena, &.{
282 return err;454 "o", &digest, output.basename,
283 };455 });
456 }
284457
285 placeholder.output.generated_file.path = output_path;458 if (self.captured_stderr) |output| {
286 argv_list.items[placeholder.index] = output_path;459 output.generated_file.path = try b.cache_root.join(arena, &.{
460 "o", &digest, output.basename,
461 });
287 }462 }
463
464 step.result_cached = true;
465 return;
288 }466 }
289467
290 try runCommand(468 const digest = man.final();
291 argv_list.items,469
292 self.builder,470 for (output_placeholders.items) |placeholder| {
293 self.expected_term,471 const output_components = .{ "o", &digest, placeholder.output.basename };
294 self.stdout_action,472 const output_sub_path = try fs.path.join(arena, &output_components);
295 self.stderr_action,473 const output_sub_dir_path = fs.path.dirname(output_sub_path).?;
296 self.stdin_behavior,474 b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
297 self.env_map,475 return step.fail("unable to make path '{}{s}': {s}", .{
298 self.cwd,476 b.cache_root, output_sub_dir_path, @errorName(err),
299 self.print,477 });
300 );478 };
301479 const output_path = try b.cache_root.join(arena, &output_components);
302 if (need_output_check) {480 placeholder.output.generated_file.path = output_path;
303 try man.writeManifest();481 const cli_arg = if (placeholder.output.prefix.len == 0)
482 output_path
483 else
484 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
485 argv_list.items[placeholder.index] = cli_arg;
304 }486 }
487
488 try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node);
489
490 try step.writeManifest(&man);
305}491}
306492
307fn formatTerm(493fn formatTerm(
308 term: ?std.ChildProcess.Term,494 term: ?std.process.Child.Term,
309 comptime fmt: []const u8,495 comptime fmt: []const u8,
310 options: std.fmt.FormatOptions,496 options: std.fmt.FormatOptions,
311 writer: anytype,497 writer: anytype,
...@@ -321,11 +507,11 @@ fn formatTerm(...@@ -321,11 +507,11 @@ fn formatTerm(
321 try writer.writeAll("exited with any code");507 try writer.writeAll("exited with any code");
322 }508 }
323}509}
324fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) {510fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) {
325 return .{ .data = term };511 return .{ .data = term };
326}512}
327513
328fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool {514fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool {
329 return if (expected) |e| switch (e) {515 return if (expected) |e| switch (e) {
330 .Exited => |expected_code| switch (actual) {516 .Exited => |expected_code| switch (actual) {
331 .Exited => |actual_code| expected_code == actual_code,517 .Exited => |actual_code| expected_code == actual_code,
...@@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)...@@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)
349 };535 };
350}536}
351537
352pub fn runCommand(538fn runCommand(
539 self: *RunStep,
353 argv: []const []const u8,540 argv: []const []const u8,
354 builder: *std.Build,541 has_side_effects: bool,
355 expected_term: ?std.ChildProcess.Term,542 digest: ?*const [std.Build.Cache.hex_digest_len]u8,
356 stdout_action: StdIoAction,543 prog_node: *std.Progress.Node,
357 stderr_action: StdIoAction,
358 stdin_behavior: std.ChildProcess.StdIo,
359 env_map: ?*EnvMap,
360 maybe_cwd: ?[]const u8,
361 print: bool,
362) !void {544) !void {
363 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path;545 const step = &self.step;
364546 const b = step.owner;
365 if (!std.process.can_spawn) {547 const arena = b.allocator;
366 const cmd = try std.mem.join(builder.allocator, " ", argv);548
367 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{549 try step.handleChildProcUnsupported(self.cwd, argv);
368 @tagName(builtin.os.tag), cmd,550 try Step.handleVerbose2(step.owner, self.cwd, self.env_map, argv);
369 });551
370 builder.allocator.free(cmd);552 const allow_skip = switch (self.stdio) {
371 return ExecError.ExecNotSupported;553 .check, .zig_test => self.skip_foreign_checks,
372 }554 else => false,
555 };
556
557 var interp_argv = std.ArrayList([]const u8).init(b.allocator);
558 defer interp_argv.deinit();
559
560 const result = spawnChildAndCollect(self, argv, has_side_effects, prog_node) catch |err| term: {
561 // InvalidExe: cpu arch mismatch
562 // FileNotFound: can happen with a wrong dynamic linker path
563 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
564 // TODO: learn the target from the binary directly rather than from
565 // relying on it being a CompileStep. This will make this logic
566 // work even for the edge case that the binary was produced by a
567 // third party.
568 const exe = switch (self.argv.items[0]) {
569 .artifact => |exe| exe,
570 else => break :interpret,
571 };
572 switch (exe.kind) {
573 .exe, .@"test" => {},
574 else => break :interpret,
575 }
576
577 const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc;
578 switch (b.host.getExternalExecutor(exe.target_info, .{
579 .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
580 .link_libc = exe.is_linking_libc,
581 })) {
582 .native, .rosetta => {
583 if (allow_skip) return error.MakeSkipped;
584 break :interpret;
585 },
586 .wine => |bin_name| {
587 if (b.enable_wine) {
588 try interp_argv.append(bin_name);
589 try interp_argv.appendSlice(argv);
590 } else {
591 return failForeign(self, "-fwine", argv[0], exe);
592 }
593 },
594 .qemu => |bin_name| {
595 if (b.enable_qemu) {
596 const glibc_dir_arg = if (need_cross_glibc)
597 b.glibc_runtimes_dir orelse return
598 else
599 null;
600
601 try interp_argv.append(bin_name);
602
603 if (glibc_dir_arg) |dir| {
604 // TODO look into making this a call to `linuxTriple`. This
605 // needs the directory to be called "i686" rather than
606 // "x86" which is why we do it manually here.
607 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
608 const cpu_arch = exe.target.getCpuArch();
609 const os_tag = exe.target.getOsTag();
610 const abi = exe.target.getAbi();
611 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
612 "i686"
613 else
614 @tagName(cpu_arch);
615 const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{
616 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
617 });
618
619 try interp_argv.append("-L");
620 try interp_argv.append(full_dir);
621 }
622
623 try interp_argv.appendSlice(argv);
624 } else {
625 return failForeign(self, "-fqemu", argv[0], exe);
626 }
627 },
628 .darling => |bin_name| {
629 if (b.enable_darling) {
630 try interp_argv.append(bin_name);
631 try interp_argv.appendSlice(argv);
632 } else {
633 return failForeign(self, "-fdarling", argv[0], exe);
634 }
635 },
636 .wasmtime => |bin_name| {
637 if (b.enable_wasmtime) {
638 try interp_argv.append(bin_name);
639 try interp_argv.append("--dir=.");
640 try interp_argv.append(argv[0]);
641 try interp_argv.append("--");
642 try interp_argv.appendSlice(argv[1..]);
643 } else {
644 return failForeign(self, "-fwasmtime", argv[0], exe);
645 }
646 },
647 .bad_dl => |foreign_dl| {
648 if (allow_skip) return error.MakeSkipped;
649
650 const host_dl = b.host.dynamic_linker.get() orelse "(none)";
651
652 return step.fail(
653 \\the host system is unable to execute binaries from the target
654 \\ because the host dynamic linker is '{s}',
655 \\ while the target dynamic linker is '{s}'.
656 \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step
657 , .{ host_dl, foreign_dl });
658 },
659 .bad_os_or_cpu => {
660 if (allow_skip) return error.MakeSkipped;
661
662 const host_name = try b.host.target.zigTriple(b.allocator);
663 const foreign_name = try exe.target.zigTriple(b.allocator);
664
665 return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{
666 host_name, foreign_name,
667 });
668 },
669 }
373670
374 var child = std.ChildProcess.init(argv, builder.allocator);671 if (exe.target.isWindows()) {
375 child.cwd = cwd;672 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
376 child.env_map = env_map orelse builder.env_map;673 self.addPathForDynLibs(exe);
674 }
377675
378 child.stdin_behavior = stdin_behavior;676 try Step.handleVerbose2(step.owner, self.cwd, self.env_map, interp_argv.items);
379 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
380 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
381677
382 if (print)678 break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| {
383 printCmd(cwd, argv);679 return step.fail("unable to spawn interpreter {s}: {s}", .{
680 interp_argv.items[0], @errorName(e),
681 });
682 };
683 }
384684
385 child.spawn() catch |err| {685 return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
386 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
387 return err;
388 };686 };
389687
390 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).688 step.result_duration_ns = result.elapsed_ns;
689 step.result_peak_rss = result.peak_rss;
690 step.test_results = result.stdio.test_results;
391691
392 var stdout: ?[]const u8 = null;692 // Capture stdout and stderr to GeneratedFile objects.
393 defer if (stdout) |s| builder.allocator.free(s);693 const Stream = struct {
694 captured: ?*Output,
695 is_null: bool,
696 bytes: []const u8,
697 };
698 for ([_]Stream{
699 .{
700 .captured = self.captured_stdout,
701 .is_null = result.stdio.stdout_null,
702 .bytes = result.stdio.stdout,
703 },
704 .{
705 .captured = self.captured_stderr,
706 .is_null = result.stdio.stderr_null,
707 .bytes = result.stdio.stderr,
708 },
709 }) |stream| {
710 if (stream.captured) |output| {
711 assert(!stream.is_null);
712
713 const output_components = .{ "o", digest.?, output.basename };
714 const output_path = try b.cache_root.join(arena, &output_components);
715 output.generated_file.path = output_path;
716
717 const sub_path = try fs.path.join(arena, &output_components);
718 const sub_path_dirname = fs.path.dirname(sub_path).?;
719 b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
720 return step.fail("unable to make path '{}{s}': {s}", .{
721 b.cache_root, sub_path_dirname, @errorName(err),
722 });
723 };
724 b.cache_root.handle.writeFile(sub_path, stream.bytes) catch |err| {
725 return step.fail("unable to write file '{}{s}': {s}", .{
726 b.cache_root, sub_path, @errorName(err),
727 });
728 };
729 }
730 }
394731
395 switch (stdout_action) {732 const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items;
396 .expect_exact, .expect_matches => {733
397 stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);734 switch (self.stdio) {
735 .check => |checks| for (checks.items) |check| switch (check) {
736 .expect_stderr_exact => |expected_bytes| {
737 assert(!result.stdio.stderr_null);
738 if (!mem.eql(u8, expected_bytes, result.stdio.stderr)) {
739 return step.fail(
740 \\
741 \\========= expected this stderr: =========
742 \\{s}
743 \\========= but found: ====================
744 \\{s}
745 \\========= from the following command: ===
746 \\{s}
747 , .{
748 expected_bytes,
749 result.stdio.stderr,
750 try Step.allocPrintCmd(arena, self.cwd, final_argv),
751 });
752 }
753 },
754 .expect_stderr_match => |match| {
755 assert(!result.stdio.stderr_null);
756 if (mem.indexOf(u8, result.stdio.stderr, match) == null) {
757 return step.fail(
758 \\
759 \\========= expected to find in stderr: =========
760 \\{s}
761 \\========= but stderr does not contain it: =====
762 \\{s}
763 \\========= from the following command: =========
764 \\{s}
765 , .{
766 match,
767 result.stdio.stderr,
768 try Step.allocPrintCmd(arena, self.cwd, final_argv),
769 });
770 }
771 },
772 .expect_stdout_exact => |expected_bytes| {
773 assert(!result.stdio.stdout_null);
774 if (!mem.eql(u8, expected_bytes, result.stdio.stdout)) {
775 return step.fail(
776 \\
777 \\========= expected this stdout: =========
778 \\{s}
779 \\========= but found: ====================
780 \\{s}
781 \\========= from the following command: ===
782 \\{s}
783 , .{
784 expected_bytes,
785 result.stdio.stdout,
786 try Step.allocPrintCmd(arena, self.cwd, final_argv),
787 });
788 }
789 },
790 .expect_stdout_match => |match| {
791 assert(!result.stdio.stdout_null);
792 if (mem.indexOf(u8, result.stdio.stdout, match) == null) {
793 return step.fail(
794 \\
795 \\========= expected to find in stdout: =========
796 \\{s}
797 \\========= but stdout does not contain it: =====
798 \\{s}
799 \\========= from the following command: =========
800 \\{s}
801 , .{
802 match,
803 result.stdio.stdout,
804 try Step.allocPrintCmd(arena, self.cwd, final_argv),
805 });
806 }
807 },
808 .expect_term => |expected_term| {
809 if (!termMatches(expected_term, result.term)) {
810 return step.fail("the following command {} (expected {}):\n{s}", .{
811 fmtTerm(result.term),
812 fmtTerm(expected_term),
813 try Step.allocPrintCmd(arena, self.cwd, final_argv),
814 });
815 }
816 },
817 },
818 .zig_test => {
819 const expected_term: std.process.Child.Term = .{ .Exited = 0 };
820 if (!termMatches(expected_term, result.term)) {
821 return step.fail("the following command {} (expected {}):\n{s}", .{
822 fmtTerm(result.term),
823 fmtTerm(expected_term),
824 try Step.allocPrintCmd(arena, self.cwd, final_argv),
825 });
826 }
827 if (!result.stdio.test_results.isSuccess()) {
828 return step.fail(
829 "the following test command failed:\n{s}",
830 .{try Step.allocPrintCmd(arena, self.cwd, final_argv)},
831 );
832 }
833 },
834 else => {
835 try step.handleChildProcessTerm(result.term, self.cwd, final_argv);
398 },836 },
399 .inherit, .ignore => {},
400 }837 }
838}
401839
402 var stderr: ?[]const u8 = null;840const ChildProcResult = struct {
403 defer if (stderr) |s| builder.allocator.free(s);841 term: std.process.Child.Term,
842 elapsed_ns: u64,
843 peak_rss: usize,
404844
405 switch (stderr_action) {845 stdio: StdIoResult,
406 .expect_exact, .expect_matches => {846};
407 stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);847
408 },848fn spawnChildAndCollect(
409 .inherit, .ignore => {},849 self: *RunStep,
850 argv: []const []const u8,
851 has_side_effects: bool,
852 prog_node: *std.Progress.Node,
853) !ChildProcResult {
854 const b = self.step.owner;
855 const arena = b.allocator;
856
857 var child = std.process.Child.init(argv, arena);
858 if (self.cwd) |cwd| {
859 child.cwd = b.pathFromRoot(cwd);
860 } else {
861 child.cwd = b.build_root.path;
862 child.cwd_dir = b.build_root.handle;
410 }863 }
864 child.env_map = self.env_map orelse b.env_map;
865 child.request_resource_usage_statistics = true;
411866
412 const term = child.wait() catch |err| {867 child.stdin_behavior = switch (self.stdio) {
413 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });868 .infer_from_args => if (has_side_effects) .Inherit else .Close,
414 return err;869 .inherit => .Inherit,
870 .check => .Close,
871 .zig_test => .Pipe,
415 };872 };
873 child.stdout_behavior = switch (self.stdio) {
874 .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
875 .inherit => .Inherit,
876 .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore,
877 .zig_test => .Pipe,
878 };
879 child.stderr_behavior = switch (self.stdio) {
880 .infer_from_args => if (has_side_effects) .Inherit else .Pipe,
881 .inherit => .Inherit,
882 .check => .Pipe,
883 .zig_test => .Pipe,
884 };
885 if (self.captured_stdout != null) child.stdout_behavior = .Pipe;
886 if (self.captured_stderr != null) child.stderr_behavior = .Pipe;
887 if (self.stdin != null) {
888 assert(child.stdin_behavior != .Inherit);
889 child.stdin_behavior = .Pipe;
890 }
416891
417 if (!termMatches(expected_term, term)) {892 try child.spawn();
418 if (builder.prominent_compile_errors) {893 var timer = try std.time.Timer.start();
419 std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) });894
420 } else {895 const result = if (self.stdio == .zig_test)
421 std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) });896 evalZigTest(self, &child, prog_node)
422 printCmd(cwd, argv);897 else
898 evalGeneric(self, &child);
899
900 const term = try child.wait();
901 const elapsed_ns = timer.read();
902
903 return .{
904 .stdio = try result,
905 .term = term,
906 .elapsed_ns = elapsed_ns,
907 .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0,
908 };
909}
910
911const StdIoResult = struct {
912 // These use boolean flags instead of optionals as a workaround for
913 // https://github.com/ziglang/zig/issues/14783
914 stdout: []const u8,
915 stderr: []const u8,
916 stdout_null: bool,
917 stderr_null: bool,
918 test_results: Step.TestResults,
919};
920
921fn evalZigTest(
922 self: *RunStep,
923 child: *std.process.Child,
924 prog_node: *std.Progress.Node,
925) !StdIoResult {
926 const gpa = self.step.owner.allocator;
927 const arena = self.step.owner.allocator;
928
929 var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
930 .stdout = child.stdout.?,
931 .stderr = child.stderr.?,
932 });
933 defer poller.deinit();
934
935 try sendMessage(child.stdin.?, .query_test_metadata);
936
937 const Header = std.zig.Server.Message.Header;
938
939 const stdout = poller.fifo(.stdout);
940 const stderr = poller.fifo(.stderr);
941
942 var fail_count: u32 = 0;
943 var skip_count: u32 = 0;
944 var leak_count: u32 = 0;
945 var test_count: u32 = 0;
946
947 var metadata: ?TestMetadata = null;
948
949 var sub_prog_node: ?std.Progress.Node = null;
950 defer if (sub_prog_node) |*n| n.end();
951
952 poll: while (true) {
953 while (stdout.readableLength() < @sizeOf(Header)) {
954 if (!(try poller.poll())) break :poll;
955 }
956 const header = stdout.reader().readStruct(Header) catch unreachable;
957 while (stdout.readableLength() < header.bytes_len) {
958 if (!(try poller.poll())) break :poll;
423 }959 }
424 return error.UnexpectedExit;960 const body = stdout.readableSliceOfLen(header.bytes_len);
961
962 switch (header.tag) {
963 .zig_version => {
964 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
965 return self.step.fail(
966 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
967 .{ builtin.zig_version_string, body },
968 );
969 }
970 },
971 .test_metadata => {
972 const TmHdr = std.zig.Server.Message.TestMetadata;
973 const tm_hdr = @ptrCast(*align(1) const TmHdr, body);
974 test_count = tm_hdr.tests_len;
975
976 const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)];
977 const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)];
978 const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)];
979 const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len];
980
981 const names = std.mem.bytesAsSlice(u32, names_bytes);
982 const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes);
983 const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes);
984 const names_aligned = try arena.alloc(u32, names.len);
985 for (names_aligned, names) |*dest, src| dest.* = src;
986
987 const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len);
988 for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src;
989
990 const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len);
991 for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src;
992
993 prog_node.setEstimatedTotalItems(names.len);
994 metadata = .{
995 .string_bytes = try arena.dupe(u8, string_bytes),
996 .names = names_aligned,
997 .async_frame_lens = async_frame_lens_aligned,
998 .expected_panic_msgs = expected_panic_msgs_aligned,
999 .next_index = 0,
1000 .prog_node = prog_node,
1001 };
1002
1003 try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node);
1004 },
1005 .test_results => {
1006 const md = metadata.?;
1007
1008 const TrHdr = std.zig.Server.Message.TestResults;
1009 const tr_hdr = @ptrCast(*align(1) const TrHdr, body);
1010 fail_count += @boolToInt(tr_hdr.flags.fail);
1011 skip_count += @boolToInt(tr_hdr.flags.skip);
1012 leak_count += @boolToInt(tr_hdr.flags.leak);
1013
1014 if (tr_hdr.flags.fail or tr_hdr.flags.leak) {
1015 const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0);
1016 const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n");
1017 const label = if (tr_hdr.flags.fail) "failed" else "leaked";
1018 if (msg.len > 0) {
1019 try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg });
1020 } else {
1021 try self.step.addError("'{s}' {s}", .{ name, label });
1022 }
1023 stderr.discard(msg.len);
1024 }
1025
1026 try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node);
1027 },
1028 else => {}, // ignore other messages
1029 }
1030
1031 stdout.discard(body.len);
425 }1032 }
4261033
427 switch (stderr_action) {1034 if (stderr.readableLength() > 0) {
428 .inherit, .ignore => {},1035 const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n");
429 .expect_exact => |expected_bytes| {1036 if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg);
430 if (!mem.eql(u8, expected_bytes, stderr.?)) {
431 std.debug.print(
432 \\
433 \\========= Expected this stderr: =========
434 \\{s}
435 \\========= But found: ====================
436 \\{s}
437 \\
438 , .{ expected_bytes, stderr.? });
439 printCmd(cwd, argv);
440 return error.TestFailed;
441 }
442 },
443 .expect_matches => |matches| for (matches) |match| {
444 if (mem.indexOf(u8, stderr.?, match) == null) {
445 std.debug.print(
446 \\
447 \\========= Expected to find in stderr: =========
448 \\{s}
449 \\========= But stderr does not contain it: =====
450 \\{s}
451 \\
452 , .{ match, stderr.? });
453 printCmd(cwd, argv);
454 return error.TestFailed;
455 }
456 },
457 }1037 }
4581038
459 switch (stdout_action) {1039 // Send EOF to stdin.
460 .inherit, .ignore => {},1040 child.stdin.?.close();
461 .expect_exact => |expected_bytes| {1041 child.stdin = null;
462 if (!mem.eql(u8, expected_bytes, stdout.?)) {1042
463 std.debug.print(1043 return .{
464 \\1044 .stdout = &.{},
465 \\========= Expected this stdout: =========1045 .stderr = &.{},
466 \\{s}1046 .stdout_null = true,
467 \\========= But found: ====================1047 .stderr_null = true,
468 \\{s}1048 .test_results = .{
469 \\1049 .test_count = test_count,
470 , .{ expected_bytes, stdout.? });1050 .fail_count = fail_count,
471 printCmd(cwd, argv);1051 .skip_count = skip_count,
472 return error.TestFailed;1052 .leak_count = leak_count,
473 }
474 },
475 .expect_matches => |matches| for (matches) |match| {
476 if (mem.indexOf(u8, stdout.?, match) == null) {
477 std.debug.print(
478 \\
479 \\========= Expected to find in stdout: =========
480 \\{s}
481 \\========= But stdout does not contain it: =====
482 \\{s}
483 \\
484 , .{ match, stdout.? });
485 printCmd(cwd, argv);
486 return error.TestFailed;
487 }
488 },1053 },
1054 };
1055}
1056
1057const TestMetadata = struct {
1058 names: []const u32,
1059 async_frame_lens: []const u32,
1060 expected_panic_msgs: []const u32,
1061 string_bytes: []const u8,
1062 next_index: u32,
1063 prog_node: *std.Progress.Node,
1064
1065 fn testName(tm: TestMetadata, index: u32) []const u8 {
1066 return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0);
1067 }
1068};
1069
1070fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void {
1071 while (metadata.next_index < metadata.names.len) {
1072 const i = metadata.next_index;
1073 metadata.next_index += 1;
1074
1075 if (metadata.async_frame_lens[i] != 0) continue;
1076 if (metadata.expected_panic_msgs[i] != 0) continue;
1077
1078 const name = metadata.testName(i);
1079 if (sub_prog_node.*) |*n| n.end();
1080 sub_prog_node.* = metadata.prog_node.start(name, 0);
1081
1082 try sendRunTestMessage(in, i);
1083 return;
1084 } else {
1085 try sendMessage(in, .exit);
489 }1086 }
490}1087}
4911088
492fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {1089fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
493 const i = man.failed_file_index orelse failWithSimpleError(err);1090 const header: std.zig.Client.Message.Header = .{
494 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);1091 .tag = tag,
495 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";1092 .bytes_len = 0,
496 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });1093 };
497 std.process.exit(1);1094 try file.writeAll(std.mem.asBytes(&header));
498}1095}
4991096
500fn failWithSimpleError(err: anyerror) noreturn {1097fn sendRunTestMessage(file: std.fs.File, index: u32) !void {
501 std.debug.print("{s}\n", .{@errorName(err)});1098 const header: std.zig.Client.Message.Header = .{
502 std.process.exit(1);1099 .tag = .run_test,
1100 .bytes_len = 4,
1101 };
1102 const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index);
1103 try file.writeAll(full_msg);
503}1104}
5041105
505fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {1106fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult {
506 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});1107 const arena = self.step.owner.allocator;
507 for (argv) |arg| {1108
508 std.debug.print("{s} ", .{arg});1109 if (self.stdin) |stdin| {
1110 child.stdin.?.writeAll(stdin) catch |err| {
1111 return self.step.fail("unable to write stdin: {s}", .{@errorName(err)});
1112 };
1113 child.stdin.?.close();
1114 child.stdin = null;
509 }1115 }
510 std.debug.print("\n", .{});
511}
5121116
513fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {1117 // These are not optionals, as a workaround for
514 addPathForDynLibsInternal(&self.step, self.builder, artifact);1118 // https://github.com/ziglang/zig/issues/14783
1119 var stdout_bytes: []const u8 = undefined;
1120 var stderr_bytes: []const u8 = undefined;
1121 var stdout_null = true;
1122 var stderr_null = true;
1123
1124 if (child.stdout) |stdout| {
1125 if (child.stderr) |stderr| {
1126 var poller = std.io.poll(arena, enum { stdout, stderr }, .{
1127 .stdout = stdout,
1128 .stderr = stderr,
1129 });
1130 defer poller.deinit();
1131
1132 while (try poller.poll()) {
1133 if (poller.fifo(.stdout).count > self.max_stdio_size)
1134 return error.StdoutStreamTooLong;
1135 if (poller.fifo(.stderr).count > self.max_stdio_size)
1136 return error.StderrStreamTooLong;
1137 }
1138
1139 stdout_bytes = try poller.fifo(.stdout).toOwnedSlice();
1140 stderr_bytes = try poller.fifo(.stderr).toOwnedSlice();
1141 stdout_null = false;
1142 stderr_null = false;
1143 } else {
1144 stdout_bytes = try stdout.reader().readAllAlloc(arena, self.max_stdio_size);
1145 stdout_null = false;
1146 }
1147 } else if (child.stderr) |stderr| {
1148 stderr_bytes = try stderr.reader().readAllAlloc(arena, self.max_stdio_size);
1149 stderr_null = false;
1150 }
1151
1152 if (!stderr_null and stderr_bytes.len > 0) {
1153 // Treat stderr as an error message.
1154 const stderr_is_diagnostic = self.captured_stderr == null and switch (self.stdio) {
1155 .check => |checks| !checksContainStderr(checks.items),
1156 else => true,
1157 };
1158 if (stderr_is_diagnostic) {
1159 try self.step.result_error_msgs.append(arena, stderr_bytes);
1160 }
1161 }
1162
1163 return .{
1164 .stdout = stdout_bytes,
1165 .stderr = stderr_bytes,
1166 .stdout_null = stdout_null,
1167 .stderr_null = stderr_null,
1168 .test_results = .{},
1169 };
515}1170}
5161171
517/// This should only be used for internal usage, this is called automatically1172fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
518/// for the user.1173 const b = self.step.owner;
519pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void {
520 for (artifact.link_objects.items) |link_object| {1174 for (artifact.link_objects.items) |link_object| {
521 switch (link_object) {1175 switch (link_object) {
522 .other_step => |other| {1176 .other_step => |other| {
523 if (other.target.isWindows() and other.isDynamicLibrary()) {1177 if (other.target.isWindows() and other.isDynamicLibrary()) {
524 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);1178 addPathDir(self, fs.path.dirname(other.getOutputSource().getPath(b)).?);
525 addPathForDynLibsInternal(step, builder, other);1179 addPathForDynLibs(self, other);
526 }1180 }
527 },1181 },
528 else => {},1182 else => {},
529 }1183 }
530 }1184 }
531}1185}
1186
1187fn failForeign(
1188 self: *RunStep,
1189 suggested_flag: []const u8,
1190 argv0: []const u8,
1191 exe: *CompileStep,
1192) error{ MakeFailed, MakeSkipped, OutOfMemory } {
1193 switch (self.stdio) {
1194 .check, .zig_test => {
1195 if (self.skip_foreign_checks)
1196 return error.MakeSkipped;
1197
1198 const b = self.step.owner;
1199 const host_name = try b.host.target.zigTriple(b.allocator);
1200 const foreign_name = try exe.target.zigTriple(b.allocator);
1201
1202 return self.step.fail(
1203 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
1204 \\ consider using {s} or enabling skip_foreign_checks in the Run step
1205 , .{ argv0, foreign_name, host_name, suggested_flag });
1206 },
1207 else => {
1208 return self.step.fail("unable to spawn foreign binary '{s}'", .{argv0});
1209 },
1210 }
1211}
1212
1213fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void {
1214 switch (stdio) {
1215 .infer_from_args, .inherit, .zig_test => {},
1216 .check => |checks| for (checks.items) |check| {
1217 hh.add(@as(std.meta.Tag(StdIo.Check), check));
1218 switch (check) {
1219 .expect_stderr_exact,
1220 .expect_stderr_match,
1221 .expect_stdout_exact,
1222 .expect_stdout_match,
1223 => |s| hh.addBytes(s),
1224
1225 .expect_term => |term| {
1226 hh.add(@as(std.meta.Tag(std.process.Child.Term), term));
1227 switch (term) {
1228 .Exited => |x| hh.add(x),
1229 .Signal, .Stopped, .Unknown => |x| hh.add(x),
1230 }
1231 },
1232 }
1233 },
1234 }
1235}
lib/std/Build/Step.zig+467-25
...@@ -1,9 +1,77 @@...@@ -1,9 +1,77 @@
1id: Id,1id: Id,
2name: []const u8,2name: []const u8,
3makeFn: *const fn (self: *Step) anyerror!void,3owner: *Build,
4makeFn: MakeFn,
5
4dependencies: std.ArrayList(*Step),6dependencies: std.ArrayList(*Step),
5loop_flag: bool,7/// This field is empty during execution of the user's build script, and
6done_flag: bool,8/// then populated during dependency loop checking in the build runner.
9dependants: std.ArrayListUnmanaged(*Step),
10state: State,
11/// Set this field to declare an upper bound on the amount of bytes of memory it will
12/// take to run the step. Zero means no limit.
13///
14/// The idea to annotate steps that might use a high amount of RAM with an
15/// upper bound. For example, perhaps a particular set of unit tests require 4
16/// GiB of RAM, and those tests will be run under 4 different build
17/// configurations at once. This would potentially require 16 GiB of memory on
18/// the system if all 4 steps executed simultaneously, which could easily be
19/// greater than what is actually available, potentially causing the system to
20/// crash when using `zig build` at the default concurrency level.
21///
22/// This field causes the build runner to do two things:
23/// 1. ulimit child processes, so that they will fail if it would exceed this
24/// memory limit. This serves to enforce that this upper bound value is
25/// correct.
26/// 2. Ensure that the set of concurrent steps at any given time have a total
27/// max_rss value that does not exceed the `max_total_rss` value of the build
28/// runner. This value is configurable on the command line, and defaults to the
29/// total system memory available.
30max_rss: usize,
31
32result_error_msgs: std.ArrayListUnmanaged([]const u8),
33result_error_bundle: std.zig.ErrorBundle,
34result_cached: bool,
35result_duration_ns: ?u64,
36/// 0 means unavailable or not reported.
37result_peak_rss: usize,
38test_results: TestResults,
39
40/// The return addresss associated with creation of this step that can be useful
41/// to print along with debugging messages.
42debug_stack_trace: [n_debug_stack_frames]usize,
43
44pub const TestResults = struct {
45 fail_count: u32 = 0,
46 skip_count: u32 = 0,
47 leak_count: u32 = 0,
48 test_count: u32 = 0,
49
50 pub fn isSuccess(tr: TestResults) bool {
51 return tr.fail_count == 0 and tr.leak_count == 0;
52 }
53
54 pub fn passCount(tr: TestResults) u32 {
55 return tr.test_count - tr.fail_count - tr.skip_count;
56 }
57};
58
59pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void;
60
61const n_debug_stack_frames = 4;
62
63pub const State = enum {
64 precheck_unstarted,
65 precheck_started,
66 precheck_done,
67 running,
68 dependency_failure,
69 success,
70 failure,
71 /// This state indicates that the step did not complete, however, it also did not fail,
72 /// and it is safe to continue executing its dependencies.
73 skipped,
74};
775
8pub const Id = enum {76pub const Id = enum {
9 top_level,77 top_level,
...@@ -17,7 +85,6 @@ pub const Id = enum {...@@ -17,7 +85,6 @@ pub const Id = enum {
17 translate_c,85 translate_c,
18 write_file,86 write_file,
19 run,87 run,
20 emulatable_run,
21 check_file,88 check_file,
22 check_object,89 check_object,
23 config_header,90 config_header,
...@@ -38,7 +105,6 @@ pub const Id = enum {...@@ -38,7 +105,6 @@ pub const Id = enum {
38 .translate_c => Build.TranslateCStep,105 .translate_c => Build.TranslateCStep,
39 .write_file => Build.WriteFileStep,106 .write_file => Build.WriteFileStep,
40 .run => Build.RunStep,107 .run => Build.RunStep,
41 .emulatable_run => Build.EmulatableRunStep,
42 .check_file => Build.CheckFileStep,108 .check_file => Build.CheckFileStep,
43 .check_object => Build.CheckObjectStep,109 .check_object => Build.CheckObjectStep,
44 .config_header => Build.ConfigHeaderStep,110 .config_header => Build.ConfigHeaderStep,
...@@ -49,39 +115,99 @@ pub const Id = enum {...@@ -49,39 +115,99 @@ pub const Id = enum {
49 }115 }
50};116};
51117
52pub fn init(118pub const Options = struct {
53 id: Id,119 id: Id,
54 name: []const u8,120 name: []const u8,
55 allocator: Allocator,121 owner: *Build,
56 makeFn: *const fn (self: *Step) anyerror!void,122 makeFn: MakeFn = makeNoOp,
57) Step {123 first_ret_addr: ?usize = null,
58 return Step{124 max_rss: usize = 0,
59 .id = id,125};
60 .name = allocator.dupe(u8, name) catch @panic("OOM"),126
61 .makeFn = makeFn,127pub fn init(options: Options) Step {
62 .dependencies = std.ArrayList(*Step).init(allocator),128 const arena = options.owner.allocator;
63 .loop_flag = false,129
64 .done_flag = false,130 var addresses = [1]usize{0} ** n_debug_stack_frames;
131 const first_ret_addr = options.first_ret_addr orelse @returnAddress();
132 var stack_trace = std.builtin.StackTrace{
133 .instruction_addresses = &addresses,
134 .index = 0,
65 };135 };
66}136 std.debug.captureStackTrace(first_ret_addr, &stack_trace);
67137
68pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {138 return .{
69 return init(id, name, allocator, makeNoOp);139 .id = options.id,
140 .name = arena.dupe(u8, options.name) catch @panic("OOM"),
141 .owner = options.owner,
142 .makeFn = options.makeFn,
143 .dependencies = std.ArrayList(*Step).init(arena),
144 .dependants = .{},
145 .state = .precheck_unstarted,
146 .max_rss = options.max_rss,
147 .debug_stack_trace = addresses,
148 .result_error_msgs = .{},
149 .result_error_bundle = std.zig.ErrorBundle.empty,
150 .result_cached = false,
151 .result_duration_ns = null,
152 .result_peak_rss = 0,
153 .test_results = .{},
154 };
70}155}
71156
72pub fn make(self: *Step) !void {157/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
73 if (self.done_flag) return;158/// have already reported the error. Otherwise, we add a simple error report
159/// here.
160pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void {
161 const arena = s.owner.allocator;
162
163 s.makeFn(s, prog_node) catch |err| switch (err) {
164 error.MakeFailed => return error.MakeFailed,
165 error.MakeSkipped => return error.MakeSkipped,
166 else => {
167 s.result_error_msgs.append(arena, @errorName(err)) catch @panic("OOM");
168 return error.MakeFailed;
169 },
170 };
171
172 if (!s.test_results.isSuccess()) {
173 return error.MakeFailed;
174 }
74175
75 try self.makeFn(self);176 if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) {
76 self.done_flag = true;177 const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{
178 s.result_peak_rss, s.max_rss,
179 }) catch @panic("OOM");
180 s.result_error_msgs.append(arena, msg) catch @panic("OOM");
181 return error.MakeFailed;
182 }
77}183}
78184
79pub fn dependOn(self: *Step, other: *Step) void {185pub fn dependOn(self: *Step, other: *Step) void {
80 self.dependencies.append(other) catch @panic("OOM");186 self.dependencies.append(other) catch @panic("OOM");
81}187}
82188
83fn makeNoOp(self: *Step) anyerror!void {189pub fn getStackTrace(s: *Step) std.builtin.StackTrace {
84 _ = self;190 const stack_addresses = &s.debug_stack_trace;
191 var len: usize = 0;
192 while (len < n_debug_stack_frames and stack_addresses[len] != 0) {
193 len += 1;
194 }
195 return .{
196 .instruction_addresses = stack_addresses,
197 .index = len,
198 };
199}
200
201fn makeNoOp(step: *Step, prog_node: *std.Progress.Node) anyerror!void {
202 _ = prog_node;
203
204 var all_cached = true;
205
206 for (step.dependencies.items) |dep| {
207 all_cached = all_cached and dep.result_cached;
208 }
209
210 step.result_cached = all_cached;
85}211}
86212
87pub fn cast(step: *Step, comptime T: type) ?*T {213pub fn cast(step: *Step, comptime T: type) ?*T {
...@@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T {...@@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
91 return null;217 return null;
92}218}
93219
220/// For debugging purposes, prints identifying information about this Step.
221pub fn dump(step: *Step) void {
222 std.debug.getStderrMutex().lock();
223 defer std.debug.getStderrMutex().unlock();
224
225 const stderr = std.io.getStdErr();
226 const w = stderr.writer();
227 const tty_config = std.debug.detectTTYConfig(stderr);
228 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
229 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
230 @errorName(err),
231 }) catch {};
232 return;
233 };
234 const ally = debug_info.allocator;
235 w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
236 std.debug.writeStackTrace(step.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
237 stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};
238 return;
239 };
240}
241
94const Step = @This();242const Step = @This();
95const std = @import("../std.zig");243const std = @import("../std.zig");
96const Build = std.Build;244const Build = std.Build;
97const Allocator = std.mem.Allocator;245const Allocator = std.mem.Allocator;
246const assert = std.debug.assert;
247const builtin = @import("builtin");
248
249pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void {
250 const arena = s.owner.allocator;
251
252 try handleChildProcUnsupported(s, null, argv);
253 try handleVerbose(s.owner, null, argv);
254
255 const result = std.ChildProcess.exec(.{
256 .allocator = arena,
257 .argv = argv,
258 }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
259
260 if (result.stderr.len > 0) {
261 try s.result_error_msgs.append(arena, result.stderr);
262 }
263
264 try handleChildProcessTerm(s, result.term, null, argv);
265}
266
267pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } {
268 try step.addError(fmt, args);
269 return error.MakeFailed;
270}
271
272pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
273 const arena = step.owner.allocator;
274 const msg = try std.fmt.allocPrint(arena, fmt, args);
275 try step.result_error_msgs.append(arena, msg);
276}
277
278/// Assumes that argv contains `--listen=-` and that the process being spawned
279/// is the zig compiler - the same version that compiled the build runner.
280pub fn evalZigProcess(
281 s: *Step,
282 argv: []const []const u8,
283 prog_node: *std.Progress.Node,
284) ![]const u8 {
285 assert(argv.len != 0);
286 const b = s.owner;
287 const arena = b.allocator;
288 const gpa = arena;
289
290 try handleChildProcUnsupported(s, null, argv);
291 try handleVerbose(s.owner, null, argv);
292
293 var child = std.ChildProcess.init(argv, arena);
294 child.env_map = b.env_map;
295 child.stdin_behavior = .Pipe;
296 child.stdout_behavior = .Pipe;
297 child.stderr_behavior = .Pipe;
298 child.request_resource_usage_statistics = true;
299
300 child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{
301 argv[0], @errorName(err),
302 });
303 var timer = try std.time.Timer.start();
304
305 var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
306 .stdout = child.stdout.?,
307 .stderr = child.stderr.?,
308 });
309 defer poller.deinit();
310
311 try sendMessage(child.stdin.?, .update);
312 try sendMessage(child.stdin.?, .exit);
313
314 const Header = std.zig.Server.Message.Header;
315 var result: ?[]const u8 = null;
316
317 var node_name: std.ArrayListUnmanaged(u8) = .{};
318 defer node_name.deinit(gpa);
319 var sub_prog_node = prog_node.start("", 0);
320 defer sub_prog_node.end();
321
322 const stdout = poller.fifo(.stdout);
323
324 poll: while (true) {
325 while (stdout.readableLength() < @sizeOf(Header)) {
326 if (!(try poller.poll())) break :poll;
327 }
328 const header = stdout.reader().readStruct(Header) catch unreachable;
329 while (stdout.readableLength() < header.bytes_len) {
330 if (!(try poller.poll())) break :poll;
331 }
332 const body = stdout.readableSliceOfLen(header.bytes_len);
333
334 switch (header.tag) {
335 .zig_version => {
336 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
337 return s.fail(
338 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
339 .{ builtin.zig_version_string, body },
340 );
341 }
342 },
343 .error_bundle => {
344 const EbHdr = std.zig.Server.Message.ErrorBundle;
345 const eb_hdr = @ptrCast(*align(1) const EbHdr, body);
346 const extra_bytes =
347 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
348 const string_bytes =
349 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
350 // TODO: use @ptrCast when the compiler supports it
351 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
352 const extra_array = try arena.alloc(u32, unaligned_extra.len);
353 // TODO: use @memcpy when it supports slices
354 for (extra_array, unaligned_extra) |*dst, src| dst.* = src;
355 s.result_error_bundle = .{
356 .string_bytes = try arena.dupe(u8, string_bytes),
357 .extra = extra_array,
358 };
359 },
360 .progress => {
361 node_name.clearRetainingCapacity();
362 try node_name.appendSlice(gpa, body);
363 sub_prog_node.setName(node_name.items);
364 },
365 .emit_bin_path => {
366 const EbpHdr = std.zig.Server.Message.EmitBinPath;
367 const ebp_hdr = @ptrCast(*align(1) const EbpHdr, body);
368 s.result_cached = ebp_hdr.flags.cache_hit;
369 result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]);
370 },
371 else => {}, // ignore other messages
372 }
373
374 stdout.discard(body.len);
375 }
376
377 const stderr = poller.fifo(.stderr);
378 if (stderr.readableLength() > 0) {
379 try s.result_error_msgs.append(arena, try stderr.toOwnedSlice());
380 }
381
382 // Send EOF to stdin.
383 child.stdin.?.close();
384 child.stdin = null;
385
386 const term = child.wait() catch |err| {
387 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });
388 };
389 s.result_duration_ns = timer.read();
390 s.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0;
391
392 // Special handling for CompileStep that is expecting compile errors.
393 if (s.cast(Build.CompileStep)) |compile| switch (term) {
394 .Exited => {
395 // Note that the exit code may be 0 in this case due to the
396 // compiler server protocol.
397 if (compile.expect_errors.len != 0 and s.result_error_bundle.errorMessageCount() > 0) {
398 return error.NeedCompileErrorCheck;
399 }
400 },
401 else => {},
402 };
403
404 try handleChildProcessTerm(s, term, null, argv);
405
406 if (s.result_error_bundle.errorMessageCount() > 0) {
407 return s.fail("the following command failed with {d} compilation errors:\n{s}", .{
408 s.result_error_bundle.errorMessageCount(),
409 try allocPrintCmd(arena, null, argv),
410 });
411 }
412
413 return result orelse return s.fail(
414 "the following command failed to communicate the compilation result:\n{s}",
415 .{try allocPrintCmd(arena, null, argv)},
416 );
417}
418
419fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
420 const header: std.zig.Client.Message.Header = .{
421 .tag = tag,
422 .bytes_len = 0,
423 };
424 try file.writeAll(std.mem.asBytes(&header));
425}
426
427pub fn handleVerbose(
428 b: *Build,
429 opt_cwd: ?[]const u8,
430 argv: []const []const u8,
431) error{OutOfMemory}!void {
432 return handleVerbose2(b, opt_cwd, null, argv);
433}
434
435pub fn handleVerbose2(
436 b: *Build,
437 opt_cwd: ?[]const u8,
438 opt_env: ?*const std.process.EnvMap,
439 argv: []const []const u8,
440) error{OutOfMemory}!void {
441 if (b.verbose) {
442 // Intention of verbose is to print all sub-process command lines to
443 // stderr before spawning them.
444 const text = try allocPrintCmd2(b.allocator, opt_cwd, opt_env, argv);
445 std.debug.print("{s}\n", .{text});
446 }
447}
448
449pub inline fn handleChildProcUnsupported(
450 s: *Step,
451 opt_cwd: ?[]const u8,
452 argv: []const []const u8,
453) error{ OutOfMemory, MakeFailed }!void {
454 if (!std.process.can_spawn) {
455 return s.fail(
456 "unable to execute the following command: host cannot spawn child processes\n{s}",
457 .{try allocPrintCmd(s.owner.allocator, opt_cwd, argv)},
458 );
459 }
460}
461
462pub fn handleChildProcessTerm(
463 s: *Step,
464 term: std.ChildProcess.Term,
465 opt_cwd: ?[]const u8,
466 argv: []const []const u8,
467) error{ MakeFailed, OutOfMemory }!void {
468 const arena = s.owner.allocator;
469 switch (term) {
470 .Exited => |code| {
471 if (code != 0) {
472 return s.fail(
473 "the following command exited with error code {d}:\n{s}",
474 .{ code, try allocPrintCmd(arena, opt_cwd, argv) },
475 );
476 }
477 },
478 .Signal, .Stopped, .Unknown => {
479 return s.fail(
480 "the following command terminated unexpectedly:\n{s}",
481 .{try allocPrintCmd(arena, opt_cwd, argv)},
482 );
483 },
484 }
485}
486
487pub fn allocPrintCmd(
488 arena: Allocator,
489 opt_cwd: ?[]const u8,
490 argv: []const []const u8,
491) Allocator.Error![]u8 {
492 return allocPrintCmd2(arena, opt_cwd, null, argv);
493}
494
495pub fn allocPrintCmd2(
496 arena: Allocator,
497 opt_cwd: ?[]const u8,
498 opt_env: ?*const std.process.EnvMap,
499 argv: []const []const u8,
500) Allocator.Error![]u8 {
501 var buf: std.ArrayListUnmanaged(u8) = .{};
502 if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd});
503 if (opt_env) |env| {
504 const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena);
505 var it = env.iterator();
506 while (it.next()) |entry| {
507 const key = entry.key_ptr.*;
508 const value = entry.value_ptr.*;
509 if (process_env_map.get(key)) |process_value| {
510 if (std.mem.eql(u8, value, process_value)) continue;
511 }
512 try buf.writer(arena).print("{s}={s} ", .{ key, value });
513 }
514 }
515 for (argv) |arg| {
516 try buf.writer(arena).print("{s} ", .{arg});
517 }
518 return buf.toOwnedSlice(arena);
519}
520
521pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool {
522 s.result_cached = man.hit() catch |err| return failWithCacheError(s, man, err);
523 return s.result_cached;
524}
525
526fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror {
527 const i = man.failed_file_index orelse return err;
528 const pp = man.files.items[i].prefixed_path orelse return err;
529 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
530 return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path });
531}
532
533pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void {
534 if (s.test_results.isSuccess()) {
535 man.writeManifest() catch |err| {
536 try s.addError("unable to write cache manifest: {s}", .{@errorName(err)});
537 };
538 }
539}
lib/std/Build/TranslateCStep.zig+30-22
...@@ -11,7 +11,6 @@ const TranslateCStep = @This();...@@ -11,7 +11,6 @@ const TranslateCStep = @This();
11pub const base_id = .translate_c;11pub const base_id = .translate_c;
1212
13step: Step,13step: Step,
14builder: *std.Build,
15source: std.Build.FileSource,14source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),15include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),16c_macros: std.ArrayList([]const u8),
...@@ -26,15 +25,19 @@ pub const Options = struct {...@@ -26,15 +25,19 @@ pub const Options = struct {
26 optimize: std.builtin.OptimizeMode,25 optimize: std.builtin.OptimizeMode,
27};26};
2827
29pub fn create(builder: *std.Build, options: Options) *TranslateCStep {28pub fn create(owner: *std.Build, options: Options) *TranslateCStep {
30 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");29 const self = owner.allocator.create(TranslateCStep) catch @panic("OOM");
31 const source = options.source_file.dupe(builder);30 const source = options.source_file.dupe(owner);
32 self.* = TranslateCStep{31 self.* = TranslateCStep{
33 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),32 .step = Step.init(.{
34 .builder = builder,33 .id = .translate_c,
34 .name = "translate-c",
35 .owner = owner,
36 .makeFn = make,
37 }),
35 .source = source,38 .source = source,
36 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),39 .include_dirs = std.ArrayList([]const u8).init(owner.allocator),
37 .c_macros = std.ArrayList([]const u8).init(builder.allocator),40 .c_macros = std.ArrayList([]const u8).init(owner.allocator),
38 .out_basename = undefined,41 .out_basename = undefined,
39 .target = options.target,42 .target = options.target,
40 .optimize = options.optimize,43 .optimize = options.optimize,
...@@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct {...@@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct {
5457
55/// Creates a step to build an executable from the translated source.58/// Creates a step to build an executable from the translated source.
56pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {59pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
57 return self.builder.addExecutable(.{60 return self.step.owner.addExecutable(.{
58 .root_source_file = .{ .generated = &self.output_file },61 .root_source_file = .{ .generated = &self.output_file },
59 .name = options.name orelse "translated_c",62 .name = options.name orelse "translated_c",
60 .version = options.version,63 .version = options.version,
...@@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp...@@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp
65}68}
6669
67pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {70pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
68 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");71 self.include_dirs.append(self.step.owner.dupePath(include_dir)) catch @panic("OOM");
69}72}
7073
71pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {74pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
72 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));75 return CheckFileStep.create(
76 self.step.owner,
77 .{ .generated = &self.output_file },
78 .{ .expected_matches = expected_matches },
79 );
73}80}
7481
75/// If the value is omitted, it is set to 1.82/// If the value is omitted, it is set to 1.
76/// `name` and `value` need not live longer than the function call.83/// `name` and `value` need not live longer than the function call.
77pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {84pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
78 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);85 const macro = std.Build.constructCMacro(self.step.owner.allocator, name, value);
79 self.c_macros.append(macro) catch @panic("OOM");86 self.c_macros.append(macro) catch @panic("OOM");
80}87}
8188
82/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.89/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
83pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {90pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
84 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");91 self.c_macros.append(self.step.owner.dupe(name_and_value)) catch @panic("OOM");
85}92}
8693
87fn make(step: *Step) !void {94fn make(step: *Step, prog_node: *std.Progress.Node) !void {
95 const b = step.owner;
88 const self = @fieldParentPtr(TranslateCStep, "step", step);96 const self = @fieldParentPtr(TranslateCStep, "step", step);
8997
90 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);98 var argv_list = std.ArrayList([]const u8).init(b.allocator);
91 try argv_list.append(self.builder.zig_exe);99 try argv_list.append(b.zig_exe);
92 try argv_list.append("translate-c");100 try argv_list.append("translate-c");
93 try argv_list.append("-lc");101 try argv_list.append("-lc");
94102
95 try argv_list.append("--enable-cache");103 try argv_list.append("--enable-cache");
104 try argv_list.append("--listen=-");
96105
97 if (!self.target.isNative()) {106 if (!self.target.isNative()) {
98 try argv_list.append("-target");107 try argv_list.append("-target");
99 try argv_list.append(try self.target.zigTriple(self.builder.allocator));108 try argv_list.append(try self.target.zigTriple(b.allocator));
100 }109 }
101110
102 switch (self.optimize) {111 switch (self.optimize) {
103 .Debug => {}, // Skip since it's the default.112 .Debug => {}, // Skip since it's the default.
104 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),113 else => try argv_list.append(b.fmt("-O{s}", .{@tagName(self.optimize)})),
105 }114 }
106115
107 for (self.include_dirs.items) |include_dir| {116 for (self.include_dirs.items) |include_dir| {
...@@ -114,16 +123,15 @@ fn make(step: *Step) !void {...@@ -114,16 +123,15 @@ fn make(step: *Step) !void {
114 try argv_list.append(c_macro);123 try argv_list.append(c_macro);
115 }124 }
116125
117 try argv_list.append(self.source.getPath(self.builder));126 try argv_list.append(self.source.getPath(b));
118127
119 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);128 const output_path = try step.evalZigProcess(argv_list.items, prog_node);
120 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
121129
122 self.out_basename = fs.path.basename(output_path);130 self.out_basename = fs.path.basename(output_path);
123 const output_dir = fs.path.dirname(output_path).?;131 const output_dir = fs.path.dirname(output_path).?;
124132
125 self.output_file.path = try fs.path.join(133 self.output_file.path = try fs.path.join(
126 self.builder.allocator,134 b.allocator,
127 &[_][]const u8{ output_dir, self.out_basename },135 &[_][]const u8{ output_dir, self.out_basename },
128 );136 );
129}137}
lib/std/Build/WriteFileStep.zig+145-67
...@@ -10,11 +10,11 @@...@@ -10,11 +10,11 @@
10//! control.10//! control.
1111
12step: Step,12step: Step,
13builder: *std.Build,
14/// The elements here are pointers because we need stable pointers for the13/// The elements here are pointers because we need stable pointers for the
15/// GeneratedFile field.14/// GeneratedFile field.
16files: std.ArrayListUnmanaged(*File),15files: std.ArrayListUnmanaged(*File),
17output_source_files: std.ArrayListUnmanaged(OutputSourceFile),16output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
17generated_directory: std.Build.GeneratedFile,
1818
19pub const base_id = .write_file;19pub const base_id = .write_file;
2020
...@@ -34,24 +34,34 @@ pub const Contents = union(enum) {...@@ -34,24 +34,34 @@ pub const Contents = union(enum) {
34 copy: std.Build.FileSource,34 copy: std.Build.FileSource,
35};35};
3636
37pub fn init(builder: *std.Build) WriteFileStep {37pub fn create(owner: *std.Build) *WriteFileStep {
38 return .{38 const wf = owner.allocator.create(WriteFileStep) catch @panic("OOM");
39 .builder = builder,39 wf.* = .{
40 .step = Step.init(.write_file, "writefile", builder.allocator, make),40 .step = Step.init(.{
41 .id = .write_file,
42 .name = "WriteFile",
43 .owner = owner,
44 .makeFn = make,
45 }),
41 .files = .{},46 .files = .{},
42 .output_source_files = .{},47 .output_source_files = .{},
48 .generated_directory = .{ .step = &wf.step },
43 };49 };
50 return wf;
44}51}
4552
46pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {53pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
47 const gpa = wf.builder.allocator;54 const b = wf.step.owner;
55 const gpa = b.allocator;
48 const file = gpa.create(File) catch @panic("OOM");56 const file = gpa.create(File) catch @panic("OOM");
49 file.* = .{57 file.* = .{
50 .generated_file = .{ .step = &wf.step },58 .generated_file = .{ .step = &wf.step },
51 .sub_path = wf.builder.dupePath(sub_path),59 .sub_path = b.dupePath(sub_path),
52 .contents = .{ .bytes = wf.builder.dupe(bytes) },60 .contents = .{ .bytes = b.dupe(bytes) },
53 };61 };
54 wf.files.append(gpa, file) catch @panic("OOM");62 wf.files.append(gpa, file) catch @panic("OOM");
63
64 wf.maybeUpdateName();
55}65}
5666
57/// Place the file into the generated directory within the local cache,67/// Place the file into the generated directory within the local cache,
...@@ -62,14 +72,18 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {...@@ -62,14 +72,18 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
62/// required sub-path exists.72/// required sub-path exists.
63/// This is the option expected to be used most commonly with `addCopyFile`.73/// This is the option expected to be used most commonly with `addCopyFile`.
64pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {74pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {
65 const gpa = wf.builder.allocator;75 const b = wf.step.owner;
76 const gpa = b.allocator;
66 const file = gpa.create(File) catch @panic("OOM");77 const file = gpa.create(File) catch @panic("OOM");
67 file.* = .{78 file.* = .{
68 .generated_file = .{ .step = &wf.step },79 .generated_file = .{ .step = &wf.step },
69 .sub_path = wf.builder.dupePath(sub_path),80 .sub_path = b.dupePath(sub_path),
70 .contents = .{ .copy = source },81 .contents = .{ .copy = source },
71 };82 };
72 wf.files.append(gpa, file) catch @panic("OOM");83 wf.files.append(gpa, file) catch @panic("OOM");
84
85 wf.maybeUpdateName();
86 source.addStepDependencies(&wf.step);
73}87}
7488
75/// A path relative to the package root.89/// A path relative to the package root.
...@@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [...@@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [
79/// those changes to version control.93/// those changes to version control.
80/// A file added this way is not available with `getFileSource`.94/// A file added this way is not available with `getFileSource`.
81pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {95pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {
82 wf.output_source_files.append(wf.builder.allocator, .{96 const b = wf.step.owner;
97 wf.output_source_files.append(b.allocator, .{
83 .contents = .{ .copy = source },98 .contents = .{ .copy = source },
84 .sub_path = sub_path,99 .sub_path = sub_path,
85 }) catch @panic("OOM");100 }) catch @panic("OOM");
101 source.addStepDependencies(&wf.step);
102}
103
104/// A path relative to the package root.
105/// Be careful with this because it updates source files. This should not be
106/// used as part of the normal build process, but as a utility occasionally
107/// run by a developer with intent to modify source files and then commit
108/// those changes to version control.
109/// A file added this way is not available with `getFileSource`.
110pub fn addBytesToSource(wf: *WriteFileStep, bytes: []const u8, sub_path: []const u8) void {
111 const b = wf.step.owner;
112 wf.output_source_files.append(b.allocator, .{
113 .contents = .{ .bytes = bytes },
114 .sub_path = sub_path,
115 }) catch @panic("OOM");
86}116}
87117
88/// Gets a file source for the given sub_path. If the file does not exist, returns `null`.118/// Gets a file source for the given sub_path. If the file does not exist, returns `null`.
...@@ -95,21 +125,63 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo...@@ -95,21 +125,63 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo
95 return null;125 return null;
96}126}
97127
98fn make(step: *Step) !void {128/// Returns a `FileSource` representing the base directory that contains all the
129/// files from this `WriteFileStep`.
130pub fn getDirectorySource(wf: *WriteFileStep) std.Build.FileSource {
131 return .{ .generated = &wf.generated_directory };
132}
133
134fn maybeUpdateName(wf: *WriteFileStep) void {
135 if (wf.files.items.len == 1) {
136 // First time adding a file; update name.
137 if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
138 wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});
139 }
140 }
141}
142
143fn make(step: *Step, prog_node: *std.Progress.Node) !void {
144 _ = prog_node;
145 const b = step.owner;
99 const wf = @fieldParentPtr(WriteFileStep, "step", step);146 const wf = @fieldParentPtr(WriteFileStep, "step", step);
100147
101 // Writing to source files is kind of an extra capability of this148 // Writing to source files is kind of an extra capability of this
102 // WriteFileStep - arguably it should be a different step. But anyway here149 // WriteFileStep - arguably it should be a different step. But anyway here
103 // it is, it happens unconditionally and does not interact with the other150 // it is, it happens unconditionally and does not interact with the other
104 // files here.151 // files here.
152 var any_miss = false;
105 for (wf.output_source_files.items) |output_source_file| {153 for (wf.output_source_files.items) |output_source_file| {
106 const basename = fs.path.basename(output_source_file.sub_path);
107 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {154 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
108 var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{});155 b.build_root.handle.makePath(dirname) catch |err| {
109 defer dir.close();156 return step.fail("unable to make path '{}{s}': {s}", .{
110 try writeFile(wf, dir, output_source_file.contents, basename);157 b.build_root, dirname, @errorName(err),
111 } else {158 });
112 try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename);159 };
160 }
161 switch (output_source_file.contents) {
162 .bytes => |bytes| {
163 b.build_root.handle.writeFile(output_source_file.sub_path, bytes) catch |err| {
164 return step.fail("unable to write file '{}{s}': {s}", .{
165 b.build_root, output_source_file.sub_path, @errorName(err),
166 });
167 };
168 any_miss = true;
169 },
170 .copy => |file_source| {
171 const source_path = file_source.getPath(b);
172 const prev_status = fs.Dir.updateFile(
173 fs.cwd(),
174 source_path,
175 b.build_root.handle,
176 output_source_file.sub_path,
177 .{},
178 ) catch |err| {
179 return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{
180 source_path, b.build_root, output_source_file.sub_path, @errorName(err),
181 });
182 };
183 any_miss = any_miss or prev_status == .stale;
184 },
113 }185 }
114 }186 }
115187
...@@ -120,7 +192,7 @@ fn make(step: *Step) !void {...@@ -120,7 +192,7 @@ fn make(step: *Step) !void {
120 // If, for example, a hard-coded path was used as the location to put WriteFileStep192 // If, for example, a hard-coded path was used as the location to put WriteFileStep
121 // files, then two WriteFileSteps executing in parallel might clobber each other.193 // files, then two WriteFileSteps executing in parallel might clobber each other.
122194
123 var man = wf.builder.cache.obtain();195 var man = b.cache.obtain();
124 defer man.deinit();196 defer man.deinit();
125197
126 // Random bytes to make WriteFileStep unique. Refresh this with198 // Random bytes to make WriteFileStep unique. Refresh this with
...@@ -135,76 +207,82 @@ fn make(step: *Step) !void {...@@ -135,76 +207,82 @@ fn make(step: *Step) !void {
135 man.hash.addBytes(bytes);207 man.hash.addBytes(bytes);
136 },208 },
137 .copy => |file_source| {209 .copy => |file_source| {
138 _ = try man.addFile(file_source.getPath(wf.builder), null);210 _ = try man.addFile(file_source.getPath(b), null);
139 },211 },
140 }212 }
141 }213 }
142214
143 if (man.hit() catch |err| failWithCacheError(man, err)) {215 if (try step.cacheHit(&man)) {
144 // Cache hit, skip writing file data.
145 const digest = man.final();216 const digest = man.final();
146 for (wf.files.items) |file| {217 for (wf.files.items) |file| {
147 file.generated_file.path = try wf.builder.cache_root.join(218 file.generated_file.path = try b.cache_root.join(b.allocator, &.{
148 wf.builder.allocator,219 "o", &digest, file.sub_path,
149 &.{ "o", &digest, file.sub_path },220 });
150 );
151 }221 }
222 wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest });
152 return;223 return;
153 }224 }
154225
155 const digest = man.final();226 const digest = man.final();
156 const cache_path = "o" ++ fs.path.sep_str ++ digest;227 const cache_path = "o" ++ fs.path.sep_str ++ digest;
157228
158 var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {229 wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest });
159 std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });230
160 return err;231 var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
232 return step.fail("unable to make path '{}{s}': {s}", .{
233 b.cache_root, cache_path, @errorName(err),
234 });
161 };235 };
162 defer cache_dir.close();236 defer cache_dir.close();
163237
164 for (wf.files.items) |file| {238 for (wf.files.items) |file| {
165 const basename = fs.path.basename(file.sub_path);
166 if (fs.path.dirname(file.sub_path)) |dirname| {239 if (fs.path.dirname(file.sub_path)) |dirname| {
167 var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{});240 cache_dir.makePath(dirname) catch |err| {
168 defer dir.close();241 return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{
169 try writeFile(wf, dir, file.contents, basename);242 b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err),
170 } else {243 });
171 try writeFile(wf, cache_dir, file.contents, basename);244 };
245 }
246 switch (file.contents) {
247 .bytes => |bytes| {
248 cache_dir.writeFile(file.sub_path, bytes) catch |err| {
249 return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{
250 b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err),
251 });
252 };
253 },
254 .copy => |file_source| {
255 const source_path = file_source.getPath(b);
256 const prev_status = fs.Dir.updateFile(
257 fs.cwd(),
258 source_path,
259 cache_dir,
260 file.sub_path,
261 .{},
262 ) catch |err| {
263 return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{
264 source_path,
265 b.cache_root,
266 cache_path,
267 fs.path.sep,
268 file.sub_path,
269 @errorName(err),
270 });
271 };
272 // At this point we already will mark the step as a cache miss.
273 // But this is kind of a partial cache hit since individual
274 // file copies may be avoided. Oh well, this information is
275 // discarded.
276 _ = prev_status;
277 },
172 }278 }
173279
174 file.generated_file.path = try wf.builder.cache_root.join(280 file.generated_file.path = try b.cache_root.join(b.allocator, &.{
175 wf.builder.allocator,281 cache_path, file.sub_path,
176 &.{ cache_path, file.sub_path },282 });
177 );
178 }
179
180 try man.writeManifest();
181}
182
183fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void {
184 // TODO after landing concurrency PR, improve error reporting here
185 switch (contents) {
186 .bytes => |bytes| return dir.writeFile(basename, bytes),
187 .copy => |file_source| {
188 const source_path = file_source.getPath(wf.builder);
189 const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{});
190 _ = prev_status; // TODO logging (affected by open PR regarding concurrency)
191 },
192 }283 }
193}
194
195/// TODO consolidate this with the same function in RunStep?
196/// Also properly deal with concurrency (see open PR)
197fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
198 const i = man.failed_file_index orelse failWithSimpleError(err);
199 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
200 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
201 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
202 std.process.exit(1);
203}
204284
205fn failWithSimpleError(err: anyerror) noreturn {285 try step.writeManifest(&man);
206 std.debug.print("{s}\n", .{@errorName(err)});
207 std.process.exit(1);
208}286}
209287
210const std = @import("../std.zig");288const std = @import("../std.zig");
lib/std/Progress.zig+72-26
...@@ -126,6 +126,21 @@ pub const Node = struct {...@@ -126,6 +126,21 @@ pub const Node = struct {
126 }126 }
127 }127 }
128128
129 /// Thread-safe.
130 pub fn setName(self: *Node, name: []const u8) void {
131 const progress = self.context;
132 progress.update_mutex.lock();
133 defer progress.update_mutex.unlock();
134 self.name = name;
135 if (self.parent) |parent| {
136 @atomicStore(?*Node, &parent.recently_updated_child, self, .Release);
137 if (parent.parent) |grand_parent| {
138 @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .Release);
139 }
140 if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer);
141 }
142 }
143
129 /// Thread-safe. 0 means unknown.144 /// Thread-safe. 0 means unknown.
130 pub fn setEstimatedTotalItems(self: *Node, count: usize) void {145 pub fn setEstimatedTotalItems(self: *Node, count: usize) void {
131 @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic);146 @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic);
...@@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void {...@@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void {
174 if (self.timer) |*timer| {189 if (self.timer) |*timer| {
175 if (!self.update_mutex.tryLock()) return;190 if (!self.update_mutex.tryLock()) return;
176 defer self.update_mutex.unlock();191 defer self.update_mutex.unlock();
177 const now = timer.read();192 maybeRefreshWithHeldLock(self, timer);
178 if (now < self.initial_delay_ns) return;
179 // TODO I have observed this to happen sometimes. I think we need to follow Rust's
180 // lead and guarantee monotonically increasing times in the std lib itself.
181 if (now < self.prev_refresh_timestamp) return;
182 if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
183 return self.refreshWithHeldLock();
184 }193 }
185}194}
186195
196fn maybeRefreshWithHeldLock(self: *Progress, timer: *std.time.Timer) void {
197 const now = timer.read();
198 if (now < self.initial_delay_ns) return;
199 // TODO I have observed this to happen sometimes. I think we need to follow Rust's
200 // lead and guarantee monotonically increasing times in the std lib itself.
201 if (now < self.prev_refresh_timestamp) return;
202 if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
203 return self.refreshWithHeldLock();
204}
205
187/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.206/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.
188pub fn refresh(self: *Progress) void {207pub fn refresh(self: *Progress) void {
189 if (!self.update_mutex.tryLock()) return;208 if (!self.update_mutex.tryLock()) return;
...@@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void {...@@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void {
192 return self.refreshWithHeldLock();211 return self.refreshWithHeldLock();
193}212}
194213
195fn refreshWithHeldLock(self: *Progress) void {214fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void {
196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;215 const file = p.terminal orelse return;
197 if (is_dumb and self.dont_print_on_dumb) return;216 var end = end_ptr.*;
198217 if (p.columns_written > 0) {
199 const file = self.terminal orelse return;
200
201 var end: usize = 0;
202 if (self.columns_written > 0) {
203 // restore the cursor position by moving the cursor218 // restore the cursor position by moving the cursor
204 // `columns_written` cells to the left, then clear the rest of the219 // `columns_written` cells to the left, then clear the rest of the
205 // line220 // line
206 if (self.supports_ansi_escape_codes) {221 if (p.supports_ansi_escape_codes) {
207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;222 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len;
208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;223 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
209 } else if (builtin.os.tag == .windows) winapi: {224 } else if (builtin.os.tag == .windows) winapi: {
210 std.debug.assert(self.is_windows_terminal);225 std.debug.assert(p.is_windows_terminal);
211226
212 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;227 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
213 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {228 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
214 // stop trying to write to this file229 // stop trying to write to this file
215 self.terminal = null;230 p.terminal = null;
216 break :winapi;231 break :winapi;
217 }232 }
218233
219 var cursor_pos = windows.COORD{234 var cursor_pos = windows.COORD{
220 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),235 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written),
221 .Y = info.dwCursorPosition.Y,236 .Y = info.dwCursorPosition.Y,
222 };237 };
223238
...@@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void {
235 &written,250 &written,
236 ) != windows.TRUE) {251 ) != windows.TRUE) {
237 // stop trying to write to this file252 // stop trying to write to this file
238 self.terminal = null;253 p.terminal = null;
239 break :winapi;254 break :winapi;
240 }255 }
241 if (windows.kernel32.FillConsoleOutputCharacterW(256 if (windows.kernel32.FillConsoleOutputCharacterW(
...@@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void {
246 &written,261 &written,
247 ) != windows.TRUE) {262 ) != windows.TRUE) {
248 // stop trying to write to this file263 // stop trying to write to this file
249 self.terminal = null;264 p.terminal = null;
250 break :winapi;265 break :winapi;
251 }266 }
252 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {267 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {
253 // stop trying to write to this file268 // stop trying to write to this file
254 self.terminal = null;269 p.terminal = null;
255 break :winapi;270 break :winapi;
256 }271 }
257 } else {272 } else {
258 // we are in a "dumb" terminal like in acme or writing to a file273 // we are in a "dumb" terminal like in acme or writing to a file
259 self.output_buffer[end] = '\n';274 p.output_buffer[end] = '\n';
260 end += 1;275 end += 1;
261 }276 }
262277
263 self.columns_written = 0;278 p.columns_written = 0;
264 }279 }
280 end_ptr.* = end;
281}
282
283fn refreshWithHeldLock(self: *Progress) void {
284 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
285 if (is_dumb and self.dont_print_on_dumb) return;
286
287 const file = self.terminal orelse return;
288
289 var end: usize = 0;
290 clearWithHeldLock(self, &end);
265291
266 if (!self.done) {292 if (!self.done) {
267 var need_ellipse = false;293 var need_ellipse = false;
...@@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {...@@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
318 self.columns_written = 0;344 self.columns_written = 0;
319}345}
320346
347/// Allows the caller to freely write to stderr until unlock_stderr() is called.
348/// During the lock, the progress information is cleared from the terminal.
349pub fn lock_stderr(p: *Progress) void {
350 p.update_mutex.lock();
351 if (p.terminal) |file| {
352 var end: usize = 0;
353 clearWithHeldLock(p, &end);
354 _ = file.write(p.output_buffer[0..end]) catch {
355 // stop trying to write to this file
356 p.terminal = null;
357 };
358 }
359 std.debug.getStderrMutex().lock();
360}
361
362pub fn unlock_stderr(p: *Progress) void {
363 std.debug.getStderrMutex().unlock();
364 p.update_mutex.unlock();
365}
366
321fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {367fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
322 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {368 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
323 const amt = written.len;369 const amt = written.len;
lib/std/Thread.zig+5
...@@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig");...@@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig");
16pub const Semaphore = @import("Thread/Semaphore.zig");16pub const Semaphore = @import("Thread/Semaphore.zig");
17pub const Condition = @import("Thread/Condition.zig");17pub const Condition = @import("Thread/Condition.zig");
18pub const RwLock = @import("Thread/RwLock.zig");18pub const RwLock = @import("Thread/RwLock.zig");
19pub const Pool = @import("Thread/Pool.zig");
20pub const WaitGroup = @import("Thread/WaitGroup.zig");
1921
20pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc;22pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc;
21const is_gnu = target.abi.isGnu();23const is_gnu = target.abi.isGnu();
...@@ -945,6 +947,7 @@ const LinuxThreadImpl = struct {...@@ -945,6 +947,7 @@ const LinuxThreadImpl = struct {
945947
946 // map all memory needed without read/write permissions948 // map all memory needed without read/write permissions
947 // to avoid committing the whole region right away949 // to avoid committing the whole region right away
950 // anonymous mapping ensures file descriptor limits are not exceeded
948 const mapped = os.mmap(951 const mapped = os.mmap(
949 null,952 null,
950 map_bytes,953 map_bytes,
...@@ -956,6 +959,8 @@ const LinuxThreadImpl = struct {...@@ -956,6 +959,8 @@ const LinuxThreadImpl = struct {
956 error.MemoryMappingNotSupported => unreachable,959 error.MemoryMappingNotSupported => unreachable,
957 error.AccessDenied => unreachable,960 error.AccessDenied => unreachable,
958 error.PermissionDenied => unreachable,961 error.PermissionDenied => unreachable,
962 error.ProcessFdQuotaExceeded => unreachable,
963 error.SystemFdQuotaExceeded => unreachable,
959 else => |e| return e,964 else => |e| return e,
960 };965 };
961 assert(mapped.len >= map_bytes);966 assert(mapped.len >= map_bytes);
lib/std/Thread/Pool.zig created+159
...@@ -0,0 +1,159 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Pool = @This();
4const WaitGroup = @import("WaitGroup.zig");
5
6mutex: std.Thread.Mutex = .{},
7cond: std.Thread.Condition = .{},
8run_queue: RunQueue = .{},
9is_running: bool = true,
10allocator: std.mem.Allocator,
11threads: []std.Thread,
12
13const RunQueue = std.SinglyLinkedList(Runnable);
14const Runnable = struct {
15 runFn: RunProto,
16};
17
18const RunProto = *const fn (*Runnable) void;
19
20pub const Options = struct {
21 allocator: std.mem.Allocator,
22 n_jobs: ?u32 = null,
23};
24
25pub fn init(pool: *Pool, options: Options) !void {
26 const allocator = options.allocator;
27
28 pool.* = .{
29 .allocator = allocator,
30 .threads = &[_]std.Thread{},
31 };
32
33 if (builtin.single_threaded) {
34 return;
35 }
36
37 const thread_count = options.n_jobs orelse @max(1, std.Thread.getCpuCount() catch 1);
38 pool.threads = try allocator.alloc(std.Thread, thread_count);
39 errdefer allocator.free(pool.threads);
40
41 // kill and join any threads we spawned previously on error.
42 var spawned: usize = 0;
43 errdefer pool.join(spawned);
44
45 for (pool.threads) |*thread| {
46 thread.* = try std.Thread.spawn(.{}, worker, .{pool});
47 spawned += 1;
48 }
49}
50
51pub fn deinit(pool: *Pool) void {
52 pool.join(pool.threads.len); // kill and join all threads.
53 pool.* = undefined;
54}
55
56fn join(pool: *Pool, spawned: usize) void {
57 if (builtin.single_threaded) {
58 return;
59 }
60
61 {
62 pool.mutex.lock();
63 defer pool.mutex.unlock();
64
65 // ensure future worker threads exit the dequeue loop
66 pool.is_running = false;
67 }
68
69 // wake up any sleeping threads (this can be done outside the mutex)
70 // then wait for all the threads we know are spawned to complete.
71 pool.cond.broadcast();
72 for (pool.threads[0..spawned]) |thread| {
73 thread.join();
74 }
75
76 pool.allocator.free(pool.threads);
77}
78
79pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void {
80 if (builtin.single_threaded) {
81 @call(.auto, func, args);
82 return;
83 }
84
85 const Args = @TypeOf(args);
86 const Closure = struct {
87 arguments: Args,
88 pool: *Pool,
89 run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } },
90
91 fn runFn(runnable: *Runnable) void {
92 const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable);
93 const closure = @fieldParentPtr(@This(), "run_node", run_node);
94 @call(.auto, func, closure.arguments);
95
96 // The thread pool's allocator is protected by the mutex.
97 const mutex = &closure.pool.mutex;
98 mutex.lock();
99 defer mutex.unlock();
100
101 closure.pool.allocator.destroy(closure);
102 }
103 };
104
105 {
106 pool.mutex.lock();
107 defer pool.mutex.unlock();
108
109 const closure = try pool.allocator.create(Closure);
110 closure.* = .{
111 .arguments = args,
112 .pool = pool,
113 };
114
115 pool.run_queue.prepend(&closure.run_node);
116 }
117
118 // Notify waiting threads outside the lock to try and keep the critical section small.
119 pool.cond.signal();
120}
121
122fn worker(pool: *Pool) void {
123 pool.mutex.lock();
124 defer pool.mutex.unlock();
125
126 while (true) {
127 while (pool.run_queue.popFirst()) |run_node| {
128 // Temporarily unlock the mutex in order to execute the run_node
129 pool.mutex.unlock();
130 defer pool.mutex.lock();
131
132 const runFn = run_node.data.runFn;
133 runFn(&run_node.data);
134 }
135
136 // Stop executing instead of waiting if the thread pool is no longer running.
137 if (pool.is_running) {
138 pool.cond.wait(&pool.mutex);
139 } else {
140 break;
141 }
142 }
143}
144
145pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void {
146 while (!wait_group.isDone()) {
147 if (blk: {
148 pool.mutex.lock();
149 defer pool.mutex.unlock();
150 break :blk pool.run_queue.popFirst();
151 }) |run_node| {
152 run_node.data.runFn(&run_node.data);
153 continue;
154 }
155
156 wait_group.wait();
157 return;
158 }
159}
lib/std/Thread/WaitGroup.zig created+46
...@@ -0,0 +1,46 @@
1const std = @import("std");
2const Atomic = std.atomic.Atomic;
3const assert = std.debug.assert;
4const WaitGroup = @This();
5
6const is_waiting: usize = 1 << 0;
7const one_pending: usize = 1 << 1;
8
9state: Atomic(usize) = Atomic(usize).init(0),
10event: std.Thread.ResetEvent = .{},
11
12pub fn start(self: *WaitGroup) void {
13 const state = self.state.fetchAdd(one_pending, .Monotonic);
14 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
15}
16
17pub fn finish(self: *WaitGroup) void {
18 const state = self.state.fetchSub(one_pending, .Release);
19 assert((state / one_pending) > 0);
20
21 if (state == (one_pending | is_waiting)) {
22 self.state.fence(.Acquire);
23 self.event.set();
24 }
25}
26
27pub fn wait(self: *WaitGroup) void {
28 var state = self.state.fetchAdd(is_waiting, .Acquire);
29 assert(state & is_waiting == 0);
30
31 if ((state / one_pending) > 0) {
32 self.event.wait();
33 }
34}
35
36pub fn reset(self: *WaitGroup) void {
37 self.state.store(0, .Monotonic);
38 self.event.reset();
39}
40
41pub fn isDone(wg: *WaitGroup) bool {
42 const state = wg.state.load(.Acquire);
43 assert(state & is_waiting == 0);
44
45 return (state / one_pending) == 0;
46}
lib/std/Uri.zig+96-13
...@@ -16,15 +16,27 @@ fragment: ?[]const u8,...@@ -16,15 +16,27 @@ fragment: ?[]const u8,
1616
17/// Applies URI encoding and replaces all reserved characters with their respective %XX code.17/// Applies URI encoding and replaces all reserved characters with their respective %XX code.
18pub fn escapeString(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 {18pub fn escapeString(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 {
19 return escapeStringWithFn(allocator, input, isUnreserved);
20}
21
22pub fn escapePath(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 {
23 return escapeStringWithFn(allocator, input, isPathChar);
24}
25
26pub fn escapeQuery(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 {
27 return escapeStringWithFn(allocator, input, isQueryChar);
28}
29
30pub fn escapeStringWithFn(allocator: std.mem.Allocator, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) std.mem.Allocator.Error![]const u8 {
19 var outsize: usize = 0;31 var outsize: usize = 0;
20 for (input) |c| {32 for (input) |c| {
21 outsize += if (isUnreserved(c)) @as(usize, 1) else 3;33 outsize += if (keepUnescaped(c)) @as(usize, 1) else 3;
22 }34 }
23 var output = try allocator.alloc(u8, outsize);35 var output = try allocator.alloc(u8, outsize);
24 var outptr: usize = 0;36 var outptr: usize = 0;
2537
26 for (input) |c| {38 for (input) |c| {
27 if (isUnreserved(c)) {39 if (keepUnescaped(c)) {
28 output[outptr] = c;40 output[outptr] = c;
29 outptr += 1;41 outptr += 1;
30 } else {42 } else {
...@@ -94,13 +106,14 @@ pub fn unescapeString(allocator: std.mem.Allocator, input: []const u8) error{Out...@@ -94,13 +106,14 @@ pub fn unescapeString(allocator: std.mem.Allocator, input: []const u8) error{Out
94106
95pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort };107pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort };
96108
97/// Parses the URI or returns an error.109/// Parses the URI or returns an error. This function is not compliant, but is required to parse
110/// some forms of URIs in the wild. Such as HTTP Location headers.
98/// The return value will contain unescaped strings pointing into the111/// The return value will contain unescaped strings pointing into the
99/// original `text`. Each component that is provided, will be non-`null`.112/// original `text`. Each component that is provided, will be non-`null`.
100pub fn parse(text: []const u8) ParseError!Uri {113pub fn parseWithoutScheme(text: []const u8) ParseError!Uri {
101 var reader = SliceReader{ .slice = text };114 var reader = SliceReader{ .slice = text };
102 var uri = Uri{115 var uri = Uri{
103 .scheme = reader.readWhile(isSchemeChar),116 .scheme = "",
104 .user = null,117 .user = null,
105 .password = null,118 .password = null,
106 .host = null,119 .host = null,
...@@ -110,14 +123,6 @@ pub fn parse(text: []const u8) ParseError!Uri {...@@ -110,14 +123,6 @@ pub fn parse(text: []const u8) ParseError!Uri {
110 .fragment = null,123 .fragment = null,
111 };124 };
112125
113 // after the scheme, a ':' must appear
114 if (reader.get()) |c| {
115 if (c != ':')
116 return error.UnexpectedCharacter;
117 } else {
118 return error.InvalidFormat;
119 }
120
121 if (reader.peekPrefix("//")) { // authority part126 if (reader.peekPrefix("//")) { // authority part
122 std.debug.assert(reader.get().? == '/');127 std.debug.assert(reader.get().? == '/');
123 std.debug.assert(reader.get().? == '/');128 std.debug.assert(reader.get().? == '/');
...@@ -179,6 +184,76 @@ pub fn parse(text: []const u8) ParseError!Uri {...@@ -179,6 +184,76 @@ pub fn parse(text: []const u8) ParseError!Uri {
179 return uri;184 return uri;
180}185}
181186
187/// Parses the URI or returns an error.
188/// The return value will contain unescaped strings pointing into the
189/// original `text`. Each component that is provided, will be non-`null`.
190pub fn parse(text: []const u8) ParseError!Uri {
191 var reader = SliceReader{ .slice = text };
192 const scheme = reader.readWhile(isSchemeChar);
193
194 // after the scheme, a ':' must appear
195 if (reader.get()) |c| {
196 if (c != ':')
197 return error.UnexpectedCharacter;
198 } else {
199 return error.InvalidFormat;
200 }
201
202 var uri = try parseWithoutScheme(reader.readUntilEof());
203 uri.scheme = scheme;
204
205 return uri;
206}
207
208/// Resolves a URI against a base URI, conforming to RFC 3986, Section 5.
209/// arena owns any memory allocated by this function.
210pub fn resolve(Base: Uri, R: Uri, strict: bool, arena: std.mem.Allocator) !Uri {
211 var T: Uri = undefined;
212
213 if (R.scheme.len > 0 and !((!strict) and (std.mem.eql(u8, R.scheme, Base.scheme)))) {
214 T.scheme = R.scheme;
215 T.user = R.user;
216 T.host = R.host;
217 T.port = R.port;
218 T.path = try std.fs.path.resolvePosix(arena, &.{ "/", R.path });
219 T.query = R.query;
220 } else {
221 if (R.host) |host| {
222 T.user = R.user;
223 T.host = host;
224 T.port = R.port;
225 T.path = R.path;
226 T.path = try std.fs.path.resolvePosix(arena, &.{ "/", R.path });
227 T.query = R.query;
228 } else {
229 if (R.path.len == 0) {
230 T.path = Base.path;
231 if (R.query) |query| {
232 T.query = query;
233 } else {
234 T.query = Base.query;
235 }
236 } else {
237 if (R.path[0] == '/') {
238 T.path = try std.fs.path.resolvePosix(arena, &.{ "/", R.path });
239 } else {
240 T.path = try std.fs.path.resolvePosix(arena, &.{ "/", Base.path, R.path });
241 }
242 T.query = R.query;
243 }
244
245 T.user = Base.user;
246 T.host = Base.host;
247 T.port = Base.port;
248 }
249 T.scheme = Base.scheme;
250 }
251
252 T.fragment = R.fragment;
253
254 return T;
255}
256
182const SliceReader = struct {257const SliceReader = struct {
183 const Self = @This();258 const Self = @This();
184259
...@@ -284,6 +359,14 @@ fn isPathSeparator(c: u8) bool {...@@ -284,6 +359,14 @@ fn isPathSeparator(c: u8) bool {
284 };359 };
285}360}
286361
362fn isPathChar(c: u8) bool {
363 return isUnreserved(c) or isSubLimit(c) or c == '/' or c == ':' or c == '@';
364}
365
366fn isQueryChar(c: u8) bool {
367 return isPathChar(c) or c == '?';
368}
369
287fn isQuerySeparator(c: u8) bool {370fn isQuerySeparator(c: u8) bool {
288 return switch (c) {371 return switch (c) {
289 '#' => true,372 '#' => true,
lib/std/array_list.zig+26-7
...@@ -141,11 +141,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -141,11 +141,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
141 return cloned;141 return cloned;
142 }142 }
143143
144 /// Insert `item` at index `n` by moving `list[n .. list.len]` to make room.144 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
145 /// If `n` is equal to the length of the list this operation is equivalent to append.
145 /// This operation is O(N).146 /// This operation is O(N).
146 /// Invalidates pointers if additional memory is needed.147 /// Invalidates pointers if additional memory is needed.
147 pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void {148 pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void {
148 try self.ensureUnusedCapacity(1);149 try self.ensureUnusedCapacity(1);
150 self.insertAssumeCapacity(n, item);
151 }
152
153 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
154 /// If `n` is equal to the length of the list this operation is equivalent to append.
155 /// This operation is O(N).
156 /// Asserts that there is enough capacity for the new item.
157 pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void {
158 assert(self.items.len < self.capacity);
149 self.items.len += 1;159 self.items.len += 1;
150160
151 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);161 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
...@@ -609,12 +619,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -609,12 +619,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
609 return cloned;619 return cloned;
610 }620 }
611621
612 /// Insert `item` at index `n`. Moves `list[n .. list.len]`622 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
613 /// to higher indices to make room.623 /// If `n` is equal to the length of the list this operation is equivalent to append.
614 /// This operation is O(N).624 /// This operation is O(N).
615 /// Invalidates pointers if additional memory is needed.625 /// Invalidates pointers if additional memory is needed.
616 pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void {626 pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void {
617 try self.ensureUnusedCapacity(allocator, 1);627 try self.ensureUnusedCapacity(allocator, 1);
628 self.insertAssumeCapacity(n, item);
629 }
630
631 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
632 /// If `n` is equal to the length of the list this operation is equivalent to append.
633 /// This operation is O(N).
634 /// Asserts that there is enough capacity for the new item.
635 pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void {
636 assert(self.items.len < self.capacity);
618 self.items.len += 1;637 self.items.len += 1;
619638
620 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);639 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
...@@ -1309,9 +1328,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" {...@@ -1309,9 +1328,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" {
1309 var list = ArrayList(i32).init(a);1328 var list = ArrayList(i32).init(a);
1310 defer list.deinit();1329 defer list.deinit();
13111330
1312 try list.append(1);1331 try list.insert(0, 1);
1313 try list.append(2);1332 try list.append(2);
1314 try list.append(3);1333 try list.insert(2, 3);
1315 try list.insert(0, 5);1334 try list.insert(0, 5);
1316 try testing.expect(list.items[0] == 5);1335 try testing.expect(list.items[0] == 5);
1317 try testing.expect(list.items[1] == 1);1336 try testing.expect(list.items[1] == 1);
...@@ -1322,9 +1341,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" {...@@ -1322,9 +1341,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" {
1322 var list = ArrayListUnmanaged(i32){};1341 var list = ArrayListUnmanaged(i32){};
1323 defer list.deinit(a);1342 defer list.deinit(a);
13241343
1325 try list.append(a, 1);1344 try list.insert(a, 0, 1);
1326 try list.append(a, 2);1345 try list.append(a, 2);
1327 try list.append(a, 3);1346 try list.insert(a, 2, 3);
1328 try list.insert(a, 0, 5);1347 try list.insert(a, 0, 5);
1329 try testing.expect(list.items[0] == 5);1348 try testing.expect(list.items[0] == 5);
1330 try testing.expect(list.items[1] == 1);1349 try testing.expect(list.items[1] == 1);
lib/std/c.zig+2-1
...@@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n...@@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n
153pub extern "c" fn unlink(path: [*:0]const u8) c_int;153pub extern "c" fn unlink(path: [*:0]const u8) c_int;
154pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int;154pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int;
155pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;155pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
156pub extern "c" fn waitpid(pid: c.pid_t, stat_loc: ?*c_int, options: c_int) c.pid_t;156pub extern "c" fn waitpid(pid: c.pid_t, status: ?*c_int, options: c_int) c.pid_t;
157pub extern "c" fn wait4(pid: c.pid_t, status: ?*c_int, options: c_int, ru: ?*c.rusage) c.pid_t;
157pub extern "c" fn fork() c_int;158pub extern "c" fn fork() c_int;
158pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;159pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;
159pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;160pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;
lib/std/c/darwin.zig+477
...@@ -8,6 +8,7 @@ const iovec_const = std.os.iovec_const;...@@ -8,6 +8,7 @@ const iovec_const = std.os.iovec_const;
88
9pub const aarch64 = @import("darwin/aarch64.zig");9pub const aarch64 = @import("darwin/aarch64.zig");
10pub const x86_64 = @import("darwin/x86_64.zig");10pub const x86_64 = @import("darwin/x86_64.zig");
11pub const cssm = @import("darwin/cssm.zig");
1112
12const arch_bits = switch (native_arch) {13const arch_bits = switch (native_arch) {
13 .aarch64 => @import("darwin/aarch64.zig"),14 .aarch64 => @import("darwin/aarch64.zig"),
...@@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE {...@@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE {
2179 return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));2180 return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
2180}2181}
21812182
2183pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
2184 if (std.os.unexpected_error_tracing) {
2185 std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)});
2186 std.debug.dumpCurrentStackTrace(null);
2187 }
2188 return error.Unexpected;
2189}
2190
2182/// Kernel return values2191/// Kernel return values
2183pub const KernE = enum(u32) {2192pub const KernE = enum(u32) {
2184 SUCCESS = 0,2193 SUCCESS = 0,
...@@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31;...@@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31;
3085pub const caddr_t = ?[*]u8;3094pub const caddr_t = ?[*]u8;
30863095
3087pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;3096pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
3097
3098pub const MachError = error{
3099 /// Not enough permissions held to perform the requested kernel
3100 /// call.
3101 PermissionDenied,
3102} || std.os.UnexpectedError;
3103
3104pub const MachTask = extern struct {
3105 port: mach_port_name_t,
3106
3107 pub fn isValid(self: MachTask) bool {
3108 return self.port != TASK_NULL;
3109 }
3110
3111 pub fn pidForTask(self: MachTask) MachError!std.os.pid_t {
3112 var pid: std.os.pid_t = undefined;
3113 switch (getKernError(pid_for_task(self.port, &pid))) {
3114 .SUCCESS => return pid,
3115 .FAILURE => return error.PermissionDenied,
3116 else => |err| return unexpectedKernError(err),
3117 }
3118 }
3119
3120 pub fn allocatePort(self: MachTask, right: MACH_PORT_RIGHT) MachError!MachTask {
3121 var out_port: mach_port_name_t = undefined;
3122 switch (getKernError(mach_port_allocate(
3123 self.port,
3124 @enumToInt(right),
3125 &out_port,
3126 ))) {
3127 .SUCCESS => return .{ .port = out_port },
3128 .FAILURE => return error.PermissionDenied,
3129 else => |err| return unexpectedKernError(err),
3130 }
3131 }
3132
3133 pub fn deallocatePort(self: MachTask, port: MachTask) void {
3134 _ = getKernError(mach_port_deallocate(self.port, port.port));
3135 }
3136
3137 pub fn insertRight(self: MachTask, port: MachTask, msg: MACH_MSG_TYPE) !void {
3138 switch (getKernError(mach_port_insert_right(
3139 self.port,
3140 port.port,
3141 port.port,
3142 @enumToInt(msg),
3143 ))) {
3144 .SUCCESS => return,
3145 .FAILURE => return error.PermissionDenied,
3146 else => |err| return unexpectedKernError(err),
3147 }
3148 }
3149
3150 pub const PortInfo = struct {
3151 mask: exception_mask_t,
3152 masks: [EXC_TYPES_COUNT]exception_mask_t,
3153 ports: [EXC_TYPES_COUNT]mach_port_t,
3154 behaviors: [EXC_TYPES_COUNT]exception_behavior_t,
3155 flavors: [EXC_TYPES_COUNT]thread_state_flavor_t,
3156 count: mach_msg_type_number_t,
3157 };
3158
3159 pub fn getExceptionPorts(self: MachTask, mask: exception_mask_t) !PortInfo {
3160 var info = PortInfo{
3161 .mask = mask,
3162 .masks = undefined,
3163 .ports = undefined,
3164 .behaviors = undefined,
3165 .flavors = undefined,
3166 .count = 0,
3167 };
3168 info.count = info.ports.len / @sizeOf(mach_port_t);
3169
3170 switch (getKernError(task_get_exception_ports(
3171 self.port,
3172 info.mask,
3173 &info.masks,
3174 &info.count,
3175 &info.ports,
3176 &info.behaviors,
3177 &info.flavors,
3178 ))) {
3179 .SUCCESS => return info,
3180 .FAILURE => return error.PermissionDenied,
3181 else => |err| return unexpectedKernError(err),
3182 }
3183 }
3184
3185 pub fn setExceptionPorts(
3186 self: MachTask,
3187 mask: exception_mask_t,
3188 new_port: MachTask,
3189 behavior: exception_behavior_t,
3190 new_flavor: thread_state_flavor_t,
3191 ) !void {
3192 switch (getKernError(task_set_exception_ports(
3193 self.port,
3194 mask,
3195 new_port.port,
3196 behavior,
3197 new_flavor,
3198 ))) {
3199 .SUCCESS => return,
3200 .FAILURE => return error.PermissionDenied,
3201 else => |err| return unexpectedKernError(err),
3202 }
3203 }
3204
3205 pub const RegionInfo = struct {
3206 pub const Tag = enum {
3207 basic,
3208 extended,
3209 top,
3210 };
3211
3212 base_addr: u64,
3213 tag: Tag,
3214 info: union {
3215 basic: vm_region_basic_info_64,
3216 extended: vm_region_extended_info,
3217 top: vm_region_top_info,
3218 },
3219 };
3220
3221 pub fn getRegionInfo(
3222 task: MachTask,
3223 address: u64,
3224 len: usize,
3225 tag: RegionInfo.Tag,
3226 ) MachError!RegionInfo {
3227 var info: RegionInfo = .{
3228 .base_addr = address,
3229 .tag = tag,
3230 .info = undefined,
3231 };
3232 switch (tag) {
3233 .basic => info.info = .{ .basic = undefined },
3234 .extended => info.info = .{ .extended = undefined },
3235 .top => info.info = .{ .top = undefined },
3236 }
3237 var base_len: mach_vm_size_t = if (len == 1) 2 else len;
3238 var objname: mach_port_t = undefined;
3239 var count: mach_msg_type_number_t = switch (tag) {
3240 .basic => VM_REGION_BASIC_INFO_COUNT,
3241 .extended => VM_REGION_EXTENDED_INFO_COUNT,
3242 .top => VM_REGION_TOP_INFO_COUNT,
3243 };
3244 switch (getKernError(mach_vm_region(
3245 task.port,
3246 &info.base_addr,
3247 &base_len,
3248 switch (tag) {
3249 .basic => VM_REGION_BASIC_INFO_64,
3250 .extended => VM_REGION_EXTENDED_INFO,
3251 .top => VM_REGION_TOP_INFO,
3252 },
3253 switch (tag) {
3254 .basic => @ptrCast(vm_region_info_t, &info.info.basic),
3255 .extended => @ptrCast(vm_region_info_t, &info.info.extended),
3256 .top => @ptrCast(vm_region_info_t, &info.info.top),
3257 },
3258 &count,
3259 &objname,
3260 ))) {
3261 .SUCCESS => return info,
3262 .FAILURE => return error.PermissionDenied,
3263 else => |err| return unexpectedKernError(err),
3264 }
3265 }
3266
3267 pub const RegionSubmapInfo = struct {
3268 pub const Tag = enum {
3269 short,
3270 full,
3271 };
3272
3273 tag: Tag,
3274 base_addr: u64,
3275 info: union {
3276 short: vm_region_submap_short_info_64,
3277 full: vm_region_submap_info_64,
3278 },
3279 };
3280
3281 pub fn getRegionSubmapInfo(
3282 task: MachTask,
3283 address: u64,
3284 len: usize,
3285 nesting_depth: u32,
3286 tag: RegionSubmapInfo.Tag,
3287 ) MachError!RegionSubmapInfo {
3288 var info: RegionSubmapInfo = .{
3289 .base_addr = address,
3290 .tag = tag,
3291 .info = undefined,
3292 };
3293 switch (tag) {
3294 .short => info.info = .{ .short = undefined },
3295 .full => info.info = .{ .full = undefined },
3296 }
3297 var nesting = nesting_depth;
3298 var base_len: mach_vm_size_t = if (len == 1) 2 else len;
3299 var count: mach_msg_type_number_t = switch (tag) {
3300 .short => VM_REGION_SUBMAP_SHORT_INFO_COUNT_64,
3301 .full => VM_REGION_SUBMAP_INFO_COUNT_64,
3302 };
3303 switch (getKernError(mach_vm_region_recurse(
3304 task.port,
3305 &info.base_addr,
3306 &base_len,
3307 &nesting,
3308 switch (tag) {
3309 .short => @ptrCast(vm_region_recurse_info_t, &info.info.short),
3310 .full => @ptrCast(vm_region_recurse_info_t, &info.info.full),
3311 },
3312 &count,
3313 ))) {
3314 .SUCCESS => return info,
3315 .FAILURE => return error.PermissionDenied,
3316 else => |err| return unexpectedKernError(err),
3317 }
3318 }
3319
3320 pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!vm_prot_t {
3321 const info = try task.getRegionSubmapInfo(address, len, 0, .short);
3322 return info.info.short.protection;
3323 }
3324
3325 pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void {
3326 return task.setProtectionImpl(address, len, true, prot);
3327 }
3328
3329 pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void {
3330 return task.setProtectionImpl(address, len, false, prot);
3331 }
3332
3333 fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: vm_prot_t) MachError!void {
3334 switch (getKernError(mach_vm_protect(task.port, address, len, @boolToInt(set_max), prot))) {
3335 .SUCCESS => return,
3336 .FAILURE => return error.PermissionDenied,
3337 else => |err| return unexpectedKernError(err),
3338 }
3339 }
3340
3341 /// Will write to VM even if current protection attributes specifically prohibit
3342 /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY
3343 /// variant, and resetting after a successful or unsuccessful write.
3344 pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
3345 const curr_prot = try task.getCurrProtection(address, buf.len);
3346 try task.setCurrProtection(
3347 address,
3348 buf.len,
3349 PROT.READ | PROT.WRITE | PROT.COPY,
3350 );
3351 defer {
3352 task.setCurrProtection(address, buf.len, curr_prot) catch {};
3353 }
3354 return task.writeMem(address, buf, arch);
3355 }
3356
3357 pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
3358 const count = buf.len;
3359 var total_written: usize = 0;
3360 var curr_addr = address;
3361 const page_size = try getPageSize(task); // TODO we probably can assume value here
3362 var out_buf = buf[0..];
3363
3364 while (total_written < count) {
3365 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written);
3366 switch (getKernError(mach_vm_write(
3367 task.port,
3368 curr_addr,
3369 @ptrToInt(out_buf.ptr),
3370 @intCast(mach_msg_type_number_t, curr_size),
3371 ))) {
3372 .SUCCESS => {},
3373 .FAILURE => return error.PermissionDenied,
3374 else => |err| return unexpectedKernError(err),
3375 }
3376
3377 switch (arch) {
3378 .aarch64 => {
3379 var mattr_value: vm_machine_attribute_val_t = MATTR_VAL_CACHE_FLUSH;
3380 switch (getKernError(vm_machine_attribute(
3381 task.port,
3382 curr_addr,
3383 curr_size,
3384 MATTR_CACHE,
3385 &mattr_value,
3386 ))) {
3387 .SUCCESS => {},
3388 .FAILURE => return error.PermissionDenied,
3389 else => |err| return unexpectedKernError(err),
3390 }
3391 },
3392 .x86_64 => {},
3393 else => unreachable,
3394 }
3395
3396 out_buf = out_buf[curr_size..];
3397 total_written += curr_size;
3398 curr_addr += curr_size;
3399 }
3400
3401 return total_written;
3402 }
3403
3404 pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize {
3405 const count = buf.len;
3406 var total_read: usize = 0;
3407 var curr_addr = address;
3408 const page_size = try getPageSize(task); // TODO we probably can assume value here
3409 var out_buf = buf[0..];
3410
3411 while (total_read < count) {
3412 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read);
3413 var curr_bytes_read: mach_msg_type_number_t = 0;
3414 var vm_memory: vm_offset_t = undefined;
3415 switch (getKernError(mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
3416 .SUCCESS => {},
3417 .FAILURE => return error.PermissionDenied,
3418 else => |err| return unexpectedKernError(err),
3419 }
3420
3421 @memcpy(out_buf[0..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read);
3422 _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);
3423
3424 out_buf = out_buf[curr_bytes_read..];
3425 curr_addr += curr_bytes_read;
3426 total_read += curr_bytes_read;
3427 }
3428
3429 return total_read;
3430 }
3431
3432 fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize {
3433 var left = count;
3434 if (page_size > 0) {
3435 const page_offset = address % page_size;
3436 const bytes_left_in_page = page_size - page_offset;
3437 if (count > bytes_left_in_page) {
3438 left = bytes_left_in_page;
3439 }
3440 }
3441 return left;
3442 }
3443
3444 fn getPageSize(task: MachTask) MachError!usize {
3445 if (task.isValid()) {
3446 var info_count = TASK_VM_INFO_COUNT;
3447 var vm_info: task_vm_info_data_t = undefined;
3448 switch (getKernError(task_info(
3449 task.port,
3450 TASK_VM_INFO,
3451 @ptrCast(task_info_t, &vm_info),
3452 &info_count,
3453 ))) {
3454 .SUCCESS => return @intCast(usize, vm_info.page_size),
3455 else => {},
3456 }
3457 }
3458 var page_size: vm_size_t = undefined;
3459 switch (getKernError(_host_page_size(mach_host_self(), &page_size))) {
3460 .SUCCESS => return page_size,
3461 else => |err| return unexpectedKernError(err),
3462 }
3463 }
3464
3465 pub fn basicTaskInfo(task: MachTask) MachError!mach_task_basic_info {
3466 var info: mach_task_basic_info = undefined;
3467 var count = MACH_TASK_BASIC_INFO_COUNT;
3468 switch (getKernError(task_info(
3469 task.port,
3470 MACH_TASK_BASIC_INFO,
3471 @ptrCast(task_info_t, &info),
3472 &count,
3473 ))) {
3474 .SUCCESS => return info,
3475 else => |err| return unexpectedKernError(err),
3476 }
3477 }
3478
3479 pub fn @"resume"(task: MachTask) MachError!void {
3480 switch (getKernError(task_resume(task.port))) {
3481 .SUCCESS => {},
3482 else => |err| return unexpectedKernError(err),
3483 }
3484 }
3485
3486 pub fn @"suspend"(task: MachTask) MachError!void {
3487 switch (getKernError(task_suspend(task.port))) {
3488 .SUCCESS => {},
3489 else => |err| return unexpectedKernError(err),
3490 }
3491 }
3492
3493 const ThreadList = struct {
3494 buf: []MachThread,
3495
3496 pub fn deinit(list: ThreadList) void {
3497 const self_task = machTaskForSelf();
3498 _ = vm_deallocate(
3499 self_task.port,
3500 @ptrToInt(list.buf.ptr),
3501 @intCast(vm_size_t, list.buf.len * @sizeOf(mach_port_t)),
3502 );
3503 }
3504 };
3505
3506 pub fn getThreads(task: MachTask) MachError!ThreadList {
3507 var thread_list: mach_port_array_t = undefined;
3508 var thread_count: mach_msg_type_number_t = undefined;
3509 switch (getKernError(task_threads(task.port, &thread_list, &thread_count))) {
3510 .SUCCESS => return ThreadList{ .buf = @ptrCast([*]MachThread, thread_list)[0..thread_count] },
3511 else => |err| return unexpectedKernError(err),
3512 }
3513 }
3514};
3515
3516pub const MachThread = extern struct {
3517 port: mach_port_t,
3518
3519 pub fn isValid(thread: MachThread) bool {
3520 return thread.port != THREAD_NULL;
3521 }
3522
3523 pub fn getBasicInfo(thread: MachThread) MachError!thread_basic_info {
3524 var info: thread_basic_info = undefined;
3525 var count = THREAD_BASIC_INFO_COUNT;
3526 switch (getKernError(thread_info(
3527 thread.port,
3528 THREAD_BASIC_INFO,
3529 @ptrCast(thread_info_t, &info),
3530 &count,
3531 ))) {
3532 .SUCCESS => return info,
3533 else => |err| return unexpectedKernError(err),
3534 }
3535 }
3536
3537 pub fn getIdentifierInfo(thread: MachThread) MachError!thread_identifier_info {
3538 var info: thread_identifier_info = undefined;
3539 var count = THREAD_IDENTIFIER_INFO_COUNT;
3540 switch (getKernError(thread_info(
3541 thread.port,
3542 THREAD_IDENTIFIER_INFO,
3543 @ptrCast(thread_info_t, &info),
3544 &count,
3545 ))) {
3546 .SUCCESS => return info,
3547 else => |err| return unexpectedKernError(err),
3548 }
3549 }
3550};
3551
3552pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask {
3553 var port: mach_port_name_t = undefined;
3554 switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) {
3555 .SUCCESS => {},
3556 .FAILURE => return error.PermissionDenied,
3557 else => |err| return unexpectedKernError(err),
3558 }
3559 return MachTask{ .port = port };
3560}
3561
3562pub fn machTaskForSelf() MachTask {
3563 return .{ .port = mach_task_self() };
3564}
lib/std/c/darwin/cssm.zig created+47
...@@ -0,0 +1,47 @@
1// Common Security Services Manager
2// Security.framework/Headers/cssm*.h
3
4// Schema Management Name Space Range Definition
5pub const DB_RECORDTYPE_SCHEMA_START = 0x00000000;
6pub const DB_RECORDTYPE_SCHEMA_END = DB_RECORDTYPE_SCHEMA_START + 4;
7
8// Open Group Application Name Space Range Definition
9pub const DB_RECORDTYPE_OPEN_GROUP_START = 0x0000000A;
10pub const DB_RECORDTYPE_OPEN_GROUP_END = DB_RECORDTYPE_OPEN_GROUP_START + 8;
11
12// Industry At Large Application Name Space Range Definition
13pub const DB_RECORDTYPE_APP_DEFINED_START = 0x80000000;
14pub const DB_RECORDTYPE_APP_DEFINED_END = 0xffffffff;
15
16pub const DB_RECORDTYPE = enum(u32) {
17 // Record Types defined in the Schema Management Name Space
18 SCHEMA_INFO = DB_RECORDTYPE_SCHEMA_START + 0,
19 SCHEMA_INDEXES = DB_RECORDTYPE_SCHEMA_START + 1,
20 SCHEMA_ATTRIBUTES = DB_RECORDTYPE_SCHEMA_START + 2,
21 SCHEMA_PARSING_MODULE = DB_RECORDTYPE_SCHEMA_START + 3,
22
23 // Record Types defined in the Open Group Application Name Space
24 ANY = DB_RECORDTYPE_OPEN_GROUP_START + 0,
25 CERT = DB_RECORDTYPE_OPEN_GROUP_START + 1,
26 CRL = DB_RECORDTYPE_OPEN_GROUP_START + 2,
27 POLICY = DB_RECORDTYPE_OPEN_GROUP_START + 3,
28 GENERIC = DB_RECORDTYPE_OPEN_GROUP_START + 4,
29 PUBLIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 5,
30 PRIVATE_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 6,
31 SYMMETRIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 7,
32 ALL_KEYS = DB_RECORDTYPE_OPEN_GROUP_START + 8,
33
34 // AppleFileDL record types
35 GENERIC_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 0,
36 INTERNET_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 1,
37 APPLESHARE_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 2,
38
39 X509_CERTIFICATE = DB_RECORDTYPE_APP_DEFINED_START + 0x1000,
40 USER_TRUST,
41 X509_CRL,
42 UNLOCK_REFERRAL,
43 EXTENDED_ATTRIBUTE,
44 METADATA = DB_RECORDTYPE_APP_DEFINED_START + 0x8000,
45
46 _,
47};
lib/std/child_process.zig+50-125
...@@ -17,10 +17,12 @@ const Os = std.builtin.Os;...@@ -17,10 +17,12 @@ const Os = std.builtin.Os;
17const TailQueue = std.TailQueue;17const TailQueue = std.TailQueue;
18const maxInt = std.math.maxInt;18const maxInt = std.math.maxInt;
19const assert = std.debug.assert;19const assert = std.debug.assert;
20const is_darwin = builtin.target.isDarwin();
2021
21pub const ChildProcess = struct {22pub const ChildProcess = struct {
22 pub const Id = switch (builtin.os.tag) {23 pub const Id = switch (builtin.os.tag) {
23 .windows => windows.HANDLE,24 .windows => windows.HANDLE,
25 .wasi => void,
24 else => os.pid_t,26 else => os.pid_t,
25 };27 };
2628
...@@ -70,6 +72,43 @@ pub const ChildProcess = struct {...@@ -70,6 +72,43 @@ pub const ChildProcess = struct {
70 /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.72 /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.
71 start_suspended: bool = false,73 start_suspended: bool = false,
7274
75 /// Set to true to obtain rusage information for the child process.
76 /// Depending on the target platform and implementation status, the
77 /// requested statistics may or may not be available. If they are
78 /// available, then the `resource_usage_statistics` field will be populated
79 /// after calling `wait`.
80 /// On Linux, this obtains rusage statistics from wait4().
81 request_resource_usage_statistics: bool = false,
82
83 /// This is available after calling wait if
84 /// `request_resource_usage_statistics` was set to `true` before calling
85 /// `spawn`.
86 resource_usage_statistics: ResourceUsageStatistics = .{},
87
88 pub const ResourceUsageStatistics = struct {
89 rusage: @TypeOf(rusage_init) = rusage_init,
90
91 /// Returns the peak resident set size of the child process, in bytes,
92 /// if available.
93 pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {
94 switch (builtin.os.tag) {
95 .linux => {
96 if (rus.rusage) |ru| {
97 return @intCast(usize, ru.maxrss) * 1024;
98 } else {
99 return null;
100 }
101 },
102 else => return null,
103 }
104 }
105
106 const rusage_init = switch (builtin.os.tag) {
107 .linux => @as(?std.os.rusage, null),
108 else => {},
109 };
110 };
111
73 pub const Arg0Expand = os.Arg0Expand;112 pub const Arg0Expand = os.Arg0Expand;
74113
75 pub const SpawnError = error{114 pub const SpawnError = error{
...@@ -90,8 +129,7 @@ pub const ChildProcess = struct {...@@ -90,8 +129,7 @@ pub const ChildProcess = struct {
90 os.SetIdError ||129 os.SetIdError ||
91 os.ChangeCurDirError ||130 os.ChangeCurDirError ||
92 windows.CreateProcessError ||131 windows.CreateProcessError ||
93 windows.WaitForSingleObjectError ||132 windows.WaitForSingleObjectError;
94 os.posix_spawn.Error;
95133
96 pub const Term = union(enum) {134 pub const Term = union(enum) {
97 Exited: u8,135 Exited: u8,
...@@ -143,10 +181,6 @@ pub const ChildProcess = struct {...@@ -143,10 +181,6 @@ pub const ChildProcess = struct {
143 @compileError("the target operating system cannot spawn processes");181 @compileError("the target operating system cannot spawn processes");
144 }182 }
145183
146 if (comptime builtin.target.isDarwin()) {
147 return self.spawnMacos();
148 }
149
150 if (builtin.os.tag == .windows) {184 if (builtin.os.tag == .windows) {
151 return self.spawnWindows();185 return self.spawnWindows();
152 } else {186 } else {
...@@ -337,10 +371,16 @@ pub const ChildProcess = struct {...@@ -337,10 +371,16 @@ pub const ChildProcess = struct {
337 }371 }
338372
339 fn waitUnwrapped(self: *ChildProcess) !void {373 fn waitUnwrapped(self: *ChildProcess) !void {
340 const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())374 const res: os.WaitPidResult = res: {
341 try os.posix_spawn.waitpid(self.id, 0)375 if (builtin.os.tag == .linux and self.request_resource_usage_statistics) {
342 else376 var ru: std.os.rusage = undefined;
343 os.waitpid(self.id, 0);377 const res = os.wait4(self.id, 0, &ru);
378 self.resource_usage_statistics.rusage = ru;
379 break :res res;
380 }
381
382 break :res os.waitpid(self.id, 0);
383 };
344 const status = res.status;384 const status = res.status;
345 self.cleanupStreams();385 self.cleanupStreams();
346 self.handleWaitResult(status);386 self.handleWaitResult(status);
...@@ -416,121 +456,6 @@ pub const ChildProcess = struct {...@@ -416,121 +456,6 @@ pub const ChildProcess = struct {
416 Term{ .Unknown = status };456 Term{ .Unknown = status };
417 }457 }
418458
419 fn spawnMacos(self: *ChildProcess) SpawnError!void {
420 const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
421 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
422 errdefer if (self.stdin_behavior == StdIo.Pipe) destroyPipe(stdin_pipe);
423
424 const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
425 errdefer if (self.stdout_behavior == StdIo.Pipe) destroyPipe(stdout_pipe);
426
427 const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
428 errdefer if (self.stderr_behavior == StdIo.Pipe) destroyPipe(stderr_pipe);
429
430 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
431 const dev_null_fd = if (any_ignore)
432 os.openZ("/dev/null", os.O.RDWR, 0) catch |err| switch (err) {
433 error.PathAlreadyExists => unreachable,
434 error.NoSpaceLeft => unreachable,
435 error.FileTooBig => unreachable,
436 error.DeviceBusy => unreachable,
437 error.FileLocksNotSupported => unreachable,
438 error.BadPathName => unreachable, // Windows-only
439 error.InvalidHandle => unreachable, // WASI-only
440 error.WouldBlock => unreachable,
441 else => |e| return e,
442 }
443 else
444 undefined;
445 defer if (any_ignore) os.close(dev_null_fd);
446
447 var attr = try os.posix_spawn.Attr.init();
448 defer attr.deinit();
449 var flags: u16 = os.darwin.POSIX_SPAWN_SETSIGDEF | os.darwin.POSIX_SPAWN_SETSIGMASK;
450 if (self.disable_aslr) {
451 flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR;
452 }
453 if (self.start_suspended) {
454 flags |= os.darwin.POSIX_SPAWN_START_SUSPENDED;
455 }
456 try attr.set(flags);
457
458 var actions = try os.posix_spawn.Actions.init();
459 defer actions.deinit();
460
461 try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe, os.STDIN_FILENO, dev_null_fd);
462 try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe, os.STDOUT_FILENO, dev_null_fd);
463 try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe, os.STDERR_FILENO, dev_null_fd);
464
465 if (self.cwd_dir) |cwd| {
466 try actions.fchdir(cwd.fd);
467 } else if (self.cwd) |cwd| {
468 try actions.chdir(cwd);
469 }
470
471 var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
472 defer arena_allocator.deinit();
473 const arena = arena_allocator.allocator();
474
475 const argv_buf = try arena.allocSentinel(?[*:0]u8, self.argv.len, null);
476 for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
477
478 const envp = if (self.env_map) |env_map| m: {
479 const envp_buf = try createNullDelimitedEnvMap(arena, env_map);
480 break :m envp_buf.ptr;
481 } else std.c.environ;
482
483 const pid = try os.posix_spawn.spawnp(self.argv[0], actions, attr, argv_buf, envp);
484
485 if (self.stdin_behavior == StdIo.Pipe) {
486 self.stdin = File{ .handle = stdin_pipe[1] };
487 } else {
488 self.stdin = null;
489 }
490 if (self.stdout_behavior == StdIo.Pipe) {
491 self.stdout = File{ .handle = stdout_pipe[0] };
492 } else {
493 self.stdout = null;
494 }
495 if (self.stderr_behavior == StdIo.Pipe) {
496 self.stderr = File{ .handle = stderr_pipe[0] };
497 } else {
498 self.stderr = null;
499 }
500
501 self.id = pid;
502 self.term = null;
503
504 if (self.stdin_behavior == StdIo.Pipe) {
505 os.close(stdin_pipe[0]);
506 }
507 if (self.stdout_behavior == StdIo.Pipe) {
508 os.close(stdout_pipe[1]);
509 }
510 if (self.stderr_behavior == StdIo.Pipe) {
511 os.close(stderr_pipe[1]);
512 }
513 }
514
515 fn setUpChildIoPosixSpawn(
516 stdio: StdIo,
517 actions: *os.posix_spawn.Actions,
518 pipe_fd: [2]i32,
519 std_fileno: i32,
520 dev_null_fd: i32,
521 ) !void {
522 switch (stdio) {
523 .Pipe => {
524 const idx: usize = if (std_fileno == 0) 0 else 1;
525 try actions.dup2(pipe_fd[idx], std_fileno);
526 try actions.close(pipe_fd[1 - idx]);
527 },
528 .Close => try actions.close(std_fileno),
529 .Inherit => {},
530 .Ignore => try actions.dup2(dev_null_fd, std_fileno),
531 }
532 }
533
534 fn spawnPosix(self: *ChildProcess) SpawnError!void {459 fn spawnPosix(self: *ChildProcess) SpawnError!void {
535 const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;460 const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
536 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;461 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
lib/std/crypto.zig+30
...@@ -1,3 +1,5 @@...@@ -1,3 +1,5 @@
1const root = @import("root");
2
1/// Authenticated Encryption with Associated Data3/// Authenticated Encryption with Associated Data
2pub const aead = struct {4pub const aead = struct {
3 pub const aegis = struct {5 pub const aegis = struct {
...@@ -66,6 +68,11 @@ pub const dh = struct {...@@ -66,6 +68,11 @@ pub const dh = struct {
66 pub const X25519 = @import("crypto/25519/x25519.zig").X25519;68 pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
67};69};
6870
71/// Key Encapsulation Mechanisms.
72pub const kem = struct {
73 pub const kyber_d00 = @import("crypto/kyber_d00.zig");
74};
75
69/// Elliptic-curve arithmetic.76/// Elliptic-curve arithmetic.
70pub const ecc = struct {77pub const ecc = struct {
71 pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;78 pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
...@@ -183,6 +190,27 @@ pub const errors = @import("crypto/errors.zig");...@@ -183,6 +190,27 @@ pub const errors = @import("crypto/errors.zig");
183pub const tls = @import("crypto/tls.zig");190pub const tls = @import("crypto/tls.zig");
184pub const Certificate = @import("crypto/Certificate.zig");191pub const Certificate = @import("crypto/Certificate.zig");
185192
193/// Side-channels mitigations.
194pub const SideChannelsMitigations = enum {
195 /// No additional side-channel mitigations are applied.
196 /// This is the fastest mode.
197 none,
198 /// The `basic` mode protects against most practical attacks, provided that the
199 /// application or implements proper defenses against brute-force attacks.
200 /// It offers a good balance between performance and security.
201 basic,
202 /// The `medium` mode offers increased resilience against side-channel attacks,
203 /// making most attacks unpractical even on shared/low latency environements.
204 /// This is the default mode.
205 medium,
206 /// The `full` mode offers the highest level of protection against side-channel attacks.
207 /// Note that this doesn't cover all possible attacks (especially power analysis or
208 /// thread-local attacks such as cachebleed), and that the performance impact is significant.
209 full,
210};
211
212pub const default_side_channels_mitigations = .medium;
213
186test {214test {
187 _ = aead.aegis.Aegis128L;215 _ = aead.aegis.Aegis128L;
188 _ = aead.aegis.Aegis256;216 _ = aead.aegis.Aegis256;
...@@ -217,6 +245,8 @@ test {...@@ -217,6 +245,8 @@ test {
217245
218 _ = dh.X25519;246 _ = dh.X25519;
219247
248 _ = kem.kyber_d00;
249
220 _ = ecc.Curve25519;250 _ = ecc.Curve25519;
221 _ = ecc.Edwards25519;251 _ = ecc.Edwards25519;
222 _ = ecc.P256;252 _ = ecc.P256;
lib/std/crypto/25519/field.zig+8-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const crypto = std.crypto;3const crypto = std.crypto;
3const readIntLittle = std.mem.readIntLittle;4const readIntLittle = std.mem.readIntLittle;
4const writeIntLittle = std.mem.writeIntLittle;5const writeIntLittle = std.mem.writeIntLittle;
...@@ -6,6 +7,12 @@ const writeIntLittle = std.mem.writeIntLittle;...@@ -6,6 +7,12 @@ const writeIntLittle = std.mem.writeIntLittle;
6const NonCanonicalError = crypto.errors.NonCanonicalError;7const NonCanonicalError = crypto.errors.NonCanonicalError;
7const NotSquareError = crypto.errors.NotSquareError;8const NotSquareError = crypto.errors.NotSquareError;
89
10// Inline conditionally, when it can result in large code generation.
11const bloaty_inline = switch (builtin.mode) {
12 .ReleaseSafe, .ReleaseFast => .Inline,
13 .Debug, .ReleaseSmall => .Unspecified,
14};
15
9pub const Fe = struct {16pub const Fe = struct {
10 limbs: [5]u64,17 limbs: [5]u64,
1118
...@@ -264,7 +271,7 @@ pub const Fe = struct {...@@ -264,7 +271,7 @@ pub const Fe = struct {
264 }271 }
265272
266 /// Multiply two field elements273 /// Multiply two field elements
267 pub inline fn mul(a: Fe, b: Fe) Fe {274 pub fn mul(a: Fe, b: Fe) callconv(bloaty_inline) Fe {
268 var ax: [5]u128 = undefined;275 var ax: [5]u128 = undefined;
269 var bx: [5]u128 = undefined;276 var bx: [5]u128 = undefined;
270 var a19: [5]u128 = undefined;277 var a19: [5]u128 = undefined;
lib/std/crypto/aes/armcrypto.zig+8-16
...@@ -32,62 +32,54 @@ pub const Block = struct {...@@ -32,62 +32,54 @@ pub const Block = struct {
32 /// Encrypt a block with a round key.32 /// Encrypt a block with a round key.
33 pub inline fn encrypt(block: Block, round_key: Block) Block {33 pub inline fn encrypt(block: Block, round_key: Block) Block {
34 return Block{34 return Block{
35 .repr = asm (35 .repr = (asm (
36 \\ mov %[out].16b, %[in].16b36 \\ mov %[out].16b, %[in].16b
37 \\ aese %[out].16b, %[zero].16b37 \\ aese %[out].16b, %[zero].16b
38 \\ aesmc %[out].16b, %[out].16b38 \\ aesmc %[out].16b, %[out].16b
39 \\ eor %[out].16b, %[out].16b, %[rk].16b
40 : [out] "=&x" (-> BlockVec),39 : [out] "=&x" (-> BlockVec),
41 : [in] "x" (block.repr),40 : [in] "x" (block.repr),
42 [rk] "x" (round_key.repr),
43 [zero] "x" (zero),41 [zero] "x" (zero),
44 ),42 )) ^ round_key.repr,
45 };43 };
46 }44 }
4745
48 /// Encrypt a block with the last round key.46 /// Encrypt a block with the last round key.
49 pub inline fn encryptLast(block: Block, round_key: Block) Block {47 pub inline fn encryptLast(block: Block, round_key: Block) Block {
50 return Block{48 return Block{
51 .repr = asm (49 .repr = (asm (
52 \\ mov %[out].16b, %[in].16b50 \\ mov %[out].16b, %[in].16b
53 \\ aese %[out].16b, %[zero].16b51 \\ aese %[out].16b, %[zero].16b
54 \\ eor %[out].16b, %[out].16b, %[rk].16b
55 : [out] "=&x" (-> BlockVec),52 : [out] "=&x" (-> BlockVec),
56 : [in] "x" (block.repr),53 : [in] "x" (block.repr),
57 [rk] "x" (round_key.repr),
58 [zero] "x" (zero),54 [zero] "x" (zero),
59 ),55 )) ^ round_key.repr,
60 };56 };
61 }57 }
6258
63 /// Decrypt a block with a round key.59 /// Decrypt a block with a round key.
64 pub inline fn decrypt(block: Block, inv_round_key: Block) Block {60 pub inline fn decrypt(block: Block, inv_round_key: Block) Block {
65 return Block{61 return Block{
66 .repr = asm (62 .repr = (asm (
67 \\ mov %[out].16b, %[in].16b63 \\ mov %[out].16b, %[in].16b
68 \\ aesd %[out].16b, %[zero].16b64 \\ aesd %[out].16b, %[zero].16b
69 \\ aesimc %[out].16b, %[out].16b65 \\ aesimc %[out].16b, %[out].16b
70 \\ eor %[out].16b, %[out].16b, %[rk].16b
71 : [out] "=&x" (-> BlockVec),66 : [out] "=&x" (-> BlockVec),
72 : [in] "x" (block.repr),67 : [in] "x" (block.repr),
73 [rk] "x" (inv_round_key.repr),
74 [zero] "x" (zero),68 [zero] "x" (zero),
75 ),69 )) ^ inv_round_key.repr,
76 };70 };
77 }71 }
7872
79 /// Decrypt a block with the last round key.73 /// Decrypt a block with the last round key.
80 pub inline fn decryptLast(block: Block, inv_round_key: Block) Block {74 pub inline fn decryptLast(block: Block, inv_round_key: Block) Block {
81 return Block{75 return Block{
82 .repr = asm (76 .repr = (asm (
83 \\ mov %[out].16b, %[in].16b77 \\ mov %[out].16b, %[in].16b
84 \\ aesd %[out].16b, %[zero].16b78 \\ aesd %[out].16b, %[zero].16b
85 \\ eor %[out].16b, %[out].16b, %[rk].16b
86 : [out] "=&x" (-> BlockVec),79 : [out] "=&x" (-> BlockVec),
87 : [in] "x" (block.repr),80 : [in] "x" (block.repr),
88 [rk] "x" (inv_round_key.repr),
89 [zero] "x" (zero),81 [zero] "x" (zero),
90 ),82 )) ^ inv_round_key.repr,
91 };83 };
92 }84 }
9385
lib/std/crypto/aes/soft.zig+302-63
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1// Based on Go stdlib implementation
2
3const std = @import("../../std.zig");1const std = @import("../../std.zig");
4const math = std.math;2const math = std.math;
5const mem = std.mem;3const mem = std.mem;
64
7const BlockVec = [4]u32;5const BlockVec = [4]u32;
86
7const side_channels_mitigations = std.options.side_channels_mitigations;
8
9/// A single AES block.9/// A single AES block.
10pub const Block = struct {10pub const Block = struct {
11 pub const block_length: usize = 16;11 pub const block_length: usize = 16;
...@@ -15,20 +15,20 @@ pub const Block = struct {...@@ -15,20 +15,20 @@ pub const Block = struct {
1515
16 /// Convert a byte sequence into an internal representation.16 /// Convert a byte sequence into an internal representation.
17 pub inline fn fromBytes(bytes: *const [16]u8) Block {17 pub inline fn fromBytes(bytes: *const [16]u8) Block {
18 const s0 = mem.readIntBig(u32, bytes[0..4]);18 const s0 = mem.readIntLittle(u32, bytes[0..4]);
19 const s1 = mem.readIntBig(u32, bytes[4..8]);19 const s1 = mem.readIntLittle(u32, bytes[4..8]);
20 const s2 = mem.readIntBig(u32, bytes[8..12]);20 const s2 = mem.readIntLittle(u32, bytes[8..12]);
21 const s3 = mem.readIntBig(u32, bytes[12..16]);21 const s3 = mem.readIntLittle(u32, bytes[12..16]);
22 return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };22 return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };
23 }23 }
2424
25 /// Convert the internal representation of a block into a byte sequence.25 /// Convert the internal representation of a block into a byte sequence.
26 pub inline fn toBytes(block: Block) [16]u8 {26 pub inline fn toBytes(block: Block) [16]u8 {
27 var bytes: [16]u8 = undefined;27 var bytes: [16]u8 = undefined;
28 mem.writeIntBig(u32, bytes[0..4], block.repr[0]);28 mem.writeIntLittle(u32, bytes[0..4], block.repr[0]);
29 mem.writeIntBig(u32, bytes[4..8], block.repr[1]);29 mem.writeIntLittle(u32, bytes[4..8], block.repr[1]);
30 mem.writeIntBig(u32, bytes[8..12], block.repr[2]);30 mem.writeIntLittle(u32, bytes[8..12], block.repr[2]);
31 mem.writeIntBig(u32, bytes[12..16], block.repr[3]);31 mem.writeIntLittle(u32, bytes[12..16], block.repr[3]);
32 return bytes;32 return bytes;
33 }33 }
3434
...@@ -50,32 +50,93 @@ pub const Block = struct {...@@ -50,32 +50,93 @@ pub const Block = struct {
50 const s2 = block.repr[2];50 const s2 = block.repr[2];
51 const s3 = block.repr[3];51 const s3 = block.repr[3];
5252
53 const t0 = round_key.repr[0] ^ table_encrypt[0][@truncate(u8, s0 >> 24)] ^ table_encrypt[1][@truncate(u8, s1 >> 16)] ^ table_encrypt[2][@truncate(u8, s2 >> 8)] ^ table_encrypt[3][@truncate(u8, s3)];53 var x: [4]u32 = undefined;
54 const t1 = round_key.repr[1] ^ table_encrypt[0][@truncate(u8, s1 >> 24)] ^ table_encrypt[1][@truncate(u8, s2 >> 16)] ^ table_encrypt[2][@truncate(u8, s3 >> 8)] ^ table_encrypt[3][@truncate(u8, s0)];54 x = table_lookup(&table_encrypt, @truncate(u8, s0), @truncate(u8, s1 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 24));
55 const t2 = round_key.repr[2] ^ table_encrypt[0][@truncate(u8, s2 >> 24)] ^ table_encrypt[1][@truncate(u8, s3 >> 16)] ^ table_encrypt[2][@truncate(u8, s0 >> 8)] ^ table_encrypt[3][@truncate(u8, s1)];55 var t0 = x[0] ^ x[1] ^ x[2] ^ x[3];
56 const t3 = round_key.repr[3] ^ table_encrypt[0][@truncate(u8, s3 >> 24)] ^ table_encrypt[1][@truncate(u8, s0 >> 16)] ^ table_encrypt[2][@truncate(u8, s1 >> 8)] ^ table_encrypt[3][@truncate(u8, s2)];56 x = table_lookup(&table_encrypt, @truncate(u8, s1), @truncate(u8, s2 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 24));
57 var t1 = x[0] ^ x[1] ^ x[2] ^ x[3];
58 x = table_lookup(&table_encrypt, @truncate(u8, s2), @truncate(u8, s3 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 24));
59 var t2 = x[0] ^ x[1] ^ x[2] ^ x[3];
60 x = table_lookup(&table_encrypt, @truncate(u8, s3), @truncate(u8, s0 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 24));
61 var t3 = x[0] ^ x[1] ^ x[2] ^ x[3];
62
63 t0 ^= round_key.repr[0];
64 t1 ^= round_key.repr[1];
65 t2 ^= round_key.repr[2];
66 t3 ^= round_key.repr[3];
67
68 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
69 }
70
71 /// Encrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS*
72 pub inline fn encryptUnprotected(block: Block, round_key: Block) Block {
73 const s0 = block.repr[0];
74 const s1 = block.repr[1];
75 const s2 = block.repr[2];
76 const s3 = block.repr[3];
77
78 var x: [4]u32 = undefined;
79 x = .{
80 table_encrypt[0][@truncate(u8, s0)],
81 table_encrypt[1][@truncate(u8, s1 >> 8)],
82 table_encrypt[2][@truncate(u8, s2 >> 16)],
83 table_encrypt[3][@truncate(u8, s3 >> 24)],
84 };
85 var t0 = x[0] ^ x[1] ^ x[2] ^ x[3];
86 x = .{
87 table_encrypt[0][@truncate(u8, s1)],
88 table_encrypt[1][@truncate(u8, s2 >> 8)],
89 table_encrypt[2][@truncate(u8, s3 >> 16)],
90 table_encrypt[3][@truncate(u8, s0 >> 24)],
91 };
92 var t1 = x[0] ^ x[1] ^ x[2] ^ x[3];
93 x = .{
94 table_encrypt[0][@truncate(u8, s2)],
95 table_encrypt[1][@truncate(u8, s3 >> 8)],
96 table_encrypt[2][@truncate(u8, s0 >> 16)],
97 table_encrypt[3][@truncate(u8, s1 >> 24)],
98 };
99 var t2 = x[0] ^ x[1] ^ x[2] ^ x[3];
100 x = .{
101 table_encrypt[0][@truncate(u8, s3)],
102 table_encrypt[1][@truncate(u8, s0 >> 8)],
103 table_encrypt[2][@truncate(u8, s1 >> 16)],
104 table_encrypt[3][@truncate(u8, s2 >> 24)],
105 };
106 var t3 = x[0] ^ x[1] ^ x[2] ^ x[3];
107
108 t0 ^= round_key.repr[0];
109 t1 ^= round_key.repr[1];
110 t2 ^= round_key.repr[2];
111 t3 ^= round_key.repr[3];
57112
58 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };113 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
59 }114 }
60115
61 /// Encrypt a block with the last round key.116 /// Encrypt a block with the last round key.
62 pub inline fn encryptLast(block: Block, round_key: Block) Block {117 pub inline fn encryptLast(block: Block, round_key: Block) Block {
63 const t0 = block.repr[0];118 const s0 = block.repr[0];
64 const t1 = block.repr[1];119 const s1 = block.repr[1];
65 const t2 = block.repr[2];120 const s2 = block.repr[2];
66 const t3 = block.repr[3];121 const s3 = block.repr[3];
67122
68 // Last round uses s-box directly and XORs to produce output.123 // Last round uses s-box directly and XORs to produce output.
69 var s0 = @as(u32, sbox_encrypt[t0 >> 24]) << 24 | @as(u32, sbox_encrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t3 & 0xff]);124 var x: [4]u8 = undefined;
70 var s1 = @as(u32, sbox_encrypt[t1 >> 24]) << 24 | @as(u32, sbox_encrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t0 & 0xff]);125 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s3 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s0));
71 var s2 = @as(u32, sbox_encrypt[t2 >> 24]) << 24 | @as(u32, sbox_encrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t1 & 0xff]);126 var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
72 var s3 = @as(u32, sbox_encrypt[t3 >> 24]) << 24 | @as(u32, sbox_encrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t2 & 0xff]);127 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s0 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s1));
73 s0 ^= round_key.repr[0];128 var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
74 s1 ^= round_key.repr[1];129 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s1 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s2));
75 s2 ^= round_key.repr[2];130 var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
76 s3 ^= round_key.repr[3];131 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s2 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s3));
132 var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
133
134 t0 ^= round_key.repr[0];
135 t1 ^= round_key.repr[1];
136 t2 ^= round_key.repr[2];
137 t3 ^= round_key.repr[3];
77138
78 return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };139 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
79 }140 }
80141
81 /// Decrypt a block with a round key.142 /// Decrypt a block with a round key.
...@@ -85,32 +146,93 @@ pub const Block = struct {...@@ -85,32 +146,93 @@ pub const Block = struct {
85 const s2 = block.repr[2];146 const s2 = block.repr[2];
86 const s3 = block.repr[3];147 const s3 = block.repr[3];
87148
88 const t0 = round_key.repr[0] ^ table_decrypt[0][@truncate(u8, s0 >> 24)] ^ table_decrypt[1][@truncate(u8, s3 >> 16)] ^ table_decrypt[2][@truncate(u8, s2 >> 8)] ^ table_decrypt[3][@truncate(u8, s1)];149 var x: [4]u32 = undefined;
89 const t1 = round_key.repr[1] ^ table_decrypt[0][@truncate(u8, s1 >> 24)] ^ table_decrypt[1][@truncate(u8, s0 >> 16)] ^ table_decrypt[2][@truncate(u8, s3 >> 8)] ^ table_decrypt[3][@truncate(u8, s2)];150 x = table_lookup(&table_decrypt, @truncate(u8, s0), @truncate(u8, s3 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 24));
90 const t2 = round_key.repr[2] ^ table_decrypt[0][@truncate(u8, s2 >> 24)] ^ table_decrypt[1][@truncate(u8, s1 >> 16)] ^ table_decrypt[2][@truncate(u8, s0 >> 8)] ^ table_decrypt[3][@truncate(u8, s3)];151 var t0 = x[0] ^ x[1] ^ x[2] ^ x[3];
91 const t3 = round_key.repr[3] ^ table_decrypt[0][@truncate(u8, s3 >> 24)] ^ table_decrypt[1][@truncate(u8, s2 >> 16)] ^ table_decrypt[2][@truncate(u8, s1 >> 8)] ^ table_decrypt[3][@truncate(u8, s0)];152 x = table_lookup(&table_decrypt, @truncate(u8, s1), @truncate(u8, s0 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 24));
153 var t1 = x[0] ^ x[1] ^ x[2] ^ x[3];
154 x = table_lookup(&table_decrypt, @truncate(u8, s2), @truncate(u8, s1 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 24));
155 var t2 = x[0] ^ x[1] ^ x[2] ^ x[3];
156 x = table_lookup(&table_decrypt, @truncate(u8, s3), @truncate(u8, s2 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 24));
157 var t3 = x[0] ^ x[1] ^ x[2] ^ x[3];
158
159 t0 ^= round_key.repr[0];
160 t1 ^= round_key.repr[1];
161 t2 ^= round_key.repr[2];
162 t3 ^= round_key.repr[3];
163
164 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
165 }
166
167 /// Decrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS*
168 pub inline fn decryptUnprotected(block: Block, round_key: Block) Block {
169 const s0 = block.repr[0];
170 const s1 = block.repr[1];
171 const s2 = block.repr[2];
172 const s3 = block.repr[3];
173
174 var x: [4]u32 = undefined;
175 x = .{
176 table_decrypt[0][@truncate(u8, s0)],
177 table_decrypt[1][@truncate(u8, s3 >> 8)],
178 table_decrypt[2][@truncate(u8, s2 >> 16)],
179 table_decrypt[3][@truncate(u8, s1 >> 24)],
180 };
181 var t0 = x[0] ^ x[1] ^ x[2] ^ x[3];
182 x = .{
183 table_decrypt[0][@truncate(u8, s1)],
184 table_decrypt[1][@truncate(u8, s0 >> 8)],
185 table_decrypt[2][@truncate(u8, s3 >> 16)],
186 table_decrypt[3][@truncate(u8, s2 >> 24)],
187 };
188 var t1 = x[0] ^ x[1] ^ x[2] ^ x[3];
189 x = .{
190 table_decrypt[0][@truncate(u8, s2)],
191 table_decrypt[1][@truncate(u8, s1 >> 8)],
192 table_decrypt[2][@truncate(u8, s0 >> 16)],
193 table_decrypt[3][@truncate(u8, s3 >> 24)],
194 };
195 var t2 = x[0] ^ x[1] ^ x[2] ^ x[3];
196 x = .{
197 table_decrypt[0][@truncate(u8, s3)],
198 table_decrypt[1][@truncate(u8, s2 >> 8)],
199 table_decrypt[2][@truncate(u8, s1 >> 16)],
200 table_decrypt[3][@truncate(u8, s0 >> 24)],
201 };
202 var t3 = x[0] ^ x[1] ^ x[2] ^ x[3];
203
204 t0 ^= round_key.repr[0];
205 t1 ^= round_key.repr[1];
206 t2 ^= round_key.repr[2];
207 t3 ^= round_key.repr[3];
92208
93 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };209 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
94 }210 }
95211
96 /// Decrypt a block with the last round key.212 /// Decrypt a block with the last round key.
97 pub inline fn decryptLast(block: Block, round_key: Block) Block {213 pub inline fn decryptLast(block: Block, round_key: Block) Block {
98 const t0 = block.repr[0];214 const s0 = block.repr[0];
99 const t1 = block.repr[1];215 const s1 = block.repr[1];
100 const t2 = block.repr[2];216 const s2 = block.repr[2];
101 const t3 = block.repr[3];217 const s3 = block.repr[3];
102218
103 // Last round uses s-box directly and XORs to produce output.219 // Last round uses s-box directly and XORs to produce output.
104 var s0 = @as(u32, sbox_decrypt[t0 >> 24]) << 24 | @as(u32, sbox_decrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t1 & 0xff]);220 var x: [4]u8 = undefined;
105 var s1 = @as(u32, sbox_decrypt[t1 >> 24]) << 24 | @as(u32, sbox_decrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t2 & 0xff]);221 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s1 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s0));
106 var s2 = @as(u32, sbox_decrypt[t2 >> 24]) << 24 | @as(u32, sbox_decrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t3 & 0xff]);222 var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
107 var s3 = @as(u32, sbox_decrypt[t3 >> 24]) << 24 | @as(u32, sbox_decrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t0 & 0xff]);223 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s2 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s1));
108 s0 ^= round_key.repr[0];224 var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
109 s1 ^= round_key.repr[1];225 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s3 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s2));
110 s2 ^= round_key.repr[2];226 var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
111 s3 ^= round_key.repr[3];227 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s0 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s3));
228 var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
229
230 t0 ^= round_key.repr[0];
231 t1 ^= round_key.repr[1];
232 t2 ^= round_key.repr[2];
233 t3 ^= round_key.repr[3];
112234
113 return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };235 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
114 }236 }
115237
116 /// Apply the bitwise XOR operation to the content of two blocks.238 /// Apply the bitwise XOR operation to the content of two blocks.
...@@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type {...@@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type {
226 const subw = struct {348 const subw = struct {
227 // Apply sbox_encrypt to each byte in w.349 // Apply sbox_encrypt to each byte in w.
228 fn func(w: u32) u32 {350 fn func(w: u32) u32 {
229 return @as(u32, sbox_encrypt[w >> 24]) << 24 | @as(u32, sbox_encrypt[w >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[w >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[w & 0xff]);351 const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, w), @truncate(u8, w >> 8), @truncate(u8, w >> 16), @truncate(u8, w >> 24));
352 return @as(u32, x[3]) << 24 | @as(u32, x[2]) << 16 | @as(u32, x[1]) << 8 | @as(u32, x[0]);
230 }353 }
231 }.func;354 }.func;
232355
...@@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type {...@@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type {
244 }367 }
245 round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t;368 round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t;
246 }369 }
370 i = 0;
371 inline while (i < round_keys.len * 4) : (i += 1) {
372 round_keys[i / 4].repr[i % 4] = @byteSwap(round_keys[i / 4].repr[i % 4]);
373 }
247 return Self{ .round_keys = round_keys };374 return Self{ .round_keys = round_keys };
248 }375 }
249376
...@@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type {...@@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type {
257 const ei = total_words - i - 4;384 const ei = total_words - i - 4;
258 comptime var j: usize = 0;385 comptime var j: usize = 0;
259 inline while (j < 4) : (j += 1) {386 inline while (j < 4) : (j += 1) {
260 var x = round_keys[(ei + j) / 4].repr[(ei + j) % 4];387 var rk = round_keys[(ei + j) / 4].repr[(ei + j) % 4];
261 if (i > 0 and i + 4 < total_words) {388 if (i > 0 and i + 4 < total_words) {
262 x = table_decrypt[0][sbox_encrypt[x >> 24]] ^ table_decrypt[1][sbox_encrypt[x >> 16 & 0xff]] ^ table_decrypt[2][sbox_encrypt[x >> 8 & 0xff]] ^ table_decrypt[3][sbox_encrypt[x & 0xff]];389 const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, rk >> 24), @truncate(u8, rk >> 16), @truncate(u8, rk >> 8), @truncate(u8, rk));
390 const y = table_lookup(&table_decrypt, x[3], x[2], x[1], x[0]);
391 rk = y[0] ^ y[1] ^ y[2] ^ y[3];
263 }392 }
264 inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x;393 inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = rk;
265 }394 }
266 }395 }
267 return Self{ .round_keys = inv_round_keys };396 return Self{ .round_keys = inv_round_keys };
...@@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type {...@@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type {
293 const round_keys = ctx.key_schedule.round_keys;422 const round_keys = ctx.key_schedule.round_keys;
294 var t = Block.fromBytes(src).xorBlocks(round_keys[0]);423 var t = Block.fromBytes(src).xorBlocks(round_keys[0]);
295 comptime var i = 1;424 comptime var i = 1;
296 inline while (i < rounds) : (i += 1) {425 if (side_channels_mitigations == .full) {
426 inline while (i < rounds) : (i += 1) {
427 t = t.encrypt(round_keys[i]);
428 }
429 } else {
430 inline while (i < 5) : (i += 1) {
431 t = t.encrypt(round_keys[i]);
432 }
433 inline while (i < rounds - 1) : (i += 1) {
434 t = t.encryptUnprotected(round_keys[i]);
435 }
297 t = t.encrypt(round_keys[i]);436 t = t.encrypt(round_keys[i]);
298 }437 }
299 t = t.encryptLast(round_keys[rounds]);438 t = t.encryptLast(round_keys[rounds]);
...@@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type {...@@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type {
305 const round_keys = ctx.key_schedule.round_keys;444 const round_keys = ctx.key_schedule.round_keys;
306 var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]);445 var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]);
307 comptime var i = 1;446 comptime var i = 1;
308 inline while (i < rounds) : (i += 1) {447 if (side_channels_mitigations == .full) {
448 inline while (i < rounds) : (i += 1) {
449 t = t.encrypt(round_keys[i]);
450 }
451 } else {
452 inline while (i < 5) : (i += 1) {
453 t = t.encrypt(round_keys[i]);
454 }
455 inline while (i < rounds - 1) : (i += 1) {
456 t = t.encryptUnprotected(round_keys[i]);
457 }
309 t = t.encrypt(round_keys[i]);458 t = t.encrypt(round_keys[i]);
310 }459 }
311 t = t.encryptLast(round_keys[rounds]);460 t = t.encryptLast(round_keys[rounds]);
...@@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type {...@@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type {
359 const inv_round_keys = ctx.key_schedule.round_keys;508 const inv_round_keys = ctx.key_schedule.round_keys;
360 var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]);509 var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]);
361 comptime var i = 1;510 comptime var i = 1;
362 inline while (i < rounds) : (i += 1) {511 if (side_channels_mitigations == .full) {
512 inline while (i < rounds) : (i += 1) {
513 t = t.decrypt(inv_round_keys[i]);
514 }
515 } else {
516 inline while (i < 5) : (i += 1) {
517 t = t.decrypt(inv_round_keys[i]);
518 }
519 inline while (i < rounds - 1) : (i += 1) {
520 t = t.decryptUnprotected(inv_round_keys[i]);
521 }
363 t = t.decrypt(inv_round_keys[i]);522 t = t.decrypt(inv_round_keys[i]);
364 }523 }
365 t = t.decryptLast(inv_round_keys[rounds]);524 t = t.decryptLast(inv_round_keys[rounds]);
...@@ -428,10 +587,11 @@ const powx = init: {...@@ -428,10 +587,11 @@ const powx = init: {
428 break :init array;587 break :init array;
429};588};
430589
431const sbox_encrypt align(64) = generateSbox(false);590const sbox_encrypt align(64) = generateSbox(false); // S-box for encryption
432const sbox_decrypt align(64) = generateSbox(true);591const sbox_key_schedule align(64) = generateSbox(false); // S-box only for key schedule, so that it uses distinct L1 cache entries than the S-box used for encryption
433const table_encrypt align(64) = generateTable(false);592const sbox_decrypt align(64) = generateSbox(true); // S-box for decryption
434const table_decrypt align(64) = generateTable(true);593const table_encrypt align(64) = generateTable(false); // 4-byte LUTs for encryption
594const table_decrypt align(64) = generateTable(true); // 4-byte LUTs for decryption
435595
436// Generate S-box substitution values.596// Generate S-box substitution values.
437fn generateSbox(invert: bool) [256]u8 {597fn generateSbox(invert: bool) [256]u8 {
...@@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 {...@@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 {
472 var table: [4][256]u32 = undefined;632 var table: [4][256]u32 = undefined;
473633
474 for (generateSbox(invert), 0..) |value, index| {634 for (generateSbox(invert), 0..) |value, index| {
475 table[0][index] = mul(value, if (invert) 0xb else 0x3);635 table[0][index] = math.shl(u32, mul(value, if (invert) 0xb else 0x3), 24);
476 table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 8);636 table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 16);
477 table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 16);637 table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 8);
478 table[0][index] |= math.shl(u32, mul(value, if (invert) 0xe else 0x2), 24);638 table[0][index] |= mul(value, if (invert) 0xe else 0x2);
479639
480 table[1][index] = math.rotr(u32, table[0][index], 8);640 table[1][index] = math.rotl(u32, table[0][index], 8);
481 table[2][index] = math.rotr(u32, table[0][index], 16);641 table[2][index] = math.rotl(u32, table[0][index], 16);
482 table[3][index] = math.rotr(u32, table[0][index], 24);642 table[3][index] = math.rotl(u32, table[0][index], 24);
483 }643 }
484644
485 return table;645 return table;
...@@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 {...@@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 {
506666
507 return @truncate(u8, s);667 return @truncate(u8, s);
508}668}
669
670const cache_line_bytes = 64;
671
672inline fn sbox_lookup(sbox: *align(64) const [256]u8, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u8 {
673 if (side_channels_mitigations == .none) {
674 return [4]u8{
675 sbox[idx0],
676 sbox[idx1],
677 sbox[idx2],
678 sbox[idx3],
679 };
680 } else {
681 const stride = switch (side_channels_mitigations) {
682 .none => unreachable,
683 .basic => sbox.len / 4,
684 .medium => sbox.len / (sbox.len / cache_line_bytes) * 2,
685 .full => sbox.len / (sbox.len / cache_line_bytes),
686 };
687 const of0 = idx0 % stride;
688 const of1 = idx1 % stride;
689 const of2 = idx2 % stride;
690 const of3 = idx3 % stride;
691 var t: [4][sbox.len / stride]u8 align(64) = undefined;
692 var i: usize = 0;
693 while (i < t[0].len) : (i += 1) {
694 const tx = sbox[i * stride ..];
695 t[0][i] = tx[of0];
696 t[1][i] = tx[of1];
697 t[2][i] = tx[of2];
698 t[3][i] = tx[of3];
699 }
700 std.mem.doNotOptimizeAway(t);
701 return [4]u8{
702 t[0][idx0 / stride],
703 t[1][idx1 / stride],
704 t[2][idx2 / stride],
705 t[3][idx3 / stride],
706 };
707 }
708}
709
710inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u32 {
711 if (side_channels_mitigations == .none) {
712 return [4]u32{
713 table[0][idx0],
714 table[1][idx1],
715 table[2][idx2],
716 table[3][idx3],
717 };
718 } else {
719 const table_bytes = @sizeOf(@TypeOf(table[0]));
720 const stride = switch (side_channels_mitigations) {
721 .none => unreachable,
722 .basic => table[0].len / 4,
723 .medium => table[0].len / (table_bytes / cache_line_bytes) * 2,
724 .full => table[0].len / (table_bytes / cache_line_bytes),
725 };
726 const of0 = idx0 % stride;
727 const of1 = idx1 % stride;
728 const of2 = idx2 % stride;
729 const of3 = idx3 % stride;
730 var t: [4][table[0].len / stride]u32 align(64) = undefined;
731 var i: usize = 0;
732 while (i < t[0].len) : (i += 1) {
733 const tx = table[0][i * stride ..];
734 t[0][i] = tx[of0];
735 t[1][i] = tx[of1];
736 t[2][i] = tx[of2];
737 t[3][i] = tx[of3];
738 }
739 std.mem.doNotOptimizeAway(t);
740 return [4]u32{
741 t[0][idx0 / stride],
742 math.rotl(u32, t[1][idx1 / stride], 8),
743 math.rotl(u32, t[2][idx2 / stride], 16),
744 math.rotl(u32, t[3][idx3 / stride], 24),
745 };
746 }
747}
lib/std/crypto/argon2.zig+29-30
...@@ -138,40 +138,39 @@ fn initHash(...@@ -138,40 +138,39 @@ fn initHash(
138}138}
139139
140fn blake2bLong(out: []u8, in: []const u8) void {140fn blake2bLong(out: []u8, in: []const u8) void {
141 var b2 = Blake2b512.init(.{ .expected_out_bits = math.min(512, out.len * 8) });141 const H = Blake2b512;
142142 var outlen_bytes: [4]u8 = undefined;
143 var buffer: [Blake2b512.digest_length]u8 = undefined;143 mem.writeIntLittle(u32, &outlen_bytes, @intCast(u32, out.len));
144 mem.writeIntLittle(u32, buffer[0..4], @intCast(u32, out.len));144
145 b2.update(buffer[0..4]);145 var out_buf: [H.digest_length]u8 = undefined;
146 b2.update(in);146
147 b2.final(&buffer);147 if (out.len <= H.digest_length) {
148148 var h = H.init(.{ .expected_out_bits = out.len * 8 });
149 if (out.len <= Blake2b512.digest_length) {149 h.update(&outlen_bytes);
150 mem.copy(u8, out, buffer[0..out.len]);150 h.update(in);
151 h.final(&out_buf);
152 mem.copy(u8, out, out_buf[0..out.len]);
151 return;153 return;
152 }154 }
153155
154 b2 = Blake2b512.init(.{});156 var h = H.init(.{});
155 mem.copy(u8, out, buffer[0..32]);157 h.update(&outlen_bytes);
156 var out_slice = out[32..];158 h.update(in);
157 while (out_slice.len > Blake2b512.digest_length) : ({159 h.final(&out_buf);
158 out_slice = out_slice[32..];160 var out_slice = out;
159 b2 = Blake2b512.init(.{});161 mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]);
160 }) {162 out_slice = out_slice[H.digest_length / 2 ..];
161 b2.update(&buffer);163
162 b2.final(&buffer);164 var in_buf: [H.digest_length]u8 = undefined;
163 mem.copy(u8, out_slice, buffer[0..32]);165 while (out_slice.len > H.digest_length) {
164 }166 mem.copy(u8, &in_buf, &out_buf);
165167 H.hash(&in_buf, &out_buf, .{});
166 var r = Blake2b512.digest_length;168 mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]);
167 if (out.len % Blake2b512.digest_length > 0) {169 out_slice = out_slice[H.digest_length / 2 ..];
168 r = ((out.len + 31) / 32) - 2;
169 b2 = Blake2b512.init(.{ .expected_out_bits = r * 8 });
170 }170 }
171171 mem.copy(u8, &in_buf, &out_buf);
172 b2.update(&buffer);172 H.hash(&in_buf, &out_buf, .{ .expected_out_bits = out_slice.len * 8 });
173 b2.final(&buffer);173 mem.copy(u8, out_slice, out_buf[0..out_slice.len]);
174 mem.copy(u8, out_slice, buffer[0..r]);
175}174}
176175
177fn initBlocks(176fn initBlocks(
lib/std/crypto/benchmark.zig+89
...@@ -27,6 +27,8 @@ const hashes = [_]Crypto{...@@ -27,6 +27,8 @@ const hashes = [_]Crypto{
27 Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },27 Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
28 Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },28 Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },
29 Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" },29 Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" },
30 Crypto{ .ty = crypto.hash.sha3.TurboShake128(null), .name = "turboshake-128" },
31 Crypto{ .ty = crypto.hash.sha3.TurboShake256(null), .name = "turboshake-256" },
30 Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },32 Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
31 Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },33 Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
32 Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },34 Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
...@@ -201,6 +203,72 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime...@@ -201,6 +203,72 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime
201 return throughput;203 return throughput;
202}204}
203205
206const kems = [_]Crypto{
207 Crypto{ .ty = crypto.kem.kyber_d00.Kyber512, .name = "kyber512d00" },
208 Crypto{ .ty = crypto.kem.kyber_d00.Kyber768, .name = "kyber768d00" },
209 Crypto{ .ty = crypto.kem.kyber_d00.Kyber1024, .name = "kyber1024d00" },
210};
211
212pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {
213 const key_pair = try Kem.KeyPair.create(null);
214
215 var timer = try Timer.start();
216 const start = timer.lap();
217 {
218 var i: usize = 0;
219 while (i < kems_count) : (i += 1) {
220 const e = key_pair.public_key.encaps(null);
221 mem.doNotOptimizeAway(&e);
222 }
223 }
224 const end = timer.read();
225
226 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
227 const throughput = @floatToInt(u64, kems_count / elapsed_s);
228
229 return throughput;
230}
231
232pub fn benchmarkKemDecaps(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {
233 const key_pair = try Kem.KeyPair.create(null);
234
235 const e = key_pair.public_key.encaps(null);
236
237 var timer = try Timer.start();
238 const start = timer.lap();
239 {
240 var i: usize = 0;
241 while (i < kems_count) : (i += 1) {
242 const ss2 = try key_pair.secret_key.decaps(&e.ciphertext);
243 mem.doNotOptimizeAway(&ss2);
244 }
245 }
246 const end = timer.read();
247
248 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
249 const throughput = @floatToInt(u64, kems_count / elapsed_s);
250
251 return throughput;
252}
253
254pub fn benchmarkKemKeyGen(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {
255 var timer = try Timer.start();
256 const start = timer.lap();
257 {
258 var i: usize = 0;
259 while (i < kems_count) : (i += 1) {
260 const key_pair = try Kem.KeyPair.create(null);
261 mem.doNotOptimizeAway(&key_pair);
262 }
263 }
264 const end = timer.read();
265
266 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
267 const throughput = @floatToInt(u64, kems_count / elapsed_s);
268
269 return throughput;
270}
271
204const aeads = [_]Crypto{272const aeads = [_]Crypto{
205 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },273 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
206 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },274 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
...@@ -483,4 +551,25 @@ pub fn main() !void {...@@ -483,4 +551,25 @@ pub fn main() !void {
483 try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });551 try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });
484 }552 }
485 }553 }
554
555 inline for (kems) |E| {
556 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
557 const throughput = try benchmarkKem(E.ty, mode(1000));
558 try stdout.print("{s:>17}: {:10} encaps/s\n", .{ E.name, throughput });
559 }
560 }
561
562 inline for (kems) |E| {
563 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
564 const throughput = try benchmarkKemDecaps(E.ty, mode(25000));
565 try stdout.print("{s:>17}: {:10} decaps/s\n", .{ E.name, throughput });
566 }
567 }
568
569 inline for (kems) |E| {
570 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
571 const throughput = try benchmarkKemKeyGen(E.ty, mode(25000));
572 try stdout.print("{s:>17}: {:10} keygen/s\n", .{ E.name, throughput });
573 }
574 }
486}575}
lib/std/crypto/blake3.zig+1-1
...@@ -200,7 +200,7 @@ const CompressGeneric = struct {...@@ -200,7 +200,7 @@ const CompressGeneric = struct {
200 }200 }
201};201};
202202
203const compress = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c)203const compress = if (builtin.cpu.arch == .x86_64)
204 CompressVectorized.compress204 CompressVectorized.compress
205else205else
206 CompressGeneric.compress;206 CompressGeneric.compress;
lib/std/crypto/gimli.zig+1-1
...@@ -152,7 +152,7 @@ pub const State = struct {...@@ -152,7 +152,7 @@ pub const State = struct {
152 self.endianSwap();152 self.endianSwap();
153 }153 }
154154
155 pub const permute = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c) impl: {155 pub const permute = if (builtin.cpu.arch == .x86_64) impl: {
156 break :impl permute_vectorized;156 break :impl permute_vectorized;
157 } else if (builtin.mode == .ReleaseSmall) impl: {157 } else if (builtin.mode == .ReleaseSmall) impl: {
158 break :impl permute_small;158 break :impl permute_small;
lib/std/crypto/keccak_p.zig+3-3
...@@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type {...@@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type {
175 /// Apply a (possibly) reduced-round permutation to the state.175 /// Apply a (possibly) reduced-round permutation to the state.
176 pub fn permuteR(self: *Self, comptime rounds: u5) void {176 pub fn permuteR(self: *Self, comptime rounds: u5) void {
177 var i = RC.len - rounds;177 var i = RC.len - rounds;
178 while (i < rounds - rounds % 3) : (i += 3) {178 while (i < RC.len - RC.len % 3) : (i += 3) {
179 self.round(RC[i]);179 self.round(RC[i]);
180 self.round(RC[i + 1]);180 self.round(RC[i + 1]);
181 self.round(RC[i + 2]);181 self.round(RC[i + 2]);
182 }182 }
183 while (i < rounds) : (i += 1) {183 while (i < RC.len) : (i += 1) {
184 self.round(RC[i]);184 self.round(RC[i]);
185 }185 }
186 }186 }
...@@ -231,7 +231,7 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti...@@ -231,7 +231,7 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti
231 bytes = bytes[rate..];231 bytes = bytes[rate..];
232 }232 }
233 if (bytes.len > 0) {233 if (bytes.len > 0) {
234 self.st.addBytes(bytes[0..]);234 mem.copy(u8, &self.buf, bytes);
235 self.offset = bytes.len;235 self.offset = bytes.len;
236 }236 }
237 }237 }
lib/std/crypto/kyber_d00.zig created+1780
...@@ -0,0 +1,1780 @@
1//! Implementation of the IND-CCA2 post-quantum secure key encapsulation
2//! mechanism (KEM) CRYSTALS-Kyber, as submitted to the third round of the NIST
3//! Post-Quantum Cryptography (v3.02/"draft00"), and selected for standardisation.
4//!
5//! Kyber will likely change before final standardisation.
6//!
7//! The namespace suffix (currently `_d00`) refers to the version currently
8//! implemented, in accordance with the draft. It may not be updated if new
9//! versions of the draft only include editorial changes.
10//!
11//! The suffix will eventually be removed once Kyber is finalized.
12//!
13//! Quoting from the CFRG I-D:
14//!
15//! Kyber is not a Diffie-Hellman (DH) style non-interactive key
16//! agreement, but instead, Kyber is a Key Encapsulation Method (KEM).
17//! In essence, a KEM is a Public-Key Encryption (PKE) scheme where the
18//! plaintext cannot be specified, but is generated as a random key as
19//! part of the encryption. A KEM can be transformed into an unrestricted
20//! PKE using HPKE (RFC9180). On its own, a KEM can be used as a key
21//! agreement method in TLS.
22//!
23//! Kyber is an IND-CCA2 secure KEM. It is constructed by applying a
24//! Fujisaki--Okamato style transformation on InnerPKE, which is the
25//! underlying IND-CPA secure Public Key Encryption scheme. We cannot
26//! use InnerPKE directly, as its ciphertexts are malleable.
27//!
28//! ```
29//! F.O. transform
30//! InnerPKE ----------------------> Kyber
31//! IND-CPA IND-CCA2
32//! ```
33//!
34//! Kyber is a lattice-based scheme. More precisely, its security is
35//! based on the learning-with-errors-and-rounding problem in module
36//! lattices (MLWER). The underlying polynomial ring R (defined in
37//! Section 5) is chosen such that multiplication is very fast using the
38//! number theoretic transform (NTT, see Section 5.1.3).
39//!
40//! An InnerPKE private key is a vector _s_ over R of length k which is
41//! _small_ in a particular way. Here k is a security parameter akin to
42//! the size of a prime modulus. For Kyber512, which targets AES-128's
43//! security level, the value of k is 2.
44//!
45//! The public key consists of two values:
46//!
47//! * _A_ a uniformly sampled k by k matrix over R _and_
48//!
49//! * _t = A s + e_, where e is a suitably small masking vector.
50//!
51//! Distinguishing between such A s + e and a uniformly sampled t is the
52//! module learning-with-errors (MLWE) problem. If that is hard, then it
53//! is also hard to recover the private key from the public key as that
54//! would allow you to distinguish between those two.
55//!
56//! To save space in the public key, A is recomputed deterministically
57//! from a seed _rho_.
58//!
59//! A ciphertext for a message m under this public key is a pair (c_1,
60//! c_2) computed roughly as follows:
61//!
62//! c_1 = Compress(A^T r + e_1, d_u)
63//! c_2 = Compress(t^T r + e_2 + Decompress(m, 1), d_v)
64//!
65//! where
66//!
67//! * e_1, e_2 and r are small blinds;
68//!
69//! * Compress(-, d) removes some information, leaving d bits per
70//! coefficient and Decompress is such that Compress after Decompress
71//! does nothing and
72//!
73//! * d_u, d_v are scheme parameters.
74//!
75//! Distinguishing such a ciphertext and uniformly sampled (c_1, c_2) is
76//! an example of the full MLWER problem, see section 4.4 of [KyberV302].
77//!
78//! To decrypt the ciphertext, one computes
79//!
80//! m = Compress(Decompress(c_2, d_v) - s^T Decompress(c_1, d_u), 1).
81//!
82//! It it not straight-forward to see that this formula is correct. In
83//! fact, there is negligable but non-zero probability that a ciphertext
84//! does not decrypt correctly given by the DFP column in Table 4. This
85//! failure probability can be computed by a careful automated analysis
86//! of the probabilities involved, see kyber_failure.py of [SecEst].
87//!
88//! [KyberV302](https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf)
89//! [I-D](https://github.com/bwesterb/draft-schwabe-cfrg-kyber)
90//! [SecEst](https://github.com/pq-crystals/security-estimates)
91
92// TODO
93//
94// - The bottleneck in Kyber are the various hash/xof calls:
95// - Optimize Zig's keccak implementation.
96// - Use SIMD to compute keccak in parallel.
97// - Can we track bounds of coefficients using comptime types without
98// duplicating code?
99// - Would be neater to have tests closer to the thing under test.
100// - When generating a keypair, we have a copy of the inner public key with
101// its large matrix A in both the public key and the private key. In Go we
102// can just have a pointer in the private key to the public key, but
103// how do we do this elegantly in Zig?
104
105const std = @import("std");
106const builtin = @import("builtin");
107
108const testing = std.testing;
109const assert = std.debug.assert;
110const crypto = std.crypto;
111const math = std.math;
112const mem = std.mem;
113const RndGen = std.rand.DefaultPrng;
114const sha3 = crypto.hash.sha3;
115
116// Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1.
117const Q: i16 = 3329;
118
119// Montgomery R
120const R: i32 = 1 << 16;
121
122// Parameter n, degree of polynomials.
123const N: usize = 256;
124
125// Size of "small" vectors used in encryption blinds.
126const eta2: u8 = 2;
127
128const Params = struct {
129 name: []const u8,
130
131 // Width and height of the matrix A.
132 k: u8,
133
134 // Size of "small" vectors used in private key and encryption blinds.
135 eta1: u8,
136
137 // How many bits to retain of u, the private-key independent part
138 // of the ciphertext.
139 du: u8,
140
141 // How many bits to retain of v, the private-key dependent part
142 // of the ciphertext.
143 dv: u8,
144};
145
146pub const Kyber512 = Kyber(.{
147 .name = "Kyber512",
148 .k = 2,
149 .eta1 = 3,
150 .du = 10,
151 .dv = 4,
152});
153
154pub const Kyber768 = Kyber(.{
155 .name = "Kyber768",
156 .k = 3,
157 .eta1 = 2,
158 .du = 10,
159 .dv = 4,
160});
161
162pub const Kyber1024 = Kyber(.{
163 .name = "Kyber1024",
164 .k = 4,
165 .eta1 = 2,
166 .du = 11,
167 .dv = 5,
168});
169
170const modes = [_]type{ Kyber512, Kyber768, Kyber1024 };
171const h_length: usize = 32;
172const inner_seed_length: usize = 32;
173const common_encaps_seed_length: usize = 32;
174const common_shared_key_size: usize = 32;
175
176fn Kyber(comptime p: Params) type {
177 return struct {
178 // Size of a ciphertext, in bytes.
179 pub const ciphertext_length = Poly.compressedSize(p.du) * p.k + Poly.compressedSize(p.dv);
180
181 const Self = @This();
182 const V = Vec(p.k);
183 const M = Mat(p.k);
184
185 /// Length (in bytes) of a shared secret.
186 pub const shared_length = common_shared_key_size;
187 /// Length (in bytes) of a seed for deterministic encapsulation.
188 pub const encaps_seed_length = common_encaps_seed_length;
189 /// Length (in bytes) of a seed for key generation.
190 pub const seed_length: usize = inner_seed_length + shared_length;
191 /// Algorithm name.
192 pub const name = p.name;
193
194 /// A shared secret, and an encapsulated (encrypted) representation of it.
195 pub const EncapsulatedSecret = struct {
196 shared_secret: [shared_length]u8,
197 ciphertext: [ciphertext_length]u8,
198 };
199
200 /// A Kyber public key.
201 pub const PublicKey = struct {
202 pk: InnerPk,
203
204 // Cached
205 hpk: [h_length]u8, // H(pk)
206
207 /// Size of a serialized representation of the key, in bytes.
208 pub const bytes_length = InnerPk.bytes_length;
209
210 /// Generates a shared secret, and encapsulates it for the public key.
211 /// If `seed` is `null`, a random seed is used. This is recommended.
212 /// If `seed` is set, encapsulation is deterministic.
213 pub fn encaps(pk: PublicKey, seed_: ?[encaps_seed_length]u8) EncapsulatedSecret {
214 const seed = seed_ orelse seed: {
215 var random_seed: [encaps_seed_length]u8 = undefined;
216 crypto.random.bytes(&random_seed);
217 break :seed random_seed;
218 };
219
220 var m: [inner_plaintext_length]u8 = undefined;
221
222 // m = H(seed)
223 var h = sha3.Sha3_256.init(.{});
224 h.update(&seed);
225 h.final(&m);
226
227 // (K', r) = G(m ‖ H(pk))
228 var kr: [inner_plaintext_length + h_length]u8 = undefined;
229 var g = sha3.Sha3_512.init(.{});
230 g.update(&m);
231 g.update(&pk.hpk);
232 g.final(&kr);
233
234 // c = innerEncrypy(pk, m, r)
235 const ct = pk.pk.encrypt(&m, kr[32..64]);
236
237 // Compute H(c) and put in second slot of kr, which will be (K', H(c)).
238 h = sha3.Sha3_256.init(.{});
239 h.update(&ct);
240 h.final(kr[32..64]);
241
242 // K = KDF(K' ‖ H(c))
243 var kdf = sha3.Shake256.init(.{});
244 kdf.update(&kr);
245 var ss: [shared_length]u8 = undefined;
246 kdf.squeeze(&ss);
247
248 return EncapsulatedSecret{
249 .shared_secret = ss,
250 .ciphertext = ct,
251 };
252 }
253
254 /// Serializes the key into a byte array.
255 pub fn toBytes(pk: PublicKey) [bytes_length]u8 {
256 return pk.pk.toBytes();
257 }
258
259 /// Deserializes the key from a byte array.
260 pub fn fromBytes(buf: *const [bytes_length]u8) !PublicKey {
261 var ret: PublicKey = undefined;
262 ret.pk = InnerPk.fromBytes(buf[0..InnerPk.bytes_length]);
263
264 var h = sha3.Sha3_256.init(.{});
265 h.update(buf);
266 h.final(&ret.hpk);
267 return ret;
268 }
269 };
270
271 /// A Kyber secret key.
272 pub const SecretKey = struct {
273 sk: InnerSk,
274 pk: InnerPk,
275 hpk: [h_length]u8, // H(pk)
276 z: [shared_length]u8,
277
278 /// Size of a serialized representation of the key, in bytes.
279 pub const bytes_length: usize =
280 InnerSk.bytes_length + InnerPk.bytes_length + h_length + shared_length;
281
282 /// Decapsulates the shared secret within ct using the private key.
283 pub fn decaps(sk: SecretKey, ct: *const [ciphertext_length]u8) ![shared_length]u8 {
284 // m' = innerDec(ct)
285 const m2 = sk.sk.decrypt(ct);
286
287 // (K'', r') = G(m' ‖ H(pk))
288 var kr2: [64]u8 = undefined;
289 var g = sha3.Sha3_512.init(.{});
290 g.update(&m2);
291 g.update(&sk.hpk);
292 g.final(&kr2);
293
294 // ct' = innerEnc(pk, m', r')
295 const ct2 = sk.pk.encrypt(&m2, kr2[32..64]);
296
297 // Compute H(ct) and put in the second slot of kr2 which will be (K'', H(ct)).
298 var h = sha3.Sha3_256.init(.{});
299 h.update(ct);
300 h.final(kr2[32..64]);
301
302 // Replace K'' by z in the first slot of kr2 if ct ≠ ct'.
303 cmov(32, kr2[0..32], sk.z, ctneq(ciphertext_length, ct.*, ct2));
304
305 // K = KDF(K''/z, H(c))
306 var kdf = sha3.Shake256.init(.{});
307 var ss: [shared_length]u8 = undefined;
308 kdf.update(&kr2);
309 kdf.squeeze(&ss);
310 return ss;
311 }
312
313 /// Serializes the key into a byte array.
314 pub fn toBytes(sk: SecretKey) [bytes_length]u8 {
315 return sk.sk.toBytes() ++ sk.pk.toBytes() ++ sk.hpk ++ sk.z;
316 }
317
318 /// Deserializes the key from a byte array.
319 pub fn fromBytes(buf: *const [bytes_length]u8) !SecretKey {
320 var ret: SecretKey = undefined;
321 comptime var s: usize = 0;
322 ret.sk = InnerSk.fromBytes(buf[s .. s + InnerSk.bytes_length]);
323 s += InnerSk.bytes_length;
324 ret.pk = InnerPk.fromBytes(buf[s .. s + InnerPk.bytes_length]);
325 s += InnerPk.bytes_length;
326 mem.copy(u8, &ret.hpk, buf[s .. s + h_length]);
327 s += h_length;
328 mem.copy(u8, &ret.z, buf[s .. s + shared_length]);
329 return ret;
330 }
331 };
332
333 /// A Kyber key pair.
334 pub const KeyPair = struct {
335 secret_key: SecretKey,
336 public_key: PublicKey,
337
338 /// Create a new key pair.
339 /// If seed is null, a random seed will be generated.
340 /// If a seed is provided, the key pair will be determinsitic.
341 pub fn create(seed_: ?[seed_length]u8) !KeyPair {
342 const seed = seed_ orelse sk: {
343 var random_seed: [seed_length]u8 = undefined;
344 crypto.random.bytes(&random_seed);
345 break :sk random_seed;
346 };
347 var ret: KeyPair = undefined;
348 mem.copy(u8, &ret.secret_key.z, seed[inner_seed_length..seed_length]);
349
350 // Generate inner key
351 innerKeyFromSeed(
352 seed[0..inner_seed_length].*,
353 &ret.public_key.pk,
354 &ret.secret_key.sk,
355 );
356 ret.secret_key.pk = ret.public_key.pk;
357
358 // Copy over z from seed.
359 mem.copy(u8, &ret.secret_key.z, seed[inner_seed_length..seed_length]);
360
361 // Compute H(pk)
362 var h = sha3.Sha3_256.init(.{});
363 h.update(&ret.public_key.pk.toBytes());
364 h.final(&ret.secret_key.hpk);
365 ret.public_key.hpk = ret.secret_key.hpk;
366
367 return ret;
368 }
369 };
370
371 // Size of plaintexts of the in
372 const inner_plaintext_length: usize = Poly.compressedSize(1);
373
374 const InnerPk = struct {
375 rho: [32]u8, // ρ, the seed for the matrix A
376 th: V, // NTT(t), normalized
377
378 // Cached values
379 aT: M,
380
381 const bytes_length = V.bytes_length + 32;
382
383 fn encrypt(
384 pk: InnerPk,
385 pt: *const [inner_plaintext_length]u8,
386 seed: *const [32]u8,
387 ) [ciphertext_length]u8 {
388 // Sample r, e₁ and e₂ appropriately
389 const rh = V.noise(p.eta1, 0, seed).ntt().barrettReduce();
390 const e1 = V.noise(eta2, p.k, seed);
391 const e2 = Poly.noise(eta2, 2 * p.k, seed);
392
393 // Next we compute u = Aᵀ r + e₁. First Aᵀ.
394 var u: V = undefined;
395 for (0..p.k) |i| {
396 // Note that coefficients of r are bounded by q and those of Aᵀ
397 // are bounded by 4.5q and so their product is bounded by 2¹⁵q
398 // as required for multiplication.
399 u.ps[i] = pk.aT.vs[i].dotHat(rh);
400 }
401
402 // Aᵀ and r were not in Montgomery form, so the Montgomery
403 // multiplications in the inner product added a factor R⁻¹ which
404 // the InvNTT cancels out.
405 u = u.barrettReduce().invNTT().add(e1).normalize();
406
407 // Next, compute v = <t, r> + e₂ + Decompress_q(m, 1)
408 const v = pk.th.dotHat(rh).barrettReduce().invNTT()
409 .add(Poly.decompress(1, pt)).add(e2).normalize();
410
411 return u.compress(p.du) ++ v.compress(p.dv);
412 }
413
414 fn toBytes(pk: InnerPk) [bytes_length]u8 {
415 return pk.th.toBytes() ++ pk.rho;
416 }
417
418 fn fromBytes(buf: *const [bytes_length]u8) InnerPk {
419 var ret: InnerPk = undefined;
420 ret.th = V.fromBytes(buf[0..V.bytes_length]).normalize();
421 mem.copy(u8, &ret.rho, buf[V.bytes_length..bytes_length]);
422 ret.aT = M.uniform(ret.rho, true);
423 return ret;
424 }
425 };
426
427 // Private key of the inner PKE
428 const InnerSk = struct {
429 sh: V, // NTT(s), normalized
430 const bytes_length = V.bytes_length;
431
432 fn decrypt(sk: InnerSk, ct: *const [ciphertext_length]u8) [inner_plaintext_length]u8 {
433 const u = V.decompress(p.du, ct[0..comptime V.compressedSize(p.du)]);
434 const v = Poly.decompress(
435 p.dv,
436 ct[comptime V.compressedSize(p.du)..ciphertext_length],
437 );
438
439 // Compute m = v - <s, u>
440 return v.sub(sk.sh.dotHat(u.ntt()).barrettReduce().invNTT())
441 .normalize().compress(1);
442 }
443
444 fn toBytes(sk: InnerSk) [bytes_length]u8 {
445 return sk.sh.toBytes();
446 }
447
448 fn fromBytes(buf: *const [bytes_length]u8) InnerSk {
449 var ret: InnerSk = undefined;
450 ret.sh = V.fromBytes(buf).normalize();
451 return ret;
452 }
453 };
454
455 // Derives inner PKE keypair from given seed.
456 fn innerKeyFromSeed(seed: [inner_seed_length]u8, pk: *InnerPk, sk: *InnerSk) void {
457 var expanded_seed: [64]u8 = undefined;
458
459 var h = sha3.Sha3_512.init(.{});
460 h.update(&seed);
461 h.final(&expanded_seed);
462 mem.copy(u8, &pk.rho, expanded_seed[0..32]);
463 const sigma = expanded_seed[32..64];
464 pk.aT = M.uniform(pk.rho, false); // Expand ρ to A; we'll transpose later on
465
466 // Sample secret vector s.
467 sk.sh = V.noise(p.eta1, 0, sigma).ntt().normalize();
468
469 const eh = Vec(p.k).noise(p.eta1, p.k, sigma).ntt(); // sample blind e.
470 var th: V = undefined;
471
472 // Next, we compute t = A s + e.
473 for (0..p.k) |i| {
474 // Note that coefficients of s are bounded by q and those of A
475 // are bounded by 4.5q and so their product is bounded by 2¹⁵q
476 // as required for multiplication.
477 // A and s were not in Montgomery form, so the Montgomery
478 // multiplications in the inner product added a factor R⁻¹ which
479 // we'll cancel out with toMont(). This will also ensure the
480 // coefficients of th are bounded in absolute value by q.
481 th.ps[i] = pk.aT.vs[i].dotHat(sk.sh).toMont();
482 }
483
484 pk.th = th.add(eh).normalize(); // bounded by 8q
485 pk.aT = pk.aT.transpose();
486 }
487 };
488}
489
490// R mod q
491const r_mod_q: i32 = @rem(@as(i32, R), Q);
492
493// R² mod q
494const r2_mod_q: i32 = @rem(r_mod_q * r_mod_q, Q);
495
496// ζ is the degree 256 primitive root of unity used for the NTT.
497const zeta: i16 = 17;
498
499// (128)⁻¹ R². Used in inverse NTT.
500const r2_over_128: i32 = @mod(invertMod(128, Q) * r2_mod_q, Q);
501
502// zetas lists precomputed powers of the primitive root of unity in
503// Montgomery representation used for the NTT:
504//
505// zetas[i] = ζᵇʳᵛ⁽ⁱ⁾ R mod q
506//
507// where ζ = 17, brv(i) is the bitreversal of a 7-bit number and R=2¹⁶ mod q.
508const zetas = computeZetas();
509
510// invNTTReductions keeps track of which coefficients to apply Barrett
511// reduction to in Poly.invNTT().
512//
513// Generated lazily: once a butterfly is computed which is about to
514// overflow the i16, the largest coefficient is reduced. If that is
515// not enough, the other coefficient is reduced as well.
516//
517// This is actually optimal, as proven in https://eprint.iacr.org/2020/1377.pdf
518// TODO generate comptime?
519const inv_ntt_reductions = [_]i16{
520 -1, // after layer 1
521 -1, // after layer 2
522 16,
523 17,
524 48,
525 49,
526 80,
527 81,
528 112,
529 113,
530 144,
531 145,
532 176,
533 177,
534 208,
535 209,
536 240, 241, -1, // after layer 3
537 0, 1, 32,
538 33, 34, 35,
539 64, 65, 96,
540 97, 98, 99,
541 128, 129,
542 160, 161, 162, 163, 192, 193, 224, 225, 226, 227, -1, // after layer 4
543 2, 3, 66, 67, 68, 69, 70, 71, 130, 131, 194,
544 195, 196, 197,
545 198, 199, -1, // after layer 5
546 4, 5, 6,
547 7, 132, 133,
548 134, 135, 136,
549 137, 138, 139,
550 140, 141,
551 142, 143, -1, // after layer 6
552 -1, // after layer 7
553};
554
555test "invNTTReductions bounds" {
556 // Checks whether the reductions proposed by invNTTReductions
557 // don't overflow during invNTT().
558 var xs = [_]i32{1} ** 256; // start at |x| ≤ q
559
560 var r: usize = 0;
561 var layer: math.Log2Int(usize) = 1;
562 while (layer < 8) : (layer += 1) {
563 const w = @as(usize, 1) << layer;
564 var i: usize = 0;
565
566 while (i + w < 256) {
567 xs[i] = xs[i] + xs[i + w];
568 try testing.expect(xs[i] <= 9); // we can't exceed 9q
569 xs[i + w] = 1;
570 i += 1;
571 if (@mod(i, w) == 0) {
572 i += w;
573 }
574 }
575
576 while (true) {
577 const j = inv_ntt_reductions[r];
578 r += 1;
579 if (j < 0) {
580 break;
581 }
582 xs[@intCast(usize, j)] = 1;
583 }
584 }
585}
586
587// Extended euclidean algorithm.
588//
589// For a, b finds x, y such that x a + y b = gcd(a, b). Used to compute
590// modular inverse.
591fn eea(a: anytype, b: @TypeOf(a)) EeaResult(@TypeOf(a)) {
592 if (a == 0) {
593 return .{ .gcd = b, .x = 0, .y = 1 };
594 }
595 const r = eea(@rem(b, a), a);
596 return .{ .gcd = r.gcd, .x = r.y - @divTrunc(b, a) * r.x, .y = r.x };
597}
598
599fn EeaResult(comptime T: type) type {
600 return struct { gcd: T, x: T, y: T };
601}
602
603// Returns least common multiple of a and b.
604fn lcm(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
605 const r = eea(a, b);
606 return a * b / r.gcd;
607}
608
609// Invert modulo p.
610fn invertMod(a: anytype, p: @TypeOf(a)) @TypeOf(a) {
611 const r = eea(a, p);
612 assert(r.gcd == 1);
613 return r.x;
614}
615
616// Reduce mod q for testing.
617fn modQ32(x: i32) i16 {
618 var y = @intCast(i16, @rem(x, @as(i32, Q)));
619 if (y < 0) {
620 y += Q;
621 }
622 return y;
623}
624
625// Given -2¹⁵ q ≤ x < 2¹⁵ q, returns -q < y < q with x 2⁻¹⁶ = y (mod q).
626fn montReduce(x: i32) i16 {
627 const qInv = comptime invertMod(@as(i32, Q), R);
628 // This is Montgomery reduction with R=2¹⁶.
629 //
630 // Note gcd(2¹⁶, q) = 1 as q is prime. Write q' := 62209 = q⁻¹ mod R.
631 // First we compute
632 //
633 // m := ((x mod R) q') mod R
634 // = x q' mod R
635 // = int16(x q')
636 // = int16(int32(x) * int32(q'))
637 //
638 // Note that x q' might be as big as 2³² and could overflow the int32
639 // multiplication in the last line. However for any int32s a and b,
640 // we have int32(int64(a)*int64(b)) = int32(a*b) and so the result is ok.
641 const m = @truncate(i16, @truncate(i32, x *% qInv));
642
643 // Note that x - m q is divisable by R; indeed modulo R we have
644 //
645 // x - m q ≡ x - x q' q ≡ x - x q⁻¹ q ≡ x - x = 0.
646 //
647 // We return y := (x - m q) / R. Note that y is indeed correct as
648 // modulo q we have
649 //
650 // y ≡ x R⁻¹ - m q R⁻¹ = x R⁻¹
651 //
652 // and as both 2¹⁵ q ≤ m q, x < 2¹⁵ q, we have
653 // 2¹⁶ q ≤ x - m q < 2¹⁶ and so q ≤ (x - m q) / R < q as desired.
654 const yR = x - @as(i32, m) * @as(i32, Q);
655 return @bitCast(i16, @truncate(u16, @bitCast(u32, yR) >> 16));
656}
657
658test "Test montReduce" {
659 var rnd = RndGen.init(0);
660 for (0..1000) |_| {
661 const bound = comptime @as(i32, Q) * (1 << 15);
662 const x = rnd.random().intRangeLessThan(i32, -bound, bound);
663 const y = montReduce(x);
664 try testing.expect(-Q < y and y < Q);
665 try testing.expectEqual(modQ32(x), modQ32(@as(i32, y) * R));
666 }
667}
668
669// Given any x, return x R mod q where R=2¹⁶.
670fn feToMont(x: i16) i16 {
671 // Note |1353 x| ≤ 1353 2¹⁵ ≤ 13318 q ≤ 2¹⁵ q and so we're within
672 // the bounds of montReduce.
673 return montReduce(@as(i32, x) * r2_mod_q);
674}
675
676test "Test feToMont" {
677 var x: i32 = -(1 << 15);
678 while (x < 1 << 15) : (x += 1) {
679 const y = feToMont(@intCast(i16, x));
680 try testing.expectEqual(modQ32(@as(i32, y)), modQ32(x * r_mod_q));
681 }
682}
683
684// Given any x, compute 0 ≤ y ≤ q with x = y (mod q).
685//
686// Beware: we might have feBarrettReduce(x) = q ≠ 0 for some x. In fact,
687// this happens if and only if x = -nq for some positive integer n.
688fn feBarrettReduce(x: i16) i16 {
689 // This is standard Barrett reduction.
690 //
691 // For any x we have x mod q = x - ⌊x/q⌋ q. We will use 20159/2²⁶ as
692 // an approximation of 1/q. Note that 0 ≤ 20159/2²⁶ - 1/q ≤ 0.135/2²⁶
693 // and so | x 20156/2²⁶ - x/q | ≤ 2⁻¹⁰ for |x| ≤ 2¹⁶. For all x
694 // not a multiple of q, the number x/q is further than 1/q from any integer
695 // and so ⌊x 20156/2²⁶⌋ = ⌊x/q⌋. If x is a multiple of q and x is positive,
696 // then x 20156/2²⁶ is larger than x/q so ⌊x 20156/2²⁶⌋ = ⌊x/q⌋ as well.
697 // Finally, if x is negative multiple of q, then ⌊x 20156/2²⁶⌋ = ⌊x/q⌋-1.
698 // Thus
699 // [ q if x=-nq for pos. integer n
700 // x - ⌊x 20156/2²⁶⌋ q = [
701 // [ x mod q otherwise
702 //
703 // To actually compute this, note that
704 //
705 // ⌊x 20156/2²⁶⌋ = (20159 x) >> 26.
706 return x -% @intCast(i16, (@as(i32, x) * 20159) >> 26) *% Q;
707}
708
709test "Test Barrett reduction" {
710 var x: i32 = -(1 << 15);
711 while (x < 1 << 15) : (x += 1) {
712 var y1 = feBarrettReduce(@intCast(i16, x));
713 const y2 = @mod(@intCast(i16, x), Q);
714 if (x < 0 and @rem(-x, Q) == 0) {
715 y1 -= Q;
716 }
717 try testing.expectEqual(y1, y2);
718 }
719}
720
721// Returns x if x < q and x - q otherwise. Assumes x ≥ -29439.
722fn csubq(x: i16) i16 {
723 var r = x;
724 r -= Q;
725 r += (r >> 15) & Q;
726 return r;
727}
728
729test "Test csubq" {
730 var x: i32 = -29439;
731 while (x < 1 << 15) : (x += 1) {
732 const y1 = csubq(@intCast(i16, x));
733 var y2 = @intCast(i16, x);
734 if (@intCast(i16, x) >= Q) {
735 y2 -= Q;
736 }
737 try testing.expectEqual(y1, y2);
738 }
739}
740
741// Compute a^s mod p.
742fn mpow(a: anytype, s: @TypeOf(a), p: @TypeOf(a)) @TypeOf(a) {
743 var ret: @TypeOf(a) = 1;
744 var s2 = s;
745 var a2 = a;
746
747 while (true) {
748 if (s2 & 1 == 1) {
749 ret = @mod(ret * a2, p);
750 }
751 s2 >>= 1;
752 if (s2 == 0) {
753 break;
754 }
755 a2 = @mod(a2 * a2, p);
756 }
757 return ret;
758}
759
760// Computes zetas table used by ntt and invNTT.
761fn computeZetas() [128]i16 {
762 @setEvalBranchQuota(10000);
763 var ret: [128]i16 = undefined;
764 for (&ret, 0..) |*r, i| {
765 const t = @intCast(i16, mpow(@as(i32, zeta), @bitReverse(@intCast(u7, i)), Q));
766 r.* = csubq(feBarrettReduce(feToMont(t)));
767 }
768 return ret;
769}
770
771// An element of our base ring R which are polynomials over ℤ_q
772// modulo the equation Xᴺ = -1, where q=3329 and N=256.
773//
774// This type is also used to store NTT-transformed polynomials,
775// see Poly.NTT().
776//
777// Coefficients aren't always reduced. See Normalize().
778const Poly = struct {
779 cs: [N]i16,
780
781 const bytes_length = N / 2 * 3;
782 const zero: Poly = .{ .cs = .{0} ** N };
783
784 fn add(a: Poly, b: Poly) Poly {
785 var ret: Poly = undefined;
786 for (0..N) |i| {
787 ret.cs[i] = a.cs[i] + b.cs[i];
788 }
789 return ret;
790 }
791
792 fn sub(a: Poly, b: Poly) Poly {
793 var ret: Poly = undefined;
794 for (0..N) |i| {
795 ret.cs[i] = a.cs[i] - b.cs[i];
796 }
797 return ret;
798 }
799
800 // For testing, generates a random polynomial with for each
801 // coefficient |x| ≤ q.
802 fn randAbsLeqQ(rnd: anytype) Poly {
803 var ret: Poly = undefined;
804 for (0..N) |i| {
805 ret.cs[i] = rnd.random().intRangeAtMost(i16, -Q, Q);
806 }
807 return ret;
808 }
809
810 // For testing, generates a random normalized polynomial.
811 fn randNormalized(rnd: anytype) Poly {
812 var ret: Poly = undefined;
813 for (0..N) |i| {
814 ret.cs[i] = rnd.random().intRangeLessThan(i16, 0, Q);
815 }
816 return ret;
817 }
818
819 // Executes a forward "NTT" on p.
820 //
821 // Assumes the coefficients are in absolute value ≤q. The resulting
822 // coefficients are in absolute value ≤7q. If the input is in Montgomery
823 // form, then the result is in Montgomery form and so (by linearity of the NTT)
824 // if the input is in regular form, then the result is also in regular form.
825 fn ntt(a: Poly) Poly {
826 // Note that ℤ_q does not have a primitive 512ᵗʰ root of unity (as 512
827 // does not divide into q-1) and so we cannot do a regular NTT. ℤ_q
828 // does have a primitive 256ᵗʰ root of unity, the smallest of which
829 // is ζ := 17.
830 //
831 // Recall that our base ring R := ℤ_q[x] / (x²⁵⁶ + 1). The polynomial
832 // x²⁵⁶+1 will not split completely (as its roots would be 512ᵗʰ roots
833 // of unity.) However, it does split almost (using ζ¹²⁸ = -1):
834 //
835 // x²⁵⁶ + 1 = (x²)¹²⁸ - ζ¹²⁸
836 // = ((x²)⁶⁴ - ζ⁶⁴)((x²)⁶⁴ + ζ⁶⁴)
837 // = ((x²)³² - ζ³²)((x²)³² + ζ³²)((x²)³² - ζ⁹⁶)((x²)³² + ζ⁹⁶)
838 // ⋮
839 // = (x² - ζ)(x² + ζ)(x² - ζ⁶⁵)(x² + ζ⁶⁵) … (x² + ζ¹²⁷)
840 //
841 // Note that the powers of ζ that appear (from the second line down) are
842 // in binary
843 //
844 // 0100000 1100000
845 // 0010000 1010000 0110000 1110000
846 // 0001000 1001000 0101000 1101000 0011000 1011000 0111000 1111000
847 // …
848 //
849 // That is: brv(2), brv(3), brv(4), …, where brv(x) denotes the 7-bit
850 // bitreversal of x. These powers of ζ are given by the Zetas array.
851 //
852 // The polynomials x² ± ζⁱ are irreducible and coprime, hence by
853 // the Chinese Remainder Theorem we know
854 //
855 // ℤ_q[x]/(x²⁵⁶+1) → ℤ_q[x]/(x²-ζ) x … x ℤ_q[x]/(x²+ζ¹²⁷)
856 //
857 // given by a ↦ ( a mod x²-ζ, …, a mod x²+ζ¹²⁷ )
858 // is an isomorphism, which is the "NTT". It can be efficiently computed by
859 //
860 //
861 // a ↦ ( a mod (x²)⁶⁴ - ζ⁶⁴, a mod (x²)⁶⁴ + ζ⁶⁴ )
862 // ↦ ( a mod (x²)³² - ζ³², a mod (x²)³² + ζ³²,
863 // a mod (x²)⁹⁶ - ζ⁹⁶, a mod (x²)⁹⁶ + ζ⁹⁶ )
864 //
865 // et cetera
866 // If N was 8 then this can be pictured in the following diagram:
867 //
868 // https://cnx.org/resources/17ee4dfe517a6adda05377b25a00bf6e6c93c334/File0026.png
869 //
870 // Each cross is a Cooley-Tukey butterfly: it's the map
871 //
872 // (a, b) ↦ (a + ζb, a - ζb)
873 //
874 // for the appropriate power ζ for that column and row group.
875 var p = a;
876 var k: usize = 0; // index into zetas
877
878 var l = N >> 1;
879 while (l > 1) : (l >>= 1) {
880 // On the nᵗʰ iteration of the l-loop, the absolute value of the
881 // coefficients are bounded by nq.
882
883 // offset effectively loops over the row groups in this column; it is
884 // the first row in the row group.
885 var offset: usize = 0;
886 while (offset < N - l) : (offset += 2 * l) {
887 k += 1;
888 const z = @as(i32, zetas[k]);
889
890 // j loops over each butterfly in the row group.
891 for (offset..offset + l) |j| {
892 const t = montReduce(z * @as(i32, p.cs[j + l]));
893 p.cs[j + l] = p.cs[j] - t;
894 p.cs[j] += t;
895 }
896 }
897 }
898
899 return p;
900 }
901
902 // Executes an inverse "NTT" on p and multiply by the Montgomery factor R.
903 //
904 // Assumes the coefficients are in absolute value ≤q. The resulting
905 // coefficients are in absolute value ≤q. If the input is in Montgomery
906 // form, then the result is in Montgomery form and so (by linearity)
907 // if the input is in regular form, then the result is also in regular form.
908 fn invNTT(a: Poly) Poly {
909 var k: usize = 127; // index into zetas
910 var r: usize = 0; // index into invNTTReductions
911 var p = a;
912
913 // We basically do the oppposite of NTT, but postpone dividing by 2 in the
914 // inverse of the Cooley-Tukey butterfly and accumulate that into a big
915 // division by 2⁷ at the end. See the comments in the ntt() function.
916
917 var l: usize = 2;
918 while (l < N) : (l <<= 1) {
919 var offset: usize = 0;
920 while (offset < N - l) : (offset += 2 * l) {
921 // As we're inverting, we need powers of ζ⁻¹ (instead of ζ).
922 // To be precise, we need ζᵇʳᵛ⁽ᵏ⁾⁻¹²⁸. However, as ζ⁻¹²⁸ = -1,
923 // we can use the existing zetas table instead of
924 // keeping a separate invZetas table as in Dilithium.
925
926 const minZeta = @as(i32, zetas[k]);
927 k -= 1;
928
929 for (offset..offset + l) |j| {
930 // Gentleman-Sande butterfly: (a, b) ↦ (a + b, ζ(a-b))
931 const t = p.cs[j + l] - p.cs[j];
932 p.cs[j] += p.cs[j + l];
933 p.cs[j + l] = montReduce(minZeta * @as(i32, t));
934
935 // Note that if we had |a| < αq and |b| < βq before the
936 // butterfly, then now we have |a| < (α+β)q and |b| < q.
937 }
938 }
939
940 // We let the invNTTReductions instruct us which coefficients to
941 // Barrett reduce.
942 while (true) {
943 const i = inv_ntt_reductions[r];
944 r += 1;
945 if (i < 0) {
946 break;
947 }
948 p.cs[@intCast(usize, i)] = feBarrettReduce(p.cs[@intCast(usize, i)]);
949 }
950 }
951
952 for (0..N) |j| {
953 // Note 1441 = (128)⁻¹ R². The coefficients are bounded by 9q, so
954 // as 1441 * 9 ≈ 2¹⁴ < 2¹⁵, we're within the required bounds
955 // for montReduce().
956 p.cs[j] = montReduce(r2_over_128 * @as(i32, p.cs[j]));
957 }
958
959 return p;
960 }
961
962 // Normalizes coefficients.
963 //
964 // Ensures each coefficient is in {0, …, q-1}.
965 fn normalize(a: Poly) Poly {
966 var ret: Poly = undefined;
967 for (0..N) |i| {
968 ret.cs[i] = csubq(feBarrettReduce(a.cs[i]));
969 }
970 return ret;
971 }
972
973 // Put p in Montgomery form.
974 fn toMont(a: Poly) Poly {
975 var ret: Poly = undefined;
976 for (0..N) |i| {
977 ret.cs[i] = feToMont(a.cs[i]);
978 }
979 return ret;
980 }
981
982 // Barret reduce coefficients.
983 //
984 // Beware, this does not fully normalize coefficients.
985 fn barrettReduce(a: Poly) Poly {
986 var ret: Poly = undefined;
987 for (0..N) |i| {
988 ret.cs[i] = feBarrettReduce(a.cs[i]);
989 }
990 return ret;
991 }
992
993 fn compressedSize(comptime d: u8) usize {
994 return @divTrunc(N * d, 8);
995 }
996
997 // Returns packed Compress_q(p, d).
998 //
999 // Assumes p is normalized.
1000 fn compress(p: Poly, comptime d: u8) [compressedSize(d)]u8 {
1001 @setEvalBranchQuota(10000);
1002 const q_over_2: u32 = comptime @divTrunc(Q, 2); // (q-1)/2
1003 const two_d_min_1: u32 = comptime (1 << d) - 1; // 2ᵈ-1
1004 var in_off: usize = 0;
1005 var out_off: usize = 0;
1006
1007 const batch_size: usize = comptime lcm(@as(i16, d), 8);
1008 const in_batch_size: usize = comptime batch_size / d;
1009 const out_batch_size: usize = comptime batch_size / 8;
1010
1011 const out_length: usize = comptime @divTrunc(N * d, 8);
1012 comptime assert(out_length * 8 == d * N);
1013 var out = [_]u8{0} ** out_length;
1014
1015 while (in_off < N) {
1016 // First we compress into in.
1017 var in: [in_batch_size]u16 = undefined;
1018 inline for (0..in_batch_size) |i| {
1019 // Compress_q(x, d) = ⌈(2ᵈ/q)x⌋ mod⁺ 2ᵈ
1020 // = ⌊(2ᵈ/q)x+½⌋ mod⁺ 2ᵈ
1021 // = ⌊((x << d) + q/2) / q⌋ mod⁺ 2ᵈ
1022 // = DIV((x << d) + q/2, q) & ((1<<d) - 1)
1023 const t = @intCast(u32, p.cs[in_off + i]) << d;
1024 in[i] = @intCast(u16, @divFloor(t + q_over_2, Q) & two_d_min_1);
1025 }
1026
1027 // Now we pack the d-bit integers from `in' into out as bytes.
1028 comptime var in_shift: usize = 0;
1029 comptime var j: usize = 0;
1030 comptime var i: usize = 0;
1031 inline while (i < in_batch_size) : (j += 1) {
1032 comptime var todo: usize = 8;
1033 inline while (todo > 0) {
1034 const out_shift = comptime 8 - todo;
1035 out[out_off + j] |= @truncate(u8, (in[i] >> in_shift) << out_shift);
1036
1037 const done = comptime @min(@min(d, todo), d - in_shift);
1038 todo -= done;
1039 in_shift += done;
1040
1041 if (in_shift == d) {
1042 in_shift = 0;
1043 i += 1;
1044 }
1045 }
1046 }
1047
1048 in_off += in_batch_size;
1049 out_off += out_batch_size;
1050 }
1051
1052 return out;
1053 }
1054
1055 // Set p to Decompress_q(m, d).
1056 fn decompress(comptime d: u8, in: *const [compressedSize(d)]u8) Poly {
1057 @setEvalBranchQuota(10000);
1058 const inLen = comptime @divTrunc(N * d, 8);
1059 comptime assert(inLen * 8 == d * N);
1060 var ret: Poly = undefined;
1061 var in_off: usize = 0;
1062 var out_off: usize = 0;
1063
1064 const batch_size: usize = comptime lcm(@as(i16, d), 8);
1065 const in_batch_size: usize = comptime batch_size / 8;
1066 const out_batch_size: usize = comptime batch_size / d;
1067
1068 while (out_off < N) {
1069 comptime var in_shift: usize = 0;
1070 comptime var j: usize = 0;
1071 comptime var i: usize = 0;
1072 inline while (i < out_batch_size) : (i += 1) {
1073 // First, unpack next coefficient.
1074 comptime var todo = d;
1075 var out: u16 = 0;
1076
1077 inline while (todo > 0) {
1078 const out_shift = comptime d - todo;
1079 const m = comptime (1 << d) - 1;
1080 out |= (@as(u16, in[in_off + j] >> in_shift) << out_shift) & m;
1081
1082 const done = comptime @min(@min(8, todo), 8 - in_shift);
1083 todo -= done;
1084 in_shift += done;
1085
1086 if (in_shift == 8) {
1087 in_shift = 0;
1088 j += 1;
1089 }
1090 }
1091
1092 // Decompress_q(x, d) = ⌈(q/2ᵈ)x⌋
1093 // = ⌊(q/2ᵈ)x+½⌋
1094 // = ⌊(qx + 2ᵈ⁻¹)/2ᵈ⌋
1095 // = (qx + (1<<(d-1))) >> d
1096 const qx = @as(u32, out) * @as(u32, Q);
1097 ret.cs[out_off + i] = @intCast(i16, (qx + (1 << (d - 1))) >> d);
1098 }
1099
1100 in_off += in_batch_size;
1101 out_off += out_batch_size;
1102 }
1103
1104 return ret;
1105 }
1106
1107 // Returns the "pointwise" multiplication a o b.
1108 //
1109 // That is: invNTT(a o b) = invNTT(a) * invNTT(b). Assumes a and b are in
1110 // Montgomery form. Products between coefficients of a and b must be strictly
1111 // bounded in absolute value by 2¹⁵q. a o b will be in Montgomery form and
1112 // bounded in absolute value by 2q.
1113 fn mulHat(a: Poly, b: Poly) Poly {
1114 // Recall from the discussion in ntt(), that a transformed polynomial is
1115 // an element of ℤ_q[x]/(x²-ζ) x … x ℤ_q[x]/(x²+ζ¹²⁷);
1116 // that is: 128 degree-one polynomials instead of simply 256 elements
1117 // from ℤ_q as in the regular NTT. So instead of pointwise multiplication,
1118 // we multiply the 128 pairs of degree-one polynomials modulo the
1119 // right equation:
1120 //
1121 // (a₁ + a₂x)(b₁ + b₂x) = a₁b₁ + a₂b₂ζ' + (a₁b₂ + a₂b₁)x,
1122 //
1123 // where ζ' is the appropriate power of ζ.
1124
1125 var p: Poly = undefined;
1126 var k: usize = 64;
1127 var i: usize = 0;
1128 while (i < N) : (i += 4) {
1129 const z = @as(i32, zetas[k]);
1130 k += 1;
1131
1132 const a1b1 = montReduce(@as(i32, a.cs[i + 1]) * @as(i32, b.cs[i + 1]));
1133 const a0b0 = montReduce(@as(i32, a.cs[i]) * @as(i32, b.cs[i]));
1134 const a1b0 = montReduce(@as(i32, a.cs[i + 1]) * @as(i32, b.cs[i]));
1135 const a0b1 = montReduce(@as(i32, a.cs[i]) * @as(i32, b.cs[i + 1]));
1136
1137 p.cs[i] = montReduce(a1b1 * z) + a0b0;
1138 p.cs[i + 1] = a0b1 + a1b0;
1139
1140 const a3b3 = montReduce(@as(i32, a.cs[i + 3]) * @as(i32, b.cs[i + 3]));
1141 const a2b2 = montReduce(@as(i32, a.cs[i + 2]) * @as(i32, b.cs[i + 2]));
1142 const a3b2 = montReduce(@as(i32, a.cs[i + 3]) * @as(i32, b.cs[i + 2]));
1143 const a2b3 = montReduce(@as(i32, a.cs[i + 2]) * @as(i32, b.cs[i + 3]));
1144
1145 p.cs[i + 2] = a2b2 - montReduce(a3b3 * z);
1146 p.cs[i + 3] = a2b3 + a3b2;
1147 }
1148
1149 return p;
1150 }
1151
1152 // Sample p from a centered binomial distribution with n=2η and p=½ - viz:
1153 // coefficients are in {-η, …, η} with probabilities
1154 //
1155 // {ncr(0, 2η)/2^2η, ncr(1, 2η)/2^2η, …, ncr(2η,2η)/2^2η}
1156 fn noise(comptime eta: u8, nonce: u8, seed: *const [32]u8) Poly {
1157 var h = sha3.Shake256.init(.{});
1158 const suffix: [1]u8 = .{nonce};
1159 h.update(seed);
1160 h.update(&suffix);
1161
1162 // The distribution at hand is exactly the same as that
1163 // of (a₁ + a₂ + … + a_η) - (b₁ + … + b_η) where a_i,b_i~U(1).
1164 // Thus we need 2η bits per coefficient.
1165 const buf_len = comptime 2 * eta * N / 8;
1166 var buf: [buf_len]u8 = undefined;
1167 h.squeeze(&buf);
1168
1169 // buf is interpreted as a₁…a_ηb₁…b_ηa₁…a_ηb₁…b_η…. We process
1170 // multiple coefficients in one batch.
1171
1172 const T = switch (builtin.target.cpu.arch) {
1173 .x86_64, .x86 => u32, // Generates better code on Intel CPUs
1174 else => u64, // u128 might be faster on some other CPUs.
1175 };
1176
1177 comptime var batch_count: usize = undefined;
1178 comptime var batch_bytes: usize = undefined;
1179 comptime var mask: T = 0;
1180 comptime {
1181 batch_count = @bitSizeOf(T) / @as(usize, 2 * eta);
1182 while (@rem(N, batch_count) != 0 and batch_count > 0) : (batch_count -= 1) {}
1183 assert(batch_count > 0);
1184 assert(@rem(2 * eta * batch_count, 8) == 0);
1185 batch_bytes = 2 * eta * batch_count / 8;
1186
1187 for (0..2 * eta * batch_count) |_| {
1188 mask <<= eta;
1189 mask |= 1;
1190 }
1191 }
1192
1193 var ret: Poly = undefined;
1194 for (0..comptime N / batch_count) |i| {
1195 // Read coefficients into t. In the case of η=3,
1196 // we have t = a₁ + 2a₂ + 4a₃ + 8b₁ + 16b₂ + …
1197 var t: T = 0;
1198 inline for (0..batch_bytes) |j| {
1199 t |= @as(T, buf[batch_bytes * i + j]) << (8 * j);
1200 }
1201
1202 // Accumelate `a's and `b's together by masking them out, shifting
1203 // and adding. For η=3, we have d = a₁ + a₂ + a₃ + 8(b₁ + b₂ + b₃) + …
1204 var d: T = 0;
1205 inline for (0..eta) |j| {
1206 d += (t >> j) & mask;
1207 }
1208
1209 // Extract each a and b separately and set coefficient in polynomial.
1210 inline for (0..batch_count) |j| {
1211 const mask2 = comptime (1 << eta) - 1;
1212 const a = @intCast(i16, (d >> (comptime (2 * j * eta))) & mask2);
1213 const b = @intCast(i16, (d >> (comptime ((2 * j + 1) * eta))) & mask2);
1214 ret.cs[batch_count * i + j] = a - b;
1215 }
1216 }
1217
1218 return ret;
1219 }
1220
1221 // Sample p uniformly from the given seed and x and y coordinates.
1222 fn uniform(seed: [32]u8, x: u8, y: u8) Poly {
1223 var h = sha3.Shake128.init(.{});
1224 const suffix: [2]u8 = .{ x, y };
1225 h.update(&seed);
1226 h.update(&suffix);
1227
1228 const buf_len = sha3.Shake128.block_length; // rate SHAKE-128
1229 var buf: [buf_len]u8 = undefined;
1230
1231 var ret: Poly = undefined;
1232 var i: usize = 0; // index into ret.cs
1233 outer: while (true) {
1234 h.squeeze(&buf);
1235
1236 var j: usize = 0; // index into buf
1237 while (j < buf_len) : (j += 3) {
1238 const b0 = @as(u16, buf[j]);
1239 const b1 = @as(u16, buf[j + 1]);
1240 const b2 = @as(u16, buf[j + 2]);
1241
1242 const ts: [2]u16 = .{
1243 b0 | ((b1 & 0xf) << 8),
1244 (b1 >> 4) | (b2 << 4),
1245 };
1246
1247 inline for (ts) |t| {
1248 if (t < Q) {
1249 ret.cs[i] = @intCast(i16, t);
1250 i += 1;
1251
1252 if (i == N) {
1253 break :outer;
1254 }
1255 }
1256 }
1257 }
1258 }
1259
1260 return ret;
1261 }
1262
1263 // Packs p.
1264 //
1265 // Assumes p is normalized (and not just Barrett reduced).
1266 fn toBytes(p: Poly) [bytes_length]u8 {
1267 var ret: [bytes_length]u8 = undefined;
1268 for (0..comptime N / 2) |i| {
1269 const t0 = @intCast(u16, p.cs[2 * i]);
1270 const t1 = @intCast(u16, p.cs[2 * i + 1]);
1271 ret[3 * i] = @truncate(u8, t0);
1272 ret[3 * i + 1] = @truncate(u8, (t0 >> 8) | (t1 << 4));
1273 ret[3 * i + 2] = @truncate(u8, t1 >> 4);
1274 }
1275 return ret;
1276 }
1277
1278 // Unpacks a Poly from buf.
1279 //
1280 // p will not be normalized; instead 0 ≤ p[i] < 4096.
1281 fn fromBytes(buf: *const [bytes_length]u8) Poly {
1282 var ret: Poly = undefined;
1283 for (0..comptime N / 2) |i| {
1284 const b0 = @as(i16, buf[3 * i]);
1285 const b1 = @as(i16, buf[3 * i + 1]);
1286 const b2 = @as(i16, buf[3 * i + 2]);
1287 ret.cs[2 * i] = b0 | ((b1 & 0xf) << 8);
1288 ret.cs[2 * i + 1] = (b1 >> 4) | b2 << 4;
1289 }
1290 return ret;
1291 }
1292};
1293
1294// A vector of K polynomials.
1295fn Vec(comptime K: u8) type {
1296 return struct {
1297 ps: [K]Poly,
1298
1299 const Self = @This();
1300 const bytes_length = K * Poly.bytes_length;
1301
1302 fn compressedSize(comptime d: u8) usize {
1303 return Poly.compressedSize(d) * K;
1304 }
1305
1306 fn ntt(a: Self) Self {
1307 var ret: Self = undefined;
1308 for (0..K) |i| {
1309 ret.ps[i] = a.ps[i].ntt();
1310 }
1311 return ret;
1312 }
1313
1314 fn invNTT(a: Self) Self {
1315 var ret: Self = undefined;
1316 for (0..K) |i| {
1317 ret.ps[i] = a.ps[i].invNTT();
1318 }
1319 return ret;
1320 }
1321
1322 fn normalize(a: Self) Self {
1323 var ret: Self = undefined;
1324 for (0..K) |i| {
1325 ret.ps[i] = a.ps[i].normalize();
1326 }
1327 return ret;
1328 }
1329
1330 fn barrettReduce(a: Self) Self {
1331 var ret: Self = undefined;
1332 for (0..K) |i| {
1333 ret.ps[i] = a.ps[i].barrettReduce();
1334 }
1335 return ret;
1336 }
1337
1338 fn add(a: Self, b: Self) Self {
1339 var ret: Self = undefined;
1340 for (0..K) |i| {
1341 ret.ps[i] = a.ps[i].add(b.ps[i]);
1342 }
1343 return ret;
1344 }
1345
1346 fn sub(a: Self, b: Self) Self {
1347 var ret: Self = undefined;
1348 for (0..K) |i| {
1349 ret.ps[i] = a.ps[i].sub(b.ps[i]);
1350 }
1351 return ret;
1352 }
1353
1354 // Samples v[i] from centered binomial distribution with the given η,
1355 // seed and nonce+i.
1356 fn noise(comptime eta: u8, nonce: u8, seed: *const [32]u8) Self {
1357 var ret: Self = undefined;
1358 for (0..K) |i| {
1359 ret.ps[i] = Poly.noise(eta, nonce + @intCast(u8, i), seed);
1360 }
1361 return ret;
1362 }
1363
1364 // Sets p to the inner product of a and b using "pointwise" multiplication.
1365 //
1366 // See MulHat() and NTT() for a description of the multiplication.
1367 // Assumes a and b are in Montgomery form. p will be in Montgomery form,
1368 // and its coefficients will be bounded in absolute value by 2kq.
1369 // If a and b are not in Montgomery form, then the action is the same
1370 // as "pointwise" multiplication followed by multiplying by R⁻¹, the inverse
1371 // of the Montgomery factor.
1372 fn dotHat(a: Self, b: Self) Poly {
1373 var ret: Poly = Poly.zero;
1374 for (0..K) |i| {
1375 ret = ret.add(a.ps[i].mulHat(b.ps[i]));
1376 }
1377 return ret;
1378 }
1379
1380 fn compress(v: Self, comptime d: u8) [compressedSize(d)]u8 {
1381 const cs = comptime Poly.compressedSize(d);
1382 var ret: [compressedSize(d)]u8 = undefined;
1383 inline for (0..K) |i| {
1384 mem.copy(u8, ret[i * cs .. (i + 1) * cs], &v.ps[i].compress(d));
1385 }
1386 return ret;
1387 }
1388
1389 fn decompress(comptime d: u8, buf: *const [compressedSize(d)]u8) Self {
1390 const cs = comptime Poly.compressedSize(d);
1391 var ret: Self = undefined;
1392 inline for (0..K) |i| {
1393 ret.ps[i] = Poly.decompress(d, buf[i * cs .. (i + 1) * cs]);
1394 }
1395 return ret;
1396 }
1397
1398 /// Serializes the key into a byte array.
1399 fn toBytes(v: Self) [bytes_length]u8 {
1400 var ret: [bytes_length]u8 = undefined;
1401 inline for (0..K) |i| {
1402 mem.copy(
1403 u8,
1404 ret[i * Poly.bytes_length .. (i + 1) * Poly.bytes_length],
1405 &v.ps[i].toBytes(),
1406 );
1407 }
1408 return ret;
1409 }
1410
1411 /// Deserializes the key from a byte array.
1412 fn fromBytes(buf: *const [bytes_length]u8) Self {
1413 var ret: Self = undefined;
1414 inline for (0..K) |i| {
1415 ret.ps[i] = Poly.fromBytes(
1416 buf[i * Poly.bytes_length .. (i + 1) * Poly.bytes_length],
1417 );
1418 }
1419 return ret;
1420 }
1421 };
1422}
1423
1424// A matrix of K vectors
1425fn Mat(comptime K: u8) type {
1426 return struct {
1427 const Self = @This();
1428 vs: [K]Vec(K),
1429
1430 fn uniform(seed: [32]u8, comptime transposed: bool) Self {
1431 var ret: Self = undefined;
1432 var i: u8 = 0;
1433 while (i < K) : (i += 1) {
1434 var j: u8 = 0;
1435 while (j < K) : (j += 1) {
1436 ret.vs[i].ps[j] = Poly.uniform(
1437 seed,
1438 if (transposed) i else j,
1439 if (transposed) j else i,
1440 );
1441 }
1442 }
1443 return ret;
1444 }
1445
1446 // Returns transpose of A
1447 fn transpose(m: Self) Self {
1448 var ret: Self = undefined;
1449 for (0..K) |i| {
1450 for (0..K) |j| {
1451 ret.vs[i].ps[j] = m.vs[j].ps[i];
1452 }
1453 }
1454 return ret;
1455 }
1456 };
1457}
1458
1459// Returns `true` if a ≠ b.
1460fn ctneq(comptime len: usize, a: [len]u8, b: [len]u8) u1 {
1461 return 1 - @boolToInt(crypto.utils.timingSafeEql([len]u8, a, b));
1462}
1463
1464// Copy src into dst given b = 1.
1465fn cmov(comptime len: usize, dst: *[len]u8, src: [len]u8, b: u1) void {
1466 const mask = @as(u8, 0) -% b;
1467 for (0..len) |i| {
1468 dst[i] ^= mask & (dst[i] ^ src[i]);
1469 }
1470}
1471
1472test "MulHat" {
1473 var rnd = RndGen.init(0);
1474
1475 for (0..100) |_| {
1476 const a = Poly.randAbsLeqQ(&rnd);
1477 const b = Poly.randAbsLeqQ(&rnd);
1478
1479 const p2 = a.ntt().mulHat(b.ntt()).barrettReduce().invNTT().normalize();
1480 var p: Poly = undefined;
1481
1482 mem.set(i16, &p.cs, 0);
1483
1484 for (0..N) |i| {
1485 for (0..N) |j| {
1486 var v = montReduce(@as(i32, a.cs[i]) * @as(i32, b.cs[j]));
1487 var k = i + j;
1488 if (k >= N) {
1489 // Recall Xᴺ = -1.
1490 k -= N;
1491 v = -v;
1492 }
1493 p.cs[k] = feBarrettReduce(v + p.cs[k]);
1494 }
1495 }
1496
1497 p = p.toMont().normalize();
1498
1499 try testing.expectEqual(p, p2);
1500 }
1501}
1502
1503test "NTT" {
1504 var rnd = RndGen.init(0);
1505
1506 for (0..1000) |_| {
1507 var p = Poly.randAbsLeqQ(&rnd);
1508 const q = p.toMont().normalize();
1509 p = p.ntt();
1510
1511 for (0..N) |i| {
1512 try testing.expect(p.cs[i] <= 7 * Q and -7 * Q <= p.cs[i]);
1513 }
1514
1515 p = p.normalize().invNTT();
1516 for (0..N) |i| {
1517 try testing.expect(p.cs[i] <= Q and -Q <= p.cs[i]);
1518 }
1519
1520 p = p.normalize();
1521
1522 try testing.expectEqual(p, q);
1523 }
1524}
1525
1526test "Compression" {
1527 var rnd = RndGen.init(0);
1528 inline for (.{ 1, 4, 5, 10, 11 }) |d| {
1529 for (0..1000) |_| {
1530 const p = Poly.randNormalized(&rnd);
1531 const pp = p.compress(d);
1532 const pq = Poly.decompress(d, &pp).compress(d);
1533 try testing.expectEqual(pp, pq);
1534 }
1535 }
1536}
1537
1538test "noise" {
1539 var seed: [32]u8 = undefined;
1540 for (&seed, 0..) |*s, i| {
1541 s.* = @intCast(u8, i);
1542 }
1543 try testing.expectEqual(Poly.noise(3, 37, &seed).cs, .{
1544 0, 0, 1, -1, 0, 2, 0, -1, -1, 3, 0, 1, -2, -2, 0, 1, -2,
1545 1, 0, -2, 3, 0, 0, 0, 1, 3, 1, 1, 2, 1, -1, -1, -1, 0,
1546 1, 0, 1, 0, 2, 0, 1, -2, 0, -1, -1, -2, 1, -1, -1, 2, -1,
1547 1, 1, 2, -3, -1, -1, 0, 0, 0, 0, 1, -1, -2, -2, 0, -2, 0,
1548 0, 0, 1, 0, -1, -1, 1, -2, 2, 0, 0, 2, -2, 0, 1, 0, 1,
1549 1, 1, 0, 1, -2, -1, -2, -1, 1, 0, 0, 0, 0, 0, 1, 0, -1,
1550 -1, 0, -1, 1, 0, 1, 0, -1, -1, 0, -2, 2, 0, -2, 1, -1, 0,
1551 1, -1, -1, 2, 1, 0, 0, -2, -1, 2, 0, 0, 0, -1, -1, 3, 1,
1552 0, 1, 0, 1, 0, 2, 1, 0, 0, 1, 0, 1, 0, 0, -1, -1, -1,
1553 0, 1, 3, 1, 0, 1, 0, 1, -1, -1, -1, -1, 0, 0, -2, -1, -1,
1554 2, 0, 1, 0, 1, 0, 2, -2, 0, 1, 1, -3, -1, -2, -1, 0, 1,
1555 0, 1, -2, 2, 2, 1, 1, 0, -1, 0, -1, -1, 1, 0, -1, 2, 1,
1556 -1, 1, 2, -2, 1, 2, 0, 1, 2, 1, 0, 0, 2, 1, 2, 1, 0,
1557 2, 1, 0, 0, -1, -1, 1, -1, 0, 1, -1, 2, 2, 0, 0, -1, 1,
1558 1, 1, 1, 0, 0, -2, 0, -1, 1, 2, 0, 0, 1, 1, -1, 1, 0,
1559 1,
1560 });
1561 try testing.expectEqual(Poly.noise(2, 37, &seed).cs, .{
1562 1, 0, 1, -1, -1, -2, -1, -1, 2, 0, -1, 0, 0, -1,
1563 1, 1, -1, 1, 0, 2, -2, 0, 1, 2, 0, 0, -1, 1,
1564 0, -1, 1, -1, 1, 2, 1, 1, 0, -1, 1, -1, -2, -1,
1565 1, -1, -1, -1, 2, -1, -1, 0, 0, 1, 1, -1, 1, 1,
1566 1, 1, -1, -2, 0, 1, 0, 0, 2, 1, -1, 2, 0, 0,
1567 1, 1, 0, -1, 0, 0, -1, -1, 2, 0, 1, -1, 2, -1,
1568 -1, -1, -1, 0, -2, 0, 2, 1, 0, 0, 0, -1, 0, 0,
1569 0, -1, -1, 0, -1, -1, 0, -1, 0, 0, -2, 1, 1, 0,
1570 1, 0, 1, 0, 1, 1, -1, 2, 0, 1, -1, 1, 2, 0,
1571 0, 0, 0, -1, -1, -1, 0, 1, 0, -1, 2, 0, 0, 1,
1572 1, 1, 0, 1, -1, 1, 2, 1, 0, 2, -1, 1, -1, -2,
1573 -1, -2, -1, 1, 0, -2, -2, -1, 1, 0, 0, 0, 0, 1,
1574 0, 0, 0, 2, 2, 0, 1, 0, -1, -1, 0, 2, 0, 0,
1575 -2, 1, 0, 2, 1, -1, -2, 0, 0, -1, 1, 1, 0, 0,
1576 2, 0, 1, 1, -2, 1, -2, 1, 1, 0, 2, 0, -1, 0,
1577 -1, 0, 1, 2, 0, 1, 0, -2, 1, -2, -2, 1, -1, 0,
1578 -1, 1, 1, 0, 0, 0, 1, 0, -1, 1, 1, 0, 0, 0,
1579 0, 1, 0, 1, -1, 0, 1, -1, -1, 2, 0, 0, 1, -1,
1580 0, 1, -1, 0,
1581 });
1582}
1583
1584test "uniform sampling" {
1585 var seed: [32]u8 = undefined;
1586 for (&seed, 0..) |*s, i| {
1587 s.* = @intCast(u8, i);
1588 }
1589 try testing.expectEqual(Poly.uniform(seed, 1, 0).cs, .{
1590 797, 993, 161, 6, 2608, 2385, 2096, 2661, 1676, 247, 2440,
1591 342, 634, 194, 1570, 2848, 986, 684, 3148, 3208, 2018, 351,
1592 2288, 612, 1394, 170, 1521, 3119, 58, 596, 2093, 1549, 409,
1593 2156, 1934, 1730, 1324, 388, 446, 418, 1719, 2202, 1812, 98,
1594 1019, 2369, 214, 2699, 28, 1523, 2824, 273, 402, 2899, 246,
1595 210, 1288, 863, 2708, 177, 3076, 349, 44, 949, 854, 1371,
1596 957, 292, 2502, 1617, 1501, 254, 7, 1761, 2581, 2206, 2655,
1597 1211, 629, 1274, 2358, 816, 2766, 2115, 2985, 1006, 2433, 856,
1598 2596, 3192, 1, 1378, 2345, 707, 1891, 1669, 536, 1221, 710,
1599 2511, 120, 1176, 322, 1897, 2309, 595, 2950, 1171, 801, 1848,
1600 695, 2912, 1396, 1931, 1775, 2904, 893, 2507, 1810, 2873, 253,
1601 1529, 1047, 2615, 1687, 831, 1414, 965, 3169, 1887, 753, 3246,
1602 1937, 115, 2953, 586, 545, 1621, 1667, 3187, 1654, 1988, 1857,
1603 512, 1239, 1219, 898, 3106, 391, 1331, 2228, 3169, 586, 2412,
1604 845, 768, 156, 662, 478, 1693, 2632, 573, 2434, 1671, 173,
1605 969, 364, 1663, 2701, 2169, 813, 1000, 1471, 720, 2431, 2530,
1606 3161, 733, 1691, 527, 2634, 335, 26, 2377, 1707, 767, 3020,
1607 950, 502, 426, 1138, 3208, 2607, 2389, 44, 1358, 1392, 2334,
1608 875, 2097, 173, 1697, 2578, 942, 1817, 974, 1165, 2853, 1958,
1609 2973, 3282, 271, 1236, 1677, 2230, 673, 1554, 96, 242, 1729,
1610 2518, 1884, 2272, 71, 1382, 924, 1807, 1610, 456, 1148, 2479,
1611 2152, 238, 2208, 2329, 713, 1175, 1196, 757, 1078, 3190, 3169,
1612 708, 3117, 154, 1751, 3225, 1364, 154, 23, 2842, 1105, 1419,
1613 79, 5, 2013,
1614 });
1615}
1616
1617test "Polynomial packing" {
1618 var rnd = RndGen.init(0);
1619
1620 for (0..1000) |_| {
1621 const p = Poly.randNormalized(&rnd);
1622 try testing.expectEqual(Poly.fromBytes(&p.toBytes()), p);
1623 }
1624}
1625
1626test "Test inner PKE" {
1627 var seed: [32]u8 = undefined;
1628 var pt: [32]u8 = undefined;
1629 for (&seed, &pt, 0..) |*s, *p, i| {
1630 s.* = @intCast(u8, i);
1631 p.* = @intCast(u8, i + 32);
1632 }
1633 inline for (modes) |mode| {
1634 for (0..100) |i| {
1635 var pk: mode.InnerPk = undefined;
1636 var sk: mode.InnerSk = undefined;
1637 seed[0] = @intCast(u8, i);
1638 mode.innerKeyFromSeed(seed, &pk, &sk);
1639 for (0..10) |j| {
1640 seed[1] = @intCast(u8, j);
1641 try testing.expectEqual(sk.decrypt(&pk.encrypt(&pt, &seed)), pt);
1642 }
1643 }
1644 }
1645}
1646
1647test "Test happy flow" {
1648 var seed: [64]u8 = undefined;
1649 for (&seed, 0..) |*s, i| {
1650 s.* = @intCast(u8, i);
1651 }
1652 inline for (modes) |mode| {
1653 for (0..100) |i| {
1654 seed[0] = @intCast(u8, i);
1655 const kp = try mode.KeyPair.create(seed);
1656 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());
1657 try testing.expectEqual(sk, kp.secret_key);
1658 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());
1659 try testing.expectEqual(pk, kp.public_key);
1660 for (0..10) |j| {
1661 seed[1] = @intCast(u8, j);
1662 const e = pk.encaps(seed[0..32].*);
1663 try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext));
1664 }
1665 }
1666 }
1667}
1668
1669// Code to test NIST Known Answer Tests (KAT), see PQCgenKAT.c.
1670
1671const sha2 = crypto.hash.sha2;
1672
1673test "NIST KAT test" {
1674 inline for (.{
1675 .{ Kyber512, "e9c2bd37133fcb40772f81559f14b1f58dccd1c816701be9ba6214d43baf4547" },
1676 .{ Kyber1024, "89248f2f33f7f4f7051729111f3049c409a933ec904aedadf035f30fa5646cd5" },
1677 .{ Kyber768, "a1e122cad3c24bc51622e4c242d8b8acbcd3f618fee4220400605ca8f9ea02c2" },
1678 }) |modeHash| {
1679 const mode = modeHash[0];
1680 var seed: [48]u8 = undefined;
1681 for (&seed, 0..) |*s, i| {
1682 s.* = @intCast(u8, i);
1683 }
1684 var f = sha2.Sha256.init(.{});
1685 const fw = f.writer();
1686 var g = NistDRBG.init(seed);
1687 try std.fmt.format(fw, "# {s}\n\n", .{mode.name});
1688 for (0..100) |i| {
1689 g.fill(&seed);
1690 try std.fmt.format(fw, "count = {}\n", .{i});
1691 try std.fmt.format(fw, "seed = {s}\n", .{std.fmt.fmtSliceHexUpper(&seed)});
1692 var g2 = NistDRBG.init(seed);
1693
1694 // This is not equivalent to g2.fill(kseed[:]). As the reference
1695 // implementation calls randombytes twice generating the keypair,
1696 // we have to do that as well.
1697 var kseed: [64]u8 = undefined;
1698 var eseed: [32]u8 = undefined;
1699 g2.fill(kseed[0..32]);
1700 g2.fill(kseed[32..64]);
1701 g2.fill(&eseed);
1702 const kp = try mode.KeyPair.create(kseed);
1703 const e = kp.public_key.encaps(eseed);
1704 const ss2 = try kp.secret_key.decaps(&e.ciphertext);
1705 try testing.expectEqual(ss2, e.shared_secret);
1706 try std.fmt.format(fw, "pk = {s}\n", .{std.fmt.fmtSliceHexUpper(&kp.public_key.toBytes())});
1707 try std.fmt.format(fw, "sk = {s}\n", .{std.fmt.fmtSliceHexUpper(&kp.secret_key.toBytes())});
1708 try std.fmt.format(fw, "ct = {s}\n", .{std.fmt.fmtSliceHexUpper(&e.ciphertext)});
1709 try std.fmt.format(fw, "ss = {s}\n\n", .{std.fmt.fmtSliceHexUpper(&e.shared_secret)});
1710 }
1711
1712 var out: [32]u8 = undefined;
1713 f.final(&out);
1714 var outHex: [64]u8 = undefined;
1715 _ = try std.fmt.bufPrint(&outHex, "{s}", .{std.fmt.fmtSliceHexLower(&out)});
1716 try testing.expectEqual(outHex, modeHash[1].*);
1717 }
1718}
1719
1720const NistDRBG = struct {
1721 key: [32]u8,
1722 v: [16]u8,
1723
1724 fn incV(g: *NistDRBG) void {
1725 var j: usize = 15;
1726 while (j >= 0) : (j -= 1) {
1727 if (g.v[j] == 255) {
1728 g.v[j] = 0;
1729 } else {
1730 g.v[j] += 1;
1731 break;
1732 }
1733 }
1734 }
1735
1736 // AES256_CTR_DRBG_Update(pd, &g.key, &g.v).
1737 fn update(g: *NistDRBG, pd: ?[48]u8) void {
1738 var buf: [48]u8 = undefined;
1739 const ctx = crypto.core.aes.Aes256.initEnc(g.key);
1740 var i: usize = 0;
1741 while (i < 3) : (i += 1) {
1742 g.incV();
1743 var block: [16]u8 = undefined;
1744 ctx.encrypt(&block, &g.v);
1745 mem.copy(u8, buf[i * 16 .. (i + 1) * 16], &block);
1746 }
1747 if (pd) |p| {
1748 for (&buf, p) |*b, x| {
1749 b.* ^= x;
1750 }
1751 }
1752 mem.copy(u8, &g.key, buf[0..32]);
1753 mem.copy(u8, &g.v, buf[32..48]);
1754 }
1755
1756 // randombytes.
1757 fn fill(g: *NistDRBG, out: []u8) void {
1758 var block: [16]u8 = undefined;
1759 var dst = out;
1760
1761 const ctx = crypto.core.aes.Aes256.initEnc(g.key);
1762 while (dst.len > 0) {
1763 g.incV();
1764 ctx.encrypt(&block, &g.v);
1765 if (dst.len < 16) {
1766 mem.copy(u8, dst, block[0..dst.len]);
1767 break;
1768 }
1769 mem.copy(u8, dst, &block);
1770 dst = dst[16..dst.len];
1771 }
1772 g.update(null);
1773 }
1774
1775 fn init(seed: [48]u8) NistDRBG {
1776 var ret: NistDRBG = .{ .key = .{0} ** 32, .v = .{0} ** 16 };
1777 ret.update(seed);
1778 return ret;
1779 }
1780};
lib/std/crypto/sha3.zig+45-2
...@@ -18,6 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead");...@@ -18,6 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead");
18pub const Shake128 = Shake(128);18pub const Shake128 = Shake(128);
19pub const Shake256 = Shake(256);19pub const Shake256 = Shake(256);
2020
21/// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level.
22/// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance.
23/// The delimiter is 0x1f by default, but can be changed for context-separation.
24pub fn TurboShake128(comptime delim: ?u8) type {
25 return TurboShake(128, delim);
26}
27
28/// TurboSHAKE256 is a XOF (a secure hash function with a variable output length), with a 256 bit security level.
29/// It is based on the same permutation as SHA3 and SHAKE256, but which much higher performance.
30/// The delimiter is 0x01 by default, but can be changed for context-separation.
31pub fn TurboShake256(comptime delim: ?u8) type {
32 return TurboShake(256, delim);
33}
34
21/// A generic Keccak hash function.35/// A generic Keccak hash function.
22pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {36pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {
23 comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length37 comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length
...@@ -76,9 +90,18 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, co...@@ -76,9 +90,18 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, co
7690
77/// The SHAKE extendable output hash function.91/// The SHAKE extendable output hash function.
78pub fn Shake(comptime security_level: u11) type {92pub fn Shake(comptime security_level: u11) type {
93 return ShakeLike(security_level, 0x1f, 24);
94}
95
96/// The TurboSHAKE extendable output hash function.
97/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/
98pub fn TurboShake(comptime security_level: u11, comptime delim: ?u8) type {
99 return ShakeLike(security_level, delim orelse 0x1f, 12);
100}
101
102fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type {
79 const f = 1600;103 const f = 1600;
80 const rounds = 24;104 const State = KeccakState(f, security_level * 2, delim, rounds);
81 const State = KeccakState(f, security_level * 2, 0x1f, rounds);
82105
83 return struct {106 return struct {
84 const Self = @This();107 const Self = @This();
...@@ -348,3 +371,23 @@ test "SHAKE-256 single" {...@@ -348,3 +371,23 @@ test "SHAKE-256 single" {
348 Shake256.hash("hello123", &out, .{});371 Shake256.hash("hello123", &out, .{});
349 try htest.assertEqual("ade612ba265f92de4a37", &out);372 try htest.assertEqual("ade612ba265f92de4a37", &out);
350}373}
374
375test "TurboSHAKE-128" {
376 var out: [32]u8 = undefined;
377 TurboShake(128, 0x06).hash("\xff", &out, .{});
378 try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out);
379}
380
381test "SHA-3 with streaming" {
382 var msg: [613]u8 = [613]u8{ 0x97, 0xd1, 0x2d, 0x1a, 0x16, 0x2d, 0x36, 0x4d, 0x20, 0x62, 0x19, 0x0b, 0x14, 0x93, 0xbb, 0xf8, 0x5b, 0xea, 0x04, 0xc2, 0x61, 0x8e, 0xd6, 0x08, 0x81, 0xa1, 0x1d, 0x73, 0x27, 0x48, 0xbf, 0xa4, 0xba, 0xb1, 0x9a, 0x48, 0x9c, 0xf9, 0x9b, 0xff, 0x34, 0x48, 0xa9, 0x75, 0xea, 0xc8, 0xa3, 0x48, 0x24, 0x9d, 0x75, 0x27, 0x48, 0xec, 0x03, 0xb0, 0xbb, 0xdf, 0x33, 0x90, 0xe3, 0x93, 0xed, 0x68, 0x24, 0x39, 0x12, 0xdf, 0xea, 0xee, 0x8c, 0x9f, 0x96, 0xde, 0x42, 0x46, 0x8c, 0x2b, 0x17, 0x83, 0x36, 0xfb, 0xf4, 0xf7, 0xff, 0x79, 0xb9, 0x45, 0x41, 0xc9, 0x56, 0x1a, 0x6b, 0x0c, 0xa4, 0x1a, 0xdd, 0x6b, 0x95, 0xe8, 0x03, 0x0f, 0x09, 0x29, 0x40, 0x1b, 0xea, 0x87, 0xfa, 0xb9, 0x18, 0xa9, 0x95, 0x07, 0x7c, 0x2f, 0x7c, 0x33, 0xfb, 0xc5, 0x11, 0x5e, 0x81, 0x0e, 0xbc, 0xae, 0xec, 0xb3, 0xe1, 0x4a, 0x26, 0x56, 0xe8, 0x5b, 0x11, 0x9d, 0x37, 0x06, 0x9b, 0x34, 0x31, 0x6e, 0xa3, 0xba, 0x41, 0xbc, 0x11, 0xd8, 0xc5, 0x15, 0xc9, 0x30, 0x2c, 0x9b, 0xb6, 0x71, 0xd8, 0x7c, 0xbc, 0x38, 0x2f, 0xd5, 0xbd, 0x30, 0x96, 0xd4, 0xa3, 0x00, 0x77, 0x9d, 0x55, 0x4a, 0x33, 0x53, 0xb6, 0xb3, 0x35, 0x1b, 0xae, 0xe5, 0xdc, 0x22, 0x23, 0x85, 0x95, 0x88, 0xf9, 0x3b, 0xbf, 0x74, 0x13, 0xaa, 0xcb, 0x0a, 0x60, 0x79, 0x13, 0x79, 0xc0, 0x4a, 0x02, 0xdb, 0x1c, 0xc9, 0xff, 0x60, 0x57, 0x9a, 0x70, 0x28, 0x58, 0x60, 0xbc, 0x57, 0x07, 0xc7, 0x47, 0x1a, 0x45, 0x71, 0x76, 0x94, 0xfb, 0x05, 0xad, 0xec, 0x12, 0x29, 0x5a, 0x44, 0x6a, 0x81, 0xd9, 0xc6, 0xf0, 0xb6, 0x9b, 0x97, 0x83, 0x69, 0xfb, 0xdc, 0x0d, 0x4a, 0x67, 0xbc, 0x72, 0xf5, 0x43, 0x5e, 0x9b, 0x13, 0xf2, 0xe4, 0x6d, 0x49, 0xdb, 0x76, 0xcb, 0x42, 0x6a, 0x3c, 0x9f, 0xa1, 0xfe, 0x5e, 0xca, 0x0a, 0xfc, 0xfa, 0x39, 0x27, 0xd1, 0x3c, 0xcb, 0x9a, 0xde, 0x4c, 0x6b, 0x09, 0x8b, 0x49, 0xfd, 0x1e, 0x3d, 0x5e, 0x67, 0x7c, 0x57, 0xad, 0x90, 0xcc, 0x46, 0x5f, 0x5c, 0xae, 0x6a, 0x9c, 0xb2, 0xcd, 0x2c, 0x89, 0x78, 0xcf, 0xf1, 0x49, 0x96, 0x55, 0x1e, 0x04, 0xef, 0x0e, 0x1c, 0xde, 0x6c, 0x96, 0x51, 0x00, 0xee, 0x9a, 0x1f, 0x8d, 0x61, 0xbc, 0xeb, 0xb1, 0xa6, 0xa5, 0x21, 0x8b, 0xa7, 0xf8, 0x25, 0x41, 0x48, 0x62, 0x5b, 0x01, 0x6c, 0x7c, 0x2a, 0xe8, 0xff, 0xf9, 0xf9, 0x1f, 0xe2, 0x79, 0x2e, 0xd1, 0xff, 0xa3, 0x2e, 0x1c, 0x3a, 0x1a, 0x5d, 0x2b, 0x7b, 0x87, 0x25, 0x22, 0xa4, 0x90, 0xea, 0x26, 0x9d, 0xdd, 0x13, 0x60, 0x4c, 0x10, 0x03, 0xf6, 0x99, 0xd3, 0x21, 0x0c, 0x69, 0xc6, 0xd8, 0xc8, 0x9e, 0x94, 0x89, 0x51, 0x21, 0xe3, 0x9a, 0xcd, 0xda, 0x54, 0x72, 0x64, 0xae, 0x94, 0x79, 0x36, 0x81, 0x44, 0x14, 0x6d, 0x3a, 0x0e, 0xa6, 0x30, 0xbf, 0x95, 0x99, 0xa6, 0xf5, 0x7f, 0x4f, 0xef, 0xc6, 0x71, 0x2f, 0x36, 0x13, 0x14, 0xa2, 0x9d, 0xc2, 0x0c, 0x0d, 0x4e, 0xc0, 0x02, 0xd3, 0x6f, 0xee, 0x98, 0x5e, 0x24, 0x31, 0x74, 0x11, 0x96, 0x6e, 0x43, 0x57, 0xe8, 0x8e, 0xa0, 0x8d, 0x3d, 0x79, 0x38, 0x20, 0xc2, 0x0f, 0xb4, 0x75, 0x99, 0x3b, 0xb1, 0xf0, 0xe8, 0xe1, 0xda, 0xf9, 0xd4, 0xe6, 0xd6, 0xf4, 0x8a, 0x32, 0x4a, 0x4a, 0x25, 0xa8, 0xd9, 0x60, 0xd6, 0x33, 0x31, 0x97, 0xb9, 0xb6, 0xed, 0x5f, 0xfc, 0x15, 0xbd, 0x13, 0xc0, 0x3a, 0x3f, 0x1f, 0x2d, 0x09, 0x1d, 0xeb, 0x69, 0x6a, 0xfe, 0xd7, 0x95, 0x3e, 0x8a, 0x4e, 0xe1, 0x6e, 0x61, 0xb2, 0x6c, 0xe3, 0x2b, 0x70, 0x60, 0x7e, 0x8c, 0xe4, 0xdd, 0x27, 0x30, 0x7e, 0x0d, 0xc7, 0xb7, 0x9a, 0x1a, 0x3c, 0xcc, 0xa7, 0x22, 0x77, 0x14, 0x05, 0x50, 0x57, 0x31, 0x1b, 0xc8, 0xbf, 0xce, 0x52, 0xaf, 0x9c, 0x8e, 0x10, 0x2e, 0xd2, 0x16, 0xb6, 0x6e, 0x43, 0x10, 0xaf, 0x8b, 0xde, 0x1d, 0x60, 0xb2, 0x7d, 0xe6, 0x2f, 0x08, 0x10, 0x12, 0x7e, 0xb4, 0x76, 0x45, 0xb6, 0xd8, 0x9b, 0x26, 0x40, 0xa1, 0x63, 0x5c, 0x7a, 0x2a, 0xb1, 0x8c, 0xd6, 0xa4, 0x6f, 0x5a, 0xae, 0x33, 0x7e, 0x6d, 0x71, 0xf5, 0xc8, 0x6d, 0x80, 0x1c, 0x35, 0xfc, 0x3f, 0xc1, 0xa6, 0xc6, 0x1a, 0x15, 0x04, 0x6d, 0x76, 0x38, 0x32, 0x95, 0xb2, 0x51, 0x1a, 0xe9, 0x3e, 0x89, 0x9f, 0x0c, 0x79 };
383 var out: [Sha3_256.digest_length]u8 = undefined;
384
385 Sha3_256.hash(&msg, &out, .{});
386 try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out);
387
388 var h = Sha3_256.init(.{});
389 h.update(msg[0..64]);
390 h.update(msg[64..613]);
391 h.final(&out);
392 try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out);
393}
lib/std/crypto/tls/Client.zig+49-1
...@@ -88,11 +88,59 @@ pub const StreamInterface = struct {...@@ -88,11 +88,59 @@ pub const StreamInterface = struct {
88 }88 }
89};89};
9090
91pub fn InitError(comptime Stream: type) type {
92 return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || error{
93 InsufficientEntropy,
94 DiskQuota,
95 LockViolation,
96 NotOpenForWriting,
97 TlsAlert,
98 TlsUnexpectedMessage,
99 TlsIllegalParameter,
100 TlsDecryptFailure,
101 TlsRecordOverflow,
102 TlsBadRecordMac,
103 CertificateFieldHasInvalidLength,
104 CertificateHostMismatch,
105 CertificatePublicKeyInvalid,
106 CertificateExpired,
107 CertificateFieldHasWrongDataType,
108 CertificateIssuerMismatch,
109 CertificateNotYetValid,
110 CertificateSignatureAlgorithmMismatch,
111 CertificateSignatureAlgorithmUnsupported,
112 CertificateSignatureInvalid,
113 CertificateSignatureInvalidLength,
114 CertificateSignatureNamedCurveUnsupported,
115 CertificateSignatureUnsupportedBitCount,
116 TlsCertificateNotVerified,
117 TlsBadSignatureScheme,
118 TlsBadRsaSignatureBitCount,
119 InvalidEncoding,
120 IdentityElement,
121 SignatureVerificationFailed,
122 TlsDecryptError,
123 TlsConnectionTruncated,
124 TlsDecodeError,
125 UnsupportedCertificateVersion,
126 CertificateTimeInvalid,
127 CertificateHasUnrecognizedObjectId,
128 CertificateHasInvalidBitString,
129 MessageTooLong,
130 NegativeIntoUnsigned,
131 TargetTooSmall,
132 BufferTooSmall,
133 InvalidSignature,
134 NotSquare,
135 NonCanonical,
136 };
137}
138
91/// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which139/// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which
92/// must conform to `StreamInterface`.140/// must conform to `StreamInterface`.
93///141///
94/// `host` is only borrowed during this function call.142/// `host` is only borrowed during this function call.
95pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) !Client {143pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client {
96 const host_len = @intCast(u16, host.len);144 const host_len = @intCast(u16, host.len);
97145
98 var random_buffer: [128]u8 = undefined;146 var random_buffer: [128]u8 = undefined;
lib/std/debug.zig+33
...@@ -635,6 +635,7 @@ pub const TTY = struct {...@@ -635,6 +635,7 @@ pub const TTY = struct {
635 pub const Color = enum {635 pub const Color = enum {
636 Red,636 Red,
637 Green,637 Green,
638 Yellow,
638 Cyan,639 Cyan,
639 White,640 White,
640 Dim,641 Dim,
...@@ -659,6 +660,7 @@ pub const TTY = struct {...@@ -659,6 +660,7 @@ pub const TTY = struct {
659 const color_string = switch (color) {660 const color_string = switch (color) {
660 .Red => "\x1b[31;1m",661 .Red => "\x1b[31;1m",
661 .Green => "\x1b[32;1m",662 .Green => "\x1b[32;1m",
663 .Yellow => "\x1b[33;1m",
662 .Cyan => "\x1b[36;1m",664 .Cyan => "\x1b[36;1m",
663 .White => "\x1b[37;1m",665 .White => "\x1b[37;1m",
664 .Bold => "\x1b[1m",666 .Bold => "\x1b[1m",
...@@ -671,6 +673,7 @@ pub const TTY = struct {...@@ -671,6 +673,7 @@ pub const TTY = struct {
671 const attributes = switch (color) {673 const attributes = switch (color) {
672 .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,674 .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,
673 .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,675 .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
676 .Yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
674 .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,677 .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
675 .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,678 .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
676 .Dim => windows.FOREGROUND_INTENSITY,679 .Dim => windows.FOREGROUND_INTENSITY,
...@@ -682,6 +685,36 @@ pub const TTY = struct {...@@ -682,6 +685,36 @@ pub const TTY = struct {
682 },685 },
683 };686 };
684 }687 }
688
689 pub fn writeDEC(conf: Config, writer: anytype, codepoint: u8) !void {
690 const bytes = switch (conf) {
691 .no_color, .windows_api => switch (codepoint) {
692 0x50...0x5e => @as(*const [1]u8, &codepoint),
693 0x6a => "+", // ┘
694 0x6b => "+", // ┐
695 0x6c => "+", // ┌
696 0x6d => "+", // └
697 0x6e => "+", // ┼
698 0x71 => "-", // ─
699 0x74 => "+", // ├
700 0x75 => "+", // ┤
701 0x76 => "+", // ┴
702 0x77 => "+", // ┬
703 0x78 => "|", // │
704 else => " ", // TODO
705 },
706 .escape_codes => switch (codepoint) {
707 // Here we avoid writing the DEC beginning sequence and
708 // ending sequence in separate syscalls by putting the
709 // beginning and ending sequence into the same string
710 // literals, to prevent terminals ending up in bad states
711 // in case a crash happens between syscalls.
712 inline 0x50...0x7f => |x| "\x1B\x28\x30" ++ [1]u8{x} ++ "\x1B\x28\x42",
713 else => unreachable,
714 },
715 };
716 return writer.writeAll(bytes);
717 }
685 };718 };
686};719};
687720
lib/std/fifo.zig+27
...@@ -164,6 +164,17 @@ pub fn LinearFifo(...@@ -164,6 +164,17 @@ pub fn LinearFifo(
164 return self.readableSliceMut(offset);164 return self.readableSliceMut(offset);
165 }165 }
166166
167 pub fn readableSliceOfLen(self: *Self, len: usize) []const T {
168 assert(len <= self.count);
169 const buf = self.readableSlice(0);
170 if (buf.len >= len) {
171 return buf[0..len];
172 } else {
173 self.realign();
174 return self.readableSlice(0)[0..len];
175 }
176 }
177
167 /// Discard first `count` items in the fifo178 /// Discard first `count` items in the fifo
168 pub fn discard(self: *Self, count: usize) void {179 pub fn discard(self: *Self, count: usize) void {
169 assert(count <= self.count);180 assert(count <= self.count);
...@@ -383,6 +394,22 @@ pub fn LinearFifo(...@@ -383,6 +394,22 @@ pub fn LinearFifo(
383 self.discard(try dest_writer.write(self.readableSlice(0)));394 self.discard(try dest_writer.write(self.readableSlice(0)));
384 }395 }
385 }396 }
397
398 pub fn toOwnedSlice(self: *Self) Allocator.Error![]T {
399 if (self.head != 0) self.realign();
400 assert(self.head == 0);
401 assert(self.count <= self.buf.len);
402 const allocator = self.allocator;
403 if (allocator.resize(self.buf, self.count)) {
404 const result = self.buf[0..self.count];
405 self.* = Self.init(allocator);
406 return result;
407 }
408 const new_memory = try allocator.dupe(T, self.buf[0..self.count]);
409 allocator.free(self.buf);
410 self.* = Self.init(allocator);
411 return new_memory;
412 }
386 };413 };
387}414}
388415
lib/std/fmt.zig+22
...@@ -2555,6 +2555,21 @@ test "bytes.hex" {...@@ -2555,6 +2555,21 @@ test "bytes.hex" {
2555 try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)});2555 try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)});
2556}2556}
25572557
2558/// Encodes a sequence of bytes as hexadecimal digits.
2559/// Returns an array containing the encoded bytes.
2560pub fn bytesToHex(input: anytype, case: Case) [input.len * 2]u8 {
2561 if (input.len == 0) return [_]u8{};
2562 comptime assert(@TypeOf(input[0]) == u8); // elements to encode must be unsigned bytes
2563
2564 const charset = "0123456789" ++ if (case == .upper) "ABCDEF" else "abcdef";
2565 var result: [input.len * 2]u8 = undefined;
2566 for (input, 0..) |b, i| {
2567 result[i * 2 + 0] = charset[b >> 4];
2568 result[i * 2 + 1] = charset[b & 15];
2569 }
2570 return result;
2571}
2572
2558/// Decodes the sequence of bytes represented by the specified string of2573/// Decodes the sequence of bytes represented by the specified string of
2559/// hexadecimal characters.2574/// hexadecimal characters.
2560/// Returns a slice of the output buffer containing the decoded bytes.2575/// Returns a slice of the output buffer containing the decoded bytes.
...@@ -2575,6 +2590,13 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {...@@ -2575,6 +2590,13 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {
2575 return out[0 .. in_i / 2];2590 return out[0 .. in_i / 2];
2576}2591}
25772592
2593test "bytesToHex" {
2594 const input = "input slice";
2595 const encoded = bytesToHex(input, .lower);
2596 var decoded: [input.len]u8 = undefined;
2597 try std.testing.expectEqualSlices(u8, input, try hexToBytes(&decoded, &encoded));
2598}
2599
2578test "hexToBytes" {2600test "hexToBytes" {
2579 var buf: [32]u8 = undefined;2601 var buf: [32]u8 = undefined;
2580 try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))});2602 try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))});
lib/std/fmt/parse_float.zig+1
...@@ -119,6 +119,7 @@ test "fmt.parseFloat hex.f16" {...@@ -119,6 +119,7 @@ test "fmt.parseFloat hex.f16" {
119}119}
120120
121test "fmt.parseFloat hex.f32" {121test "fmt.parseFloat hex.f32" {
122 try testing.expectError(error.InvalidCharacter, parseFloat(f32, "0x"));
122 try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0);123 try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0);
123 try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5);124 try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5);
124 try testing.expectEqual(try parseFloat(f32, "0x10p+10"), 16384.0);125 try testing.expectEqual(try parseFloat(f32, "0x10p+10"), 16384.0);
lib/std/fmt/parse_float/parse.zig+2
...@@ -107,6 +107,8 @@ fn parsePartialNumberBase(comptime T: type, stream: *FloatStream, negative: bool...@@ -107,6 +107,8 @@ fn parsePartialNumberBase(comptime T: type, stream: *FloatStream, negative: bool
107 tryParseDigits(MantissaT, stream, &mantissa, info.base);107 tryParseDigits(MantissaT, stream, &mantissa, info.base);
108 var int_end = stream.offsetTrue();108 var int_end = stream.offsetTrue();
109 var n_digits = @intCast(isize, stream.offsetTrue());109 var n_digits = @intCast(isize, stream.offsetTrue());
110 // the base being 16 implies a 0x prefix, which shouldn't be included in the digit count
111 if (info.base == 16) n_digits -= 2;
110112
111 // handle dot with the following digits113 // handle dot with the following digits
112 var exponent: i64 = 0;114 var exponent: i64 = 0;
lib/std/fs/file.zig+33-5
...@@ -1048,12 +1048,27 @@ pub const File = struct {...@@ -1048,12 +1048,27 @@ pub const File = struct {
1048 /// Returns the number of bytes read. If the number read is smaller than the total bytes1048 /// Returns the number of bytes read. If the number read is smaller than the total bytes
1049 /// from all the buffers, it means the file reached the end. Reaching the end of a file1049 /// from all the buffers, it means the file reached the end. Reaching the end of a file
1050 /// is not an error condition.1050 /// is not an error condition.
1051 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in1051 ///
1052 /// order to handle partial reads from the underlying OS layer.1052 /// The `iovecs` parameter is mutable because:
1053 /// See https://github.com/ziglang/zig/issues/76991053 /// * This function needs to mutate the fields in order to handle partial
1054 /// reads from the underlying OS layer.
1055 /// * The OS layer expects pointer addresses to be inside the application's address space
1056 /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer
1057 /// addresses when the length is zero. So this function modifies the iov_base fields
1058 /// when the length is zero.
1059 ///
1060 /// Related open issue: https://github.com/ziglang/zig/issues/7699
1054 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {1061 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
1055 if (iovecs.len == 0) return 0;1062 if (iovecs.len == 0) return 0;
10561063
1064 // We use the address of this local variable for all zero-length
1065 // vectors so that the OS does not complain that we are giving it
1066 // addresses outside the application's address space.
1067 var garbage: [1]u8 = undefined;
1068 for (iovecs) |*v| {
1069 if (v.iov_len == 0) v.iov_base = &garbage;
1070 }
1071
1057 var i: usize = 0;1072 var i: usize = 0;
1058 var off: usize = 0;1073 var off: usize = 0;
1059 while (true) {1074 while (true) {
...@@ -1181,13 +1196,26 @@ pub const File = struct {...@@ -1181,13 +1196,26 @@ pub const File = struct {
1181 }1196 }
1182 }1197 }
11831198
1184 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in1199 /// The `iovecs` parameter is mutable because:
1185 /// order to handle partial writes from the underlying OS layer.1200 /// * This function needs to mutate the fields in order to handle partial
1201 /// writes from the underlying OS layer.
1202 /// * The OS layer expects pointer addresses to be inside the application's address space
1203 /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer
1204 /// addresses when the length is zero. So this function modifies the iov_base fields
1205 /// when the length is zero.
1186 /// See https://github.com/ziglang/zig/issues/76991206 /// See https://github.com/ziglang/zig/issues/7699
1187 /// See equivalent function: `std.net.Stream.writevAll`.1207 /// See equivalent function: `std.net.Stream.writevAll`.
1188 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {1208 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
1189 if (iovecs.len == 0) return;1209 if (iovecs.len == 0) return;
11901210
1211 // We use the address of this local variable for all zero-length
1212 // vectors so that the OS does not complain that we are giving it
1213 // addresses outside the application's address space.
1214 var garbage: [1]u8 = undefined;
1215 for (iovecs) |*v| {
1216 if (v.iov_len == 0) v.iov_base = &garbage;
1217 }
1218
1191 var i: usize = 0;1219 var i: usize = 0;
1192 while (true) {1220 while (true) {
1193 var amt = try self.writev(iovecs[i..]);1221 var amt = try self.writev(iovecs[i..]);
lib/std/fs/test.zig+22-8
...@@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" {...@@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" {
1124test "open file with exclusive nonblocking lock twice (absolute paths)" {1124test "open file with exclusive nonblocking lock twice (absolute paths)" {
1125 if (builtin.os.tag == .wasi) return error.SkipZigTest;1125 if (builtin.os.tag == .wasi) return error.SkipZigTest;
11261126
1127 const allocator = testing.allocator;1127 var random_bytes: [12]u8 = undefined;
1128 std.crypto.random.bytes(&random_bytes);
11281129
1129 const cwd = try std.process.getCwdAlloc(allocator);1130 var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1130 defer allocator.free(cwd);1131 _ = fs.base64_encoder.encode(&random_b64, &random_bytes);
1131 const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" };
1132 const filename = try fs.path.resolve(allocator, &file_paths);
1133 defer allocator.free(filename);
11341132
1135 const file1 = try fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });1133 const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt";
11361134
1137 const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });1135 const gpa = testing.allocator;
1136
1137 const cwd = try std.process.getCwdAlloc(gpa);
1138 defer gpa.free(cwd);
1139
1140 const filename = try fs.path.resolve(gpa, &[_][]const u8{ cwd, sub_path });
1141 defer gpa.free(filename);
1142
1143 const file1 = try fs.createFileAbsolute(filename, .{
1144 .lock = .Exclusive,
1145 .lock_nonblocking = true,
1146 });
1147
1148 const file2 = fs.createFileAbsolute(filename, .{
1149 .lock = .Exclusive,
1150 .lock_nonblocking = true,
1151 });
1138 file1.close();1152 file1.close();
1139 try testing.expectError(error.WouldBlock, file2);1153 try testing.expectError(error.WouldBlock, file2);
11401154
lib/std/heap.zig+1
...@@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig"...@@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig"
19pub const WasmAllocator = @import("heap/WasmAllocator.zig");19pub const WasmAllocator = @import("heap/WasmAllocator.zig");
20pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");20pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");
21pub const PageAllocator = @import("heap/PageAllocator.zig");21pub const PageAllocator = @import("heap/PageAllocator.zig");
22pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig");
2223
23const memory_pool = @import("heap/memory_pool.zig");24const memory_pool = @import("heap/memory_pool.zig");
24pub const MemoryPool = memory_pool.MemoryPool;25pub const MemoryPool = memory_pool.MemoryPool;
lib/std/heap/ThreadSafeAllocator.zig created+45
...@@ -0,0 +1,45 @@
1//! Wraps a non-thread-safe allocator and makes it thread-safe.
2
3child_allocator: Allocator,
4mutex: std.Thread.Mutex = .{},
5
6pub fn allocator(self: *ThreadSafeAllocator) Allocator {
7 return .{
8 .ptr = self,
9 .vtable = &.{
10 .alloc = alloc,
11 .resize = resize,
12 .free = free,
13 },
14 };
15}
16
17fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
18 const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx));
19 self.mutex.lock();
20 defer self.mutex.unlock();
21
22 return self.child_allocator.rawAlloc(n, log2_ptr_align, ra);
23}
24
25fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool {
26 const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx));
27
28 self.mutex.lock();
29 defer self.mutex.unlock();
30
31 return self.child_allocator.rawResize(buf, log2_buf_align, new_len, ret_addr);
32}
33
34fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void {
35 const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx));
36
37 self.mutex.lock();
38 defer self.mutex.unlock();
39
40 return self.child_allocator.rawFree(buf, log2_buf_align, ret_addr);
41}
42
43const std = @import("../std.zig");
44const ThreadSafeAllocator = @This();
45const Allocator = std.mem.Allocator;
lib/std/http.zig+15
...@@ -248,9 +248,24 @@ pub const Status = enum(u10) {...@@ -248,9 +248,24 @@ pub const Status = enum(u10) {
248248
249pub const TransferEncoding = enum {249pub const TransferEncoding = enum {
250 chunked,250 chunked,
251 // compression is intentionally omitted here, as std.http.Client stores it as content-encoding
252};
253
254pub const ContentEncoding = enum {
251 compress,255 compress,
252 deflate,256 deflate,
253 gzip,257 gzip,
258 zstd,
259};
260
261pub const Connection = enum {
262 keep_alive,
263 close,
264};
265
266pub const CustomHeader = struct {
267 name: []const u8,
268 value: []const u8,
254};269};
255270
256const std = @import("std.zig");271const std = @import("std.zig");
lib/std/http/Client.zig+251-785
...@@ -13,6 +13,12 @@ const Uri = std.Uri;...@@ -13,6 +13,12 @@ const Uri = std.Uri;
13const Allocator = std.mem.Allocator;13const Allocator = std.mem.Allocator;
14const testing = std.testing;14const testing = std.testing;
1515
16pub const Request = @import("Client/Request.zig");
17pub const Response = @import("Client/Response.zig");
18
19pub const default_connection_pool_size = 32;
20const connection_pool_size = std.options.http_connection_pool_size;
21
16/// Used for tcpConnectToHost and storing HTTP headers when an externally22/// Used for tcpConnectToHost and storing HTTP headers when an externally
17/// managed buffer is not provided.23/// managed buffer is not provided.
18allocator: Allocator,24allocator: Allocator,
...@@ -21,853 +27,256 @@ ca_bundle: std.crypto.Certificate.Bundle = .{},...@@ -21,853 +27,256 @@ ca_bundle: std.crypto.Certificate.Bundle = .{},
21/// it will first rescan the system for root certificates.27/// it will first rescan the system for root certificates.
22next_https_rescan_certs: bool = true,28next_https_rescan_certs: bool = true,
2329
24pub const Connection = struct {30connection_pool: ConnectionPool = .{},
25 stream: net.Stream,
26 /// undefined unless protocol is tls.
27 tls_client: std.crypto.tls.Client,
28 protocol: Protocol,
2931
30 pub const Protocol = enum { plain, tls };32pub const ConnectionPool = struct {
33 pub const Criteria = struct {
34 host: []const u8,
35 port: u16,
36 is_tls: bool,
37 };
3138
32 pub fn read(conn: *Connection, buffer: []u8) !usize {39 const Queue = std.TailQueue(Connection);
33 switch (conn.protocol) {40 pub const Node = Queue.Node;
34 .plain => return conn.stream.read(buffer),41
35 .tls => return conn.tls_client.read(conn.stream, buffer),42 mutex: std.Thread.Mutex = .{},
43 used: Queue = .{},
44 free: Queue = .{},
45 free_len: usize = 0,
46 free_size: usize = default_connection_pool_size,
47
48 /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe.
49 /// If no connection is found, null is returned.
50 pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Node {
51 pool.mutex.lock();
52 defer pool.mutex.unlock();
53
54 var next = pool.free.last;
55 while (next) |node| : (next = node.prev) {
56 if ((node.data.protocol == .tls) != criteria.is_tls) continue;
57 if (node.data.port != criteria.port) continue;
58 if (std.mem.eql(u8, node.data.host, criteria.host)) continue;
59
60 pool.acquireUnsafe(node);
61 return node;
36 }62 }
37 }
3863
39 pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize {64 return null;
40 switch (conn.protocol) {
41 .plain => return conn.stream.readAtLeast(buffer, len),
42 .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len),
43 }
44 }65 }
4566
46 pub fn writeAll(conn: *Connection, buffer: []const u8) !void {67 /// Acquires an existing connection from the connection pool. This function is not threadsafe.
47 switch (conn.protocol) {68 pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void {
48 .plain => return conn.stream.writeAll(buffer),69 pool.free.remove(node);
49 .tls => return conn.tls_client.writeAll(conn.stream, buffer),70 pool.free_len -= 1;
50 }71
72 pool.used.append(node);
51 }73 }
5274
53 pub fn write(conn: *Connection, buffer: []const u8) !usize {75 /// Acquires an existing connection from the connection pool. This function is threadsafe.
54 switch (conn.protocol) {76 pub fn acquire(pool: *ConnectionPool, node: *Node) void {
55 .plain => return conn.stream.write(buffer),77 pool.mutex.lock();
56 .tls => return conn.tls_client.write(conn.stream, buffer),78 defer pool.mutex.unlock();
57 }79
80 return pool.acquireUnsafe(node);
58 }81 }
59};
6082
61/// TODO: emit error.UnexpectedEndOfStream or something like that when the read83 /// Tries to release a connection back to the connection pool. This function is threadsafe.
62/// data does not match the content length. This is necessary since HTTPS disables84 /// If the connection is marked as closing, it will be closed instead.
63/// close_notify protection on underlying TLS streams.85 pub fn release(pool: *ConnectionPool, client: *Client, node: *Node) void {
64pub const Request = struct {86 pool.mutex.lock();
65 client: *Client,87 defer pool.mutex.unlock();
66 connection: Connection,
67 redirects_left: u32,
68 response: Response,
69 /// These are stored in Request so that they are available when following
70 /// redirects.
71 headers: Headers,
72
73 pub const Response = struct {
74 headers: Response.Headers,
75 state: State,
76 header_bytes_owned: bool,
77 /// This could either be a fixed buffer provided by the API user or it
78 /// could be our own array list.
79 header_bytes: std.ArrayListUnmanaged(u8),
80 max_header_bytes: usize,
81 next_chunk_length: u64,
82
83 pub const Headers = struct {
84 status: http.Status,
85 version: http.Version,
86 location: ?[]const u8 = null,
87 content_length: ?u64 = null,
88 transfer_encoding: ?http.TransferEncoding = null,
89
90 pub fn parse(bytes: []const u8) !Response.Headers {
91 var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n");
92
93 const first_line = it.first();
94 if (first_line.len < 12)
95 return error.ShortHttpStatusLine;
96
97 const version: http.Version = switch (int64(first_line[0..8])) {
98 int64("HTTP/1.0") => .@"HTTP/1.0",
99 int64("HTTP/1.1") => .@"HTTP/1.1",
100 else => return error.BadHttpVersion,
101 };
102 if (first_line[8] != ' ') return error.HttpHeadersInvalid;
103 const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*));
104
105 var headers: Response.Headers = .{
106 .version = version,
107 .status = status,
108 };
109
110 while (it.next()) |line| {
111 if (line.len == 0) return error.HttpHeadersInvalid;
112 switch (line[0]) {
113 ' ', '\t' => return error.HttpHeaderContinuationsUnsupported,
114 else => {},
115 }
116 var line_it = mem.split(u8, line, ": ");
117 const header_name = line_it.first();
118 const header_value = line_it.rest();
119 if (std.ascii.eqlIgnoreCase(header_name, "location")) {
120 if (headers.location != null) return error.HttpHeadersInvalid;
121 headers.location = header_value;
122 } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) {
123 if (headers.content_length != null) return error.HttpHeadersInvalid;
124 headers.content_length = try std.fmt.parseInt(u64, header_value, 10);
125 } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) {
126 if (headers.transfer_encoding != null) return error.HttpHeadersInvalid;
127 headers.transfer_encoding = std.meta.stringToEnum(http.TransferEncoding, header_value) orelse
128 return error.HttpTransferEncodingUnsupported;
129 }
130 }
131
132 return headers;
133 }
134
135 test "parse headers" {
136 const example =
137 "HTTP/1.1 301 Moved Permanently\r\n" ++
138 "Location: https://www.example.com/\r\n" ++
139 "Content-Type: text/html; charset=UTF-8\r\n" ++
140 "Content-Length: 220\r\n\r\n";
141 const parsed = try Response.Headers.parse(example);
142 try testing.expectEqual(http.Version.@"HTTP/1.1", parsed.version);
143 try testing.expectEqual(http.Status.moved_permanently, parsed.status);
144 try testing.expectEqualStrings("https://www.example.com/", parsed.location orelse
145 return error.TestFailed);
146 try testing.expectEqual(@as(?u64, 220), parsed.content_length);
147 }
148
149 test "header continuation" {
150 const example =
151 "HTTP/1.0 200 OK\r\n" ++
152 "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++
153 "Content-Length: 220\r\n\r\n";
154 try testing.expectError(
155 error.HttpHeaderContinuationsUnsupported,
156 Response.Headers.parse(example),
157 );
158 }
159
160 test "extra content length" {
161 const example =
162 "HTTP/1.0 200 OK\r\n" ++
163 "Content-Length: 220\r\n" ++
164 "Content-Type: text/html; charset=UTF-8\r\n" ++
165 "content-length: 220\r\n\r\n";
166 try testing.expectError(
167 error.HttpHeadersInvalid,
168 Response.Headers.parse(example),
169 );
170 }
171 };
172
173 pub const State = enum {
174 /// Begin header parsing states.
175 invalid,
176 start,
177 seen_r,
178 seen_rn,
179 seen_rnr,
180 finished,
181 /// Begin transfer-encoding: chunked parsing states.
182 chunk_size_prefix_r,
183 chunk_size_prefix_n,
184 chunk_size,
185 chunk_r,
186 chunk_data,
187
188 pub fn zeroMeansEnd(state: State) bool {
189 return switch (state) {
190 .finished, .chunk_data => true,
191 else => false,
192 };
193 }
194 };
195
196 pub fn initDynamic(max: usize) Response {
197 return .{
198 .state = .start,
199 .headers = undefined,
200 .header_bytes = .{},
201 .max_header_bytes = max,
202 .header_bytes_owned = true,
203 .next_chunk_length = undefined,
204 };
205 }
20688
207 pub fn initStatic(buf: []u8) Response {89 pool.used.remove(node);
208 return .{
209 .state = .start,
210 .headers = undefined,
211 .header_bytes = .{ .items = buf[0..0], .capacity = buf.len },
212 .max_header_bytes = buf.len,
213 .header_bytes_owned = false,
214 .next_chunk_length = undefined,
215 };
216 }
21790
218 /// Returns how many bytes are part of HTTP headers. Always less than or91 if (node.data.closing) {
219 /// equal to bytes.len. If the amount returned is less than bytes.len, it92 node.data.close(client);
220 /// means the headers ended and the first byte after the double \r\n\r\n is
221 /// located at `bytes[result]`.
222 pub fn findHeadersEnd(r: *Response, bytes: []const u8) usize {
223 var index: usize = 0;
224
225 // TODO: https://github.com/ziglang/zig/issues/8220
226 state: while (true) {
227 switch (r.state) {
228 .invalid => unreachable,
229 .finished => unreachable,
230 .start => while (true) {
231 switch (bytes.len - index) {
232 0 => return index,
233 1 => {
234 if (bytes[index] == '\r')
235 r.state = .seen_r;
236 return index + 1;
237 },
238 2 => {
239 if (int16(bytes[index..][0..2]) == int16("\r\n")) {
240 r.state = .seen_rn;
241 } else if (bytes[index + 1] == '\r') {
242 r.state = .seen_r;
243 }
244 return index + 2;
245 },
246 3 => {
247 if (int16(bytes[index..][0..2]) == int16("\r\n") and
248 bytes[index + 2] == '\r')
249 {
250 r.state = .seen_rnr;
251 } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n")) {
252 r.state = .seen_rn;
253 } else if (bytes[index + 2] == '\r') {
254 r.state = .seen_r;
255 }
256 return index + 3;
257 },
258 4...15 => {
259 if (int32(bytes[index..][0..4]) == int32("\r\n\r\n")) {
260 r.state = .finished;
261 return index + 4;
262 } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n") and
263 bytes[index + 3] == '\r')
264 {
265 r.state = .seen_rnr;
266 index += 4;
267 continue :state;
268 } else if (int16(bytes[index + 2 ..][0..2]) == int16("\r\n")) {
269 r.state = .seen_rn;
270 index += 4;
271 continue :state;
272 } else if (bytes[index + 3] == '\r') {
273 r.state = .seen_r;
274 index += 4;
275 continue :state;
276 }
277 index += 4;
278 continue;
279 },
280 else => {
281 const chunk = bytes[index..][0..16];
282 const v: @Vector(16, u8) = chunk.*;
283 const matches_r = v == @splat(16, @as(u8, '\r'));
284 const iota = std.simd.iota(u8, 16);
285 const default = @splat(16, @as(u8, 16));
286 const sub_index = @reduce(.Min, @select(u8, matches_r, iota, default));
287 switch (sub_index) {
288 0...12 => {
289 index += sub_index + 4;
290 if (int32(chunk[sub_index..][0..4]) == int32("\r\n\r\n")) {
291 r.state = .finished;
292 return index;
293 }
294 continue;
295 },
296 13 => {
297 index += 16;
298 if (int16(chunk[14..][0..2]) == int16("\n\r")) {
299 r.state = .seen_rnr;
300 continue :state;
301 }
302 continue;
303 },
304 14 => {
305 index += 16;
306 if (chunk[15] == '\n') {
307 r.state = .seen_rn;
308 continue :state;
309 }
310 continue;
311 },
312 15 => {
313 r.state = .seen_r;
314 index += 16;
315 continue :state;
316 },
317 16 => {
318 index += 16;
319 continue;
320 },
321 else => unreachable,
322 }
323 },
324 }
325 },
326
327 .seen_r => switch (bytes.len - index) {
328 0 => return index,
329 1 => {
330 switch (bytes[index]) {
331 '\n' => r.state = .seen_rn,
332 '\r' => r.state = .seen_r,
333 else => r.state = .start,
334 }
335 return index + 1;
336 },
337 2 => {
338 if (int16(bytes[index..][0..2]) == int16("\n\r")) {
339 r.state = .seen_rnr;
340 return index + 2;
341 }
342 r.state = .start;
343 return index + 2;
344 },
345 else => {
346 if (int16(bytes[index..][0..2]) == int16("\n\r") and
347 bytes[index + 2] == '\n')
348 {
349 r.state = .finished;
350 return index + 3;
351 }
352 index += 3;
353 r.state = .start;
354 continue :state;
355 },
356 },
357 .seen_rn => switch (bytes.len - index) {
358 0 => return index,
359 1 => {
360 switch (bytes[index]) {
361 '\r' => r.state = .seen_rnr,
362 else => r.state = .start,
363 }
364 return index + 1;
365 },
366 else => {
367 if (int16(bytes[index..][0..2]) == int16("\r\n")) {
368 r.state = .finished;
369 return index + 2;
370 }
371 index += 2;
372 r.state = .start;
373 continue :state;
374 },
375 },
376 .seen_rnr => switch (bytes.len - index) {
377 0 => return index,
378 else => {
379 if (bytes[index] == '\n') {
380 r.state = .finished;
381 return index + 1;
382 }
383 index += 1;
384 r.state = .start;
385 continue :state;
386 },
387 },
388 .chunk_size_prefix_r => unreachable,
389 .chunk_size_prefix_n => unreachable,
390 .chunk_size => unreachable,
391 .chunk_r => unreachable,
392 .chunk_data => unreachable,
393 }
394
395 return index;
396 }
397 }
39893
399 pub fn findChunkedLen(r: *Response, bytes: []const u8) usize {94 return client.allocator.destroy(node);
400 var i: usize = 0;
401 if (r.state == .chunk_size) {
402 while (i < bytes.len) : (i += 1) {
403 const digit = switch (bytes[i]) {
404 '0'...'9' => |b| b - '0',
405 'A'...'Z' => |b| b - 'A' + 10,
406 'a'...'z' => |b| b - 'a' + 10,
407 '\r' => {
408 r.state = .chunk_r;
409 i += 1;
410 break;
411 },
412 else => {
413 r.state = .invalid;
414 return i;
415 },
416 };
417 const mul = @mulWithOverflow(r.next_chunk_length, 16);
418 if (mul[1] != 0) {
419 r.state = .invalid;
420 return i;
421 }
422 const add = @addWithOverflow(mul[0], digit);
423 if (add[1] != 0) {
424 r.state = .invalid;
425 return i;
426 }
427 r.next_chunk_length = add[0];
428 } else {
429 return i;
430 }
431 }
432 assert(r.state == .chunk_r);
433 if (i == bytes.len) return i;
434
435 if (bytes[i] == '\n') {
436 r.state = .chunk_data;
437 return i + 1;
438 } else {
439 r.state = .invalid;
440 return i;
441 }
442 }95 }
44396
444 fn parseInt3(nnn: @Vector(3, u8)) u10 {97 if (pool.free_len + 1 >= pool.free_size) {
445 const zero: @Vector(3, u8) = .{ '0', '0', '0' };98 const popped = pool.free.popFirst() orelse unreachable;
446 const mmm: @Vector(3, u10) = .{ 100, 10, 1 };
447 return @reduce(.Add, @as(@Vector(3, u10), nnn -% zero) *% mmm);
448 }
44999
450 test parseInt3 {100 popped.data.close(client);
451 const expectEqual = std.testing.expectEqual;
452 try expectEqual(@as(u10, 0), parseInt3("000".*));
453 try expectEqual(@as(u10, 418), parseInt3("418".*));
454 try expectEqual(@as(u10, 999), parseInt3("999".*));
455 }
456101
457 test "find headers end basic" {102 return client.allocator.destroy(popped);
458 var buffer: [1]u8 = undefined;
459 var r = Response.initStatic(&buffer);
460 try testing.expectEqual(@as(usize, 10), r.findHeadersEnd("HTTP/1.1 4"));
461 try testing.expectEqual(@as(usize, 2), r.findHeadersEnd("18"));
462 try testing.expectEqual(@as(usize, 8), r.findHeadersEnd(" lol\r\n\r\nblah blah"));
463 }103 }
464104
465 test "find headers end vectorized" {105 pool.free.append(node);
466 var buffer: [1]u8 = undefined;106 pool.free_len += 1;
467 var r = Response.initStatic(&buffer);107 }
468 const example =
469 "HTTP/1.1 301 Moved Permanently\r\n" ++
470 "Location: https://www.example.com/\r\n" ++
471 "Content-Type: text/html; charset=UTF-8\r\n" ++
472 "Content-Length: 220\r\n" ++
473 "\r\ncontent";
474 try testing.expectEqual(@as(usize, 131), r.findHeadersEnd(example));
475 }
476108
477 test "find headers end bug" {109 /// Adds a newly created node to the pool of used connections. This function is threadsafe.
478 var buffer: [1]u8 = undefined;110 pub fn addUsed(pool: *ConnectionPool, node: *Node) void {
479 var r = Response.initStatic(&buffer);111 pool.mutex.lock();
480 const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";112 defer pool.mutex.unlock();
481 const example =
482 "HTTP/1.1 200 OK\r\n" ++
483 "Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++
484 "content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++
485 "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++
486 "Content-Type: application/x-gzip\r\n" ++
487 "ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++
488 "Strict-Transport-Security: max-age=31536000\r\n" ++
489 "Vary: Authorization,Accept-Encoding,Origin\r\n" ++
490 "X-Content-Type-Options: nosniff\r\n" ++
491 "X-Frame-Options: deny\r\n" ++
492 "X-XSS-Protection: 1; mode=block\r\n" ++
493 "Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++
494 "Transfer-Encoding: chunked\r\n" ++
495 "X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++
496 "connection: close\r\n\r\n" ++ trail;
497 try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example));
498 }
499 };
500113
501 pub const Headers = struct {114 pool.used.append(node);
502 version: http.Version = .@"HTTP/1.1",115 }
503 method: http.Method = .GET,
504 };
505116
506 pub const Options = struct {117 pub fn deinit(pool: *ConnectionPool, client: *Client) void {
507 max_redirects: u32 = 3,118 pool.mutex.lock();
508 header_strategy: HeaderStrategy = .{ .dynamic = 16 * 1024 },
509
510 pub const HeaderStrategy = union(enum) {
511 /// In this case, the client's Allocator will be used to store the
512 /// entire HTTP header. This value is the maximum total size of
513 /// HTTP headers allowed, otherwise
514 /// error.HttpHeadersExceededSizeLimit is returned from read().
515 dynamic: usize,
516 /// This is used to store the entire HTTP header. If the HTTP
517 /// header is too big to fit, `error.HttpHeadersExceededSizeLimit`
518 /// is returned from read(). When this is used, `error.OutOfMemory`
519 /// cannot be returned from `read()`.
520 static: []u8,
521 };
522 };
523119
524 /// May be skipped if header strategy is buffer.120 var next = pool.free.first;
525 pub fn deinit(req: *Request) void {121 while (next) |node| {
526 if (req.response.header_bytes_owned) {122 defer client.allocator.destroy(node);
527 req.response.header_bytes.deinit(req.client.allocator);123 next = node.next;
124
125 node.data.close(client);
528 }126 }
529 req.* = undefined;127
128 next = pool.used.first;
129 while (next) |node| {
130 defer client.allocator.destroy(node);
131 next = node.next;
132
133 node.data.close(client);
134 }
135
136 pool.* = undefined;
530 }137 }
138};
139
140pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.ReaderRaw);
141pub const GzipDecompressor = std.compress.gzip.Decompress(Request.ReaderRaw);
142pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Request.ReaderRaw, .{});
143
144pub const Connection = struct {
145 stream: net.Stream,
146 /// undefined unless protocol is tls.
147 tls_client: *std.crypto.tls.Client, // TODO: allocate this, it's currently 16 KB.
148 protocol: Protocol,
149 host: []u8,
150 port: u16,
151
152 // This connection has been part of a non keepalive request and cannot be added to the pool.
153 closing: bool = false,
531154
532 pub const Reader = std.io.Reader(*Request, ReadError, read);155 pub const Protocol = enum { plain, tls };
533156
534 pub fn reader(req: *Request) Reader {157 pub fn read(conn: *Connection, buffer: []u8) !usize {
535 return .{ .context = req };158 switch (conn.protocol) {
159 .plain => return conn.stream.read(buffer),
160 .tls => return conn.tls_client.read(conn.stream, buffer),
161 }
536 }162 }
537163
538 pub fn readAll(req: *Request, buffer: []u8) !usize {164 pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize {
539 return readAtLeast(req, buffer, buffer.len);165 switch (conn.protocol) {
166 .plain => return conn.stream.readAtLeast(buffer, len),
167 .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len),
168 }
540 }169 }
541170
542 pub const ReadError = net.Stream.ReadError || error{171 pub const ReadError = net.Stream.ReadError || error{
543 // From HTTP protocol
544 HttpHeadersInvalid,
545 HttpHeadersExceededSizeLimit,
546 HttpRedirectMissingLocation,
547 HttpTransferEncodingUnsupported,
548 HttpContentLengthUnknown,
549 TooManyHttpRedirects,
550 ShortHttpStatusLine,
551 BadHttpVersion,
552 HttpHeaderContinuationsUnsupported,
553 UnsupportedUrlScheme,
554 UriMissingHost,
555 UnknownHostName,
556
557 // Network problems
558 NetworkUnreachable,
559 HostLacksNetworkAddresses,
560 TemporaryNameServerFailure,
561 NameServerFailure,
562 ProtocolFamilyNotAvailable,
563 ProtocolNotSupported,
564
565 // System resource problems
566 ProcessFdQuotaExceeded,
567 SystemFdQuotaExceeded,
568 OutOfMemory,
569
570 // TLS problems
571 InsufficientEntropy,
572 TlsConnectionTruncated,172 TlsConnectionTruncated,
573 TlsRecordOverflow,173 TlsRecordOverflow,
574 TlsDecodeError,174 TlsDecodeError,
575 TlsAlert,175 TlsAlert,
576 TlsBadRecordMac,176 TlsBadRecordMac,
177 Overflow,
577 TlsBadLength,178 TlsBadLength,
578 TlsIllegalParameter,179 TlsIllegalParameter,
579 TlsUnexpectedMessage,180 TlsUnexpectedMessage,
580 TlsDecryptFailure,
581 CertificateFieldHasInvalidLength,
582 CertificateHostMismatch,
583 CertificatePublicKeyInvalid,
584 CertificateExpired,
585 CertificateFieldHasWrongDataType,
586 CertificateIssuerMismatch,
587 CertificateNotYetValid,
588 CertificateSignatureAlgorithmMismatch,
589 CertificateSignatureAlgorithmUnsupported,
590 CertificateSignatureInvalid,
591 CertificateSignatureInvalidLength,
592 CertificateSignatureNamedCurveUnsupported,
593 CertificateSignatureUnsupportedBitCount,
594 TlsCertificateNotVerified,
595 TlsBadSignatureScheme,
596 TlsBadRsaSignatureBitCount,
597 TlsDecryptError,
598 UnsupportedCertificateVersion,
599 CertificateTimeInvalid,
600 CertificateHasUnrecognizedObjectId,
601 CertificateHasInvalidBitString,
602 CertificateAuthorityBundleTooBig,
603
604 // TODO: convert to higher level errors
605 InvalidFormat,
606 InvalidPort,
607 UnexpectedCharacter,
608 Overflow,
609 InvalidCharacter,
610 AddressFamilyNotSupported,
611 AddressInUse,
612 AddressNotAvailable,
613 ConnectionPending,
614 ConnectionRefused,
615 FileNotFound,
616 PermissionDenied,
617 ServiceUnavailable,
618 SocketTypeNotSupported,
619 FileTooBig,
620 LockViolation,
621 NoSpaceLeft,
622 NotOpenForWriting,
623 InvalidEncoding,
624 IdentityElement,
625 NonCanonical,
626 SignatureVerificationFailed,
627 MessageTooLong,
628 NegativeIntoUnsigned,
629 TargetTooSmall,
630 BufferTooSmall,
631 InvalidSignature,
632 NotSquare,
633 DiskQuota,
634 InvalidEnd,
635 Incomplete,
636 InvalidIpv4Mapping,
637 InvalidIPAddressFormat,
638 BadPathName,
639 DeviceBusy,
640 FileBusy,
641 FileLocksNotSupported,
642 InvalidHandle,
643 InvalidUtf8,
644 NameTooLong,
645 NoDevice,
646 PathAlreadyExists,
647 PipeBusy,
648 SharingViolation,
649 SymLinkLoop,
650 FileSystem,
651 InterfaceNotFound,
652 AlreadyBound,
653 FileDescriptorNotASocket,
654 NetworkSubsystemFailed,
655 NotDir,
656 ReadOnlyFileSystem,
657 Unseekable,
658 MissingEndCertificateMarker,
659 InvalidPadding,
660 EndOfStream,
661 };181 };
662182
663 pub fn read(req: *Request, buffer: []u8) ReadError!usize {183 pub const Reader = std.io.Reader(*Connection, ReadError, read);
664 return readAtLeast(req, buffer, 1);184
185 pub fn reader(conn: *Connection) Reader {
186 return Reader{ .context = conn };
665 }187 }
666188
667 pub fn readAtLeast(req: *Request, buffer: []u8, len: usize) !usize {189 pub fn writeAll(conn: *Connection, buffer: []const u8) !void {
668 assert(len <= buffer.len);190 switch (conn.protocol) {
669 var index: usize = 0;191 .plain => return conn.stream.writeAll(buffer),
670 while (index < len) {192 .tls => return conn.tls_client.writeAll(conn.stream, buffer),
671 const zero_means_end = req.response.state.zeroMeansEnd();
672 const amt = try readAdvanced(req, buffer[index..]);
673 if (amt == 0 and zero_means_end) break;
674 index += amt;
675 }193 }
676 return index;
677 }194 }
678195
679 /// This one can return 0 without meaning EOF.196 pub fn write(conn: *Connection, buffer: []const u8) !usize {
680 /// TODO change to readvAdvanced197 switch (conn.protocol) {
681 pub fn readAdvanced(req: *Request, buffer: []u8) !usize {198 .plain => return conn.stream.write(buffer),
682 var in = buffer[0..try req.connection.read(buffer)];199 .tls => return conn.tls_client.write(conn.stream, buffer),
683 var out_index: usize = 0;
684 while (true) {
685 switch (req.response.state) {
686 .invalid => unreachable,
687 .start, .seen_r, .seen_rn, .seen_rnr => {
688 const i = req.response.findHeadersEnd(in);
689 if (req.response.state == .invalid) return error.HttpHeadersInvalid;
690
691 const headers_data = in[0..i];
692 if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) {
693 return error.HttpHeadersExceededSizeLimit;
694 }
695 try req.response.header_bytes.appendSlice(req.client.allocator, headers_data);
696
697 if (req.response.state == .finished) {
698 req.response.headers = try Response.Headers.parse(req.response.header_bytes.items);
699
700 if (req.response.headers.status.class() == .redirect) {
701 if (req.redirects_left == 0) return error.TooManyHttpRedirects;
702 const location = req.response.headers.location orelse
703 return error.HttpRedirectMissingLocation;
704 const new_url = try std.Uri.parse(location);
705 const new_req = try req.client.request(new_url, req.headers, .{
706 .max_redirects = req.redirects_left - 1,
707 .header_strategy = if (req.response.header_bytes_owned) .{
708 .dynamic = req.response.max_header_bytes,
709 } else .{
710 .static = req.response.header_bytes.unusedCapacitySlice(),
711 },
712 });
713 req.deinit();
714 req.* = new_req;
715 assert(out_index == 0);
716 in = buffer[0..try req.connection.read(buffer)];
717 continue;
718 }
719
720 if (req.response.headers.transfer_encoding) |transfer_encoding| {
721 switch (transfer_encoding) {
722 .chunked => {
723 req.response.next_chunk_length = 0;
724 req.response.state = .chunk_size;
725 },
726 .compress => return error.HttpTransferEncodingUnsupported,
727 .deflate => return error.HttpTransferEncodingUnsupported,
728 .gzip => return error.HttpTransferEncodingUnsupported,
729 }
730 } else if (req.response.headers.content_length) |content_length| {
731 req.response.next_chunk_length = content_length;
732 } else {
733 return error.HttpContentLengthUnknown;
734 }
735
736 in = in[i..];
737 continue;
738 }
739
740 assert(out_index == 0);
741 return 0;
742 },
743 .finished => {
744 if (in.ptr == buffer.ptr) {
745 return in.len;
746 } else {
747 mem.copy(u8, buffer[out_index..], in);
748 return out_index + in.len;
749 }
750 },
751 .chunk_size_prefix_r => switch (in.len) {
752 0 => return out_index,
753 1 => switch (in[0]) {
754 '\r' => {
755 req.response.state = .chunk_size_prefix_n;
756 return out_index;
757 },
758 else => {
759 req.response.state = .invalid;
760 return error.HttpHeadersInvalid;
761 },
762 },
763 else => switch (int16(in[0..2])) {
764 int16("\r\n") => {
765 in = in[2..];
766 req.response.state = .chunk_size;
767 continue;
768 },
769 else => {
770 req.response.state = .invalid;
771 return error.HttpHeadersInvalid;
772 },
773 },
774 },
775 .chunk_size_prefix_n => switch (in.len) {
776 0 => return out_index,
777 else => switch (in[0]) {
778 '\n' => {
779 in = in[1..];
780 req.response.state = .chunk_size;
781 continue;
782 },
783 else => {
784 req.response.state = .invalid;
785 return error.HttpHeadersInvalid;
786 },
787 },
788 },
789 .chunk_size, .chunk_r => {
790 const i = req.response.findChunkedLen(in);
791 switch (req.response.state) {
792 .invalid => return error.HttpHeadersInvalid,
793 .chunk_data => {
794 if (req.response.next_chunk_length == 0) {
795 req.response.state = .start;
796 return out_index;
797 }
798 in = in[i..];
799 continue;
800 },
801 .chunk_size => return out_index,
802 else => unreachable,
803 }
804 },
805 .chunk_data => {
806 // TODO https://github.com/ziglang/zig/issues/14039
807 const sub_amt = @intCast(usize, @min(req.response.next_chunk_length, in.len));
808 req.response.next_chunk_length -= sub_amt;
809 if (req.response.next_chunk_length > 0) {
810 if (in.ptr == buffer.ptr) {
811 return sub_amt;
812 } else {
813 mem.copy(u8, buffer[out_index..], in[0..sub_amt]);
814 out_index += sub_amt;
815 return out_index;
816 }
817 }
818 mem.copy(u8, buffer[out_index..], in[0..sub_amt]);
819 out_index += sub_amt;
820 req.response.state = .chunk_size_prefix_r;
821 in = in[sub_amt..];
822 continue;
823 },
824 }
825 }200 }
826 }201 }
827202
828 inline fn int16(array: *const [2]u8) u16 {203 pub const WriteError = net.Stream.WriteError || error{};
829 return @bitCast(u16, array.*);204 pub const Writer = std.io.Writer(*Connection, WriteError, write);
830 }
831205
832 inline fn int32(array: *const [4]u8) u32 {206 pub fn writer(conn: *Connection) Writer {
833 return @bitCast(u32, array.*);207 return Writer{ .context = conn };
834 }208 }
835209
836 inline fn int64(array: *const [8]u8) u64 {210 pub fn close(conn: *Connection, client: *const Client) void {
837 return @bitCast(u64, array.*);211 if (conn.protocol == .tls) {
838 }212 // try to cleanly close the TLS connection, for any server that cares.
213 _ = conn.tls_client.writeEnd(conn.stream, "", true) catch {};
214 client.allocator.destroy(conn.tls_client);
215 }
839216
840 test {217 conn.stream.close();
841 _ = Response;218
219 client.allocator.free(conn.host);
842 }220 }
843};221};
844222
845pub fn deinit(client: *Client) void {223pub fn deinit(client: *Client) void {
224 client.connection_pool.deinit(client);
225
846 client.ca_bundle.deinit(client.allocator);226 client.ca_bundle.deinit(client.allocator);
847 client.* = undefined;227 client.* = undefined;
848}228}
849229
850pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !Connection {230pub const ConnectError = std.mem.Allocator.Error || net.TcpConnectToHostError || std.crypto.tls.Client.InitError(net.Stream);
851 var conn: Connection = .{231
232pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node {
233 if (client.connection_pool.findConnection(.{
234 .host = host,
235 .port = port,
236 .is_tls = protocol == .tls,
237 })) |node|
238 return node;
239
240 const conn = try client.allocator.create(ConnectionPool.Node);
241 errdefer client.allocator.destroy(conn);
242 conn.* = .{ .data = undefined };
243
244 conn.data = .{
852 .stream = try net.tcpConnectToHost(client.allocator, host, port),245 .stream = try net.tcpConnectToHost(client.allocator, host, port),
853 .tls_client = undefined,246 .tls_client = undefined,
854 .protocol = protocol,247 .protocol = protocol,
248 .host = try client.allocator.dupe(u8, host),
249 .port = port,
855 };250 };
856251
857 switch (protocol) {252 switch (protocol) {
858 .plain => {},253 .plain => {},
859 .tls => {254 .tls => {
860 conn.tls_client = try std.crypto.tls.Client.init(conn.stream, client.ca_bundle, host);255 conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client);
256 conn.data.tls_client.* = try std.crypto.tls.Client.init(conn.data.stream, client.ca_bundle, host);
861 // This is appropriate for HTTPS because the HTTP headers contain257 // This is appropriate for HTTPS because the HTTP headers contain
862 // the content length which is used to detect truncation attacks.258 // the content length which is used to detect truncation attacks.
863 conn.tls_client.allow_truncation_attacks = true;259 conn.data.tls_client.allow_truncation_attacks = true;
864 },260 },
865 }261 }
866262
263 client.connection_pool.addUsed(conn);
264
867 return conn;265 return conn;
868}266}
869267
870pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) !Request {268pub const RequestError = ConnectError || Connection.WriteError || error{
269 UnsupportedUrlScheme,
270 UriMissingHost,
271
272 CertificateAuthorityBundleTooBig,
273 InvalidPadding,
274 MissingEndCertificateMarker,
275 Unseekable,
276 EndOfStream,
277};
278
279pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) RequestError!Request {
871 const protocol: Connection.Protocol = if (mem.eql(u8, uri.scheme, "http"))280 const protocol: Connection.Protocol = if (mem.eql(u8, uri.scheme, "http"))
872 .plain281 .plain
873 else if (mem.eql(u8, uri.scheme, "https"))282 else if (mem.eql(u8, uri.scheme, "https"))
...@@ -883,34 +292,89 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req...@@ -883,34 +292,89 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req
883 const host = uri.host orelse return error.UriMissingHost;292 const host = uri.host orelse return error.UriMissingHost;
884293
885 if (client.next_https_rescan_certs and protocol == .tls) {294 if (client.next_https_rescan_certs and protocol == .tls) {
886 try client.ca_bundle.rescan(client.allocator);295 client.connection_pool.mutex.lock(); // TODO: this could be so much better than reusing the connection pool mutex.
887 client.next_https_rescan_certs = false;296 defer client.connection_pool.mutex.unlock();
297
298 if (client.next_https_rescan_certs) {
299 try client.ca_bundle.rescan(client.allocator);
300 client.next_https_rescan_certs = false;
301 }
888 }302 }
889303
890 var req: Request = .{304 var req: Request = .{
305 .uri = uri,
891 .client = client,306 .client = client,
892 .headers = headers,307 .headers = headers,
893 .connection = try client.connect(host, port, protocol),308 .connection = try client.connect(host, port, protocol),
894 .redirects_left = options.max_redirects,309 .redirects_left = options.max_redirects,
310 .handle_redirects = options.handle_redirects,
311 .compression_init = false,
895 .response = switch (options.header_strategy) {312 .response = switch (options.header_strategy) {
896 .dynamic => |max| Request.Response.initDynamic(max),313 .dynamic => |max| Response.initDynamic(max),
897 .static => |buf| Request.Response.initStatic(buf),314 .static => |buf| Response.initStatic(buf),
898 },315 },
316 .arena = undefined,
899 };317 };
900318
319 req.arena = std.heap.ArenaAllocator.init(client.allocator);
320
901 {321 {
902 var h = try std.BoundedArray(u8, 1000).init(0);322 var buffered = std.io.bufferedWriter(req.connection.data.writer());
903 try h.appendSlice(@tagName(headers.method));323 const writer = buffered.writer();
904 try h.appendSlice(" ");324
905 try h.appendSlice(uri.path);325 const escaped_path = try Uri.escapePath(client.allocator, uri.path);
906 try h.appendSlice(" ");326 defer client.allocator.free(escaped_path);
907 try h.appendSlice(@tagName(headers.version));327
908 try h.appendSlice("\r\nHost: ");328 const escaped_query = if (uri.query) |q| try Uri.escapeQuery(client.allocator, q) else null;
909 try h.appendSlice(host);329 defer if (escaped_query) |q| client.allocator.free(q);
910 try h.appendSlice("\r\nConnection: close\r\n\r\n");330
911331 const escaped_fragment = if (uri.fragment) |f| try Uri.escapeQuery(client.allocator, f) else null;
912 const header_bytes = h.slice();332 defer if (escaped_fragment) |f| client.allocator.free(f);
913 try req.connection.writeAll(header_bytes);333
334 try writer.writeAll(@tagName(headers.method));
335 try writer.writeByte(' ');
336 if (escaped_path.len == 0) {
337 try writer.writeByte('/');
338 } else {
339 try writer.writeAll(escaped_path);
340 }
341 if (escaped_query) |q| {
342 try writer.writeByte('?');
343 try writer.writeAll(q);
344 }
345 if (escaped_fragment) |f| {
346 try writer.writeByte('#');
347 try writer.writeAll(f);
348 }
349 try writer.writeByte(' ');
350 try writer.writeAll(@tagName(headers.version));
351 try writer.writeAll("\r\nHost: ");
352 try writer.writeAll(host);
353 try writer.writeAll("\r\nUser-Agent: ");
354 try writer.writeAll(headers.user_agent);
355 if (headers.connection == .close) {
356 try writer.writeAll("\r\nConnection: close");
357 } else {
358 try writer.writeAll("\r\nConnection: keep-alive");
359 }
360 try writer.writeAll("\r\nAccept-Encoding: gzip, deflate, zstd");
361
362 switch (headers.transfer_encoding) {
363 .chunked => try writer.writeAll("\r\nTransfer-Encoding: chunked"),
364 .content_length => |content_length| try writer.print("\r\nContent-Length: {d}", .{content_length}),
365 .none => {},
366 }
367
368 for (headers.custom) |header| {
369 try writer.writeAll("\r\n");
370 try writer.writeAll(header.name);
371 try writer.writeAll(": ");
372 try writer.writeAll(header.value);
373 }
374
375 try writer.writeAll("\r\n\r\n");
376
377 try buffered.flush();
914 }378 }
915379
916 return req;380 return req;
...@@ -924,5 +388,7 @@ test {...@@ -924,5 +388,7 @@ test {
924 return error.SkipZigTest;388 return error.SkipZigTest;
925 }389 }
926390
391 if (builtin.os.tag == .wasi) return error.SkipZigTest;
392
927 _ = Request;393 _ = Request;
928}394}
lib/std/http/Client/Request.zig created+482
...@@ -0,0 +1,482 @@
1const std = @import("std");
2const http = std.http;
3const Uri = std.Uri;
4const mem = std.mem;
5const assert = std.debug.assert;
6
7const Client = @import("../Client.zig");
8const Connection = Client.Connection;
9const ConnectionNode = Client.ConnectionPool.Node;
10const Response = @import("Response.zig");
11
12const Request = @This();
13
14const read_buffer_size = 8192;
15const ReadBufferIndex = std.math.IntFittingRange(0, read_buffer_size);
16
17uri: Uri,
18client: *Client,
19connection: *ConnectionNode,
20response: Response,
21/// These are stored in Request so that they are available when following
22/// redirects.
23headers: Headers,
24
25redirects_left: u32,
26handle_redirects: bool,
27compression_init: bool,
28
29/// Used as a allocator for resolving redirects locations.
30arena: std.heap.ArenaAllocator,
31
32/// Read buffer for the connection. This is used to pull in large amounts of data from the connection even if the user asks for a small amount. This can probably be removed with careful planning.
33read_buffer: [read_buffer_size]u8 = undefined,
34read_buffer_start: ReadBufferIndex = 0,
35read_buffer_len: ReadBufferIndex = 0,
36
37pub const RequestTransfer = union(enum) {
38 content_length: u64,
39 chunked: void,
40 none: void,
41};
42
43pub const Headers = struct {
44 version: http.Version = .@"HTTP/1.1",
45 method: http.Method = .GET,
46 user_agent: []const u8 = "zig (std.http)",
47 connection: http.Connection = .keep_alive,
48 transfer_encoding: RequestTransfer = .none,
49
50 custom: []const http.CustomHeader = &[_]http.CustomHeader{},
51};
52
53pub const Options = struct {
54 handle_redirects: bool = true,
55 max_redirects: u32 = 3,
56 header_strategy: HeaderStrategy = .{ .dynamic = 16 * 1024 },
57
58 pub const HeaderStrategy = union(enum) {
59 /// In this case, the client's Allocator will be used to store the
60 /// entire HTTP header. This value is the maximum total size of
61 /// HTTP headers allowed, otherwise
62 /// error.HttpHeadersExceededSizeLimit is returned from read().
63 dynamic: usize,
64 /// This is used to store the entire HTTP header. If the HTTP
65 /// header is too big to fit, `error.HttpHeadersExceededSizeLimit`
66 /// is returned from read(). When this is used, `error.OutOfMemory`
67 /// cannot be returned from `read()`.
68 static: []u8,
69 };
70};
71
72/// Frees all resources associated with the request.
73pub fn deinit(req: *Request) void {
74 switch (req.response.compression) {
75 .none => {},
76 .deflate => |*deflate| deflate.deinit(),
77 .gzip => |*gzip| gzip.deinit(),
78 .zstd => |*zstd| zstd.deinit(),
79 }
80
81 if (req.response.header_bytes_owned) {
82 req.response.header_bytes.deinit(req.client.allocator);
83 }
84
85 if (!req.response.done) {
86 // If the response wasn't fully read, then we need to close the connection.
87 req.connection.data.closing = true;
88 req.client.connection_pool.release(req.client, req.connection);
89 }
90
91 req.arena.deinit();
92 req.* = undefined;
93}
94
95pub const ReadRawError = Connection.ReadError || Uri.ParseError || Client.RequestError || error{
96 UnexpectedEndOfStream,
97 TooManyHttpRedirects,
98 HttpRedirectMissingLocation,
99 HttpHeadersInvalid,
100};
101
102pub const ReaderRaw = std.io.Reader(*Request, ReadRawError, readRaw);
103
104/// Read from the underlying stream, without decompressing or parsing the headers. Must be called
105/// after waitForCompleteHead() has returned successfully.
106pub fn readRaw(req: *Request, buffer: []u8) ReadRawError!usize {
107 assert(req.response.state.isContent());
108
109 var index: usize = 0;
110 while (index == 0) {
111 const amt = try req.readRawAdvanced(buffer[index..]);
112 if (amt == 0 and req.response.done) break;
113 index += amt;
114 }
115
116 return index;
117}
118
119fn checkForCompleteHead(req: *Request, buffer: []u8) !usize {
120 switch (req.response.state) {
121 .invalid => unreachable,
122 .start, .seen_r, .seen_rn, .seen_rnr => {},
123 else => return 0, // No more headers to read.
124 }
125
126 const i = req.response.findHeadersEnd(buffer[0..]);
127 if (req.response.state == .invalid) return error.HttpHeadersInvalid;
128
129 const headers_data = buffer[0..i];
130 if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) {
131 return error.HttpHeadersExceededSizeLimit;
132 }
133 try req.response.header_bytes.appendSlice(req.client.allocator, headers_data);
134
135 if (req.response.state == .finished) {
136 req.response.headers = try Response.Headers.parse(req.response.header_bytes.items);
137
138 if (req.response.headers.upgrade) |_| {
139 req.connection.data.closing = false;
140 req.response.done = true;
141 return i;
142 }
143
144 if (req.response.headers.connection == .keep_alive) {
145 req.connection.data.closing = false;
146 } else {
147 req.connection.data.closing = true;
148 }
149
150 if (req.response.headers.transfer_encoding) |transfer_encoding| {
151 switch (transfer_encoding) {
152 .chunked => {
153 req.response.next_chunk_length = 0;
154 req.response.state = .chunk_size;
155 },
156 }
157 } else if (req.response.headers.content_length) |content_length| {
158 req.response.next_chunk_length = content_length;
159
160 if (content_length == 0) req.response.done = true;
161 } else {
162 req.response.done = true;
163 }
164
165 return i;
166 }
167
168 return 0;
169}
170
171pub const WaitForCompleteHeadError = ReadRawError || error{
172 UnexpectedEndOfStream,
173
174 HttpHeadersExceededSizeLimit,
175 ShortHttpStatusLine,
176 BadHttpVersion,
177 HttpHeaderContinuationsUnsupported,
178 HttpTransferEncodingUnsupported,
179 HttpConnectionHeaderUnsupported,
180};
181
182/// Reads a complete response head. Any leftover data is stored in the request. This function is idempotent.
183pub fn waitForCompleteHead(req: *Request) WaitForCompleteHeadError!void {
184 if (req.response.state.isContent()) return;
185
186 while (true) {
187 const nread = try req.connection.data.read(req.read_buffer[0..]);
188 const amt = try checkForCompleteHead(req, req.read_buffer[0..nread]);
189
190 if (amt != 0) {
191 req.read_buffer_start = @intCast(ReadBufferIndex, amt);
192 req.read_buffer_len = @intCast(ReadBufferIndex, nread);
193 return;
194 } else if (nread == 0) {
195 return error.UnexpectedEndOfStream;
196 }
197 }
198}
199
200/// This one can return 0 without meaning EOF.
201fn readRawAdvanced(req: *Request, buffer: []u8) !usize {
202 assert(req.response.state.isContent());
203 if (req.response.done) return 0;
204
205 // var in: []const u8 = undefined;
206 if (req.read_buffer_start == req.read_buffer_len) {
207 const nread = try req.connection.data.read(req.read_buffer[0..]);
208 if (nread == 0) return error.UnexpectedEndOfStream;
209
210 req.read_buffer_start = 0;
211 req.read_buffer_len = @intCast(ReadBufferIndex, nread);
212 }
213
214 var out_index: usize = 0;
215 while (true) {
216 switch (req.response.state) {
217 .invalid, .start, .seen_r, .seen_rn, .seen_rnr => unreachable,
218 .finished => {
219 // TODO https://github.com/ziglang/zig/issues/14039
220 const buf_avail = req.read_buffer_len - req.read_buffer_start;
221 const data_avail = req.response.next_chunk_length;
222 const out_avail = buffer.len;
223
224 if (req.handle_redirects and req.response.headers.status.class() == .redirect) {
225 const can_read = @intCast(usize, @min(buf_avail, data_avail));
226 req.response.next_chunk_length -= can_read;
227
228 if (req.response.next_chunk_length == 0) {
229 req.client.connection_pool.release(req.client, req.connection);
230 req.connection = undefined;
231 req.response.done = true;
232 }
233
234 return 0; // skip over as much data as possible
235 }
236
237 const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail));
238 req.response.next_chunk_length -= can_read;
239
240 mem.copy(u8, buffer[0..], req.read_buffer[req.read_buffer_start..][0..can_read]);
241 req.read_buffer_start += @intCast(ReadBufferIndex, can_read);
242
243 if (req.response.next_chunk_length == 0) {
244 req.client.connection_pool.release(req.client, req.connection);
245 req.connection = undefined;
246 req.response.done = true;
247 }
248
249 return can_read;
250 },
251 .chunk_size_prefix_r => switch (req.read_buffer_len - req.read_buffer_start) {
252 0 => return out_index,
253 1 => switch (req.read_buffer[req.read_buffer_start]) {
254 '\r' => {
255 req.response.state = .chunk_size_prefix_n;
256 return out_index;
257 },
258 else => {
259 req.response.state = .invalid;
260 return error.HttpHeadersInvalid;
261 },
262 },
263 else => switch (int16(req.read_buffer[req.read_buffer_start..][0..2])) {
264 int16("\r\n") => {
265 req.read_buffer_start += 2;
266 req.response.state = .chunk_size;
267 continue;
268 },
269 else => {
270 req.response.state = .invalid;
271 return error.HttpHeadersInvalid;
272 },
273 },
274 },
275 .chunk_size_prefix_n => switch (req.read_buffer_len - req.read_buffer_start) {
276 0 => return out_index,
277 else => switch (req.read_buffer[req.read_buffer_start]) {
278 '\n' => {
279 req.read_buffer_start += 1;
280 req.response.state = .chunk_size;
281 continue;
282 },
283 else => {
284 req.response.state = .invalid;
285 return error.HttpHeadersInvalid;
286 },
287 },
288 },
289 .chunk_size, .chunk_r => {
290 const i = req.response.findChunkedLen(req.read_buffer[req.read_buffer_start..req.read_buffer_len]);
291 switch (req.response.state) {
292 .invalid => return error.HttpHeadersInvalid,
293 .chunk_data => {
294 if (req.response.next_chunk_length == 0) {
295 req.response.done = true;
296 req.client.connection_pool.release(req.client, req.connection);
297 req.connection = undefined;
298
299 return out_index;
300 }
301
302 req.read_buffer_start += @intCast(ReadBufferIndex, i);
303 continue;
304 },
305 .chunk_size => return out_index,
306 else => unreachable,
307 }
308 },
309 .chunk_data => {
310 // TODO https://github.com/ziglang/zig/issues/14039
311 const buf_avail = req.read_buffer_len - req.read_buffer_start;
312 const data_avail = req.response.next_chunk_length;
313 const out_avail = buffer.len - out_index;
314
315 if (req.handle_redirects and req.response.headers.status.class() == .redirect) {
316 const can_read = @intCast(usize, @min(buf_avail, data_avail));
317 req.response.next_chunk_length -= can_read;
318
319 if (req.response.next_chunk_length == 0) {
320 req.client.connection_pool.release(req.client, req.connection);
321 req.connection = undefined;
322 req.response.done = true;
323 continue;
324 }
325
326 return 0; // skip over as much data as possible
327 }
328
329 const can_read = @intCast(usize, @min(@min(buf_avail, data_avail), out_avail));
330 req.response.next_chunk_length -= can_read;
331
332 mem.copy(u8, buffer[out_index..], req.read_buffer[req.read_buffer_start..][0..can_read]);
333 req.read_buffer_start += @intCast(ReadBufferIndex, can_read);
334 out_index += can_read;
335
336 if (req.response.next_chunk_length == 0) {
337 req.response.state = .chunk_size_prefix_r;
338
339 continue;
340 }
341
342 return out_index;
343 },
344 }
345 }
346}
347
348pub const ReadError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error || WaitForCompleteHeadError || error{ BadHeader, InvalidCompression, StreamTooLong, InvalidWindowSize, CompressionNotSupported };
349
350pub const Reader = std.io.Reader(*Request, ReadError, read);
351
352pub fn reader(req: *Request) Reader {
353 return .{ .context = req };
354}
355
356pub fn read(req: *Request, buffer: []u8) ReadError!usize {
357 while (true) {
358 if (!req.response.state.isContent()) try req.waitForCompleteHead();
359
360 if (req.handle_redirects and req.response.headers.status.class() == .redirect) {
361 assert(try req.readRaw(buffer) == 0);
362
363 if (req.redirects_left == 0) return error.TooManyHttpRedirects;
364
365 const location = req.response.headers.location orelse
366 return error.HttpRedirectMissingLocation;
367 const new_url = Uri.parse(location) catch try Uri.parseWithoutScheme(location);
368
369 var new_arena = std.heap.ArenaAllocator.init(req.client.allocator);
370 const resolved_url = try req.uri.resolve(new_url, false, new_arena.allocator());
371 errdefer new_arena.deinit();
372
373 req.arena.deinit();
374 req.arena = new_arena;
375
376 const new_req = try req.client.request(resolved_url, req.headers, .{
377 .max_redirects = req.redirects_left - 1,
378 .header_strategy = if (req.response.header_bytes_owned) .{
379 .dynamic = req.response.max_header_bytes,
380 } else .{
381 .static = req.response.header_bytes.unusedCapacitySlice(),
382 },
383 });
384 req.deinit();
385 req.* = new_req;
386 } else {
387 break;
388 }
389 }
390
391 if (req.response.compression == .none) {
392 if (req.response.headers.transfer_compression) |compression| {
393 switch (compression) {
394 .compress => return error.CompressionNotSupported,
395 .deflate => req.response.compression = .{
396 .deflate = try std.compress.zlib.zlibStream(req.client.allocator, ReaderRaw{ .context = req }),
397 },
398 .gzip => req.response.compression = .{
399 .gzip = try std.compress.gzip.decompress(req.client.allocator, ReaderRaw{ .context = req }),
400 },
401 .zstd => req.response.compression = .{
402 .zstd = std.compress.zstd.decompressStream(req.client.allocator, ReaderRaw{ .context = req }),
403 },
404 }
405 }
406 }
407
408 return switch (req.response.compression) {
409 .deflate => |*deflate| try deflate.read(buffer),
410 .gzip => |*gzip| try gzip.read(buffer),
411 .zstd => |*zstd| try zstd.read(buffer),
412 else => try req.readRaw(buffer),
413 };
414}
415
416pub fn readAll(req: *Request, buffer: []u8) !usize {
417 var index: usize = 0;
418 while (index < buffer.len) {
419 const amt = try read(req, buffer[index..]);
420 if (amt == 0) break;
421 index += amt;
422 }
423 return index;
424}
425
426pub const WriteError = Connection.WriteError || error{MessageTooLong};
427
428pub const Writer = std.io.Writer(*Request, WriteError, write);
429
430pub fn writer(req: *Request) Writer {
431 return .{ .context = req };
432}
433
434/// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent.
435pub fn write(req: *Request, bytes: []const u8) !usize {
436 switch (req.headers.transfer_encoding) {
437 .chunked => {
438 try req.connection.data.writer().print("{x}\r\n", .{bytes.len});
439 try req.connection.data.writeAll(bytes);
440 try req.connection.data.writeAll("\r\n");
441
442 return bytes.len;
443 },
444 .content_length => |*len| {
445 if (len.* < bytes.len) return error.MessageTooLong;
446
447 const amt = try req.connection.data.write(bytes);
448 len.* -= amt;
449 return amt;
450 },
451 .none => return error.NotWriteable,
452 }
453}
454
455/// Finish the body of a request. This notifies the server that you have no more data to send.
456pub fn finish(req: *Request) !void {
457 switch (req.headers.transfer_encoding) {
458 .chunked => try req.connection.data.writeAll("0\r\n"),
459 .content_length => |len| if (len != 0) return error.MessageNotCompleted,
460 .none => {},
461 }
462}
463
464inline fn int16(array: *const [2]u8) u16 {
465 return @bitCast(u16, array.*);
466}
467
468inline fn int32(array: *const [4]u8) u32 {
469 return @bitCast(u32, array.*);
470}
471
472inline fn int64(array: *const [8]u8) u64 {
473 return @bitCast(u64, array.*);
474}
475
476test {
477 const builtin = @import("builtin");
478
479 if (builtin.os.tag == .wasi) return error.SkipZigTest;
480
481 _ = Response;
482}
lib/std/http/Client/Response.zig created+509
...@@ -0,0 +1,509 @@
1const std = @import("std");
2const http = std.http;
3const mem = std.mem;
4const testing = std.testing;
5const assert = std.debug.assert;
6
7const Client = @import("../Client.zig");
8const Response = @This();
9
10headers: Headers,
11state: State,
12header_bytes_owned: bool,
13/// This could either be a fixed buffer provided by the API user or it
14/// could be our own array list.
15header_bytes: std.ArrayListUnmanaged(u8),
16max_header_bytes: usize,
17next_chunk_length: u64,
18done: bool = false,
19
20compression: union(enum) {
21 deflate: Client.DeflateDecompressor,
22 gzip: Client.GzipDecompressor,
23 zstd: Client.ZstdDecompressor,
24 none: void,
25} = .none,
26
27pub const Headers = struct {
28 status: http.Status,
29 version: http.Version,
30 location: ?[]const u8 = null,
31 content_length: ?u64 = null,
32 transfer_encoding: ?http.TransferEncoding = null,
33 transfer_compression: ?http.ContentEncoding = null,
34 connection: http.Connection = .close,
35 upgrade: ?[]const u8 = null,
36
37 number_of_headers: usize = 0,
38
39 pub fn parse(bytes: []const u8) !Headers {
40 var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n");
41
42 const first_line = it.first();
43 if (first_line.len < 12)
44 return error.ShortHttpStatusLine;
45
46 const version: http.Version = switch (int64(first_line[0..8])) {
47 int64("HTTP/1.0") => .@"HTTP/1.0",
48 int64("HTTP/1.1") => .@"HTTP/1.1",
49 else => return error.BadHttpVersion,
50 };
51 if (first_line[8] != ' ') return error.HttpHeadersInvalid;
52 const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*));
53
54 var headers: Headers = .{
55 .version = version,
56 .status = status,
57 };
58
59 while (it.next()) |line| {
60 headers.number_of_headers += 1;
61
62 if (line.len == 0) return error.HttpHeadersInvalid;
63 switch (line[0]) {
64 ' ', '\t' => return error.HttpHeaderContinuationsUnsupported,
65 else => {},
66 }
67 var line_it = mem.split(u8, line, ": ");
68 const header_name = line_it.first();
69 const header_value = line_it.rest();
70 if (std.ascii.eqlIgnoreCase(header_name, "location")) {
71 if (headers.location != null) return error.HttpHeadersInvalid;
72 headers.location = header_value;
73 } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) {
74 if (headers.content_length != null) return error.HttpHeadersInvalid;
75 headers.content_length = try std.fmt.parseInt(u64, header_value, 10);
76 } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) {
77 if (headers.transfer_encoding != null or headers.transfer_compression != null) return error.HttpHeadersInvalid;
78
79 // Transfer-Encoding: second, first
80 // Transfer-Encoding: deflate, chunked
81 var iter = std.mem.splitBackwards(u8, header_value, ",");
82
83 if (iter.next()) |first| {
84 const trimmed = std.mem.trim(u8, first, " ");
85
86 if (std.meta.stringToEnum(http.TransferEncoding, trimmed)) |te| {
87 headers.transfer_encoding = te;
88 } else if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
89 headers.transfer_compression = ce;
90 } else {
91 return error.HttpTransferEncodingUnsupported;
92 }
93 }
94
95 if (iter.next()) |second| {
96 if (headers.transfer_compression != null) return error.HttpTransferEncodingUnsupported;
97
98 const trimmed = std.mem.trim(u8, second, " ");
99
100 if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
101 headers.transfer_compression = ce;
102 } else {
103 return error.HttpTransferEncodingUnsupported;
104 }
105 }
106
107 if (iter.next()) |_| return error.HttpTransferEncodingUnsupported;
108 } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) {
109 if (headers.transfer_compression != null) return error.HttpHeadersInvalid;
110
111 const trimmed = std.mem.trim(u8, header_value, " ");
112
113 if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
114 headers.transfer_compression = ce;
115 } else {
116 return error.HttpTransferEncodingUnsupported;
117 }
118 } else if (std.ascii.eqlIgnoreCase(header_name, "connection")) {
119 if (std.ascii.eqlIgnoreCase(header_value, "keep-alive")) {
120 headers.connection = .keep_alive;
121 } else if (std.ascii.eqlIgnoreCase(header_value, "close")) {
122 headers.connection = .close;
123 } else {
124 return error.HttpConnectionHeaderUnsupported;
125 }
126 } else if (std.ascii.eqlIgnoreCase(header_name, "upgrade")) {
127 headers.upgrade = header_value;
128 }
129 }
130
131 return headers;
132 }
133
134 test "parse headers" {
135 const example =
136 "HTTP/1.1 301 Moved Permanently\r\n" ++
137 "Location: https://www.example.com/\r\n" ++
138 "Content-Type: text/html; charset=UTF-8\r\n" ++
139 "Content-Length: 220\r\n\r\n";
140 const parsed = try Headers.parse(example);
141 try testing.expectEqual(http.Version.@"HTTP/1.1", parsed.version);
142 try testing.expectEqual(http.Status.moved_permanently, parsed.status);
143 try testing.expectEqualStrings("https://www.example.com/", parsed.location orelse
144 return error.TestFailed);
145 try testing.expectEqual(@as(?u64, 220), parsed.content_length);
146 }
147
148 test "header continuation" {
149 const example =
150 "HTTP/1.0 200 OK\r\n" ++
151 "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++
152 "Content-Length: 220\r\n\r\n";
153 try testing.expectError(
154 error.HttpHeaderContinuationsUnsupported,
155 Headers.parse(example),
156 );
157 }
158
159 test "extra content length" {
160 const example =
161 "HTTP/1.0 200 OK\r\n" ++
162 "Content-Length: 220\r\n" ++
163 "Content-Type: text/html; charset=UTF-8\r\n" ++
164 "content-length: 220\r\n\r\n";
165 try testing.expectError(
166 error.HttpHeadersInvalid,
167 Headers.parse(example),
168 );
169 }
170};
171
172inline fn int16(array: *const [2]u8) u16 {
173 return @bitCast(u16, array.*);
174}
175
176inline fn int32(array: *const [4]u8) u32 {
177 return @bitCast(u32, array.*);
178}
179
180inline fn int64(array: *const [8]u8) u64 {
181 return @bitCast(u64, array.*);
182}
183
184pub const State = enum {
185 /// Begin header parsing states.
186 invalid,
187 start,
188 seen_r,
189 seen_rn,
190 seen_rnr,
191 finished,
192 /// Begin transfer-encoding: chunked parsing states.
193 chunk_size_prefix_r,
194 chunk_size_prefix_n,
195 chunk_size,
196 chunk_r,
197 chunk_data,
198
199 pub fn isContent(self: State) bool {
200 return switch (self) {
201 .invalid, .start, .seen_r, .seen_rn, .seen_rnr => false,
202 .finished, .chunk_size_prefix_r, .chunk_size_prefix_n, .chunk_size, .chunk_r, .chunk_data => true,
203 };
204 }
205};
206
207pub fn initDynamic(max: usize) Response {
208 return .{
209 .state = .start,
210 .headers = undefined,
211 .header_bytes = .{},
212 .max_header_bytes = max,
213 .header_bytes_owned = true,
214 .next_chunk_length = undefined,
215 };
216}
217
218pub fn initStatic(buf: []u8) Response {
219 return .{
220 .state = .start,
221 .headers = undefined,
222 .header_bytes = .{ .items = buf[0..0], .capacity = buf.len },
223 .max_header_bytes = buf.len,
224 .header_bytes_owned = false,
225 .next_chunk_length = undefined,
226 };
227}
228
229/// Returns how many bytes are part of HTTP headers. Always less than or
230/// equal to bytes.len. If the amount returned is less than bytes.len, it
231/// means the headers ended and the first byte after the double \r\n\r\n is
232/// located at `bytes[result]`.
233pub fn findHeadersEnd(r: *Response, bytes: []const u8) usize {
234 var index: usize = 0;
235
236 // TODO: https://github.com/ziglang/zig/issues/8220
237 state: while (true) {
238 switch (r.state) {
239 .invalid => unreachable,
240 .finished => unreachable,
241 .start => while (true) {
242 switch (bytes.len - index) {
243 0 => return index,
244 1 => {
245 if (bytes[index] == '\r')
246 r.state = .seen_r;
247 return index + 1;
248 },
249 2 => {
250 if (int16(bytes[index..][0..2]) == int16("\r\n")) {
251 r.state = .seen_rn;
252 } else if (bytes[index + 1] == '\r') {
253 r.state = .seen_r;
254 }
255 return index + 2;
256 },
257 3 => {
258 if (int16(bytes[index..][0..2]) == int16("\r\n") and
259 bytes[index + 2] == '\r')
260 {
261 r.state = .seen_rnr;
262 } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n")) {
263 r.state = .seen_rn;
264 } else if (bytes[index + 2] == '\r') {
265 r.state = .seen_r;
266 }
267 return index + 3;
268 },
269 4...15 => {
270 if (int32(bytes[index..][0..4]) == int32("\r\n\r\n")) {
271 r.state = .finished;
272 return index + 4;
273 } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n") and
274 bytes[index + 3] == '\r')
275 {
276 r.state = .seen_rnr;
277 index += 4;
278 continue :state;
279 } else if (int16(bytes[index + 2 ..][0..2]) == int16("\r\n")) {
280 r.state = .seen_rn;
281 index += 4;
282 continue :state;
283 } else if (bytes[index + 3] == '\r') {
284 r.state = .seen_r;
285 index += 4;
286 continue :state;
287 }
288 index += 4;
289 continue;
290 },
291 else => {
292 const chunk = bytes[index..][0..16];
293 const v: @Vector(16, u8) = chunk.*;
294 const matches_r = v == @splat(16, @as(u8, '\r'));
295 const iota = std.simd.iota(u8, 16);
296 const default = @splat(16, @as(u8, 16));
297 const sub_index = @reduce(.Min, @select(u8, matches_r, iota, default));
298 switch (sub_index) {
299 0...12 => {
300 index += sub_index + 4;
301 if (int32(chunk[sub_index..][0..4]) == int32("\r\n\r\n")) {
302 r.state = .finished;
303 return index;
304 }
305 continue;
306 },
307 13 => {
308 index += 16;
309 if (int16(chunk[14..][0..2]) == int16("\n\r")) {
310 r.state = .seen_rnr;
311 continue :state;
312 }
313 continue;
314 },
315 14 => {
316 index += 16;
317 if (chunk[15] == '\n') {
318 r.state = .seen_rn;
319 continue :state;
320 }
321 continue;
322 },
323 15 => {
324 r.state = .seen_r;
325 index += 16;
326 continue :state;
327 },
328 16 => {
329 index += 16;
330 continue;
331 },
332 else => unreachable,
333 }
334 },
335 }
336 },
337
338 .seen_r => switch (bytes.len - index) {
339 0 => return index,
340 1 => {
341 switch (bytes[index]) {
342 '\n' => r.state = .seen_rn,
343 '\r' => r.state = .seen_r,
344 else => r.state = .start,
345 }
346 return index + 1;
347 },
348 2 => {
349 if (int16(bytes[index..][0..2]) == int16("\n\r")) {
350 r.state = .seen_rnr;
351 return index + 2;
352 }
353 r.state = .start;
354 return index + 2;
355 },
356 else => {
357 if (int16(bytes[index..][0..2]) == int16("\n\r") and
358 bytes[index + 2] == '\n')
359 {
360 r.state = .finished;
361 return index + 3;
362 }
363 index += 3;
364 r.state = .start;
365 continue :state;
366 },
367 },
368 .seen_rn => switch (bytes.len - index) {
369 0 => return index,
370 1 => {
371 switch (bytes[index]) {
372 '\r' => r.state = .seen_rnr,
373 else => r.state = .start,
374 }
375 return index + 1;
376 },
377 else => {
378 if (int16(bytes[index..][0..2]) == int16("\r\n")) {
379 r.state = .finished;
380 return index + 2;
381 }
382 index += 2;
383 r.state = .start;
384 continue :state;
385 },
386 },
387 .seen_rnr => switch (bytes.len - index) {
388 0 => return index,
389 else => {
390 if (bytes[index] == '\n') {
391 r.state = .finished;
392 return index + 1;
393 }
394 index += 1;
395 r.state = .start;
396 continue :state;
397 },
398 },
399 .chunk_size_prefix_r => unreachable,
400 .chunk_size_prefix_n => unreachable,
401 .chunk_size => unreachable,
402 .chunk_r => unreachable,
403 .chunk_data => unreachable,
404 }
405
406 return index;
407 }
408}
409
410pub fn findChunkedLen(r: *Response, bytes: []const u8) usize {
411 var i: usize = 0;
412 if (r.state == .chunk_size) {
413 while (i < bytes.len) : (i += 1) {
414 const digit = switch (bytes[i]) {
415 '0'...'9' => |b| b - '0',
416 'A'...'Z' => |b| b - 'A' + 10,
417 'a'...'z' => |b| b - 'a' + 10,
418 '\r' => {
419 r.state = .chunk_r;
420 i += 1;
421 break;
422 },
423 else => {
424 r.state = .invalid;
425 return i;
426 },
427 };
428 const mul = @mulWithOverflow(r.next_chunk_length, 16);
429 if (mul[1] != 0) {
430 r.state = .invalid;
431 return i;
432 }
433 const add = @addWithOverflow(mul[0], digit);
434 if (add[1] != 0) {
435 r.state = .invalid;
436 return i;
437 }
438 r.next_chunk_length = add[0];
439 } else {
440 return i;
441 }
442 }
443 assert(r.state == .chunk_r);
444 if (i == bytes.len) return i;
445
446 if (bytes[i] == '\n') {
447 r.state = .chunk_data;
448 return i + 1;
449 } else {
450 r.state = .invalid;
451 return i;
452 }
453}
454
455fn parseInt3(nnn: @Vector(3, u8)) u10 {
456 const zero: @Vector(3, u8) = .{ '0', '0', '0' };
457 const mmm: @Vector(3, u10) = .{ 100, 10, 1 };
458 return @reduce(.Add, @as(@Vector(3, u10), nnn -% zero) *% mmm);
459}
460
461test parseInt3 {
462 const expectEqual = std.testing.expectEqual;
463 try expectEqual(@as(u10, 0), parseInt3("000".*));
464 try expectEqual(@as(u10, 418), parseInt3("418".*));
465 try expectEqual(@as(u10, 999), parseInt3("999".*));
466}
467
468test "find headers end basic" {
469 var buffer: [1]u8 = undefined;
470 var r = Response.initStatic(&buffer);
471 try testing.expectEqual(@as(usize, 10), r.findHeadersEnd("HTTP/1.1 4"));
472 try testing.expectEqual(@as(usize, 2), r.findHeadersEnd("18"));
473 try testing.expectEqual(@as(usize, 8), r.findHeadersEnd(" lol\r\n\r\nblah blah"));
474}
475
476test "find headers end vectorized" {
477 var buffer: [1]u8 = undefined;
478 var r = Response.initStatic(&buffer);
479 const example =
480 "HTTP/1.1 301 Moved Permanently\r\n" ++
481 "Location: https://www.example.com/\r\n" ++
482 "Content-Type: text/html; charset=UTF-8\r\n" ++
483 "Content-Length: 220\r\n" ++
484 "\r\ncontent";
485 try testing.expectEqual(@as(usize, 131), r.findHeadersEnd(example));
486}
487
488test "find headers end bug" {
489 var buffer: [1]u8 = undefined;
490 var r = Response.initStatic(&buffer);
491 const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
492 const example =
493 "HTTP/1.1 200 OK\r\n" ++
494 "Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++
495 "content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++
496 "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++
497 "Content-Type: application/x-gzip\r\n" ++
498 "ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++
499 "Strict-Transport-Security: max-age=31536000\r\n" ++
500 "Vary: Authorization,Accept-Encoding,Origin\r\n" ++
501 "X-Content-Type-Options: nosniff\r\n" ++
502 "X-Frame-Options: deny\r\n" ++
503 "X-XSS-Protection: 1; mode=block\r\n" ++
504 "Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++
505 "Transfer-Encoding: chunked\r\n" ++
506 "X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++
507 "connection: close\r\n\r\n" ++ trail;
508 try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example));
509}
lib/std/math/big/int.zig+1
...@@ -1674,6 +1674,7 @@ pub const Mutable = struct {...@@ -1674,6 +1674,7 @@ pub const Mutable = struct {
16741674
1675 /// If a is positive, this passes through to truncate.1675 /// If a is positive, this passes through to truncate.
1676 /// If a is negative, then r is set to positive with the bit pattern ~(a - 1).1676 /// If a is negative, then r is set to positive with the bit pattern ~(a - 1).
1677 /// r may alias a.
1677 ///1678 ///
1678 /// Asserts `r` has enough storage to store the result.1679 /// Asserts `r` has enough storage to store the result.
1679 /// The upper bound is `calcTwosCompLimbCount(a.len)`.1680 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
lib/std/mem.zig+4-9
...@@ -196,13 +196,8 @@ test "Allocator.resize" {...@@ -196,13 +196,8 @@ test "Allocator.resize" {
196/// dest.len must be >= source.len.196/// dest.len must be >= source.len.
197/// If the slices overlap, dest.ptr must be <= src.ptr.197/// If the slices overlap, dest.ptr must be <= src.ptr.
198pub fn copy(comptime T: type, dest: []T, source: []const T) void {198pub fn copy(comptime T: type, dest: []T, source: []const T) void {
199 // TODO instead of manually doing this check for the whole array199 for (dest[0..source.len], source) |*d, s|
200 // and turning off runtime safety, the compiler should detect loops like200 d.* = s;
201 // this and automatically omit safety checks for loops
202 @setRuntimeSafety(false);
203 assert(dest.len >= source.len);
204 for (source, 0..) |s, i|
205 dest[i] = s;
206}201}
207202
208/// Copy all of source into dest at position 0.203/// Copy all of source into dest at position 0.
...@@ -611,8 +606,8 @@ test "lessThan" {...@@ -611,8 +606,8 @@ test "lessThan" {
611pub fn eql(comptime T: type, a: []const T, b: []const T) bool {606pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
612 if (a.len != b.len) return false;607 if (a.len != b.len) return false;
613 if (a.ptr == b.ptr) return true;608 if (a.ptr == b.ptr) return true;
614 for (a, 0..) |item, index| {609 for (a, b) |a_elem, b_elem| {
615 if (b[index] != item) return false;610 if (a_elem != b_elem) return false;
616 }611 }
617 return true;612 return true;
618}613}
lib/std/multi_array_list.zig+13-2
...@@ -68,6 +68,15 @@ pub fn MultiArrayList(comptime S: type) type {...@@ -68,6 +68,15 @@ pub fn MultiArrayList(comptime S: type) type {
68 other.deinit(gpa);68 other.deinit(gpa);
69 self.* = undefined;69 self.* = undefined;
70 }70 }
71
72 /// This function is used in the debugger pretty formatters in tools/ to fetch the
73 /// child field order and entry type to facilitate fancy debug printing for this type.
74 fn dbHelper(self: *Slice, child: *S, field: *Field, entry: *Entry) void {
75 _ = self;
76 _ = child;
77 _ = field;
78 _ = entry;
79 }
71 };80 };
7281
73 const Self = @This();82 const Self = @This();
...@@ -463,16 +472,18 @@ pub fn MultiArrayList(comptime S: type) type {...@@ -463,16 +472,18 @@ pub fn MultiArrayList(comptime S: type) type {
463 } });472 } });
464 };473 };
465 /// This function is used in the debugger pretty formatters in tools/ to fetch the474 /// This function is used in the debugger pretty formatters in tools/ to fetch the
466 /// child type to facilitate fancy debug printing for this type.475 /// child field order and entry type to facilitate fancy debug printing for this type.
467 fn dbHelper(self: *Self, child: *S, entry: *Entry) void {476 fn dbHelper(self: *Self, child: *S, field: *Field, entry: *Entry) void {
468 _ = self;477 _ = self;
469 _ = child;478 _ = child;
479 _ = field;
470 _ = entry;480 _ = entry;
471 }481 }
472482
473 comptime {483 comptime {
474 if (builtin.mode == .Debug) {484 if (builtin.mode == .Debug) {
475 _ = dbHelper;485 _ = dbHelper;
486 _ = Slice.dbHelper;
476 }487 }
477 }488 }
478 };489 };
lib/std/net.zig+31-3
...@@ -702,8 +702,10 @@ pub const AddressList = struct {...@@ -702,8 +702,10 @@ pub const AddressList = struct {
702 }702 }
703};703};
704704
705pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError;
706
705/// All memory allocated with `allocator` will be freed before this function returns.707/// All memory allocated with `allocator` will be freed before this function returns.
706pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) !Stream {708pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream {
707 const list = try getAddressList(allocator, name, port);709 const list = try getAddressList(allocator, name, port);
708 defer list.deinit();710 defer list.deinit();
709711
...@@ -720,7 +722,9 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) !...@@ -720,7 +722,9 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) !
720 return std.os.ConnectError.ConnectionRefused;722 return std.os.ConnectError.ConnectionRefused;
721}723}
722724
723pub fn tcpConnectToAddress(address: Address) !Stream {725pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError;
726
727pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
724 const nonblock = if (std.io.is_async) os.SOCK.NONBLOCK else 0;728 const nonblock = if (std.io.is_async) os.SOCK.NONBLOCK else 0;
725 const sock_flags = os.SOCK.STREAM | nonblock |729 const sock_flags = os.SOCK.STREAM | nonblock |
726 (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC);730 (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC);
...@@ -737,8 +741,32 @@ pub fn tcpConnectToAddress(address: Address) !Stream {...@@ -737,8 +741,32 @@ pub fn tcpConnectToAddress(address: Address) !Stream {
737 return Stream{ .handle = sockfd };741 return Stream{ .handle = sockfd };
738}742}
739743
744const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || error{
745 // TODO: break this up into error sets from the various underlying functions
746
747 TemporaryNameServerFailure,
748 NameServerFailure,
749 AddressFamilyNotSupported,
750 UnknownHostName,
751 ServiceUnavailable,
752 Unexpected,
753
754 HostLacksNetworkAddresses,
755
756 InvalidCharacter,
757 InvalidEnd,
758 NonCanonical,
759 Overflow,
760 Incomplete,
761 InvalidIpv4Mapping,
762 InvalidIPAddressFormat,
763
764 InterfaceNotFound,
765 FileSystem,
766};
767
740/// Call `AddressList.deinit` on the result.768/// Call `AddressList.deinit` on the result.
741pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*AddressList {769pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList {
742 const result = blk: {770 const result = blk: {
743 var arena = std.heap.ArenaAllocator.init(allocator);771 var arena = std.heap.ArenaAllocator.init(allocator);
744 errdefer arena.deinit();772 errdefer arena.deinit();
lib/std/os.zig+84-39
...@@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator;...@@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator;
29const Preopen = std.fs.wasi.Preopen;29const Preopen = std.fs.wasi.Preopen;
30const PreopenList = std.fs.wasi.PreopenList;30const PreopenList = std.fs.wasi.PreopenList;
3131
32pub const darwin = @import("os/darwin.zig");32pub const darwin = std.c;
33pub const dragonfly = std.c;33pub const dragonfly = std.c;
34pub const freebsd = std.c;34pub const freebsd = std.c;
35pub const haiku = std.c;35pub const haiku = std.c;
...@@ -41,8 +41,6 @@ pub const plan9 = @import("os/plan9.zig");...@@ -41,8 +41,6 @@ pub const plan9 = @import("os/plan9.zig");
41pub const uefi = @import("os/uefi.zig");41pub const uefi = @import("os/uefi.zig");
42pub const wasi = @import("os/wasi.zig");42pub const wasi = @import("os/wasi.zig");
43pub const windows = @import("os/windows.zig");43pub const windows = @import("os/windows.zig");
44pub const posix_spawn = @import("os/posix_spawn.zig");
45pub const ptrace = @import("os/ptrace.zig");
4644
47comptime {45comptime {
48 assert(@import("std") == std); // std lib tests require --zig-lib-dir46 assert(@import("std") == std); // std lib tests require --zig-lib-dir
...@@ -56,7 +54,6 @@ test {...@@ -56,7 +54,6 @@ test {
56 }54 }
57 _ = wasi;55 _ = wasi;
58 _ = windows;56 _ = windows;
59 _ = posix_spawn;
6057
61 _ = @import("os/test.zig");58 _ = @import("os/test.zig");
62}59}
...@@ -253,6 +250,25 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin....@@ -253,6 +250,25 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.
253 else => undefined,250 else => undefined,
254};251};
255252
253pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");
254
255fn noopSigHandler(_: c_int) callconv(.C) void {}
256
257/// On default executed by posix startup code before main(), if SIGPIPE is supported.
258pub fn maybeIgnoreSigpipe() void {
259 if (have_sigpipe_support and !std.options.keep_sigpipe) {
260 const act = Sigaction{
261 // We set handler to a noop function instead of SIG.IGN so we don't leak our
262 // signal disposition to a child process
263 .handler = .{ .handler = noopSigHandler },
264 .mask = empty_sigset,
265 .flags = 0,
266 };
267 sigaction(SIG.PIPE, &act, null) catch |err|
268 std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)});
269 }
270}
271
256/// To obtain errno, call this function with the return value of the272/// To obtain errno, call this function with the return value of the
257/// system function call. For some systems this will obtain the value directly273/// system function call. For some systems this will obtain the value directly
258/// from the return code; for others it will use a thread-local errno variable.274/// from the return code; for others it will use a thread-local errno variable.
...@@ -575,22 +591,12 @@ pub fn abort() noreturn {...@@ -575,22 +591,12 @@ pub fn abort() noreturn {
575 raise(SIG.KILL) catch {};591 raise(SIG.KILL) catch {};
576 exit(127); // Pid 1 might not be signalled in some containers.592 exit(127); // Pid 1 might not be signalled in some containers.
577 }593 }
578 if (builtin.os.tag == .uefi) {594 switch (builtin.os.tag) {
579 exit(0); // TODO choose appropriate exit code595 .uefi, .wasi, .cuda => @trap(),
580 }596 else => system.abort(),
581 if (builtin.os.tag == .wasi) {
582 exit(1);
583 }
584 if (builtin.os.tag == .cuda) {
585 // TODO: introduce `@trap` instead of abusing https://github.com/ziglang/zig/issues/2291
586 @"llvm.trap"();
587 }597 }
588
589 system.abort();
590}598}
591599
592extern fn @"llvm.trap"() noreturn;
593
594pub const RaiseError = UnexpectedError;600pub const RaiseError = UnexpectedError;
595601
596pub fn raise(sig: u8) RaiseError!void {602pub fn raise(sig: u8) RaiseError!void {
...@@ -759,6 +765,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -759,6 +765,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
759/// This operation is non-atomic on the following systems:765/// This operation is non-atomic on the following systems:
760/// * Windows766/// * Windows
761/// On these systems, the read races with concurrent writes to the same file descriptor.767/// On these systems, the read races with concurrent writes to the same file descriptor.
768///
769/// This function assumes that all vectors, including zero-length vectors, have
770/// a pointer within the address space of the application.
762pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {771pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
763 if (builtin.os.tag == .windows) {772 if (builtin.os.tag == .windows) {
764 // TODO improve this to use ReadFileScatter773 // TODO improve this to use ReadFileScatter
...@@ -1036,6 +1045,8 @@ pub const WriteError = error{...@@ -1036,6 +1045,8 @@ pub const WriteError = error{
1036 FileTooBig,1045 FileTooBig,
1037 InputOutput,1046 InputOutput,
1038 NoSpaceLeft,1047 NoSpaceLeft,
1048 DeviceBusy,
1049 InvalidArgument,
10391050
1040 /// In WASI, this error may occur when the file descriptor does1051 /// In WASI, this error may occur when the file descriptor does
1041 /// not hold the required rights to write to it.1052 /// not hold the required rights to write to it.
...@@ -1122,7 +1133,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -1122,7 +1133,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
1122 switch (errno(rc)) {1133 switch (errno(rc)) {
1123 .SUCCESS => return @intCast(usize, rc),1134 .SUCCESS => return @intCast(usize, rc),
1124 .INTR => continue,1135 .INTR => continue,
1125 .INVAL => unreachable,1136 .INVAL => return error.InvalidArgument,
1126 .FAULT => unreachable,1137 .FAULT => unreachable,
1127 .AGAIN => return error.WouldBlock,1138 .AGAIN => return error.WouldBlock,
1128 .BADF => return error.NotOpenForWriting, // can be a race condition.1139 .BADF => return error.NotOpenForWriting, // can be a race condition.
...@@ -1134,6 +1145,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -1134,6 +1145,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
1134 .PERM => return error.AccessDenied,1145 .PERM => return error.AccessDenied,
1135 .PIPE => return error.BrokenPipe,1146 .PIPE => return error.BrokenPipe,
1136 .CONNRESET => return error.ConnectionResetByPeer,1147 .CONNRESET => return error.ConnectionResetByPeer,
1148 .BUSY => return error.DeviceBusy,
1137 else => |err| return unexpectedErrno(err),1149 else => |err| return unexpectedErrno(err),
1138 }1150 }
1139 }1151 }
...@@ -1157,6 +1169,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -1157,6 +1169,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
1157/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.1169/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1158///1170///
1159/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.1171/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
1172///
1173/// This function assumes that all vectors, including zero-length vectors, have
1174/// a pointer within the address space of the application.
1160pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {1175pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
1161 if (builtin.os.tag == .windows) {1176 if (builtin.os.tag == .windows) {
1162 // TODO improve this to use WriteFileScatter1177 // TODO improve this to use WriteFileScatter
...@@ -1191,7 +1206,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {...@@ -1191,7 +1206,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
1191 switch (errno(rc)) {1206 switch (errno(rc)) {
1192 .SUCCESS => return @intCast(usize, rc),1207 .SUCCESS => return @intCast(usize, rc),
1193 .INTR => continue,1208 .INTR => continue,
1194 .INVAL => unreachable,1209 .INVAL => return error.InvalidArgument,
1195 .FAULT => unreachable,1210 .FAULT => unreachable,
1196 .AGAIN => return error.WouldBlock,1211 .AGAIN => return error.WouldBlock,
1197 .BADF => return error.NotOpenForWriting, // Can be a race condition.1212 .BADF => return error.NotOpenForWriting, // Can be a race condition.
...@@ -1203,6 +1218,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {...@@ -1203,6 +1218,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
1203 .PERM => return error.AccessDenied,1218 .PERM => return error.AccessDenied,
1204 .PIPE => return error.BrokenPipe,1219 .PIPE => return error.BrokenPipe,
1205 .CONNRESET => return error.ConnectionResetByPeer,1220 .CONNRESET => return error.ConnectionResetByPeer,
1221 .BUSY => return error.DeviceBusy,
1206 else => |err| return unexpectedErrno(err),1222 else => |err| return unexpectedErrno(err),
1207 }1223 }
1208 }1224 }
...@@ -1285,7 +1301,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -1285,7 +1301,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
1285 switch (errno(rc)) {1301 switch (errno(rc)) {
1286 .SUCCESS => return @intCast(usize, rc),1302 .SUCCESS => return @intCast(usize, rc),
1287 .INTR => continue,1303 .INTR => continue,
1288 .INVAL => unreachable,1304 .INVAL => return error.InvalidArgument,
1289 .FAULT => unreachable,1305 .FAULT => unreachable,
1290 .AGAIN => return error.WouldBlock,1306 .AGAIN => return error.WouldBlock,
1291 .BADF => return error.NotOpenForWriting, // Can be a race condition.1307 .BADF => return error.NotOpenForWriting, // Can be a race condition.
...@@ -1299,6 +1315,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -1299,6 +1315,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
1299 .NXIO => return error.Unseekable,1315 .NXIO => return error.Unseekable,
1300 .SPIPE => return error.Unseekable,1316 .SPIPE => return error.Unseekable,
1301 .OVERFLOW => return error.Unseekable,1317 .OVERFLOW => return error.Unseekable,
1318 .BUSY => return error.DeviceBusy,
1302 else => |err| return unexpectedErrno(err),1319 else => |err| return unexpectedErrno(err),
1303 }1320 }
1304 }1321 }
...@@ -1374,7 +1391,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz...@@ -1374,7 +1391,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
1374 switch (errno(rc)) {1391 switch (errno(rc)) {
1375 .SUCCESS => return @intCast(usize, rc),1392 .SUCCESS => return @intCast(usize, rc),
1376 .INTR => continue,1393 .INTR => continue,
1377 .INVAL => unreachable,1394 .INVAL => return error.InvalidArgument,
1378 .FAULT => unreachable,1395 .FAULT => unreachable,
1379 .AGAIN => return error.WouldBlock,1396 .AGAIN => return error.WouldBlock,
1380 .BADF => return error.NotOpenForWriting, // Can be a race condition.1397 .BADF => return error.NotOpenForWriting, // Can be a race condition.
...@@ -1388,6 +1405,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz...@@ -1388,6 +1405,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
1388 .NXIO => return error.Unseekable,1405 .NXIO => return error.Unseekable,
1389 .SPIPE => return error.Unseekable,1406 .SPIPE => return error.Unseekable,
1390 .OVERFLOW => return error.Unseekable,1407 .OVERFLOW => return error.Unseekable,
1408 .BUSY => return error.DeviceBusy,
1391 else => |err| return unexpectedErrno(err),1409 else => |err| return unexpectedErrno(err),
1392 }1410 }
1393 }1411 }
...@@ -3456,7 +3474,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi...@@ -3456,7 +3474,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi
3456 const rc = system.bind(sock, addr, len);3474 const rc = system.bind(sock, addr, len);
3457 switch (errno(rc)) {3475 switch (errno(rc)) {
3458 .SUCCESS => return,3476 .SUCCESS => return,
3459 .ACCES => return error.AccessDenied,3477 .ACCES, .PERM => return error.AccessDenied,
3460 .ADDRINUSE => return error.AddressInUse,3478 .ADDRINUSE => return error.AddressInUse,
3461 .BADF => unreachable, // always a race condition if this error is returned3479 .BADF => unreachable, // always a race condition if this error is returned
3462 .INVAL => unreachable, // invalid parameters3480 .INVAL => unreachable, // invalid parameters
...@@ -3983,13 +4001,32 @@ pub const WaitPidResult = struct {...@@ -3983,13 +4001,32 @@ pub const WaitPidResult = struct {
3983};4001};
39844002
3985/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit4003/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
3986/// `fork` and `execve` method. If you spawned your child process using `posix_spawn` method,4004/// `fork` and `execve` method.
3987/// use `std.os.posix_spawn.waitpid` instead.
3988pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {4005pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
3989 const Status = if (builtin.link_libc) c_int else u32;4006 const Status = if (builtin.link_libc) c_int else u32;
3990 var status: Status = undefined;4007 var status: Status = undefined;
4008 const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags;
4009 while (true) {
4010 const rc = system.waitpid(pid, &status, coerced_flags);
4011 switch (errno(rc)) {
4012 .SUCCESS => return .{
4013 .pid = @intCast(pid_t, rc),
4014 .status = @bitCast(u32, status),
4015 },
4016 .INTR => continue,
4017 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
4018 .INVAL => unreachable, // Invalid flags.
4019 else => unreachable,
4020 }
4021 }
4022}
4023
4024pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
4025 const Status = if (builtin.link_libc) c_int else u32;
4026 var status: Status = undefined;
4027 const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags;
3991 while (true) {4028 while (true) {
3992 const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags);4029 const rc = system.wait4(pid, &status, coerced_flags, ru);
3993 switch (errno(rc)) {4030 switch (errno(rc)) {
3994 .SUCCESS => return .{4031 .SUCCESS => return .{
3995 .pid = @intCast(pid_t, rc),4032 .pid = @intCast(pid_t, rc),
...@@ -4310,6 +4347,8 @@ pub const MMapError = error{...@@ -4310,6 +4347,8 @@ pub const MMapError = error{
4310 /// a filesystem that was mounted no-exec.4347 /// a filesystem that was mounted no-exec.
4311 PermissionDenied,4348 PermissionDenied,
4312 LockedMemoryLimitExceeded,4349 LockedMemoryLimitExceeded,
4350 ProcessFdQuotaExceeded,
4351 SystemFdQuotaExceeded,
4313 OutOfMemory,4352 OutOfMemory,
4314} || UnexpectedError;4353} || UnexpectedError;
43154354
...@@ -4351,6 +4390,8 @@ pub fn mmap(...@@ -4351,6 +4390,8 @@ pub fn mmap(
4351 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.4390 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
4352 .NODEV => return error.MemoryMappingNotSupported,4391 .NODEV => return error.MemoryMappingNotSupported,
4353 .INVAL => unreachable, // Invalid parameters to mmap()4392 .INVAL => unreachable, // Invalid parameters to mmap()
4393 .MFILE => return error.ProcessFdQuotaExceeded,
4394 .NFILE => return error.SystemFdQuotaExceeded,
4354 .NOMEM => return error.OutOfMemory,4395 .NOMEM => return error.OutOfMemory,
4355 else => return unexpectedErrno(err),4396 else => return unexpectedErrno(err),
4356 }4397 }
...@@ -7086,20 +7127,24 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {...@@ -7086,20 +7127,24 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
7086 };7127 };
7087}7128}
70887129
7089pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");7130pub const PtraceError = error{
70907131 DeviceBusy,
7091fn noopSigHandler(_: c_int) callconv(.C) void {}7132 ProcessNotFound,
7133 PermissionDenied,
7134} || UnexpectedError;
70927135
7093pub fn maybeIgnoreSigpipe() void {7136/// TODO on other OSes
7094 if (have_sigpipe_support and !std.options.keep_sigpipe) {7137pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
7095 const act = Sigaction{7138 switch (builtin.os.tag) {
7096 // We set handler to a noop function instead of SIG.IGN so we don't leak our7139 .macos, .ios, .tvos, .watchos => {},
7097 // signal disposition to a child process7140 else => @compileError("TODO implement ptrace"),
7098 .handler = .{ .handler = noopSigHandler },
7099 .mask = empty_sigset,
7100 .flags = 0,
7101 };
7102 sigaction(SIG.PIPE, &act, null) catch |err|
7103 std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)});
7104 }7141 }
7142 return switch (errno(system.ptrace(request, pid, addr, signal))) {
7143 .SUCCESS => {},
7144 .SRCH => error.ProcessNotFound,
7145 .INVAL => unreachable,
7146 .PERM => error.PermissionDenied,
7147 .BUSY => error.DeviceBusy,
7148 else => |err| return unexpectedErrno(err),
7149 };
7105}7150}
lib/std/os/darwin.zig deleted-540
...@@ -1,540 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const log = std.log;
4const mem = std.mem;
5
6pub const cssm = @import("darwin/cssm.zig");
7
8pub usingnamespace std.c;
9pub usingnamespace mach_task;
10
11const mach_task = if (builtin.target.isDarwin()) struct {
12 pub const MachError = error{
13 /// Not enough permissions held to perform the requested kernel
14 /// call.
15 PermissionDenied,
16 /// Kernel returned an unhandled and unexpected error code.
17 /// This is a catch-all for any yet unobserved kernel response
18 /// to some Mach message.
19 Unexpected,
20 };
21
22 pub const MachTask = extern struct {
23 port: std.c.mach_port_name_t,
24
25 pub fn isValid(self: MachTask) bool {
26 return self.port != std.c.TASK_NULL;
27 }
28
29 pub fn pidForTask(self: MachTask) MachError!std.os.pid_t {
30 var pid: std.os.pid_t = undefined;
31 switch (std.c.getKernError(std.c.pid_for_task(self.port, &pid))) {
32 .SUCCESS => return pid,
33 .FAILURE => return error.PermissionDenied,
34 else => |err| {
35 log.err("pid_for_task kernel call failed with error code: {s}", .{@tagName(err)});
36 return error.Unexpected;
37 },
38 }
39 }
40
41 pub fn allocatePort(self: MachTask, right: std.c.MACH_PORT_RIGHT) MachError!MachTask {
42 var out_port: std.c.mach_port_name_t = undefined;
43 switch (std.c.getKernError(std.c.mach_port_allocate(
44 self.port,
45 @enumToInt(right),
46 &out_port,
47 ))) {
48 .SUCCESS => return .{ .port = out_port },
49 .FAILURE => return error.PermissionDenied,
50 else => |err| {
51 log.err("mach_task_allocate kernel call failed with error code: {s}", .{@tagName(err)});
52 return error.Unexpected;
53 },
54 }
55 }
56
57 pub fn deallocatePort(self: MachTask, port: MachTask) void {
58 _ = std.c.getKernError(std.c.mach_port_deallocate(self.port, port.port));
59 }
60
61 pub fn insertRight(self: MachTask, port: MachTask, msg: std.c.MACH_MSG_TYPE) !void {
62 switch (std.c.getKernError(std.c.mach_port_insert_right(
63 self.port,
64 port.port,
65 port.port,
66 @enumToInt(msg),
67 ))) {
68 .SUCCESS => return,
69 .FAILURE => return error.PermissionDenied,
70 else => |err| {
71 log.err("mach_port_insert_right kernel call failed with error code: {s}", .{@tagName(err)});
72 return error.Unexpected;
73 },
74 }
75 }
76
77 pub const PortInfo = struct {
78 mask: std.c.exception_mask_t,
79 masks: [std.c.EXC_TYPES_COUNT]std.c.exception_mask_t,
80 ports: [std.c.EXC_TYPES_COUNT]std.c.mach_port_t,
81 behaviors: [std.c.EXC_TYPES_COUNT]std.c.exception_behavior_t,
82 flavors: [std.c.EXC_TYPES_COUNT]std.c.thread_state_flavor_t,
83 count: std.c.mach_msg_type_number_t,
84 };
85
86 pub fn getExceptionPorts(self: MachTask, mask: std.c.exception_mask_t) !PortInfo {
87 var info = PortInfo{
88 .mask = mask,
89 .masks = undefined,
90 .ports = undefined,
91 .behaviors = undefined,
92 .flavors = undefined,
93 .count = 0,
94 };
95 info.count = info.ports.len / @sizeOf(std.c.mach_port_t);
96
97 switch (std.c.getKernError(std.c.task_get_exception_ports(
98 self.port,
99 info.mask,
100 &info.masks,
101 &info.count,
102 &info.ports,
103 &info.behaviors,
104 &info.flavors,
105 ))) {
106 .SUCCESS => return info,
107 .FAILURE => return error.PermissionDenied,
108 else => |err| {
109 log.err("task_get_exception_ports kernel call failed with error code: {s}", .{@tagName(err)});
110 return error.Unexpected;
111 },
112 }
113 }
114
115 pub fn setExceptionPorts(
116 self: MachTask,
117 mask: std.c.exception_mask_t,
118 new_port: MachTask,
119 behavior: std.c.exception_behavior_t,
120 new_flavor: std.c.thread_state_flavor_t,
121 ) !void {
122 switch (std.c.getKernError(std.c.task_set_exception_ports(
123 self.port,
124 mask,
125 new_port.port,
126 behavior,
127 new_flavor,
128 ))) {
129 .SUCCESS => return,
130 .FAILURE => return error.PermissionDenied,
131 else => |err| {
132 log.err("task_set_exception_ports kernel call failed with error code: {s}", .{@tagName(err)});
133 return error.Unexpected;
134 },
135 }
136 }
137
138 pub const RegionInfo = struct {
139 pub const Tag = enum {
140 basic,
141 extended,
142 top,
143 };
144
145 base_addr: u64,
146 tag: Tag,
147 info: union {
148 basic: std.c.vm_region_basic_info_64,
149 extended: std.c.vm_region_extended_info,
150 top: std.c.vm_region_top_info,
151 },
152 };
153
154 pub fn getRegionInfo(
155 task: MachTask,
156 address: u64,
157 len: usize,
158 tag: RegionInfo.Tag,
159 ) MachError!RegionInfo {
160 var info: RegionInfo = .{
161 .base_addr = address,
162 .tag = tag,
163 .info = undefined,
164 };
165 switch (tag) {
166 .basic => info.info = .{ .basic = undefined },
167 .extended => info.info = .{ .extended = undefined },
168 .top => info.info = .{ .top = undefined },
169 }
170 var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len;
171 var objname: std.c.mach_port_t = undefined;
172 var count: std.c.mach_msg_type_number_t = switch (tag) {
173 .basic => std.c.VM_REGION_BASIC_INFO_COUNT,
174 .extended => std.c.VM_REGION_EXTENDED_INFO_COUNT,
175 .top => std.c.VM_REGION_TOP_INFO_COUNT,
176 };
177 switch (std.c.getKernError(std.c.mach_vm_region(
178 task.port,
179 &info.base_addr,
180 &base_len,
181 switch (tag) {
182 .basic => std.c.VM_REGION_BASIC_INFO_64,
183 .extended => std.c.VM_REGION_EXTENDED_INFO,
184 .top => std.c.VM_REGION_TOP_INFO,
185 },
186 switch (tag) {
187 .basic => @ptrCast(std.c.vm_region_info_t, &info.info.basic),
188 .extended => @ptrCast(std.c.vm_region_info_t, &info.info.extended),
189 .top => @ptrCast(std.c.vm_region_info_t, &info.info.top),
190 },
191 &count,
192 &objname,
193 ))) {
194 .SUCCESS => return info,
195 .FAILURE => return error.PermissionDenied,
196 else => |err| {
197 log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)});
198 return error.Unexpected;
199 },
200 }
201 }
202
203 pub const RegionSubmapInfo = struct {
204 pub const Tag = enum {
205 short,
206 full,
207 };
208
209 tag: Tag,
210 base_addr: u64,
211 info: union {
212 short: std.c.vm_region_submap_short_info_64,
213 full: std.c.vm_region_submap_info_64,
214 },
215 };
216
217 pub fn getRegionSubmapInfo(
218 task: MachTask,
219 address: u64,
220 len: usize,
221 nesting_depth: u32,
222 tag: RegionSubmapInfo.Tag,
223 ) MachError!RegionSubmapInfo {
224 var info: RegionSubmapInfo = .{
225 .base_addr = address,
226 .tag = tag,
227 .info = undefined,
228 };
229 switch (tag) {
230 .short => info.info = .{ .short = undefined },
231 .full => info.info = .{ .full = undefined },
232 }
233 var nesting = nesting_depth;
234 var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len;
235 var count: std.c.mach_msg_type_number_t = switch (tag) {
236 .short => std.c.VM_REGION_SUBMAP_SHORT_INFO_COUNT_64,
237 .full => std.c.VM_REGION_SUBMAP_INFO_COUNT_64,
238 };
239 switch (std.c.getKernError(std.c.mach_vm_region_recurse(
240 task.port,
241 &info.base_addr,
242 &base_len,
243 &nesting,
244 switch (tag) {
245 .short => @ptrCast(std.c.vm_region_recurse_info_t, &info.info.short),
246 .full => @ptrCast(std.c.vm_region_recurse_info_t, &info.info.full),
247 },
248 &count,
249 ))) {
250 .SUCCESS => return info,
251 .FAILURE => return error.PermissionDenied,
252 else => |err| {
253 log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)});
254 return error.Unexpected;
255 },
256 }
257 }
258
259 pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!std.c.vm_prot_t {
260 const info = try task.getRegionSubmapInfo(address, len, 0, .short);
261 return info.info.short.protection;
262 }
263
264 pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
265 return task.setProtectionImpl(address, len, true, prot);
266 }
267
268 pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
269 return task.setProtectionImpl(address, len, false, prot);
270 }
271
272 fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: std.c.vm_prot_t) MachError!void {
273 switch (std.c.getKernError(std.c.mach_vm_protect(task.port, address, len, @boolToInt(set_max), prot))) {
274 .SUCCESS => return,
275 .FAILURE => return error.PermissionDenied,
276 else => |err| {
277 log.err("mach_vm_protect kernel call failed with error code: {s}", .{@tagName(err)});
278 return error.Unexpected;
279 },
280 }
281 }
282
283 /// Will write to VM even if current protection attributes specifically prohibit
284 /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY
285 /// variant, and resetting after a successful or unsuccessful write.
286 pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
287 const curr_prot = try task.getCurrProtection(address, buf.len);
288 try task.setCurrProtection(
289 address,
290 buf.len,
291 std.c.PROT.READ | std.c.PROT.WRITE | std.c.PROT.COPY,
292 );
293 defer {
294 task.setCurrProtection(address, buf.len, curr_prot) catch {};
295 }
296 return task.writeMem(address, buf, arch);
297 }
298
299 pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
300 const count = buf.len;
301 var total_written: usize = 0;
302 var curr_addr = address;
303 const page_size = try getPageSize(task); // TODO we probably can assume value here
304 var out_buf = buf[0..];
305
306 while (total_written < count) {
307 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written);
308 switch (std.c.getKernError(std.c.mach_vm_write(
309 task.port,
310 curr_addr,
311 @ptrToInt(out_buf.ptr),
312 @intCast(std.c.mach_msg_type_number_t, curr_size),
313 ))) {
314 .SUCCESS => {},
315 .FAILURE => return error.PermissionDenied,
316 else => |err| {
317 log.err("mach_vm_write kernel call failed with error code: {s}", .{@tagName(err)});
318 return error.Unexpected;
319 },
320 }
321
322 switch (arch) {
323 .aarch64 => {
324 var mattr_value: std.c.vm_machine_attribute_val_t = std.c.MATTR_VAL_CACHE_FLUSH;
325 switch (std.c.getKernError(std.c.vm_machine_attribute(
326 task.port,
327 curr_addr,
328 curr_size,
329 std.c.MATTR_CACHE,
330 &mattr_value,
331 ))) {
332 .SUCCESS => {},
333 .FAILURE => return error.PermissionDenied,
334 else => |err| {
335 log.err("vm_machine_attribute kernel call failed with error code: {s}", .{@tagName(err)});
336 return error.Unexpected;
337 },
338 }
339 },
340 .x86_64 => {},
341 else => unreachable,
342 }
343
344 out_buf = out_buf[curr_size..];
345 total_written += curr_size;
346 curr_addr += curr_size;
347 }
348
349 return total_written;
350 }
351
352 pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize {
353 const count = buf.len;
354 var total_read: usize = 0;
355 var curr_addr = address;
356 const page_size = try getPageSize(task); // TODO we probably can assume value here
357 var out_buf = buf[0..];
358
359 while (total_read < count) {
360 const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read);
361 var curr_bytes_read: std.c.mach_msg_type_number_t = 0;
362 var vm_memory: std.c.vm_offset_t = undefined;
363 switch (std.c.getKernError(std.c.mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
364 .SUCCESS => {},
365 .FAILURE => return error.PermissionDenied,
366 else => |err| {
367 log.err("mach_vm_read kernel call failed with error code: {s}", .{@tagName(err)});
368 return error.Unexpected;
369 },
370 }
371
372 @memcpy(out_buf[0..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read);
373 _ = std.c.vm_deallocate(std.c.mach_task_self(), vm_memory, curr_bytes_read);
374
375 out_buf = out_buf[curr_bytes_read..];
376 curr_addr += curr_bytes_read;
377 total_read += curr_bytes_read;
378 }
379
380 return total_read;
381 }
382
383 fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize {
384 var left = count;
385 if (page_size > 0) {
386 const page_offset = address % page_size;
387 const bytes_left_in_page = page_size - page_offset;
388 if (count > bytes_left_in_page) {
389 left = bytes_left_in_page;
390 }
391 }
392 return left;
393 }
394
395 fn getPageSize(task: MachTask) MachError!usize {
396 if (task.isValid()) {
397 var info_count = std.c.TASK_VM_INFO_COUNT;
398 var vm_info: std.c.task_vm_info_data_t = undefined;
399 switch (std.c.getKernError(std.c.task_info(
400 task.port,
401 std.c.TASK_VM_INFO,
402 @ptrCast(std.c.task_info_t, &vm_info),
403 &info_count,
404 ))) {
405 .SUCCESS => return @intCast(usize, vm_info.page_size),
406 else => {},
407 }
408 }
409 var page_size: std.c.vm_size_t = undefined;
410 switch (std.c.getKernError(std.c._host_page_size(std.c.mach_host_self(), &page_size))) {
411 .SUCCESS => return page_size,
412 else => |err| {
413 log.err("_host_page_size kernel call failed with error code: {s}", .{@tagName(err)});
414 return error.Unexpected;
415 },
416 }
417 }
418
419 pub fn basicTaskInfo(task: MachTask) MachError!std.c.mach_task_basic_info {
420 var info: std.c.mach_task_basic_info = undefined;
421 var count = std.c.MACH_TASK_BASIC_INFO_COUNT;
422 switch (std.c.getKernError(std.c.task_info(
423 task.port,
424 std.c.MACH_TASK_BASIC_INFO,
425 @ptrCast(std.c.task_info_t, &info),
426 &count,
427 ))) {
428 .SUCCESS => return info,
429 else => |err| {
430 log.err("task_info kernel call failed with error code: {s}", .{@tagName(err)});
431 return error.Unexpected;
432 },
433 }
434 }
435
436 pub fn @"resume"(task: MachTask) MachError!void {
437 switch (std.c.getKernError(std.c.task_resume(task.port))) {
438 .SUCCESS => {},
439 else => |err| {
440 log.err("task_resume kernel call failed with error code: {s}", .{@tagName(err)});
441 return error.Unexpected;
442 },
443 }
444 }
445
446 pub fn @"suspend"(task: MachTask) MachError!void {
447 switch (std.c.getKernError(std.c.task_suspend(task.port))) {
448 .SUCCESS => {},
449 else => |err| {
450 log.err("task_suspend kernel call failed with error code: {s}", .{@tagName(err)});
451 return error.Unexpected;
452 },
453 }
454 }
455
456 const ThreadList = struct {
457 buf: []MachThread,
458
459 pub fn deinit(list: ThreadList) void {
460 const self_task = machTaskForSelf();
461 _ = std.c.vm_deallocate(
462 self_task.port,
463 @ptrToInt(list.buf.ptr),
464 @intCast(std.c.vm_size_t, list.buf.len * @sizeOf(std.c.mach_port_t)),
465 );
466 }
467 };
468
469 pub fn getThreads(task: MachTask) MachError!ThreadList {
470 var thread_list: std.c.mach_port_array_t = undefined;
471 var thread_count: std.c.mach_msg_type_number_t = undefined;
472 switch (std.c.getKernError(std.c.task_threads(task.port, &thread_list, &thread_count))) {
473 .SUCCESS => return ThreadList{ .buf = @ptrCast([*]MachThread, thread_list)[0..thread_count] },
474 else => |err| {
475 log.err("task_threads kernel call failed with error code: {s}", .{@tagName(err)});
476 return error.Unexpected;
477 },
478 }
479 }
480 };
481
482 pub const MachThread = extern struct {
483 port: std.c.mach_port_t,
484
485 pub fn isValid(thread: MachThread) bool {
486 return thread.port != std.c.THREAD_NULL;
487 }
488
489 pub fn getBasicInfo(thread: MachThread) MachError!std.c.thread_basic_info {
490 var info: std.c.thread_basic_info = undefined;
491 var count = std.c.THREAD_BASIC_INFO_COUNT;
492 switch (std.c.getKernError(std.c.thread_info(
493 thread.port,
494 std.c.THREAD_BASIC_INFO,
495 @ptrCast(std.c.thread_info_t, &info),
496 &count,
497 ))) {
498 .SUCCESS => return info,
499 else => |err| {
500 log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)});
501 return error.Unexpected;
502 },
503 }
504 }
505
506 pub fn getIdentifierInfo(thread: MachThread) MachError!std.c.thread_identifier_info {
507 var info: std.c.thread_identifier_info = undefined;
508 var count = std.c.THREAD_IDENTIFIER_INFO_COUNT;
509 switch (std.c.getKernError(std.c.thread_info(
510 thread.port,
511 std.c.THREAD_IDENTIFIER_INFO,
512 @ptrCast(std.c.thread_info_t, &info),
513 &count,
514 ))) {
515 .SUCCESS => return info,
516 else => |err| {
517 log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)});
518 return error.Unexpected;
519 },
520 }
521 }
522 };
523
524 pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask {
525 var port: std.c.mach_port_name_t = undefined;
526 switch (std.c.getKernError(std.c.task_for_pid(std.c.mach_task_self(), pid, &port))) {
527 .SUCCESS => {},
528 .FAILURE => return error.PermissionDenied,
529 else => |err| {
530 log.err("task_for_pid kernel call failed with error code: {s}", .{@tagName(err)});
531 return error.Unexpected;
532 },
533 }
534 return MachTask{ .port = port };
535 }
536
537 pub fn machTaskForSelf() MachTask {
538 return .{ .port = std.c.mach_task_self() };
539 }
540} else struct {};
lib/std/os/darwin/cssm.zig deleted-47
...@@ -1,47 +0,0 @@
1// Common Security Services Manager
2// Security.framework/Headers/cssm*.h
3
4// Schema Management Name Space Range Definition
5pub const DB_RECORDTYPE_SCHEMA_START = 0x00000000;
6pub const DB_RECORDTYPE_SCHEMA_END = DB_RECORDTYPE_SCHEMA_START + 4;
7
8// Open Group Application Name Space Range Definition
9pub const DB_RECORDTYPE_OPEN_GROUP_START = 0x0000000A;
10pub const DB_RECORDTYPE_OPEN_GROUP_END = DB_RECORDTYPE_OPEN_GROUP_START + 8;
11
12// Industry At Large Application Name Space Range Definition
13pub const DB_RECORDTYPE_APP_DEFINED_START = 0x80000000;
14pub const DB_RECORDTYPE_APP_DEFINED_END = 0xffffffff;
15
16pub const DB_RECORDTYPE = enum(u32) {
17 // Record Types defined in the Schema Management Name Space
18 SCHEMA_INFO = DB_RECORDTYPE_SCHEMA_START + 0,
19 SCHEMA_INDEXES = DB_RECORDTYPE_SCHEMA_START + 1,
20 SCHEMA_ATTRIBUTES = DB_RECORDTYPE_SCHEMA_START + 2,
21 SCHEMA_PARSING_MODULE = DB_RECORDTYPE_SCHEMA_START + 3,
22
23 // Record Types defined in the Open Group Application Name Space
24 ANY = DB_RECORDTYPE_OPEN_GROUP_START + 0,
25 CERT = DB_RECORDTYPE_OPEN_GROUP_START + 1,
26 CRL = DB_RECORDTYPE_OPEN_GROUP_START + 2,
27 POLICY = DB_RECORDTYPE_OPEN_GROUP_START + 3,
28 GENERIC = DB_RECORDTYPE_OPEN_GROUP_START + 4,
29 PUBLIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 5,
30 PRIVATE_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 6,
31 SYMMETRIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 7,
32 ALL_KEYS = DB_RECORDTYPE_OPEN_GROUP_START + 8,
33
34 // AppleFileDL record types
35 GENERIC_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 0,
36 INTERNET_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 1,
37 APPLESHARE_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 2,
38
39 X509_CERTIFICATE = DB_RECORDTYPE_APP_DEFINED_START + 0x1000,
40 USER_TRUST,
41 X509_CRL,
42 UNLOCK_REFERRAL,
43 EXTENDED_ATTRIBUTE,
44 METADATA = DB_RECORDTYPE_APP_DEFINED_START + 0x8000,
45
46 _,
47};
lib/std/os/linux.zig+74-10
...@@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {...@@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {
944 return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);944 return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);
945}945}
946946
947pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize {
948 return syscall4(
949 .wait4,
950 @bitCast(usize, @as(isize, pid)),
951 @ptrToInt(status),
952 flags,
953 @ptrToInt(usage),
954 );
955}
956
947pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {957pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {
948 return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0);958 return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0);
949}959}
...@@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u...@@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u
1716 );1726 );
1717}1727}
17181728
1719pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize {1729pub fn process_vm_readv(pid: pid_t, local: []iovec, remote: []const iovec_const, flags: usize) usize {
1720 return syscall6(1730 return syscall6(
1721 .process_vm_readv,1731 .process_vm_readv,
1722 @bitCast(usize, @as(isize, pid)),1732 @bitCast(usize, @as(isize, pid)),
1723 @ptrToInt(local),1733 @ptrToInt(local.ptr),
1724 local_count,1734 local.len,
1725 @ptrToInt(remote),1735 @ptrToInt(remote.ptr),
1726 remote_count,1736 remote.len,
1727 flags,1737 flags,
1728 );1738 );
1729}1739}
17301740
1731pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize {1741pub fn process_vm_writev(pid: pid_t, local: []const iovec_const, remote: []const iovec_const, flags: usize) usize {
1732 return syscall6(1742 return syscall6(
1733 .process_vm_writev,1743 .process_vm_writev,
1734 @bitCast(usize, @as(isize, pid)),1744 @bitCast(usize, @as(isize, pid)),
1735 @ptrToInt(local),1745 @ptrToInt(local.ptr),
1736 local_count,1746 local.len,
1737 @ptrToInt(remote),1747 @ptrToInt(remote.ptr),
1738 remote_count,1748 remote.len,
1739 flags,1749 flags,
1740 );1750 );
1741}1751}
...@@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize {...@@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize {
1820 return syscall3(.seccomp, operation, flags, @ptrToInt(args));1830 return syscall3(.seccomp, operation, flags, @ptrToInt(args));
1821}1831}
18221832
1833pub fn ptrace(
1834 req: u32,
1835 pid: pid_t,
1836 addr: usize,
1837 data: usize,
1838 addr2: usize,
1839) usize {
1840 return syscall5(
1841 .ptrace,
1842 req,
1843 @bitCast(usize, @as(isize, pid)),
1844 addr,
1845 data,
1846 addr2,
1847 );
1848}
1849
1823pub const E = switch (native_arch) {1850pub const E = switch (native_arch) {
1824 .mips, .mipsel => @import("linux/errno/mips.zig").E,1851 .mips, .mipsel => @import("linux/errno/mips.zig").E,
1825 .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E,1852 .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E,
...@@ -5721,3 +5748,40 @@ pub const AUDIT = struct {...@@ -5721,3 +5748,40 @@ pub const AUDIT = struct {
5721 }5748 }
5722 };5749 };
5723};5750};
5751
5752pub const PTRACE = struct {
5753 pub const TRACEME = 0;
5754 pub const PEEKTEXT = 1;
5755 pub const PEEKDATA = 2;
5756 pub const PEEKUSER = 3;
5757 pub const POKETEXT = 4;
5758 pub const POKEDATA = 5;
5759 pub const POKEUSER = 6;
5760 pub const CONT = 7;
5761 pub const KILL = 8;
5762 pub const SINGLESTEP = 9;
5763 pub const GETREGS = 12;
5764 pub const SETREGS = 13;
5765 pub const GETFPREGS = 14;
5766 pub const SETFPREGS = 15;
5767 pub const ATTACH = 16;
5768 pub const DETACH = 17;
5769 pub const GETFPXREGS = 18;
5770 pub const SETFPXREGS = 19;
5771 pub const SYSCALL = 24;
5772 pub const SETOPTIONS = 0x4200;
5773 pub const GETEVENTMSG = 0x4201;
5774 pub const GETSIGINFO = 0x4202;
5775 pub const SETSIGINFO = 0x4203;
5776 pub const GETREGSET = 0x4204;
5777 pub const SETREGSET = 0x4205;
5778 pub const SEIZE = 0x4206;
5779 pub const INTERRUPT = 0x4207;
5780 pub const LISTEN = 0x4208;
5781 pub const PEEKSIGINFO = 0x4209;
5782 pub const GETSIGMASK = 0x420a;
5783 pub const SETSIGMASK = 0x420b;
5784 pub const SECCOMP_GET_FILTER = 0x420c;
5785 pub const SECCOMP_GET_METADATA = 0x420d;
5786 pub const GET_SYSCALL_INFO = 0x420e;
5787};
lib/std/os/linux/io_uring.zig+104-66
...@@ -1728,10 +1728,12 @@ test "writev/fsync/readv" {...@@ -1728,10 +1728,12 @@ test "writev/fsync/readv" {
1728 };1728 };
1729 defer ring.deinit();1729 defer ring.deinit();
17301730
1731 var tmp = std.testing.tmpDir(.{});
1732 defer tmp.cleanup();
1733
1731 const path = "test_io_uring_writev_fsync_readv";1734 const path = "test_io_uring_writev_fsync_readv";
1732 const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true });1735 const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true });
1733 defer file.close();1736 defer file.close();
1734 defer std.fs.cwd().deleteFile(path) catch {};
1735 const fd = file.handle;1737 const fd = file.handle;
17361738
1737 const buffer_write = [_]u8{42} ** 128;1739 const buffer_write = [_]u8{42} ** 128;
...@@ -1796,10 +1798,11 @@ test "write/read" {...@@ -1796,10 +1798,11 @@ test "write/read" {
1796 };1798 };
1797 defer ring.deinit();1799 defer ring.deinit();
17981800
1801 var tmp = std.testing.tmpDir(.{});
1802 defer tmp.cleanup();
1799 const path = "test_io_uring_write_read";1803 const path = "test_io_uring_write_read";
1800 const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true });1804 const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true });
1801 defer file.close();1805 defer file.close();
1802 defer std.fs.cwd().deleteFile(path) catch {};
1803 const fd = file.handle;1806 const fd = file.handle;
18041807
1805 const buffer_write = [_]u8{97} ** 20;1808 const buffer_write = [_]u8{97} ** 20;
...@@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" {...@@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" {
1842 };1845 };
1843 defer ring.deinit();1846 defer ring.deinit();
18441847
1848 var tmp = std.testing.tmpDir(.{});
1849 defer tmp.cleanup();
1850
1845 const path = "test_io_uring_write_read_fixed";1851 const path = "test_io_uring_write_read_fixed";
1846 const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true });1852 const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true });
1847 defer file.close();1853 defer file.close();
1848 defer std.fs.cwd().deleteFile(path) catch {};
1849 const fd = file.handle;1854 const fd = file.handle;
18501855
1851 var raw_buffers: [2][11]u8 = undefined;1856 var raw_buffers: [2][11]u8 = undefined;
...@@ -1899,8 +1904,10 @@ test "openat" {...@@ -1899,8 +1904,10 @@ test "openat" {
1899 };1904 };
1900 defer ring.deinit();1905 defer ring.deinit();
19011906
1907 var tmp = std.testing.tmpDir(.{});
1908 defer tmp.cleanup();
1909
1902 const path = "test_io_uring_openat";1910 const path = "test_io_uring_openat";
1903 defer std.fs.cwd().deleteFile(path) catch {};
19041911
1905 // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/120141912 // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014
1906 const path_addr = if (builtin.zig_backend == .stage2_llvm) p: {1913 const path_addr = if (builtin.zig_backend == .stage2_llvm) p: {
...@@ -1910,12 +1917,12 @@ test "openat" {...@@ -1910,12 +1917,12 @@ test "openat" {
19101917
1911 const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT;1918 const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT;
1912 const mode: os.mode_t = 0o666;1919 const mode: os.mode_t = 0o666;
1913 const sqe_openat = try ring.openat(0x33333333, linux.AT.FDCWD, path, flags, mode);1920 const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode);
1914 try testing.expectEqual(linux.io_uring_sqe{1921 try testing.expectEqual(linux.io_uring_sqe{
1915 .opcode = .OPENAT,1922 .opcode = .OPENAT,
1916 .flags = 0,1923 .flags = 0,
1917 .ioprio = 0,1924 .ioprio = 0,
1918 .fd = linux.AT.FDCWD,1925 .fd = tmp.dir.fd,
1919 .off = 0,1926 .off = 0,
1920 .addr = path_addr,1927 .addr = path_addr,
1921 .len = mode,1928 .len = mode,
...@@ -1931,12 +1938,6 @@ test "openat" {...@@ -1931,12 +1938,6 @@ test "openat" {
1931 const cqe_openat = try ring.copy_cqe();1938 const cqe_openat = try ring.copy_cqe();
1932 try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data);1939 try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data);
1933 if (cqe_openat.err() == .INVAL) return error.SkipZigTest;1940 if (cqe_openat.err() == .INVAL) return error.SkipZigTest;
1934 // AT.FDCWD is not fully supported before kernel 5.6:
1935 // See https://lore.kernel.org/io-uring/20200207155039.12819-1-axboe@kernel.dk/T/
1936 // We use IORING_FEAT_RW_CUR_POS to know if we are pre-5.6 since that feature was added in 5.6.
1937 if (cqe_openat.err() == .BADF and (ring.features & linux.IORING_FEAT_RW_CUR_POS) == 0) {
1938 return error.SkipZigTest;
1939 }
1940 if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res});1941 if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res});
1941 try testing.expect(cqe_openat.res > 0);1942 try testing.expect(cqe_openat.res > 0);
1942 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);1943 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);
...@@ -1954,10 +1955,12 @@ test "close" {...@@ -1954,10 +1955,12 @@ test "close" {
1954 };1955 };
1955 defer ring.deinit();1956 defer ring.deinit();
19561957
1958 var tmp = std.testing.tmpDir(.{});
1959 defer tmp.cleanup();
1960
1957 const path = "test_io_uring_close";1961 const path = "test_io_uring_close";
1958 const file = try std.fs.cwd().createFile(path, .{});1962 const file = try tmp.dir.createFile(path, .{});
1959 errdefer file.close();1963 errdefer file.close();
1960 defer std.fs.cwd().deleteFile(path) catch {};
19611964
1962 const sqe_close = try ring.close(0x44444444, file.handle);1965 const sqe_close = try ring.close(0x44444444, file.handle);
1963 try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode);1966 try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode);
...@@ -1976,6 +1979,11 @@ test "close" {...@@ -1976,6 +1979,11 @@ test "close" {
1976test "accept/connect/send/recv" {1979test "accept/connect/send/recv" {
1977 if (builtin.os.tag != .linux) return error.SkipZigTest;1980 if (builtin.os.tag != .linux) return error.SkipZigTest;
19781981
1982 if (true) {
1983 // https://github.com/ziglang/zig/issues/14907
1984 return error.SkipZigTest;
1985 }
1986
1979 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {1987 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
1980 error.SystemOutdated => return error.SkipZigTest,1988 error.SystemOutdated => return error.SkipZigTest,
1981 error.PermissionDenied => return error.SkipZigTest,1989 error.PermissionDenied => return error.SkipZigTest,
...@@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" {...@@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" {
2017test "sendmsg/recvmsg" {2025test "sendmsg/recvmsg" {
2018 if (builtin.os.tag != .linux) return error.SkipZigTest;2026 if (builtin.os.tag != .linux) return error.SkipZigTest;
20192027
2028 if (true) {
2029 // https://github.com/ziglang/zig/issues/14907
2030 return error.SkipZigTest;
2031 }
2032
2020 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {2033 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {
2021 error.SystemOutdated => return error.SkipZigTest,2034 error.SystemOutdated => return error.SkipZigTest,
2022 error.PermissionDenied => return error.SkipZigTest,2035 error.PermissionDenied => return error.SkipZigTest,
...@@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" {...@@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" {
2024 };2037 };
2025 defer ring.deinit();2038 defer ring.deinit();
20262039
2040 if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907
2027 const address_server = try net.Address.parseIp4("127.0.0.1", 3131);2041 const address_server = try net.Address.parseIp4("127.0.0.1", 3131);
20282042
2029 const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);2043 const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);
...@@ -2223,6 +2237,11 @@ test "timeout_remove" {...@@ -2223,6 +2237,11 @@ test "timeout_remove" {
2223test "accept/connect/recv/link_timeout" {2237test "accept/connect/recv/link_timeout" {
2224 if (builtin.os.tag != .linux) return error.SkipZigTest;2238 if (builtin.os.tag != .linux) return error.SkipZigTest;
22252239
2240 if (true) {
2241 // https://github.com/ziglang/zig/issues/14907
2242 return error.SkipZigTest;
2243 }
2244
2226 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {2245 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
2227 error.SystemOutdated => return error.SkipZigTest,2246 error.SystemOutdated => return error.SkipZigTest,
2228 error.PermissionDenied => return error.SkipZigTest,2247 error.PermissionDenied => return error.SkipZigTest,
...@@ -2279,10 +2298,12 @@ test "fallocate" {...@@ -2279,10 +2298,12 @@ test "fallocate" {
2279 };2298 };
2280 defer ring.deinit();2299 defer ring.deinit();
22812300
2301 var tmp = std.testing.tmpDir(.{});
2302 defer tmp.cleanup();
2303
2282 const path = "test_io_uring_fallocate";2304 const path = "test_io_uring_fallocate";
2283 const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });2305 const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
2284 defer file.close();2306 defer file.close();
2285 defer std.fs.cwd().deleteFile(path) catch {};
22862307
2287 try testing.expectEqual(@as(u64, 0), (try file.stat()).size);2308 try testing.expectEqual(@as(u64, 0), (try file.stat()).size);
22882309
...@@ -2323,10 +2344,11 @@ test "statx" {...@@ -2323,10 +2344,11 @@ test "statx" {
2323 };2344 };
2324 defer ring.deinit();2345 defer ring.deinit();
23252346
2347 var tmp = std.testing.tmpDir(.{});
2348 defer tmp.cleanup();
2326 const path = "test_io_uring_statx";2349 const path = "test_io_uring_statx";
2327 const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });2350 const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
2328 defer file.close();2351 defer file.close();
2329 defer std.fs.cwd().deleteFile(path) catch {};
23302352
2331 try testing.expectEqual(@as(u64, 0), (try file.stat()).size);2353 try testing.expectEqual(@as(u64, 0), (try file.stat()).size);
23322354
...@@ -2335,14 +2357,14 @@ test "statx" {...@@ -2335,14 +2357,14 @@ test "statx" {
2335 var buf: linux.Statx = undefined;2357 var buf: linux.Statx = undefined;
2336 const sqe = try ring.statx(2358 const sqe = try ring.statx(
2337 0xaaaaaaaa,2359 0xaaaaaaaa,
2338 linux.AT.FDCWD,2360 tmp.dir.fd,
2339 path,2361 path,
2340 0,2362 0,
2341 linux.STATX_SIZE,2363 linux.STATX_SIZE,
2342 &buf,2364 &buf,
2343 );2365 );
2344 try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode);2366 try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode);
2345 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);2367 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2346 try testing.expectEqual(@as(u32, 1), try ring.submit());2368 try testing.expectEqual(@as(u32, 1), try ring.submit());
23472369
2348 const cqe = try ring.copy_cqe();2370 const cqe = try ring.copy_cqe();
...@@ -2355,8 +2377,6 @@ test "statx" {...@@ -2355,8 +2377,6 @@ test "statx" {
2355 // The filesystem containing the file referred to by fd does not support this operation;2377 // The filesystem containing the file referred to by fd does not support this operation;
2356 // or the mode is not supported by the filesystem containing the file referred to by fd:2378 // or the mode is not supported by the filesystem containing the file referred to by fd:
2357 .OPNOTSUPP => return error.SkipZigTest,2379 .OPNOTSUPP => return error.SkipZigTest,
2358 // The kernel is too old to support FDCWD for dir_fd
2359 .BADF => return error.SkipZigTest,
2360 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),2380 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
2361 }2381 }
2362 try testing.expectEqual(linux.io_uring_cqe{2382 try testing.expectEqual(linux.io_uring_cqe{
...@@ -2372,6 +2392,11 @@ test "statx" {...@@ -2372,6 +2392,11 @@ test "statx" {
2372test "accept/connect/recv/cancel" {2392test "accept/connect/recv/cancel" {
2373 if (builtin.os.tag != .linux) return error.SkipZigTest;2393 if (builtin.os.tag != .linux) return error.SkipZigTest;
23742394
2395 if (true) {
2396 // https://github.com/ziglang/zig/issues/14907
2397 return error.SkipZigTest;
2398 }
2399
2375 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {2400 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
2376 error.SystemOutdated => return error.SkipZigTest,2401 error.SystemOutdated => return error.SkipZigTest,
2377 error.PermissionDenied => return error.SkipZigTest,2402 error.PermissionDenied => return error.SkipZigTest,
...@@ -2509,6 +2534,11 @@ test "register_files_update" {...@@ -2509,6 +2534,11 @@ test "register_files_update" {
2509test "shutdown" {2534test "shutdown" {
2510 if (builtin.os.tag != .linux) return error.SkipZigTest;2535 if (builtin.os.tag != .linux) return error.SkipZigTest;
25112536
2537 if (true) {
2538 // https://github.com/ziglang/zig/issues/14907
2539 return error.SkipZigTest;
2540 }
2541
2512 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {2542 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
2513 error.SystemOutdated => return error.SkipZigTest,2543 error.SystemOutdated => return error.SkipZigTest,
2514 error.PermissionDenied => return error.SkipZigTest,2544 error.PermissionDenied => return error.SkipZigTest,
...@@ -2516,6 +2546,7 @@ test "shutdown" {...@@ -2516,6 +2546,7 @@ test "shutdown" {
2516 };2546 };
2517 defer ring.deinit();2547 defer ring.deinit();
25182548
2549 if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907
2519 const address = try net.Address.parseIp4("127.0.0.1", 3131);2550 const address = try net.Address.parseIp4("127.0.0.1", 3131);
25202551
2521 // Socket bound, expect shutdown to work2552 // Socket bound, expect shutdown to work
...@@ -2579,28 +2610,28 @@ test "renameat" {...@@ -2579,28 +2610,28 @@ test "renameat" {
2579 const old_path = "test_io_uring_renameat_old";2610 const old_path = "test_io_uring_renameat_old";
2580 const new_path = "test_io_uring_renameat_new";2611 const new_path = "test_io_uring_renameat_new";
25812612
2613 var tmp = std.testing.tmpDir(.{});
2614 defer tmp.cleanup();
2615
2582 // Write old file with data2616 // Write old file with data
25832617
2584 const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 });2618 const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 });
2585 defer {2619 defer old_file.close();
2586 old_file.close();
2587 std.fs.cwd().deleteFile(new_path) catch {};
2588 }
2589 try old_file.writeAll("hello");2620 try old_file.writeAll("hello");
25902621
2591 // Submit renameat2622 // Submit renameat
25922623
2593 var sqe = try ring.renameat(2624 var sqe = try ring.renameat(
2594 0x12121212,2625 0x12121212,
2595 linux.AT.FDCWD,2626 tmp.dir.fd,
2596 old_path,2627 old_path,
2597 linux.AT.FDCWD,2628 tmp.dir.fd,
2598 new_path,2629 new_path,
2599 0,2630 0,
2600 );2631 );
2601 try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode);2632 try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode);
2602 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);2633 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2603 try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len));2634 try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len));
2604 try testing.expectEqual(@as(u32, 1), try ring.submit());2635 try testing.expectEqual(@as(u32, 1), try ring.submit());
26052636
2606 const cqe = try ring.copy_cqe();2637 const cqe = try ring.copy_cqe();
...@@ -2618,7 +2649,7 @@ test "renameat" {...@@ -2618,7 +2649,7 @@ test "renameat" {
26182649
2619 // Validate that the old file doesn't exist anymore2650 // Validate that the old file doesn't exist anymore
2620 {2651 {
2621 _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) {2652 _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) {
2622 error.FileNotFound => {},2653 error.FileNotFound => {},
2623 else => std.debug.panic("unexpected error: {}", .{err}),2654 else => std.debug.panic("unexpected error: {}", .{err}),
2624 };2655 };
...@@ -2626,7 +2657,7 @@ test "renameat" {...@@ -2626,7 +2657,7 @@ test "renameat" {
26262657
2627 // Validate that the new file exists with the proper content2658 // Validate that the new file exists with the proper content
2628 {2659 {
2629 const new_file = try std.fs.cwd().openFile(new_path, .{});2660 const new_file = try tmp.dir.openFile(new_path, .{});
2630 defer new_file.close();2661 defer new_file.close();
26312662
2632 var new_file_data: [16]u8 = undefined;2663 var new_file_data: [16]u8 = undefined;
...@@ -2647,22 +2678,24 @@ test "unlinkat" {...@@ -2647,22 +2678,24 @@ test "unlinkat" {
26472678
2648 const path = "test_io_uring_unlinkat";2679 const path = "test_io_uring_unlinkat";
26492680
2681 var tmp = std.testing.tmpDir(.{});
2682 defer tmp.cleanup();
2683
2650 // Write old file with data2684 // Write old file with data
26512685
2652 const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });2686 const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
2653 defer file.close();2687 defer file.close();
2654 defer std.fs.cwd().deleteFile(path) catch {};
26552688
2656 // Submit unlinkat2689 // Submit unlinkat
26572690
2658 var sqe = try ring.unlinkat(2691 var sqe = try ring.unlinkat(
2659 0x12121212,2692 0x12121212,
2660 linux.AT.FDCWD,2693 tmp.dir.fd,
2661 path,2694 path,
2662 0,2695 0,
2663 );2696 );
2664 try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode);2697 try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode);
2665 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);2698 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2666 try testing.expectEqual(@as(u32, 1), try ring.submit());2699 try testing.expectEqual(@as(u32, 1), try ring.submit());
26672700
2668 const cqe = try ring.copy_cqe();2701 const cqe = try ring.copy_cqe();
...@@ -2679,7 +2712,7 @@ test "unlinkat" {...@@ -2679,7 +2712,7 @@ test "unlinkat" {
2679 }, cqe);2712 }, cqe);
26802713
2681 // Validate that the file doesn't exist anymore2714 // Validate that the file doesn't exist anymore
2682 _ = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) {2715 _ = tmp.dir.openFile(path, .{}) catch |err| switch (err) {
2683 error.FileNotFound => {},2716 error.FileNotFound => {},
2684 else => std.debug.panic("unexpected error: {}", .{err}),2717 else => std.debug.panic("unexpected error: {}", .{err}),
2685 };2718 };
...@@ -2695,20 +2728,21 @@ test "mkdirat" {...@@ -2695,20 +2728,21 @@ test "mkdirat" {
2695 };2728 };
2696 defer ring.deinit();2729 defer ring.deinit();
26972730
2698 const path = "test_io_uring_mkdirat";2731 var tmp = std.testing.tmpDir(.{});
2732 defer tmp.cleanup();
26992733
2700 defer std.fs.cwd().deleteDir(path) catch {};2734 const path = "test_io_uring_mkdirat";
27012735
2702 // Submit mkdirat2736 // Submit mkdirat
27032737
2704 var sqe = try ring.mkdirat(2738 var sqe = try ring.mkdirat(
2705 0x12121212,2739 0x12121212,
2706 linux.AT.FDCWD,2740 tmp.dir.fd,
2707 path,2741 path,
2708 0o0755,2742 0o0755,
2709 );2743 );
2710 try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode);2744 try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode);
2711 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);2745 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2712 try testing.expectEqual(@as(u32, 1), try ring.submit());2746 try testing.expectEqual(@as(u32, 1), try ring.submit());
27132747
2714 const cqe = try ring.copy_cqe();2748 const cqe = try ring.copy_cqe();
...@@ -2725,7 +2759,7 @@ test "mkdirat" {...@@ -2725,7 +2759,7 @@ test "mkdirat" {
2725 }, cqe);2759 }, cqe);
27262760
2727 // Validate that the directory exist2761 // Validate that the directory exist
2728 _ = try std.fs.cwd().openDir(path, .{});2762 _ = try tmp.dir.openDir(path, .{});
2729}2763}
27302764
2731test "symlinkat" {2765test "symlinkat" {
...@@ -2738,26 +2772,25 @@ test "symlinkat" {...@@ -2738,26 +2772,25 @@ test "symlinkat" {
2738 };2772 };
2739 defer ring.deinit();2773 defer ring.deinit();
27402774
2775 var tmp = std.testing.tmpDir(.{});
2776 defer tmp.cleanup();
2777
2741 const path = "test_io_uring_symlinkat";2778 const path = "test_io_uring_symlinkat";
2742 const link_path = "test_io_uring_symlinkat_link";2779 const link_path = "test_io_uring_symlinkat_link";
27432780
2744 const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });2781 const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
2745 defer {2782 defer file.close();
2746 file.close();
2747 std.fs.cwd().deleteFile(path) catch {};
2748 std.fs.cwd().deleteFile(link_path) catch {};
2749 }
27502783
2751 // Submit symlinkat2784 // Submit symlinkat
27522785
2753 var sqe = try ring.symlinkat(2786 var sqe = try ring.symlinkat(
2754 0x12121212,2787 0x12121212,
2755 path,2788 path,
2756 linux.AT.FDCWD,2789 tmp.dir.fd,
2757 link_path,2790 link_path,
2758 );2791 );
2759 try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode);2792 try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode);
2760 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);2793 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2761 try testing.expectEqual(@as(u32, 1), try ring.submit());2794 try testing.expectEqual(@as(u32, 1), try ring.submit());
27622795
2763 const cqe = try ring.copy_cqe();2796 const cqe = try ring.copy_cqe();
...@@ -2774,7 +2807,7 @@ test "symlinkat" {...@@ -2774,7 +2807,7 @@ test "symlinkat" {
2774 }, cqe);2807 }, cqe);
27752808
2776 // Validate that the symlink exist2809 // Validate that the symlink exist
2777 _ = try std.fs.cwd().openFile(link_path, .{});2810 _ = try tmp.dir.openFile(link_path, .{});
2778}2811}
27792812
2780test "linkat" {2813test "linkat" {
...@@ -2787,32 +2820,31 @@ test "linkat" {...@@ -2787,32 +2820,31 @@ test "linkat" {
2787 };2820 };
2788 defer ring.deinit();2821 defer ring.deinit();
27892822
2823 var tmp = std.testing.tmpDir(.{});
2824 defer tmp.cleanup();
2825
2790 const first_path = "test_io_uring_linkat_first";2826 const first_path = "test_io_uring_linkat_first";
2791 const second_path = "test_io_uring_linkat_second";2827 const second_path = "test_io_uring_linkat_second";
27922828
2793 // Write file with data2829 // Write file with data
27942830
2795 const first_file = try std.fs.cwd().createFile(first_path, .{ .truncate = true, .mode = 0o666 });2831 const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 });
2796 defer {2832 defer first_file.close();
2797 first_file.close();
2798 std.fs.cwd().deleteFile(first_path) catch {};
2799 std.fs.cwd().deleteFile(second_path) catch {};
2800 }
2801 try first_file.writeAll("hello");2833 try first_file.writeAll("hello");
28022834
2803 // Submit linkat2835 // Submit linkat
28042836
2805 var sqe = try ring.linkat(2837 var sqe = try ring.linkat(
2806 0x12121212,2838 0x12121212,
2807 linux.AT.FDCWD,2839 tmp.dir.fd,
2808 first_path,2840 first_path,
2809 linux.AT.FDCWD,2841 tmp.dir.fd,
2810 second_path,2842 second_path,
2811 0,2843 0,
2812 );2844 );
2813 try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode);2845 try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode);
2814 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);2846 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2815 try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len));2847 try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len));
2816 try testing.expectEqual(@as(u32, 1), try ring.submit());2848 try testing.expectEqual(@as(u32, 1), try ring.submit());
28172849
2818 const cqe = try ring.copy_cqe();2850 const cqe = try ring.copy_cqe();
...@@ -2829,7 +2861,7 @@ test "linkat" {...@@ -2829,7 +2861,7 @@ test "linkat" {
2829 }, cqe);2861 }, cqe);
28302862
2831 // Validate the second file2863 // Validate the second file
2832 const second_file = try std.fs.cwd().openFile(second_path, .{});2864 const second_file = try tmp.dir.openFile(second_path, .{});
2833 defer second_file.close();2865 defer second_file.close();
28342866
2835 var second_file_data: [16]u8 = undefined;2867 var second_file_data: [16]u8 = undefined;
...@@ -3060,6 +3092,11 @@ test "remove_buffers" {...@@ -3060,6 +3092,11 @@ test "remove_buffers" {
3060test "provide_buffers: accept/connect/send/recv" {3092test "provide_buffers: accept/connect/send/recv" {
3061 if (builtin.os.tag != .linux) return error.SkipZigTest;3093 if (builtin.os.tag != .linux) return error.SkipZigTest;
30623094
3095 if (true) {
3096 // https://github.com/ziglang/zig/issues/14907
3097 return error.SkipZigTest;
3098 }
3099
3063 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {3100 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
3064 error.SystemOutdated => return error.SkipZigTest,3101 error.SystemOutdated => return error.SkipZigTest,
3065 error.PermissionDenied => return error.SkipZigTest,3102 error.PermissionDenied => return error.SkipZigTest,
...@@ -3236,6 +3273,7 @@ const SocketTestHarness = struct {...@@ -3236,6 +3273,7 @@ const SocketTestHarness = struct {
3236fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness {3273fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness {
3237 // Create a TCP server socket3274 // Create a TCP server socket
32383275
3276 if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907
3239 const address = try net.Address.parseIp4("127.0.0.1", 3131);3277 const address = try net.Address.parseIp4("127.0.0.1", 3131);
3240 const kernel_backlog = 1;3278 const kernel_backlog = 1;
3241 const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);3279 const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
lib/std/os/linux/test.zig+15-18
...@@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual;...@@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual;
8const fs = std.fs;8const fs = std.fs;
99
10test "fallocate" {10test "fallocate" {
11 var tmp = std.testing.tmpDir(.{});
12 defer tmp.cleanup();
13
11 const path = "test_fallocate";14 const path = "test_fallocate";
12 const file = try fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });15 const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
13 defer file.close();16 defer file.close();
14 defer fs.cwd().deleteFile(path) catch {};
1517
16 try expect((try file.stat()).size == 0);18 try expect((try file.stat()).size == 0);
1719
...@@ -67,12 +69,12 @@ test "timer" {...@@ -67,12 +69,12 @@ test "timer" {
67}69}
6870
69test "statx" {71test "statx" {
72 var tmp = std.testing.tmpDir(.{});
73 defer tmp.cleanup();
74
70 const tmp_file_name = "just_a_temporary_file.txt";75 const tmp_file_name = "just_a_temporary_file.txt";
71 var file = try fs.cwd().createFile(tmp_file_name, .{});76 var file = try tmp.dir.createFile(tmp_file_name, .{});
72 defer {77 defer file.close();
73 file.close();
74 fs.cwd().deleteFile(tmp_file_name) catch {};
75 }
7678
77 var statx_buf: linux.Statx = undefined;79 var statx_buf: linux.Statx = undefined;
78 switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) {80 switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) {
...@@ -105,21 +107,16 @@ test "user and group ids" {...@@ -105,21 +107,16 @@ test "user and group ids" {
105}107}
106108
107test "fadvise" {109test "fadvise" {
110 var tmp = std.testing.tmpDir(.{});
111 defer tmp.cleanup();
112
108 const tmp_file_name = "temp_posix_fadvise.txt";113 const tmp_file_name = "temp_posix_fadvise.txt";
109 var file = try fs.cwd().createFile(tmp_file_name, .{});114 var file = try tmp.dir.createFile(tmp_file_name, .{});
110 defer {115 defer file.close();
111 file.close();
112 fs.cwd().deleteFile(tmp_file_name) catch {};
113 }
114116
115 var buf: [2048]u8 = undefined;117 var buf: [2048]u8 = undefined;
116 try file.writeAll(&buf);118 try file.writeAll(&buf);
117119
118 const ret = linux.fadvise(120 const ret = linux.fadvise(file.handle, 0, 0, linux.POSIX_FADV.SEQUENTIAL);
119 file.handle,
120 0,
121 0,
122 linux.POSIX_FADV.SEQUENTIAL,
123 );
124 try expectEqual(@as(usize, 0), ret);121 try expectEqual(@as(usize, 0), ret);
125}122}
lib/std/os/posix_spawn.zig deleted-290
...@@ -1,290 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const os = @import("../os.zig");
5const system = os.system;
6const errno = system.getErrno;
7const fd_t = system.fd_t;
8const mode_t = system.mode_t;
9const pid_t = system.pid_t;
10const unexpectedErrno = os.unexpectedErrno;
11const UnexpectedError = os.UnexpectedError;
12const toPosixPath = os.toPosixPath;
13const WaitPidResult = os.WaitPidResult;
14
15pub usingnamespace posix_spawn;
16
17pub const Error = error{
18 SystemResources,
19 InvalidFileDescriptor,
20 NameTooLong,
21 TooBig,
22 PermissionDenied,
23 InputOutput,
24 FileSystem,
25 FileNotFound,
26 InvalidExe,
27 NotDir,
28 FileBusy,
29
30 /// Returned when the child fails to execute either in the pre-exec() initialization step, or
31 /// when exec(3) is invoked.
32 ChildExecFailed,
33} || UnexpectedError;
34
35const posix_spawn = if (builtin.target.isDarwin()) struct {
36 pub const Attr = struct {
37 attr: system.posix_spawnattr_t,
38
39 pub fn init() Error!Attr {
40 var attr: system.posix_spawnattr_t = undefined;
41 switch (errno(system.posix_spawnattr_init(&attr))) {
42 .SUCCESS => return Attr{ .attr = attr },
43 .NOMEM => return error.SystemResources,
44 .INVAL => unreachable,
45 else => |err| return unexpectedErrno(err),
46 }
47 }
48
49 pub fn deinit(self: *Attr) void {
50 defer self.* = undefined;
51 switch (errno(system.posix_spawnattr_destroy(&self.attr))) {
52 .SUCCESS => return,
53 .INVAL => unreachable, // Invalid parameters.
54 else => unreachable,
55 }
56 }
57
58 pub fn get(self: Attr) Error!u16 {
59 var flags: c_short = undefined;
60 switch (errno(system.posix_spawnattr_getflags(&self.attr, &flags))) {
61 .SUCCESS => return @bitCast(u16, flags),
62 .INVAL => unreachable,
63 else => |err| return unexpectedErrno(err),
64 }
65 }
66
67 pub fn set(self: *Attr, flags: u16) Error!void {
68 switch (errno(system.posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) {
69 .SUCCESS => return,
70 .INVAL => unreachable,
71 else => |err| return unexpectedErrno(err),
72 }
73 }
74 };
75
76 pub const Actions = struct {
77 actions: system.posix_spawn_file_actions_t,
78
79 pub fn init() Error!Actions {
80 var actions: system.posix_spawn_file_actions_t = undefined;
81 switch (errno(system.posix_spawn_file_actions_init(&actions))) {
82 .SUCCESS => return Actions{ .actions = actions },
83 .NOMEM => return error.SystemResources,
84 .INVAL => unreachable,
85 else => |err| return unexpectedErrno(err),
86 }
87 }
88
89 pub fn deinit(self: *Actions) void {
90 defer self.* = undefined;
91 switch (errno(system.posix_spawn_file_actions_destroy(&self.actions))) {
92 .SUCCESS => return,
93 .INVAL => unreachable, // Invalid parameters.
94 else => unreachable,
95 }
96 }
97
98 pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void {
99 const posix_path = try toPosixPath(path);
100 return self.openZ(fd, &posix_path, flags, mode);
101 }
102
103 pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void {
104 switch (errno(system.posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, flags), mode))) {
105 .SUCCESS => return,
106 .BADF => return error.InvalidFileDescriptor,
107 .NOMEM => return error.SystemResources,
108 .NAMETOOLONG => return error.NameTooLong,
109 .INVAL => unreachable, // the value of file actions is invalid
110 else => |err| return unexpectedErrno(err),
111 }
112 }
113
114 pub fn close(self: *Actions, fd: fd_t) Error!void {
115 switch (errno(system.posix_spawn_file_actions_addclose(&self.actions, fd))) {
116 .SUCCESS => return,
117 .BADF => return error.InvalidFileDescriptor,
118 .NOMEM => return error.SystemResources,
119 .INVAL => unreachable, // the value of file actions is invalid
120 .NAMETOOLONG => unreachable,
121 else => |err| return unexpectedErrno(err),
122 }
123 }
124
125 pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void {
126 switch (errno(system.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
127 .SUCCESS => return,
128 .BADF => return error.InvalidFileDescriptor,
129 .NOMEM => return error.SystemResources,
130 .INVAL => unreachable, // the value of file actions is invalid
131 .NAMETOOLONG => unreachable,
132 else => |err| return unexpectedErrno(err),
133 }
134 }
135
136 pub fn inherit(self: *Actions, fd: fd_t) Error!void {
137 switch (errno(system.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
138 .SUCCESS => return,
139 .BADF => return error.InvalidFileDescriptor,
140 .NOMEM => return error.SystemResources,
141 .INVAL => unreachable, // the value of file actions is invalid
142 .NAMETOOLONG => unreachable,
143 else => |err| return unexpectedErrno(err),
144 }
145 }
146
147 pub fn chdir(self: *Actions, path: []const u8) Error!void {
148 const posix_path = try toPosixPath(path);
149 return self.chdirZ(&posix_path);
150 }
151
152 pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
153 switch (errno(system.posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
154 .SUCCESS => return,
155 .NOMEM => return error.SystemResources,
156 .NAMETOOLONG => return error.NameTooLong,
157 .BADF => unreachable,
158 .INVAL => unreachable, // the value of file actions is invalid
159 else => |err| return unexpectedErrno(err),
160 }
161 }
162
163 pub fn fchdir(self: *Actions, fd: fd_t) Error!void {
164 switch (errno(system.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
165 .SUCCESS => return,
166 .BADF => return error.InvalidFileDescriptor,
167 .NOMEM => return error.SystemResources,
168 .INVAL => unreachable, // the value of file actions is invalid
169 .NAMETOOLONG => unreachable,
170 else => |err| return unexpectedErrno(err),
171 }
172 }
173 };
174
175 pub fn spawn(
176 path: []const u8,
177 actions: ?Actions,
178 attr: ?Attr,
179 argv: [*:null]?[*:0]const u8,
180 envp: [*:null]?[*:0]const u8,
181 ) Error!pid_t {
182 const posix_path = try toPosixPath(path);
183 return spawnZ(&posix_path, actions, attr, argv, envp);
184 }
185
186 pub fn spawnZ(
187 path: [*:0]const u8,
188 actions: ?Actions,
189 attr: ?Attr,
190 argv: [*:null]?[*:0]const u8,
191 envp: [*:null]?[*:0]const u8,
192 ) Error!pid_t {
193 var pid: pid_t = undefined;
194 switch (errno(system.posix_spawn(
195 &pid,
196 path,
197 if (actions) |a| &a.actions else null,
198 if (attr) |a| &a.attr else null,
199 argv,
200 envp,
201 ))) {
202 .SUCCESS => return pid,
203 .@"2BIG" => return error.TooBig,
204 .NOMEM => return error.SystemResources,
205 .BADF => return error.InvalidFileDescriptor,
206 .ACCES => return error.PermissionDenied,
207 .IO => return error.InputOutput,
208 .LOOP => return error.FileSystem,
209 .NAMETOOLONG => return error.NameTooLong,
210 .NOENT => return error.FileNotFound,
211 .NOEXEC => return error.InvalidExe,
212 .NOTDIR => return error.NotDir,
213 .TXTBSY => return error.FileBusy,
214 .BADARCH => return error.InvalidExe,
215 .BADEXEC => return error.InvalidExe,
216 .FAULT => unreachable,
217 .INVAL => unreachable,
218 else => |err| return unexpectedErrno(err),
219 }
220 }
221
222 pub fn spawnp(
223 file: []const u8,
224 actions: ?Actions,
225 attr: ?Attr,
226 argv: [*:null]?[*:0]const u8,
227 envp: [*:null]?[*:0]const u8,
228 ) Error!pid_t {
229 const posix_file = try toPosixPath(file);
230 return spawnpZ(&posix_file, actions, attr, argv, envp);
231 }
232
233 pub fn spawnpZ(
234 file: [*:0]const u8,
235 actions: ?Actions,
236 attr: ?Attr,
237 argv: [*:null]?[*:0]const u8,
238 envp: [*:null]?[*:0]const u8,
239 ) Error!pid_t {
240 var pid: pid_t = undefined;
241 switch (errno(system.posix_spawnp(
242 &pid,
243 file,
244 if (actions) |a| &a.actions else null,
245 if (attr) |a| &a.attr else null,
246 argv,
247 envp,
248 ))) {
249 .SUCCESS => return pid,
250 .@"2BIG" => return error.TooBig,
251 .NOMEM => return error.SystemResources,
252 .BADF => return error.InvalidFileDescriptor,
253 .ACCES => return error.PermissionDenied,
254 .IO => return error.InputOutput,
255 .LOOP => return error.FileSystem,
256 .NAMETOOLONG => return error.NameTooLong,
257 .NOENT => return error.FileNotFound,
258 .NOEXEC => return error.InvalidExe,
259 .NOTDIR => return error.NotDir,
260 .TXTBSY => return error.FileBusy,
261 .BADARCH => return error.InvalidExe,
262 .BADEXEC => return error.InvalidExe,
263 .FAULT => unreachable,
264 .INVAL => unreachable,
265 else => |err| return unexpectedErrno(err),
266 }
267 }
268
269 /// Use this version of the `waitpid` wrapper if you spawned your child process using `posix_spawn`
270 /// or `posix_spawnp` syscalls.
271 /// See also `std.os.waitpid` for an alternative if your child process was spawned via `fork` and
272 /// `execve` method.
273 pub fn waitpid(pid: pid_t, flags: u32) Error!WaitPidResult {
274 const Status = if (builtin.link_libc) c_int else u32;
275 var status: Status = undefined;
276 while (true) {
277 const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags);
278 switch (errno(rc)) {
279 .SUCCESS => return WaitPidResult{
280 .pid = @intCast(pid_t, rc),
281 .status = @bitCast(u32, status),
282 },
283 .INTR => continue,
284 .CHILD => return error.ChildExecFailed,
285 .INVAL => unreachable, // Invalid flags.
286 else => unreachable,
287 }
288 }
289 }
290} else struct {};
lib/std/os/ptrace.zig deleted-28
...@@ -1,28 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const os = @import("../os.zig");
5const system = os.system;
6const errno = system.getErrno;
7const pid_t = system.pid_t;
8const unexpectedErrno = os.unexpectedErrno;
9const UnexpectedError = os.UnexpectedError;
10
11pub usingnamespace ptrace;
12
13const ptrace = if (builtin.target.isDarwin()) struct {
14 pub const PtraceError = error{
15 ProcessNotFound,
16 PermissionDenied,
17 } || UnexpectedError;
18
19 pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
20 switch (errno(system.ptrace(request, pid, addr, signal))) {
21 .SUCCESS => return,
22 .SRCH => return error.ProcessNotFound,
23 .INVAL => unreachable,
24 .BUSY, .PERM => return error.PermissionDenied,
25 else => |err| return unexpectedErrno(err),
26 }
27 }
28} else struct {};
lib/std/os/windows.zig+47-35
...@@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
105 // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.105 // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
106 const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;106 const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
107107
108 const rc = ntdll.NtCreateFile(108 while (true) {
109 &result,109 const rc = ntdll.NtCreateFile(
110 options.access_mask,110 &result,
111 &attr,111 options.access_mask,
112 &io,112 &attr,
113 null,113 &io,
114 FILE_ATTRIBUTE_NORMAL,114 null,
115 options.share_access,115 FILE_ATTRIBUTE_NORMAL,
116 options.creation,116 options.share_access,
117 flags,117 options.creation,
118 null,118 flags,
119 0,119 null,
120 );120 0,
121 switch (rc) {121 );
122 .SUCCESS => {122 switch (rc) {
123 if (std.io.is_async and options.io_mode == .evented) {123 .SUCCESS => {
124 _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined;124 if (std.io.is_async and options.io_mode == .evented) {
125 }125 _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined;
126 return result;126 }
127 },127 return result;
128 .OBJECT_NAME_INVALID => unreachable,128 },
129 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,129 .OBJECT_NAME_INVALID => unreachable,
130 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,130 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
131 .NO_MEDIA_IN_DEVICE => return error.NoDevice,131 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
132 .INVALID_PARAMETER => unreachable,132 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
133 .SHARING_VIOLATION => return error.AccessDenied,133 .INVALID_PARAMETER => unreachable,
134 .ACCESS_DENIED => return error.AccessDenied,134 .SHARING_VIOLATION => return error.AccessDenied,
135 .PIPE_BUSY => return error.PipeBusy,135 .ACCESS_DENIED => return error.AccessDenied,
136 .OBJECT_PATH_SYNTAX_BAD => unreachable,136 .PIPE_BUSY => return error.PipeBusy,
137 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,137 .OBJECT_PATH_SYNTAX_BAD => unreachable,
138 .FILE_IS_A_DIRECTORY => return error.IsDir,138 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
139 .NOT_A_DIRECTORY => return error.NotDir,139 .FILE_IS_A_DIRECTORY => return error.IsDir,
140 .USER_MAPPED_FILE => return error.AccessDenied,140 .NOT_A_DIRECTORY => return error.NotDir,
141 .INVALID_HANDLE => unreachable,141 .USER_MAPPED_FILE => return error.AccessDenied,
142 else => return unexpectedStatus(rc),142 .INVALID_HANDLE => unreachable,
143 .DELETE_PENDING => {
144 // This error means that there *was* a file in this location on
145 // the file system, but it was deleted. However, the OS is not
146 // finished with the deletion operation, and so this CreateFile
147 // call has failed. There is not really a sane way to handle
148 // this other than retrying the creation after the OS finishes
149 // the deletion.
150 std.time.sleep(std.time.ns_per_ms);
151 continue;
152 },
153 else => return unexpectedStatus(rc),
154 }
143 }155 }
144}156}
145157
lib/std/os/windows/advapi32.zig+2
...@@ -27,6 +27,8 @@ pub extern "advapi32" fn RegQueryValueExW(...@@ -27,6 +27,8 @@ pub extern "advapi32" fn RegQueryValueExW(
27 lpcbData: ?*DWORD,27 lpcbData: ?*DWORD,
28) callconv(WINAPI) LSTATUS;28) callconv(WINAPI) LSTATUS;
2929
30pub extern "advapi32" fn RegCloseKey(hKey: HKEY) callconv(WINAPI) LSTATUS;
31
30// RtlGenRandom is known as SystemFunction036 under advapi3232// RtlGenRandom is known as SystemFunction036 under advapi32
31// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */33// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
32pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(WINAPI) BOOL;34pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(WINAPI) BOOL;
lib/std/os/windows/kernel32.zig+3
...@@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;...@@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;
67const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;67const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
68const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;68const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;
69const MODULEENTRY32 = windows.MODULEENTRY32;69const MODULEENTRY32 = windows.MODULEENTRY32;
70const ULONGLONG = windows.ULONGLONG;
7071
71pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;72pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
72pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;73pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
...@@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW(...@@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW(
457 samDesired: REGSAM,458 samDesired: REGSAM,
458 phkResult: *HKEY,459 phkResult: *HKEY,
459) callconv(WINAPI) LSTATUS;460) callconv(WINAPI) LSTATUS;
461
462pub extern "kernel32" fn GetPhysicallyInstalledSystemMemory(TotalMemoryInKilobytes: *ULONGLONG) BOOL;
lib/std/process.zig+48-18
...@@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator...@@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator
828 return ArgIterator.initWithAllocator(allocator);828 return ArgIterator.initWithAllocator(allocator);
829}829}
830830
831test "args iterator" {
832 var ga = std.testing.allocator;
833 var it = try argsWithAllocator(ga);
834 defer it.deinit(); // no-op unless WASI or Windows
835
836 const prog_name = it.next() orelse unreachable;
837 const expected_suffix = switch (builtin.os.tag) {
838 .wasi => "test.wasm",
839 .windows => "test.exe",
840 else => "test",
841 };
842 const given_suffix = std.fs.path.basename(prog_name);
843
844 try testing.expect(mem.eql(u8, expected_suffix, given_suffix));
845 try testing.expect(it.next() == null);
846 try testing.expect(!it.skip());
847}
848
849/// Caller must call argsFree on result.831/// Caller must call argsFree on result.
850pub fn argsAlloc(allocator: Allocator) ![][:0]u8 {832pub fn argsAlloc(allocator: Allocator) ![][:0]u8 {
851 // TODO refactor to only make 1 allocation.833 // TODO refactor to only make 1 allocation.
...@@ -1169,3 +1151,51 @@ pub fn execve(...@@ -1169,3 +1151,51 @@ pub fn execve(
11691151
1170 return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);1152 return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);
1171}1153}
1154
1155pub const TotalSystemMemoryError = error{
1156 UnknownTotalSystemMemory,
1157};
1158
1159/// Returns the total system memory, in bytes.
1160pub fn totalSystemMemory() TotalSystemMemoryError!usize {
1161 switch (builtin.os.tag) {
1162 .linux => {
1163 return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory;
1164 },
1165 .windows => {
1166 var kilobytes: std.os.windows.ULONGLONG = undefined;
1167 assert(std.os.windows.kernel32.GetPhysicallyInstalledSystemMemory(&kilobytes) == std.os.windows.TRUE);
1168 return kilobytes * 1024;
1169 },
1170 else => return error.UnknownTotalSystemMemory,
1171 }
1172}
1173
1174fn totalSystemMemoryLinux() !usize {
1175 var file = try std.fs.openFileAbsoluteZ("/proc/meminfo", .{});
1176 defer file.close();
1177 var buf: [50]u8 = undefined;
1178 const amt = try file.read(&buf);
1179 if (amt != 50) return error.Unexpected;
1180 var it = std.mem.tokenize(u8, buf[0..amt], " \n");
1181 const label = it.next().?;
1182 if (!std.mem.eql(u8, label, "MemTotal:")) return error.Unexpected;
1183 const int_text = it.next() orelse return error.Unexpected;
1184 const units = it.next() orelse return error.Unexpected;
1185 if (!std.mem.eql(u8, units, "kB")) return error.Unexpected;
1186 const kilobytes = try std.fmt.parseInt(usize, int_text, 10);
1187 return kilobytes * 1024;
1188}
1189
1190/// Indicate that we are now terminating with a successful exit code.
1191/// In debug builds, this is a no-op, so that the calling code's
1192/// cleanup mechanisms are tested and so that external tools that
1193/// check for resource leaks can be accurate. In release builds, this
1194/// calls exit(0), and does not return.
1195pub fn cleanExit() void {
1196 if (builtin.mode == .Debug) {
1197 return;
1198 } else {
1199 exit(0);
1200 }
1201}
lib/std/std.zig+10
...@@ -185,6 +185,16 @@ pub const options = struct {...@@ -185,6 +185,16 @@ pub const options = struct {
185 options_override.keep_sigpipe185 options_override.keep_sigpipe
186 else186 else
187 false;187 false;
188
189 pub const http_connection_pool_size = if (@hasDecl(options_override, "http_connection_pool_size"))
190 options_override.http_connection_pool_size
191 else
192 http.Client.default_connection_pool_size;
193
194 pub const side_channels_mitigations: crypto.SideChannelsMitigations = if (@hasDecl(options_override, "side_channels_mitigations"))
195 options_override.side_channels_mitigations
196 else
197 crypto.default_side_channels_mitigations;
188};198};
189199
190// This forces the start.zig file to be imported, and the comptime logic inside that200// This forces the start.zig file to be imported, and the comptime logic inside that
lib/std/target.zig+2-10
...@@ -724,11 +724,7 @@ pub const Target = struct {...@@ -724,11 +724,7 @@ pub const Target = struct {
724724
725 /// Adds the specified feature set but not its dependencies.725 /// Adds the specified feature set but not its dependencies.
726 pub fn addFeatureSet(set: *Set, other_set: Set) void {726 pub fn addFeatureSet(set: *Set, other_set: Set) void {
727 if (builtin.zig_backend == .stage2_c) {727 set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints);
728 for (&set.ints, 0..) |*int, i| int.* |= other_set.ints[i];
729 } else {
730 set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints);
731 }
732 }728 }
733729
734 /// Removes the specified feature but not its dependents.730 /// Removes the specified feature but not its dependents.
...@@ -740,11 +736,7 @@ pub const Target = struct {...@@ -740,11 +736,7 @@ pub const Target = struct {
740736
741 /// Removes the specified feature but not its dependents.737 /// Removes the specified feature but not its dependents.
742 pub fn removeFeatureSet(set: *Set, other_set: Set) void {738 pub fn removeFeatureSet(set: *Set, other_set: Set) void {
743 if (builtin.zig_backend == .stage2_c) {739 set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints);
744 for (&set.ints, 0..) |*int, i| int.* &= ~other_set.ints[i];
745 } else {
746 set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints);
747 }
748 }740 }
749741
750 pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {742 pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {
lib/std/zig.zig+3
...@@ -3,6 +3,9 @@ const tokenizer = @import("zig/tokenizer.zig");...@@ -3,6 +3,9 @@ const tokenizer = @import("zig/tokenizer.zig");
3const fmt = @import("zig/fmt.zig");3const fmt = @import("zig/fmt.zig");
4const assert = std.debug.assert;4const assert = std.debug.assert;
55
6pub const ErrorBundle = @import("zig/ErrorBundle.zig");
7pub const Server = @import("zig/Server.zig");
8pub const Client = @import("zig/Client.zig");
6pub const Token = tokenizer.Token;9pub const Token = tokenizer.Token;
7pub const Tokenizer = tokenizer.Tokenizer;10pub const Tokenizer = tokenizer.Tokenizer;
8pub const fmtId = fmt.fmtId;11pub const fmtId = fmt.fmtId;
lib/std/zig/Ast.zig+6-3
...@@ -1407,7 +1407,8 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {...@@ -1407,7 +1407,8 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {
1407 .type_expr = data.lhs,1407 .type_expr = data.lhs,
1408 .value_expr = extra.value_expr,1408 .value_expr = extra.value_expr,
1409 .align_expr = extra.align_expr,1409 .align_expr = extra.align_expr,
1410 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,1410 .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or
1411 tree.tokens.items(.tag)[main_token + 1] != .colon,
1411 });1412 });
1412}1413}
14131414
...@@ -1420,7 +1421,8 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {...@@ -1420,7 +1421,8 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {
1420 .type_expr = data.lhs,1421 .type_expr = data.lhs,
1421 .value_expr = data.rhs,1422 .value_expr = data.rhs,
1422 .align_expr = 0,1423 .align_expr = 0,
1423 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,1424 .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or
1425 tree.tokens.items(.tag)[main_token + 1] != .colon,
1424 });1426 });
1425}1427}
14261428
...@@ -1433,7 +1435,8 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {...@@ -1433,7 +1435,8 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {
1433 .type_expr = data.lhs,1435 .type_expr = data.lhs,
1434 .value_expr = 0,1436 .value_expr = 0,
1435 .align_expr = data.rhs,1437 .align_expr = data.rhs,
1436 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,1438 .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or
1439 tree.tokens.items(.tag)[main_token + 1] != .colon,
1437 });1440 });
1438}1441}
14391442
lib/std/zig/Client.zig created+39
...@@ -0,0 +1,39 @@
1pub const Message = struct {
2 pub const Header = extern struct {
3 tag: Tag,
4 /// Size of the body only; does not include this Header.
5 bytes_len: u32,
6 };
7
8 pub const Tag = enum(u32) {
9 /// Tells the compiler to shut down cleanly.
10 /// No body.
11 exit,
12 /// Tells the compiler to detect changes in source files and update the
13 /// affected output compilation artifacts.
14 /// If one of the compilation artifacts is an executable that is
15 /// running as a child process, the compiler will wait for it to exit
16 /// before performing the update.
17 /// No body.
18 update,
19 /// Tells the compiler to execute the executable as a child process.
20 /// No body.
21 run,
22 /// Tells the compiler to detect changes in source files and update the
23 /// affected output compilation artifacts.
24 /// If one of the compilation artifacts is an executable that is
25 /// running as a child process, the compiler will perform a hot code
26 /// swap.
27 /// No body.
28 hot_update,
29 /// Ask the test runner for metadata about all the unit tests that can
30 /// be run. Server will respond with a `test_metadata` message.
31 /// No body.
32 query_test_metadata,
33 /// Ask the test runner to run a particular test.
34 /// The message body is a u32 test index.
35 run_test,
36
37 _,
38 };
39};
lib/std/zig/ErrorBundle.zig created+515
...@@ -0,0 +1,515 @@
1//! To support incremental compilation, errors are stored in various places
2//! so that they can be created and destroyed appropriately. This structure
3//! is used to collect all the errors from the various places into one
4//! convenient place for API users to consume.
5//!
6//! There is one special encoding for this data structure. If both arrays are
7//! empty, it means there are no errors. This special encoding exists so that
8//! heap allocation is not needed in the common case of no errors.
9
10string_bytes: []const u8,
11/// The first thing in this array is an `ErrorMessageList`.
12extra: []const u32,
13
14/// Special encoding when there are no errors.
15pub const empty: ErrorBundle = .{
16 .string_bytes = &.{},
17 .extra = &.{},
18};
19
20// An index into `extra` pointing at an `ErrorMessage`.
21pub const MessageIndex = enum(u32) {
22 _,
23};
24
25// An index into `extra` pointing at an `SourceLocation`.
26pub const SourceLocationIndex = enum(u32) {
27 none = 0,
28 _,
29};
30
31/// There will be a MessageIndex for each len at start.
32pub const ErrorMessageList = struct {
33 len: u32,
34 start: u32,
35 /// null-terminated string index. 0 means no compile log text.
36 compile_log_text: u32,
37};
38
39/// Trailing:
40/// * ReferenceTrace for each reference_trace_len
41pub const SourceLocation = struct {
42 /// null terminated string index
43 src_path: u32,
44 line: u32,
45 column: u32,
46 /// byte offset of starting token
47 span_start: u32,
48 /// byte offset of main error location
49 span_main: u32,
50 /// byte offset of end of last token
51 span_end: u32,
52 /// null terminated string index, possibly null.
53 /// Does not include the trailing newline.
54 source_line: u32 = 0,
55 reference_trace_len: u32 = 0,
56};
57
58/// Trailing:
59/// * MessageIndex for each notes_len.
60pub const ErrorMessage = struct {
61 /// null terminated string index
62 msg: u32,
63 /// Usually one, but incremented for redundant messages.
64 count: u32 = 1,
65 src_loc: SourceLocationIndex = .none,
66 notes_len: u32 = 0,
67};
68
69pub const ReferenceTrace = struct {
70 /// null terminated string index
71 /// Except for the sentinel ReferenceTrace element, in which case:
72 /// * 0 means remaining references hidden
73 /// * >0 means N references hidden
74 decl_name: u32,
75 /// Index into extra of a SourceLocation
76 /// If this is 0, this is the sentinel ReferenceTrace element.
77 src_loc: SourceLocationIndex,
78};
79
80pub fn deinit(eb: *ErrorBundle, gpa: Allocator) void {
81 gpa.free(eb.string_bytes);
82 gpa.free(eb.extra);
83 eb.* = undefined;
84}
85
86pub fn errorMessageCount(eb: ErrorBundle) u32 {
87 if (eb.extra.len == 0) return 0;
88 return eb.getErrorMessageList().len;
89}
90
91pub fn getErrorMessageList(eb: ErrorBundle) ErrorMessageList {
92 return eb.extraData(ErrorMessageList, 0).data;
93}
94
95pub fn getMessages(eb: ErrorBundle) []const MessageIndex {
96 const list = eb.getErrorMessageList();
97 return @ptrCast([]const MessageIndex, eb.extra[list.start..][0..list.len]);
98}
99
100pub fn getErrorMessage(eb: ErrorBundle, index: MessageIndex) ErrorMessage {
101 return eb.extraData(ErrorMessage, @enumToInt(index)).data;
102}
103
104pub fn getSourceLocation(eb: ErrorBundle, index: SourceLocationIndex) SourceLocation {
105 assert(index != .none);
106 return eb.extraData(SourceLocation, @enumToInt(index)).data;
107}
108
109pub fn getNotes(eb: ErrorBundle, index: MessageIndex) []const MessageIndex {
110 const notes_len = eb.getErrorMessage(index).notes_len;
111 const start = @enumToInt(index) + @typeInfo(ErrorMessage).Struct.fields.len;
112 return @ptrCast([]const MessageIndex, eb.extra[start..][0..notes_len]);
113}
114
115pub fn getCompileLogOutput(eb: ErrorBundle) [:0]const u8 {
116 return nullTerminatedString(eb, getErrorMessageList(eb).compile_log_text);
117}
118
119/// Returns the requested data, as well as the new index which is at the start of the
120/// trailers for the object.
121fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, end: usize } {
122 const fields = @typeInfo(T).Struct.fields;
123 var i: usize = index;
124 var result: T = undefined;
125 inline for (fields) |field| {
126 @field(result, field.name) = switch (field.type) {
127 u32 => eb.extra[i],
128 MessageIndex => @intToEnum(MessageIndex, eb.extra[i]),
129 SourceLocationIndex => @intToEnum(SourceLocationIndex, eb.extra[i]),
130 else => @compileError("bad field type"),
131 };
132 i += 1;
133 }
134 return .{
135 .data = result,
136 .end = i,
137 };
138}
139
140/// Given an index into `string_bytes` returns the null-terminated string found there.
141pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 {
142 const string_bytes = eb.string_bytes;
143 var end: usize = index;
144 while (string_bytes[end] != 0) {
145 end += 1;
146 }
147 return string_bytes[index..end :0];
148}
149
150pub const RenderOptions = struct {
151 ttyconf: std.debug.TTY.Config,
152 include_reference_trace: bool = true,
153 include_source_line: bool = true,
154 include_log_text: bool = true,
155};
156
157pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
158 std.debug.getStderrMutex().lock();
159 defer std.debug.getStderrMutex().unlock();
160 const stderr = std.io.getStdErr();
161 return renderToWriter(eb, options, stderr.writer()) catch return;
162}
163
164pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) anyerror!void {
165 for (eb.getMessages()) |err_msg| {
166 try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .Red, 0);
167 }
168
169 if (options.include_log_text) {
170 const log_text = eb.getCompileLogOutput();
171 if (log_text.len != 0) {
172 try writer.writeAll("\nCompile Log Output:\n");
173 try writer.writeAll(log_text);
174 }
175 }
176}
177
178fn renderErrorMessageToWriter(
179 eb: ErrorBundle,
180 options: RenderOptions,
181 err_msg_index: MessageIndex,
182 stderr: anytype,
183 kind: []const u8,
184 color: std.debug.TTY.Color,
185 indent: usize,
186) anyerror!void {
187 const ttyconf = options.ttyconf;
188 var counting_writer = std.io.countingWriter(stderr);
189 const counting_stderr = counting_writer.writer();
190 const err_msg = eb.getErrorMessage(err_msg_index);
191 if (err_msg.src_loc != .none) {
192 const src = eb.extraData(SourceLocation, @enumToInt(err_msg.src_loc));
193 try counting_stderr.writeByteNTimes(' ', indent);
194 try ttyconf.setColor(stderr, .Bold);
195 try counting_stderr.print("{s}:{d}:{d}: ", .{
196 eb.nullTerminatedString(src.data.src_path),
197 src.data.line + 1,
198 src.data.column + 1,
199 });
200 try ttyconf.setColor(stderr, color);
201 try counting_stderr.writeAll(kind);
202 try counting_stderr.writeAll(": ");
203 // This is the length of the part before the error message:
204 // e.g. "file.zig:4:5: error: "
205 const prefix_len = @intCast(usize, counting_stderr.context.bytes_written);
206 try ttyconf.setColor(stderr, .Reset);
207 try ttyconf.setColor(stderr, .Bold);
208 if (err_msg.count == 1) {
209 try writeMsg(eb, err_msg, stderr, prefix_len);
210 try stderr.writeByte('\n');
211 } else {
212 try writeMsg(eb, err_msg, stderr, prefix_len);
213 try ttyconf.setColor(stderr, .Dim);
214 try stderr.print(" ({d} times)\n", .{err_msg.count});
215 }
216 try ttyconf.setColor(stderr, .Reset);
217 if (src.data.source_line != 0 and options.include_source_line) {
218 const line = eb.nullTerminatedString(src.data.source_line);
219 for (line) |b| switch (b) {
220 '\t' => try stderr.writeByte(' '),
221 else => try stderr.writeByte(b),
222 };
223 try stderr.writeByte('\n');
224 // TODO basic unicode code point monospace width
225 const before_caret = src.data.span_main - src.data.span_start;
226 // -1 since span.main includes the caret
227 const after_caret = src.data.span_end - src.data.span_main -| 1;
228 try stderr.writeByteNTimes(' ', src.data.column - before_caret);
229 try ttyconf.setColor(stderr, .Green);
230 try stderr.writeByteNTimes('~', before_caret);
231 try stderr.writeByte('^');
232 try stderr.writeByteNTimes('~', after_caret);
233 try stderr.writeByte('\n');
234 try ttyconf.setColor(stderr, .Reset);
235 }
236 for (eb.getNotes(err_msg_index)) |note| {
237 try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent);
238 }
239 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
240 try ttyconf.setColor(stderr, .Reset);
241 try ttyconf.setColor(stderr, .Dim);
242 try stderr.print("referenced by:\n", .{});
243 var ref_index = src.end;
244 for (0..src.data.reference_trace_len) |_| {
245 const ref_trace = eb.extraData(ReferenceTrace, ref_index);
246 ref_index = ref_trace.end;
247 if (ref_trace.data.src_loc != .none) {
248 const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);
249 try stderr.print(" {s}: {s}:{d}:{d}\n", .{
250 eb.nullTerminatedString(ref_trace.data.decl_name),
251 eb.nullTerminatedString(ref_src.src_path),
252 ref_src.line + 1,
253 ref_src.column + 1,
254 });
255 } else if (ref_trace.data.decl_name != 0) {
256 const count = ref_trace.data.decl_name;
257 try stderr.print(
258 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
259 .{ count, count + src.data.reference_trace_len - 1 },
260 );
261 } else {
262 try stderr.print(
263 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
264 .{},
265 );
266 }
267 }
268 try stderr.writeByte('\n');
269 try ttyconf.setColor(stderr, .Reset);
270 }
271 } else {
272 try ttyconf.setColor(stderr, color);
273 try stderr.writeByteNTimes(' ', indent);
274 try stderr.writeAll(kind);
275 try stderr.writeAll(": ");
276 try ttyconf.setColor(stderr, .Reset);
277 const msg = eb.nullTerminatedString(err_msg.msg);
278 if (err_msg.count == 1) {
279 try stderr.print("{s}\n", .{msg});
280 } else {
281 try stderr.print("{s}", .{msg});
282 try ttyconf.setColor(stderr, .Dim);
283 try stderr.print(" ({d} times)\n", .{err_msg.count});
284 }
285 try ttyconf.setColor(stderr, .Reset);
286 for (eb.getNotes(err_msg_index)) |note| {
287 try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent + 4);
288 }
289 }
290}
291
292/// Splits the error message up into lines to properly indent them
293/// to allow for long, good-looking error messages.
294///
295/// This is used to split the message in `@compileError("hello\nworld")` for example.
296fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, stderr: anytype, indent: usize) !void {
297 var lines = std.mem.split(u8, eb.nullTerminatedString(err_msg.msg), "\n");
298 while (lines.next()) |line| {
299 try stderr.writeAll(line);
300 if (lines.index == null) break;
301 try stderr.writeByte('\n');
302 try stderr.writeByteNTimes(' ', indent);
303 }
304}
305
306const std = @import("std");
307const ErrorBundle = @This();
308const Allocator = std.mem.Allocator;
309const assert = std.debug.assert;
310
311pub const Wip = struct {
312 gpa: Allocator,
313 string_bytes: std.ArrayListUnmanaged(u8),
314 /// The first thing in this array is a ErrorMessageList.
315 extra: std.ArrayListUnmanaged(u32),
316 root_list: std.ArrayListUnmanaged(MessageIndex),
317
318 pub fn init(wip: *Wip, gpa: Allocator) !void {
319 wip.* = .{
320 .gpa = gpa,
321 .string_bytes = .{},
322 .extra = .{},
323 .root_list = .{},
324 };
325
326 // So that 0 can be used to indicate a null string.
327 try wip.string_bytes.append(gpa, 0);
328
329 assert(0 == try addExtra(wip, ErrorMessageList{
330 .len = 0,
331 .start = 0,
332 .compile_log_text = 0,
333 }));
334 }
335
336 pub fn deinit(wip: *Wip) void {
337 const gpa = wip.gpa;
338 wip.root_list.deinit(gpa);
339 wip.string_bytes.deinit(gpa);
340 wip.extra.deinit(gpa);
341 wip.* = undefined;
342 }
343
344 pub fn toOwnedBundle(wip: *Wip, compile_log_text: []const u8) !ErrorBundle {
345 const gpa = wip.gpa;
346 if (wip.root_list.items.len == 0) {
347 assert(compile_log_text.len == 0);
348 // Special encoding when there are no errors.
349 wip.deinit();
350 wip.* = .{
351 .gpa = gpa,
352 .string_bytes = .{},
353 .extra = .{},
354 .root_list = .{},
355 };
356 return empty;
357 }
358
359 const compile_log_str_index = if (compile_log_text.len == 0) 0 else str: {
360 const str = @intCast(u32, wip.string_bytes.items.len);
361 try wip.string_bytes.ensureUnusedCapacity(gpa, compile_log_text.len + 1);
362 wip.string_bytes.appendSliceAssumeCapacity(compile_log_text);
363 wip.string_bytes.appendAssumeCapacity(0);
364 break :str str;
365 };
366
367 wip.setExtra(0, ErrorMessageList{
368 .len = @intCast(u32, wip.root_list.items.len),
369 .start = @intCast(u32, wip.extra.items.len),
370 .compile_log_text = compile_log_str_index,
371 });
372 try wip.extra.appendSlice(gpa, @ptrCast([]const u32, wip.root_list.items));
373 wip.root_list.clearAndFree(gpa);
374 return .{
375 .string_bytes = try wip.string_bytes.toOwnedSlice(gpa),
376 .extra = try wip.extra.toOwnedSlice(gpa),
377 };
378 }
379
380 pub fn tmpBundle(wip: Wip) ErrorBundle {
381 return .{
382 .string_bytes = wip.string_bytes.items,
383 .extra = wip.extra.items,
384 };
385 }
386
387 pub fn addString(wip: *Wip, s: []const u8) !u32 {
388 const gpa = wip.gpa;
389 const index = @intCast(u32, wip.string_bytes.items.len);
390 try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);
391 wip.string_bytes.appendSliceAssumeCapacity(s);
392 wip.string_bytes.appendAssumeCapacity(0);
393 return index;
394 }
395
396 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) !u32 {
397 const gpa = wip.gpa;
398 const index = @intCast(u32, wip.string_bytes.items.len);
399 try wip.string_bytes.writer(gpa).print(fmt, args);
400 try wip.string_bytes.append(gpa, 0);
401 return index;
402 }
403
404 pub fn addRootErrorMessage(wip: *Wip, em: ErrorMessage) !void {
405 try wip.root_list.ensureUnusedCapacity(wip.gpa, 1);
406 wip.root_list.appendAssumeCapacity(try addErrorMessage(wip, em));
407 }
408
409 pub fn addErrorMessage(wip: *Wip, em: ErrorMessage) !MessageIndex {
410 return @intToEnum(MessageIndex, try addExtra(wip, em));
411 }
412
413 pub fn addErrorMessageAssumeCapacity(wip: *Wip, em: ErrorMessage) MessageIndex {
414 return @intToEnum(MessageIndex, addExtraAssumeCapacity(wip, em));
415 }
416
417 pub fn addSourceLocation(wip: *Wip, sl: SourceLocation) !SourceLocationIndex {
418 return @intToEnum(SourceLocationIndex, try addExtra(wip, sl));
419 }
420
421 pub fn addReferenceTrace(wip: *Wip, rt: ReferenceTrace) !void {
422 _ = try addExtra(wip, rt);
423 }
424
425 pub fn addBundle(wip: *Wip, other: ErrorBundle) !void {
426 const gpa = wip.gpa;
427
428 try wip.string_bytes.ensureUnusedCapacity(gpa, other.string_bytes.len);
429 try wip.extra.ensureUnusedCapacity(gpa, other.extra.len);
430
431 const other_list = other.getMessages();
432
433 // The ensureUnusedCapacity call above guarantees this.
434 const notes_start = wip.reserveNotes(@intCast(u32, other_list.len)) catch unreachable;
435 for (notes_start.., other_list) |note, message| {
436 wip.extra.items[note] = @enumToInt(wip.addOtherMessage(other, message) catch unreachable);
437 }
438 }
439
440 pub fn reserveNotes(wip: *Wip, notes_len: u32) !u32 {
441 try wip.extra.ensureUnusedCapacity(wip.gpa, notes_len +
442 notes_len * @typeInfo(ErrorBundle.ErrorMessage).Struct.fields.len);
443 wip.extra.items.len += notes_len;
444 return @intCast(u32, wip.extra.items.len - notes_len);
445 }
446
447 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
448 const other_msg = other.getErrorMessage(msg_index);
449 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);
450 const msg = try wip.addErrorMessage(.{
451 .msg = try wip.addString(other.nullTerminatedString(other_msg.msg)),
452 .count = other_msg.count,
453 .src_loc = src_loc,
454 .notes_len = other_msg.notes_len,
455 });
456 const notes_start = try wip.reserveNotes(other_msg.notes_len);
457 for (notes_start.., other.getNotes(msg_index)) |note, other_note| {
458 wip.extra.items[note] = @enumToInt(try wip.addOtherMessage(other, other_note));
459 }
460 return msg;
461 }
462
463 fn addOtherSourceLocation(
464 wip: *Wip,
465 other: ErrorBundle,
466 index: SourceLocationIndex,
467 ) !SourceLocationIndex {
468 if (index == .none) return .none;
469 const other_sl = other.getSourceLocation(index);
470
471 const src_loc = try wip.addSourceLocation(.{
472 .src_path = try wip.addString(other.nullTerminatedString(other_sl.src_path)),
473 .line = other_sl.line,
474 .column = other_sl.column,
475 .span_start = other_sl.span_start,
476 .span_main = other_sl.span_main,
477 .span_end = other_sl.span_end,
478 .source_line = try wip.addString(other.nullTerminatedString(other_sl.source_line)),
479 .reference_trace_len = other_sl.reference_trace_len,
480 });
481
482 // TODO: also add the reference trace
483
484 return src_loc;
485 }
486
487 fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 {
488 const gpa = wip.gpa;
489 const fields = @typeInfo(@TypeOf(extra)).Struct.fields;
490 try wip.extra.ensureUnusedCapacity(gpa, fields.len);
491 return addExtraAssumeCapacity(wip, extra);
492 }
493
494 fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 {
495 const fields = @typeInfo(@TypeOf(extra)).Struct.fields;
496 const result = @intCast(u32, wip.extra.items.len);
497 wip.extra.items.len += fields.len;
498 setExtra(wip, result, extra);
499 return result;
500 }
501
502 fn setExtra(wip: *Wip, index: usize, extra: anytype) void {
503 const fields = @typeInfo(@TypeOf(extra)).Struct.fields;
504 var i = index;
505 inline for (fields) |field| {
506 wip.extra.items[i] = switch (field.type) {
507 u32 => @field(extra, field.name),
508 MessageIndex => @enumToInt(@field(extra, field.name)),
509 SourceLocationIndex => @enumToInt(@field(extra, field.name)),
510 else => @compileError("bad field type"),
511 };
512 i += 1;
513 }
514 }
515};
lib/std/zig/Server.zig created+305
...@@ -0,0 +1,305 @@
1in: std.fs.File,
2out: std.fs.File,
3receive_fifo: std.fifo.LinearFifo(u8, .Dynamic),
4
5pub const Message = struct {
6 pub const Header = extern struct {
7 tag: Tag,
8 /// Size of the body only; does not include this Header.
9 bytes_len: u32,
10 };
11
12 pub const Tag = enum(u32) {
13 /// Body is a UTF-8 string.
14 zig_version,
15 /// Body is an ErrorBundle.
16 error_bundle,
17 /// Body is a UTF-8 string.
18 progress,
19 /// Body is a EmitBinPath.
20 emit_bin_path,
21 /// Body is a TestMetadata
22 test_metadata,
23 /// Body is a TestResults
24 test_results,
25
26 _,
27 };
28
29 /// Trailing:
30 /// * extra: [extra_len]u32,
31 /// * string_bytes: [string_bytes_len]u8,
32 /// See `std.zig.ErrorBundle`.
33 pub const ErrorBundle = extern struct {
34 extra_len: u32,
35 string_bytes_len: u32,
36 };
37
38 /// Trailing:
39 /// * name: [tests_len]u32
40 /// - null-terminated string_bytes index
41 /// * async_frame_len: [tests_len]u32,
42 /// - 0 means not async
43 /// * expected_panic_msg: [tests_len]u32,
44 /// - null-terminated string_bytes index
45 /// - 0 means does not expect pani
46 /// * string_bytes: [string_bytes_len]u8,
47 pub const TestMetadata = extern struct {
48 string_bytes_len: u32,
49 tests_len: u32,
50 };
51
52 pub const TestResults = extern struct {
53 index: u32,
54 flags: Flags,
55
56 pub const Flags = packed struct(u8) {
57 fail: bool,
58 skip: bool,
59 leak: bool,
60
61 reserved: u5 = 0,
62 };
63 };
64
65 /// Trailing:
66 /// * the file system path the emitted binary can be found
67 pub const EmitBinPath = extern struct {
68 flags: Flags,
69
70 pub const Flags = packed struct(u8) {
71 cache_hit: bool,
72 reserved: u7 = 0,
73 };
74 };
75};
76
77pub const Options = struct {
78 gpa: Allocator,
79 in: std.fs.File,
80 out: std.fs.File,
81 zig_version: []const u8,
82};
83
84pub fn init(options: Options) !Server {
85 var s: Server = .{
86 .in = options.in,
87 .out = options.out,
88 .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa),
89 };
90 try s.serveStringMessage(.zig_version, options.zig_version);
91 return s;
92}
93
94pub fn deinit(s: *Server) void {
95 s.receive_fifo.deinit();
96 s.* = undefined;
97}
98
99pub fn receiveMessage(s: *Server) !InMessage.Header {
100 const Header = InMessage.Header;
101 const fifo = &s.receive_fifo;
102
103 while (true) {
104 const buf = fifo.readableSlice(0);
105 assert(fifo.readableLength() == buf.len);
106 if (buf.len >= @sizeOf(Header)) {
107 const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
108 // workaround for https://github.com/ziglang/zig/issues/14904
109 const bytes_len = bswap_and_workaround_u32(&header.bytes_len);
110 // workaround for https://github.com/ziglang/zig/issues/14904
111 const tag = bswap_and_workaround_tag(&header.tag);
112
113 if (buf.len - @sizeOf(Header) >= bytes_len) {
114 fifo.discard(@sizeOf(Header));
115 return .{
116 .tag = tag,
117 .bytes_len = bytes_len,
118 };
119 } else {
120 const needed = bytes_len - (buf.len - @sizeOf(Header));
121 const write_buffer = try fifo.writableWithSize(needed);
122 const amt = try s.in.read(write_buffer);
123 fifo.update(amt);
124 continue;
125 }
126 }
127
128 const write_buffer = try fifo.writableWithSize(256);
129 const amt = try s.in.read(write_buffer);
130 fifo.update(amt);
131 }
132}
133
134pub fn receiveBody_u32(s: *Server) !u32 {
135 const fifo = &s.receive_fifo;
136 const buf = fifo.readableSlice(0);
137 const result = @ptrCast(*align(1) const u32, buf[0..4]).*;
138 fifo.discard(4);
139 return bswap(result);
140}
141
142pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
143 return s.serveMessage(.{
144 .tag = tag,
145 .bytes_len = @intCast(u32, msg.len),
146 }, &.{msg});
147}
148
149pub fn serveMessage(
150 s: *const Server,
151 header: OutMessage.Header,
152 bufs: []const []const u8,
153) !void {
154 var iovecs: [10]std.os.iovec_const = undefined;
155 const header_le = bswap(header);
156 iovecs[0] = .{
157 .iov_base = @ptrCast([*]const u8, &header_le),
158 .iov_len = @sizeOf(OutMessage.Header),
159 };
160 for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
161 iovec.* = .{
162 .iov_base = buf.ptr,
163 .iov_len = buf.len,
164 };
165 }
166 try s.out.writevAll(iovecs[0 .. bufs.len + 1]);
167}
168
169pub fn serveEmitBinPath(
170 s: *Server,
171 fs_path: []const u8,
172 header: OutMessage.EmitBinPath,
173) !void {
174 try s.serveMessage(.{
175 .tag = .emit_bin_path,
176 .bytes_len = @intCast(u32, fs_path.len + @sizeOf(OutMessage.EmitBinPath)),
177 }, &.{
178 std.mem.asBytes(&header),
179 fs_path,
180 });
181}
182
183pub fn serveTestResults(
184 s: *Server,
185 msg: OutMessage.TestResults,
186) !void {
187 const msg_le = bswap(msg);
188 try s.serveMessage(.{
189 .tag = .test_results,
190 .bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)),
191 }, &.{
192 std.mem.asBytes(&msg_le),
193 });
194}
195
196pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
197 const eb_hdr: OutMessage.ErrorBundle = .{
198 .extra_len = @intCast(u32, error_bundle.extra.len),
199 .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len),
200 };
201 const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
202 4 * error_bundle.extra.len + error_bundle.string_bytes.len;
203 try s.serveMessage(.{
204 .tag = .error_bundle,
205 .bytes_len = @intCast(u32, bytes_len),
206 }, &.{
207 std.mem.asBytes(&eb_hdr),
208 // TODO: implement @ptrCast between slices changing the length
209 std.mem.sliceAsBytes(error_bundle.extra),
210 error_bundle.string_bytes,
211 });
212}
213
214pub const TestMetadata = struct {
215 names: []u32,
216 async_frame_sizes: []u32,
217 expected_panic_msgs: []u32,
218 string_bytes: []const u8,
219};
220
221pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
222 const header: OutMessage.TestMetadata = .{
223 .tests_len = bswap(@intCast(u32, test_metadata.names.len)),
224 .string_bytes_len = bswap(@intCast(u32, test_metadata.string_bytes.len)),
225 };
226 const bytes_len = @sizeOf(OutMessage.TestMetadata) +
227 3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len;
228
229 if (need_bswap) {
230 bswap_u32_array(test_metadata.names);
231 bswap_u32_array(test_metadata.async_frame_sizes);
232 bswap_u32_array(test_metadata.expected_panic_msgs);
233 }
234 defer if (need_bswap) {
235 bswap_u32_array(test_metadata.names);
236 bswap_u32_array(test_metadata.async_frame_sizes);
237 bswap_u32_array(test_metadata.expected_panic_msgs);
238 };
239
240 return s.serveMessage(.{
241 .tag = .test_metadata,
242 .bytes_len = @intCast(u32, bytes_len),
243 }, &.{
244 std.mem.asBytes(&header),
245 // TODO: implement @ptrCast between slices changing the length
246 std.mem.sliceAsBytes(test_metadata.names),
247 std.mem.sliceAsBytes(test_metadata.async_frame_sizes),
248 std.mem.sliceAsBytes(test_metadata.expected_panic_msgs),
249 test_metadata.string_bytes,
250 });
251}
252
253fn bswap(x: anytype) @TypeOf(x) {
254 if (!need_bswap) return x;
255
256 const T = @TypeOf(x);
257 switch (@typeInfo(T)) {
258 .Enum => return @intToEnum(T, @byteSwap(@enumToInt(x))),
259 .Int => return @byteSwap(x),
260 .Struct => |info| switch (info.layout) {
261 .Extern => {
262 var result: T = undefined;
263 inline for (info.fields) |field| {
264 @field(result, field.name) = bswap(@field(x, field.name));
265 }
266 return result;
267 },
268 .Packed => {
269 const I = info.backing_integer.?;
270 return @bitCast(T, @byteSwap(@bitCast(I, x)));
271 },
272 .Auto => @compileError("auto layout struct"),
273 },
274 else => @compileError("bswap on type " ++ @typeName(T)),
275 }
276}
277
278fn bswap_u32_array(slice: []u32) void {
279 comptime assert(need_bswap);
280 for (slice) |*elem| elem.* = @byteSwap(elem.*);
281}
282
283/// workaround for https://github.com/ziglang/zig/issues/14904
284fn bswap_and_workaround_u32(x: *align(1) const u32) u32 {
285 const bytes_ptr = @ptrCast(*const [4]u8, x);
286 return std.mem.readIntLittle(u32, bytes_ptr);
287}
288
289/// workaround for https://github.com/ziglang/zig/issues/14904
290fn bswap_and_workaround_tag(x: *align(1) const InMessage.Tag) InMessage.Tag {
291 const bytes_ptr = @ptrCast(*const [4]u8, x);
292 const int = std.mem.readIntLittle(u32, bytes_ptr);
293 return @intToEnum(InMessage.Tag, int);
294}
295
296const OutMessage = std.zig.Server.Message;
297const InMessage = std.zig.Client.Message;
298
299const Server = @This();
300const builtin = @import("builtin");
301const std = @import("std");
302const Allocator = std.mem.Allocator;
303const assert = std.debug.assert;
304const native_endian = builtin.target.cpu.arch.endian();
305const need_bswap = native_endian != .Little;
lib/std/zig/system/NativeTargetInfo.zig+5
...@@ -1090,6 +1090,11 @@ pub fn getExternalExecutor(...@@ -1090,6 +1090,11 @@ pub fn getExternalExecutor(
1090 switch (candidate.target.os.tag) {1090 switch (candidate.target.os.tag) {
1091 .windows => {1091 .windows => {
1092 if (options.allow_wine) {1092 if (options.allow_wine) {
1093 // x86_64 wine does not support emulating aarch64-windows and
1094 // vice versa.
1095 if (candidate.target.cpu.arch != builtin.cpu.arch) {
1096 return bad_result;
1097 }
1093 switch (candidate.target.cpu.arch.ptrBitWidth()) {1098 switch (candidate.target.cpu.arch.ptrBitWidth()) {
1094 32 => return Executor{ .wine = "wine" },1099 32 => return Executor{ .wine = "wine" },
1095 64 => return Executor{ .wine = "wine64" },1100 64 => return Executor{ .wine = "wine64" },
lib/test_runner.zig+123-45
...@@ -8,14 +8,126 @@ pub const std_options = struct {...@@ -8,14 +8,126 @@ pub const std_options = struct {
8};8};
99
10var log_err_count: usize = 0;10var log_err_count: usize = 0;
11var cmdline_buffer: [4096]u8 = undefined;
12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1113
12pub fn main() void {14pub fn main() void {
13 if (builtin.zig_backend != .stage1 and15 if (builtin.zig_backend == .stage2_wasm or
14 builtin.zig_backend != .stage2_llvm and16 builtin.zig_backend == .stage2_x86_64 or
15 builtin.zig_backend != .stage2_c)17 builtin.zig_backend == .stage2_aarch64)
16 {18 {
17 return main2() catch @panic("test failure");19 return mainSimple() catch @panic("test failure");
20 }
21
22 const args = std.process.argsAlloc(fba.allocator()) catch
23 @panic("unable to parse command line args");
24
25 var listen = false;
26
27 for (args[1..]) |arg| {
28 if (std.mem.eql(u8, arg, "--listen=-")) {
29 listen = true;
30 } else {
31 @panic("unrecognized command line argument");
32 }
33 }
34
35 if (listen) {
36 return mainServer() catch @panic("internal test runner failure");
37 } else {
38 return mainTerminal();
39 }
40}
41
42fn mainServer() !void {
43 var server = try std.zig.Server.init(.{
44 .gpa = fba.allocator(),
45 .in = std.io.getStdIn(),
46 .out = std.io.getStdOut(),
47 .zig_version = builtin.zig_version_string,
48 });
49 defer server.deinit();
50
51 while (true) {
52 const hdr = try server.receiveMessage();
53 switch (hdr.tag) {
54 .exit => {
55 return std.process.exit(0);
56 },
57 .query_test_metadata => {
58 std.testing.allocator_instance = .{};
59 defer if (std.testing.allocator_instance.deinit()) {
60 @panic("internal test runner memory leak");
61 };
62
63 var string_bytes: std.ArrayListUnmanaged(u8) = .{};
64 defer string_bytes.deinit(std.testing.allocator);
65 try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null.
66
67 const test_fns = builtin.test_functions;
68 const names = try std.testing.allocator.alloc(u32, test_fns.len);
69 defer std.testing.allocator.free(names);
70 const async_frame_sizes = try std.testing.allocator.alloc(u32, test_fns.len);
71 defer std.testing.allocator.free(async_frame_sizes);
72 const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len);
73 defer std.testing.allocator.free(expected_panic_msgs);
74
75 for (test_fns, names, async_frame_sizes, expected_panic_msgs) |test_fn, *name, *async_frame_size, *expected_panic_msg| {
76 name.* = @intCast(u32, string_bytes.items.len);
77 try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1);
78 string_bytes.appendSliceAssumeCapacity(test_fn.name);
79 string_bytes.appendAssumeCapacity(0);
80
81 async_frame_size.* = @intCast(u32, test_fn.async_frame_size orelse 0);
82 expected_panic_msg.* = 0;
83 }
84
85 try server.serveTestMetadata(.{
86 .names = names,
87 .async_frame_sizes = async_frame_sizes,
88 .expected_panic_msgs = expected_panic_msgs,
89 .string_bytes = string_bytes.items,
90 });
91 },
92
93 .run_test => {
94 std.testing.allocator_instance = .{};
95 const index = try server.receiveBody_u32();
96 const test_fn = builtin.test_functions[index];
97 if (test_fn.async_frame_size != null)
98 @panic("TODO test runner implement async tests");
99 var fail = false;
100 var skip = false;
101 var leak = false;
102 test_fn.func() catch |err| switch (err) {
103 error.SkipZigTest => skip = true,
104 else => {
105 fail = true;
106 if (@errorReturnTrace()) |trace| {
107 std.debug.dumpStackTrace(trace.*);
108 }
109 },
110 };
111 leak = std.testing.allocator_instance.deinit();
112 try server.serveTestResults(.{
113 .index = index,
114 .flags = .{
115 .fail = fail,
116 .skip = skip,
117 .leak = leak,
118 },
119 });
120 },
121
122 else => {
123 std.debug.print("unsupported message: {x}", .{@enumToInt(hdr.tag)});
124 std.process.exit(1);
125 },
126 }
18 }127 }
128}
129
130fn mainTerminal() void {
19 const test_fn_list = builtin.test_functions;131 const test_fn_list = builtin.test_functions;
20 var ok_count: usize = 0;132 var ok_count: usize = 0;
21 var skip_count: usize = 0;133 var skip_count: usize = 0;
...@@ -118,51 +230,17 @@ pub fn log(...@@ -118,51 +230,17 @@ pub fn log(
118 }230 }
119}231}
120232
121pub fn main2() anyerror!void {233/// Simpler main(), exercising fewer language features, so that
122 var skipped: usize = 0;234/// work-in-progress backends can handle it.
123 var failed: usize = 0;235pub fn mainSimple() anyerror!void {
124 // Simpler main(), exercising fewer language features, so that stage2 can handle it.236 //const stderr = std.io.getStdErr();
125 for (builtin.test_functions) |test_fn| {237 for (builtin.test_functions) |test_fn| {
126 test_fn.func() catch |err| {238 test_fn.func() catch |err| {
127 if (err != error.SkipZigTest) {239 if (err != error.SkipZigTest) {
128 failed += 1;240 //stderr.writeAll(test_fn.name) catch {};
129 } else {241 //stderr.writeAll("\n") catch {};
130 skipped += 1;242 return err;
131 }243 }
132 };244 };
133 }245 }
134 if (builtin.zig_backend == .stage2_wasm or
135 builtin.zig_backend == .stage2_x86_64 or
136 builtin.zig_backend == .stage2_aarch64 or
137 builtin.zig_backend == .stage2_llvm or
138 builtin.zig_backend == .stage2_c)
139 {
140 const passed = builtin.test_functions.len - skipped - failed;
141 const stderr = std.io.getStdErr();
142 writeInt(stderr, passed) catch {};
143 stderr.writeAll(" passed; ") catch {};
144 writeInt(stderr, skipped) catch {};
145 stderr.writeAll(" skipped; ") catch {};
146 writeInt(stderr, failed) catch {};
147 stderr.writeAll(" failed.\n") catch {};
148 }
149 if (failed != 0) {
150 return error.TestsFailed;
151 }
152}
153
154fn writeInt(stderr: std.fs.File, int: usize) anyerror!void {
155 const base = 10;
156 var buf: [100]u8 = undefined;
157 var a: usize = int;
158 var index: usize = buf.len;
159 while (true) {
160 const digit = a % base;
161 index -= 1;
162 buf[index] = std.fmt.digitToChar(@intCast(u8, digit), .lower);
163 a /= base;
164 if (a == 0) break;
165 }
166 const slice = buf[index..];
167 try stderr.writeAll(slice);
168}246}
lib/zig.h+1097-214
...@@ -37,6 +37,14 @@ typedef char bool;...@@ -37,6 +37,14 @@ typedef char bool;
37#define zig_has_attribute(attribute) 037#define zig_has_attribute(attribute) 0
38#endif38#endif
3939
40#if __LITTLE_ENDIAN__ || _MSC_VER
41#define zig_little_endian 1
42#define zig_big_endian 0
43#else
44#define zig_little_endian 0
45#define zig_big_endian 1
46#endif
47
40#if __STDC_VERSION__ >= 201112L48#if __STDC_VERSION__ >= 201112L
41#define zig_threadlocal _Thread_local49#define zig_threadlocal _Thread_local
42#elif defined(__GNUC__)50#elif defined(__GNUC__)
...@@ -180,16 +188,38 @@ typedef char bool;...@@ -180,16 +188,38 @@ typedef char bool;
180#define zig_export(sig, symbol, name) __asm(name " = " symbol)188#define zig_export(sig, symbol, name) __asm(name " = " symbol)
181#endif189#endif
182190
191#if zig_has_builtin(trap)
192#define zig_trap() __builtin_trap()
193#elif _MSC_VER && (_M_IX86 || _M_X64)
194#define zig_trap() __ud2()
195#elif _MSC_VER
196#define zig_trap() __fastfail(0)
197#elif defined(__i386__) || defined(__x86_64__)
198#define zig_trap() __asm__ volatile("ud2");
199#elif defined(__arm__) || defined(__aarch64__)
200#define zig_breakpoint() __asm__ volatile("udf #0");
201#else
202#include <stdlib.h>
203#define zig_trap() abort()
204#endif
205
183#if zig_has_builtin(debugtrap)206#if zig_has_builtin(debugtrap)
184#define zig_breakpoint() __builtin_debugtrap()207#define zig_breakpoint() __builtin_debugtrap()
185#elif zig_has_builtin(trap) || defined(zig_gnuc)
186#define zig_breakpoint() __builtin_trap()
187#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)208#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
188#define zig_breakpoint() __debugbreak()209#define zig_breakpoint() __debugbreak()
189#elif defined(__i386__) || defined(__x86_64__)210#elif defined(__i386__) || defined(__x86_64__)
190#define zig_breakpoint() __asm__ volatile("int $0x03");211#define zig_breakpoint() __asm__ volatile("int $0x03");
212#elif defined(__arm__)
213#define zig_breakpoint() __asm__ volatile("bkpt #0");
214#elif defined(__aarch64__)
215#define zig_breakpoint() __asm__ volatile("brk #0");
191#else216#else
217#include <signal.h>
218#if defined(SIGTRAP)
192#define zig_breakpoint() raise(SIGTRAP)219#define zig_breakpoint() raise(SIGTRAP)
220#else
221#define zig_breakpoint() zig_breakpoint_unavailable
222#endif
193#endif223#endif
194224
195#if zig_has_builtin(return_address) || defined(zig_gnuc)225#if zig_has_builtin(return_address) || defined(zig_gnuc)
...@@ -598,12 +628,6 @@ static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8...@@ -598,12 +628,6 @@ static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8
598#endif628#endif
599}629}
600630
601static inline void zig_vaddo_u32(uint8_t *ov, uint32_t *res, int n,
602 const uint32_t *lhs, const uint32_t *rhs, uint8_t bits)
603{
604 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits);
605}
606
607zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow);631zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow);
608static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {632static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
609#if zig_has_builtin(add_overflow) || defined(zig_gnuc)633#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
...@@ -618,12 +642,6 @@ static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t...@@ -618,12 +642,6 @@ static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t
618 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);642 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
619}643}
620644
621static inline void zig_vaddo_i32(uint8_t *ov, int32_t *res, int n,
622 const int32_t *lhs, const int32_t *rhs, uint8_t bits)
623{
624 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits);
625}
626
627static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {645static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
628#if zig_has_builtin(add_overflow) || defined(zig_gnuc)646#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
629 uint64_t full_res;647 uint64_t full_res;
...@@ -636,12 +654,6 @@ static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8...@@ -636,12 +654,6 @@ static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8
636#endif654#endif
637}655}
638656
639static inline void zig_vaddo_u64(uint8_t *ov, uint64_t *res, int n,
640 const uint64_t *lhs, const uint64_t *rhs, uint8_t bits)
641{
642 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits);
643}
644
645zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow);657zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow);
646static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {658static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
647#if zig_has_builtin(add_overflow) || defined(zig_gnuc)659#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
...@@ -656,12 +668,6 @@ static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t...@@ -656,12 +668,6 @@ static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t
656 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);668 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
657}669}
658670
659static inline void zig_vaddo_i64(uint8_t *ov, int64_t *res, int n,
660 const int64_t *lhs, const int64_t *rhs, uint8_t bits)
661{
662 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits);
663}
664
665static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {671static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
666#if zig_has_builtin(add_overflow) || defined(zig_gnuc)672#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
667 uint8_t full_res;673 uint8_t full_res;
...@@ -676,12 +682,6 @@ static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b...@@ -676,12 +682,6 @@ static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b
676#endif682#endif
677}683}
678684
679static inline void zig_vaddo_u8(uint8_t *ov, uint8_t *res, int n,
680 const uint8_t *lhs, const uint8_t *rhs, uint8_t bits)
681{
682 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits);
683}
684
685static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {685static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
686#if zig_has_builtin(add_overflow) || defined(zig_gnuc)686#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
687 int8_t full_res;687 int8_t full_res;
...@@ -696,12 +696,6 @@ static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits...@@ -696,12 +696,6 @@ static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits
696#endif696#endif
697}697}
698698
699static inline void zig_vaddo_i8(uint8_t *ov, int8_t *res, int n,
700 const int8_t *lhs, const int8_t *rhs, uint8_t bits)
701{
702 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits);
703}
704
705static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {699static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
706#if zig_has_builtin(add_overflow) || defined(zig_gnuc)700#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
707 uint16_t full_res;701 uint16_t full_res;
...@@ -716,12 +710,6 @@ static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8...@@ -716,12 +710,6 @@ static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8
716#endif710#endif
717}711}
718712
719static inline void zig_vaddo_u16(uint8_t *ov, uint16_t *res, int n,
720 const uint16_t *lhs, const uint16_t *rhs, uint8_t bits)
721{
722 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits);
723}
724
725static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {713static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
726#if zig_has_builtin(add_overflow) || defined(zig_gnuc)714#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
727 int16_t full_res;715 int16_t full_res;
...@@ -736,12 +724,6 @@ static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t...@@ -736,12 +724,6 @@ static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t
736#endif724#endif
737}725}
738726
739static inline void zig_vaddo_i16(uint8_t *ov, int16_t *res, int n,
740 const int16_t *lhs, const int16_t *rhs, uint8_t bits)
741{
742 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits);
743}
744
745static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {727static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
746#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)728#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
747 uint32_t full_res;729 uint32_t full_res;
...@@ -754,12 +736,6 @@ static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8...@@ -754,12 +736,6 @@ static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8
754#endif736#endif
755}737}
756738
757static inline void zig_vsubo_u32(uint8_t *ov, uint32_t *res, int n,
758 const uint32_t *lhs, const uint32_t *rhs, uint8_t bits)
759{
760 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits);
761}
762
763zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow);739zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow);
764static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {740static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
765#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)741#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
...@@ -774,12 +750,6 @@ static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t...@@ -774,12 +750,6 @@ static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t
774 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);750 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
775}751}
776752
777static inline void zig_vsubo_i32(uint8_t *ov, int32_t *res, int n,
778 const int32_t *lhs, const int32_t *rhs, uint8_t bits)
779{
780 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits);
781}
782
783static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {753static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
784#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)754#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
785 uint64_t full_res;755 uint64_t full_res;
...@@ -792,12 +762,6 @@ static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8...@@ -792,12 +762,6 @@ static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8
792#endif762#endif
793}763}
794764
795static inline void zig_vsubo_u64(uint8_t *ov, uint64_t *res, int n,
796 const uint64_t *lhs, const uint64_t *rhs, uint8_t bits)
797{
798 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits);
799}
800
801zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow);765zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow);
802static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {766static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
803#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)767#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
...@@ -812,12 +776,6 @@ static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t...@@ -812,12 +776,6 @@ static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t
812 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);776 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
813}777}
814778
815static inline void zig_vsubo_i64(uint8_t *ov, int64_t *res, int n,
816 const int64_t *lhs, const int64_t *rhs, uint8_t bits)
817{
818 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits);
819}
820
821static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {779static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
822#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)780#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
823 uint8_t full_res;781 uint8_t full_res;
...@@ -832,12 +790,6 @@ static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b...@@ -832,12 +790,6 @@ static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b
832#endif790#endif
833}791}
834792
835static inline void zig_vsubo_u8(uint8_t *ov, uint8_t *res, int n,
836 const uint8_t *lhs, const uint8_t *rhs, uint8_t bits)
837{
838 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits);
839}
840
841static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {793static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
842#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)794#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
843 int8_t full_res;795 int8_t full_res;
...@@ -852,13 +804,6 @@ static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits...@@ -852,13 +804,6 @@ static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits
852#endif804#endif
853}805}
854806
855static inline void zig_vsubo_i8(uint8_t *ov, int8_t *res, int n,
856 const int8_t *lhs, const int8_t *rhs, uint8_t bits)
857{
858 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits);
859}
860
861
862static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {807static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
863#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)808#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
864 uint16_t full_res;809 uint16_t full_res;
...@@ -873,13 +818,6 @@ static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8...@@ -873,13 +818,6 @@ static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8
873#endif818#endif
874}819}
875820
876static inline void zig_vsubo_u16(uint8_t *ov, uint16_t *res, int n,
877 const uint16_t *lhs, const uint16_t *rhs, uint8_t bits)
878{
879 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits);
880}
881
882
883static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {821static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
884#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)822#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
885 int16_t full_res;823 int16_t full_res;
...@@ -894,12 +832,6 @@ static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t...@@ -894,12 +832,6 @@ static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t
894#endif832#endif
895}833}
896834
897static inline void zig_vsubo_i16(uint8_t *ov, int16_t *res, int n,
898 const int16_t *lhs, const int16_t *rhs, uint8_t bits)
899{
900 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits);
901}
902
903static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {835static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
904#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)836#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
905 uint32_t full_res;837 uint32_t full_res;
...@@ -912,12 +844,6 @@ static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8...@@ -912,12 +844,6 @@ static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8
912#endif844#endif
913}845}
914846
915static inline void zig_vmulo_u32(uint8_t *ov, uint32_t *res, int n,
916 const uint32_t *lhs, const uint32_t *rhs, uint8_t bits)
917{
918 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits);
919}
920
921zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow);847zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow);
922static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {848static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
923#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)849#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
...@@ -932,12 +858,6 @@ static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t...@@ -932,12 +858,6 @@ static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t
932 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);858 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
933}859}
934860
935static inline void zig_vmulo_i32(uint8_t *ov, int32_t *res, int n,
936 const int32_t *lhs, const int32_t *rhs, uint8_t bits)
937{
938 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits);
939}
940
941static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {861static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
942#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)862#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
943 uint64_t full_res;863 uint64_t full_res;
...@@ -950,12 +870,6 @@ static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8...@@ -950,12 +870,6 @@ static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8
950#endif870#endif
951}871}
952872
953static inline void zig_vmulo_u64(uint8_t *ov, uint64_t *res, int n,
954 const uint64_t *lhs, const uint64_t *rhs, uint8_t bits)
955{
956 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits);
957}
958
959zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow);873zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow);
960static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {874static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
961#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)875#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
...@@ -970,12 +884,6 @@ static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t...@@ -970,12 +884,6 @@ static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t
970 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);884 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
971}885}
972886
973static inline void zig_vmulo_i64(uint8_t *ov, int64_t *res, int n,
974 const int64_t *lhs, const int64_t *rhs, uint8_t bits)
975{
976 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits);
977}
978
979static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {887static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
980#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)888#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
981 uint8_t full_res;889 uint8_t full_res;
...@@ -990,12 +898,6 @@ static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b...@@ -990,12 +898,6 @@ static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b
990#endif898#endif
991}899}
992900
993static inline void zig_vmulo_u8(uint8_t *ov, uint8_t *res, int n,
994 const uint8_t *lhs, const uint8_t *rhs, uint8_t bits)
995{
996 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits);
997}
998
999static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {901static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
1000#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)902#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
1001 int8_t full_res;903 int8_t full_res;
...@@ -1010,12 +912,6 @@ static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits...@@ -1010,12 +912,6 @@ static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits
1010#endif912#endif
1011}913}
1012914
1013static inline void zig_vmulo_i8(uint8_t *ov, int8_t *res, int n,
1014 const int8_t *lhs, const int8_t *rhs, uint8_t bits)
1015{
1016 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits);
1017}
1018
1019static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {915static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
1020#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)916#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
1021 uint16_t full_res;917 uint16_t full_res;
...@@ -1030,12 +926,6 @@ static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8...@@ -1030,12 +926,6 @@ static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8
1030#endif926#endif
1031}927}
1032928
1033static inline void zig_vmulo_u16(uint8_t *ov, uint16_t *res, int n,
1034 const uint16_t *lhs, const uint16_t *rhs, uint8_t bits)
1035{
1036 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits);
1037}
1038
1039static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {929static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
1040#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)930#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
1041 int16_t full_res;931 int16_t full_res;
...@@ -1050,12 +940,6 @@ static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t...@@ -1050,12 +940,6 @@ static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t
1050#endif940#endif
1051}941}
1052942
1053static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n,
1054 const int16_t *lhs, const int16_t *rhs, uint8_t bits)
1055{
1056 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits);
1057}
1058
1059#define zig_int_builtins(w) \943#define zig_int_builtins(w) \
1060 static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \944 static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \
1061 *res = zig_shlw_u##w(lhs, rhs, bits); \945 *res = zig_shlw_u##w(lhs, rhs, bits); \
...@@ -1354,8 +1238,8 @@ typedef signed __int128 zig_i128;...@@ -1354,8 +1238,8 @@ typedef signed __int128 zig_i128;
13541238
1355#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))1239#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
1356#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo))1240#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo))
1357#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)1241#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1358#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)1242#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
1359#define zig_hi_u128(val) ((uint64_t)((val) >> 64))1243#define zig_hi_u128(val) ((uint64_t)((val) >> 64))
1360#define zig_lo_u128(val) ((uint64_t)((val) >> 0))1244#define zig_lo_u128(val) ((uint64_t)((val) >> 0))
1361#define zig_hi_i128(val) (( int64_t)((val) >> 64))1245#define zig_hi_i128(val) (( int64_t)((val) >> 64))
...@@ -1373,7 +1257,7 @@ typedef signed __int128 zig_i128;...@@ -1373,7 +1257,7 @@ typedef signed __int128 zig_i128;
13731257
1374#else /* zig_has_int128 */1258#else /* zig_has_int128 */
13751259
1376#if __LITTLE_ENDIAN__ || _MSC_VER1260#if zig_little_endian
1377typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;1261typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
1378typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;1262typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
1379#else1263#else
...@@ -1385,11 +1269,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;...@@ -1385,11 +1269,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
1385#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })1269#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })
13861270
1387#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */1271#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */
1388#define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }1272#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1389#define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }1273#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1390#else /* But non-MSVC doesn't like the unprotected commas */1274#else /* But non-MSVC doesn't like the unprotected commas */
1391#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)1275#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1392#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)1276#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
1393#endif1277#endif
1394#define zig_hi_u128(val) ((val).hi)1278#define zig_hi_u128(val) ((val).hi)
1395#define zig_lo_u128(val) ((val).lo)1279#define zig_lo_u128(val) ((val).lo)
...@@ -1632,7 +1516,9 @@ static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) {...@@ -1632,7 +1516,9 @@ static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) {
1632}1516}
16331517
1634static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) {1518static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) {
1635 return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val));1519 if (bits > UINT8_C(64)) return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val));
1520 int64_t lo = zig_wrap_i64((int64_t)zig_lo_i128(val), bits);
1521 return zig_make_i128(zig_shr_i64(lo, 63), (uint64_t)lo);
1636}1522}
16371523
1638static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) {1524static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) {
...@@ -1903,6 +1789,1010 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {...@@ -1903,6 +1789,1010 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
1903 return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));1789 return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));
1904}1790}
19051791
1792/* ========================== Big Integer Support =========================== */
1793
1794static inline uint16_t zig_int_bytes(uint16_t bits) {
1795 uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT;
1796 uint16_t alignment = ZIG_TARGET_MAX_INT_ALIGNMENT;
1797 while (alignment / 2 >= bytes) alignment /= 2;
1798 return (bytes + alignment - 1) / alignment * alignment;
1799}
1800
1801static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
1802 const uint8_t *lhs_bytes = lhs;
1803 const uint8_t *rhs_bytes = rhs;
1804 uint16_t byte_offset = 0;
1805 bool do_signed = is_signed;
1806 uint16_t remaining_bytes = zig_int_bytes(bits);
1807
1808#if zig_little_endian
1809 byte_offset = remaining_bytes;
1810#endif
1811
1812 while (remaining_bytes >= 128 / CHAR_BIT) {
1813 int32_t limb_cmp;
1814
1815#if zig_little_endian
1816 byte_offset -= 128 / CHAR_BIT;
1817#endif
1818
1819 if (do_signed) {
1820 zig_i128 lhs_limb;
1821 zig_i128 rhs_limb;
1822
1823 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1824 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1825 limb_cmp = zig_cmp_i128(lhs_limb, rhs_limb);
1826 do_signed = false;
1827 } else {
1828 zig_u128 lhs_limb;
1829 zig_u128 rhs_limb;
1830
1831 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1832 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1833 limb_cmp = zig_cmp_u128(lhs_limb, rhs_limb);
1834 }
1835
1836 if (limb_cmp != 0) return limb_cmp;
1837 remaining_bytes -= 128 / CHAR_BIT;
1838
1839#if zig_big_endian
1840 byte_offset += 128 / CHAR_BIT;
1841#endif
1842 }
1843
1844 while (remaining_bytes >= 64 / CHAR_BIT) {
1845#if zig_little_endian
1846 byte_offset -= 64 / CHAR_BIT;
1847#endif
1848
1849 if (do_signed) {
1850 int64_t lhs_limb;
1851 int64_t rhs_limb;
1852
1853 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1854 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1855 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1856 do_signed = false;
1857 } else {
1858 uint64_t lhs_limb;
1859 uint64_t rhs_limb;
1860
1861 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1862 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1863 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1864 }
1865
1866 remaining_bytes -= 64 / CHAR_BIT;
1867
1868#if zig_big_endian
1869 byte_offset += 64 / CHAR_BIT;
1870#endif
1871 }
1872
1873 while (remaining_bytes >= 32 / CHAR_BIT) {
1874#if zig_little_endian
1875 byte_offset -= 32 / CHAR_BIT;
1876#endif
1877
1878 if (do_signed) {
1879 int32_t lhs_limb;
1880 int32_t rhs_limb;
1881
1882 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1883 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1884 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1885 do_signed = false;
1886 } else {
1887 uint32_t lhs_limb;
1888 uint32_t rhs_limb;
1889
1890 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1891 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1892 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1893 }
1894
1895 remaining_bytes -= 32 / CHAR_BIT;
1896
1897#if zig_big_endian
1898 byte_offset += 32 / CHAR_BIT;
1899#endif
1900 }
1901
1902 while (remaining_bytes >= 16 / CHAR_BIT) {
1903#if zig_little_endian
1904 byte_offset -= 16 / CHAR_BIT;
1905#endif
1906
1907 if (do_signed) {
1908 int16_t lhs_limb;
1909 int16_t rhs_limb;
1910
1911 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1912 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1913 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1914 do_signed = false;
1915 } else {
1916 uint16_t lhs_limb;
1917 uint16_t rhs_limb;
1918
1919 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1920 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1921 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1922 }
1923
1924 remaining_bytes -= 16 / CHAR_BIT;
1925
1926#if zig_big_endian
1927 byte_offset += 16 / CHAR_BIT;
1928#endif
1929 }
1930
1931 while (remaining_bytes >= 8 / CHAR_BIT) {
1932#if zig_little_endian
1933 byte_offset -= 8 / CHAR_BIT;
1934#endif
1935
1936 if (do_signed) {
1937 int8_t lhs_limb;
1938 int8_t rhs_limb;
1939
1940 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1941 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1942 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1943 do_signed = false;
1944 } else {
1945 uint8_t lhs_limb;
1946 uint8_t rhs_limb;
1947
1948 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1949 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1950 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1951 }
1952
1953 remaining_bytes -= 8 / CHAR_BIT;
1954
1955#if zig_big_endian
1956 byte_offset += 8 / CHAR_BIT;
1957#endif
1958 }
1959
1960 return 0;
1961}
1962
1963static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
1964 uint8_t *res_bytes = res;
1965 const uint8_t *lhs_bytes = lhs;
1966 const uint8_t *rhs_bytes = rhs;
1967 uint16_t byte_offset = 0;
1968 uint16_t remaining_bytes = zig_int_bytes(bits);
1969 uint16_t top_bits = remaining_bytes * 8 - bits;
1970 bool overflow = false;
1971
1972#if zig_big_endian
1973 byte_offset = remaining_bytes;
1974#endif
1975
1976 while (remaining_bytes >= 128 / CHAR_BIT) {
1977 uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0);
1978
1979#if zig_big_endian
1980 byte_offset -= 128 / CHAR_BIT;
1981#endif
1982
1983 if (remaining_bytes == 128 / CHAR_BIT && is_signed) {
1984 zig_i128 res_limb;
1985 zig_i128 tmp_limb;
1986 zig_i128 lhs_limb;
1987 zig_i128 rhs_limb;
1988 bool limb_overflow;
1989
1990 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1991 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1992 limb_overflow = zig_addo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
1993 overflow = limb_overflow ^ zig_addo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
1994 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
1995 } else {
1996 zig_u128 res_limb;
1997 zig_u128 tmp_limb;
1998 zig_u128 lhs_limb;
1999 zig_u128 rhs_limb;
2000 bool limb_overflow;
2001
2002 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2003 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2004 limb_overflow = zig_addo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2005 overflow = limb_overflow ^ zig_addo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
2006 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2007 }
2008
2009 remaining_bytes -= 128 / CHAR_BIT;
2010
2011#if zig_little_endian
2012 byte_offset += 128 / CHAR_BIT;
2013#endif
2014 }
2015
2016 while (remaining_bytes >= 64 / CHAR_BIT) {
2017 uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0);
2018
2019#if zig_big_endian
2020 byte_offset -= 64 / CHAR_BIT;
2021#endif
2022
2023 if (remaining_bytes == 64 / CHAR_BIT && is_signed) {
2024 int64_t res_limb;
2025 int64_t tmp_limb;
2026 int64_t lhs_limb;
2027 int64_t rhs_limb;
2028 bool limb_overflow;
2029
2030 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2031 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2032 limb_overflow = zig_addo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2033 overflow = limb_overflow ^ zig_addo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits);
2034 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2035 } else {
2036 uint64_t res_limb;
2037 uint64_t tmp_limb;
2038 uint64_t lhs_limb;
2039 uint64_t rhs_limb;
2040 bool limb_overflow;
2041
2042 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2043 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2044 limb_overflow = zig_addo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2045 overflow = limb_overflow ^ zig_addo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits);
2046 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2047 }
2048
2049 remaining_bytes -= 64 / CHAR_BIT;
2050
2051#if zig_little_endian
2052 byte_offset += 64 / CHAR_BIT;
2053#endif
2054 }
2055
2056 while (remaining_bytes >= 32 / CHAR_BIT) {
2057 uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0);
2058
2059#if zig_big_endian
2060 byte_offset -= 32 / CHAR_BIT;
2061#endif
2062
2063 if (remaining_bytes == 32 / CHAR_BIT && is_signed) {
2064 int32_t res_limb;
2065 int32_t tmp_limb;
2066 int32_t lhs_limb;
2067 int32_t rhs_limb;
2068 bool limb_overflow;
2069
2070 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2071 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2072 limb_overflow = zig_addo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2073 overflow = limb_overflow ^ zig_addo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits);
2074 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2075 } else {
2076 uint32_t res_limb;
2077 uint32_t tmp_limb;
2078 uint32_t lhs_limb;
2079 uint32_t rhs_limb;
2080 bool limb_overflow;
2081
2082 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2083 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2084 limb_overflow = zig_addo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2085 overflow = limb_overflow ^ zig_addo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits);
2086 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2087 }
2088
2089 remaining_bytes -= 32 / CHAR_BIT;
2090
2091#if zig_little_endian
2092 byte_offset += 32 / CHAR_BIT;
2093#endif
2094 }
2095
2096 while (remaining_bytes >= 16 / CHAR_BIT) {
2097 uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0);
2098
2099#if zig_big_endian
2100 byte_offset -= 16 / CHAR_BIT;
2101#endif
2102
2103 if (remaining_bytes == 16 / CHAR_BIT && is_signed) {
2104 int16_t res_limb;
2105 int16_t tmp_limb;
2106 int16_t lhs_limb;
2107 int16_t rhs_limb;
2108 bool limb_overflow;
2109
2110 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2111 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2112 limb_overflow = zig_addo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2113 overflow = limb_overflow ^ zig_addo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits);
2114 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2115 } else {
2116 uint16_t res_limb;
2117 uint16_t tmp_limb;
2118 uint16_t lhs_limb;
2119 uint16_t rhs_limb;
2120 bool limb_overflow;
2121
2122 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2123 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2124 limb_overflow = zig_addo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2125 overflow = limb_overflow ^ zig_addo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits);
2126 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2127 }
2128
2129 remaining_bytes -= 16 / CHAR_BIT;
2130
2131#if zig_little_endian
2132 byte_offset += 16 / CHAR_BIT;
2133#endif
2134 }
2135
2136 while (remaining_bytes >= 8 / CHAR_BIT) {
2137 uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0);
2138
2139#if zig_big_endian
2140 byte_offset -= 8 / CHAR_BIT;
2141#endif
2142
2143 if (remaining_bytes == 8 / CHAR_BIT && is_signed) {
2144 int8_t res_limb;
2145 int8_t tmp_limb;
2146 int8_t lhs_limb;
2147 int8_t rhs_limb;
2148 bool limb_overflow;
2149
2150 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2151 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2152 limb_overflow = zig_addo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2153 overflow = limb_overflow ^ zig_addo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits);
2154 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2155 } else {
2156 uint8_t res_limb;
2157 uint8_t tmp_limb;
2158 uint8_t lhs_limb;
2159 uint8_t rhs_limb;
2160 bool limb_overflow;
2161
2162 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2163 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2164 limb_overflow = zig_addo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2165 overflow = limb_overflow ^ zig_addo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits);
2166 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2167 }
2168
2169 remaining_bytes -= 8 / CHAR_BIT;
2170
2171#if zig_little_endian
2172 byte_offset += 8 / CHAR_BIT;
2173#endif
2174 }
2175
2176 return overflow;
2177}
2178
2179static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2180 uint8_t *res_bytes = res;
2181 const uint8_t *lhs_bytes = lhs;
2182 const uint8_t *rhs_bytes = rhs;
2183 uint16_t byte_offset = 0;
2184 uint16_t remaining_bytes = zig_int_bytes(bits);
2185 uint16_t top_bits = remaining_bytes * 8 - bits;
2186 bool overflow = false;
2187
2188#if zig_big_endian
2189 byte_offset = remaining_bytes;
2190#endif
2191
2192 while (remaining_bytes >= 128 / CHAR_BIT) {
2193 uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0);
2194
2195#if zig_big_endian
2196 byte_offset -= 128 / CHAR_BIT;
2197#endif
2198
2199 if (remaining_bytes == 128 / CHAR_BIT && is_signed) {
2200 zig_i128 res_limb;
2201 zig_i128 tmp_limb;
2202 zig_i128 lhs_limb;
2203 zig_i128 rhs_limb;
2204 bool limb_overflow;
2205
2206 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2207 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2208 limb_overflow = zig_subo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2209 overflow = limb_overflow ^ zig_subo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
2210 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2211 } else {
2212 zig_u128 res_limb;
2213 zig_u128 tmp_limb;
2214 zig_u128 lhs_limb;
2215 zig_u128 rhs_limb;
2216 bool limb_overflow;
2217
2218 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2219 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2220 limb_overflow = zig_subo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2221 overflow = limb_overflow ^ zig_subo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
2222 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2223 }
2224
2225 remaining_bytes -= 128 / CHAR_BIT;
2226
2227#if zig_little_endian
2228 byte_offset += 128 / CHAR_BIT;
2229#endif
2230 }
2231
2232 while (remaining_bytes >= 64 / CHAR_BIT) {
2233 uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0);
2234
2235#if zig_big_endian
2236 byte_offset -= 64 / CHAR_BIT;
2237#endif
2238
2239 if (remaining_bytes == 64 / CHAR_BIT && is_signed) {
2240 int64_t res_limb;
2241 int64_t tmp_limb;
2242 int64_t lhs_limb;
2243 int64_t rhs_limb;
2244 bool limb_overflow;
2245
2246 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2247 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2248 limb_overflow = zig_subo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2249 overflow = limb_overflow ^ zig_subo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits);
2250 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2251 } else {
2252 uint64_t res_limb;
2253 uint64_t tmp_limb;
2254 uint64_t lhs_limb;
2255 uint64_t rhs_limb;
2256 bool limb_overflow;
2257
2258 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2259 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2260 limb_overflow = zig_subo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2261 overflow = limb_overflow ^ zig_subo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits);
2262 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2263 }
2264
2265 remaining_bytes -= 64 / CHAR_BIT;
2266
2267#if zig_little_endian
2268 byte_offset += 64 / CHAR_BIT;
2269#endif
2270 }
2271
2272 while (remaining_bytes >= 32 / CHAR_BIT) {
2273 uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0);
2274
2275#if zig_big_endian
2276 byte_offset -= 32 / CHAR_BIT;
2277#endif
2278
2279 if (remaining_bytes == 32 / CHAR_BIT && is_signed) {
2280 int32_t res_limb;
2281 int32_t tmp_limb;
2282 int32_t lhs_limb;
2283 int32_t rhs_limb;
2284 bool limb_overflow;
2285
2286 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2287 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2288 limb_overflow = zig_subo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2289 overflow = limb_overflow ^ zig_subo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits);
2290 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2291 } else {
2292 uint32_t res_limb;
2293 uint32_t tmp_limb;
2294 uint32_t lhs_limb;
2295 uint32_t rhs_limb;
2296 bool limb_overflow;
2297
2298 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2299 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2300 limb_overflow = zig_subo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2301 overflow = limb_overflow ^ zig_subo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits);
2302 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2303 }
2304
2305 remaining_bytes -= 32 / CHAR_BIT;
2306
2307#if zig_little_endian
2308 byte_offset += 32 / CHAR_BIT;
2309#endif
2310 }
2311
2312 while (remaining_bytes >= 16 / CHAR_BIT) {
2313 uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0);
2314
2315#if zig_big_endian
2316 byte_offset -= 16 / CHAR_BIT;
2317#endif
2318
2319 if (remaining_bytes == 16 / CHAR_BIT && is_signed) {
2320 int16_t res_limb;
2321 int16_t tmp_limb;
2322 int16_t lhs_limb;
2323 int16_t rhs_limb;
2324 bool limb_overflow;
2325
2326 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2327 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2328 limb_overflow = zig_subo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2329 overflow = limb_overflow ^ zig_subo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits);
2330 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2331 } else {
2332 uint16_t res_limb;
2333 uint16_t tmp_limb;
2334 uint16_t lhs_limb;
2335 uint16_t rhs_limb;
2336 bool limb_overflow;
2337
2338 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2339 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2340 limb_overflow = zig_subo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2341 overflow = limb_overflow ^ zig_subo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits);
2342 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2343 }
2344
2345 remaining_bytes -= 16 / CHAR_BIT;
2346
2347#if zig_little_endian
2348 byte_offset += 16 / CHAR_BIT;
2349#endif
2350 }
2351
2352 while (remaining_bytes >= 8 / CHAR_BIT) {
2353 uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0);
2354
2355#if zig_big_endian
2356 byte_offset -= 8 / CHAR_BIT;
2357#endif
2358
2359 if (remaining_bytes == 8 / CHAR_BIT && is_signed) {
2360 int8_t res_limb;
2361 int8_t tmp_limb;
2362 int8_t lhs_limb;
2363 int8_t rhs_limb;
2364 bool limb_overflow;
2365
2366 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2367 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2368 limb_overflow = zig_subo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2369 overflow = limb_overflow ^ zig_subo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits);
2370 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2371 } else {
2372 uint8_t res_limb;
2373 uint8_t tmp_limb;
2374 uint8_t lhs_limb;
2375 uint8_t rhs_limb;
2376 bool limb_overflow;
2377
2378 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2379 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2380 limb_overflow = zig_subo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2381 overflow = limb_overflow ^ zig_subo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits);
2382 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2383 }
2384
2385 remaining_bytes -= 8 / CHAR_BIT;
2386
2387#if zig_little_endian
2388 byte_offset += 8 / CHAR_BIT;
2389#endif
2390 }
2391
2392 return overflow;
2393}
2394
2395static inline void zig_addw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2396 (void)zig_addo_big(res, lhs, rhs, is_signed, bits);
2397}
2398
2399static inline void zig_subw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2400 (void)zig_subo_big(res, lhs, rhs, is_signed, bits);
2401}
2402
2403zig_extern void __udivei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
2404static inline void zig_div_trunc_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2405 if (!is_signed) {
2406 __udivei4(res, lhs, rhs, bits);
2407 return;
2408 }
2409
2410 zig_trap();
2411}
2412
2413static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2414 if (!is_signed) {
2415 zig_div_trunc_big(res, lhs, rhs, is_signed, bits);
2416 return;
2417 }
2418
2419 zig_trap();
2420}
2421
2422zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
2423static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2424 if (!is_signed) {
2425 __umodei4(res, lhs, rhs, bits);
2426 return;
2427 }
2428
2429 zig_trap();
2430}
2431
2432static inline void zig_mod_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2433 if (!is_signed) {
2434 zig_rem_big(res, lhs, rhs, is_signed, bits);
2435 return;
2436 }
2437
2438 zig_trap();
2439}
2440
2441static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) {
2442 const uint8_t *val_bytes = val;
2443 uint16_t byte_offset = 0;
2444 uint16_t remaining_bytes = zig_int_bytes(bits);
2445 uint16_t skip_bits = remaining_bytes * 8 - bits;
2446 uint16_t total_lz = 0;
2447 uint16_t limb_lz;
2448 (void)is_signed;
2449
2450#if zig_little_endian
2451 byte_offset = remaining_bytes;
2452#endif
2453
2454 while (remaining_bytes >= 128 / CHAR_BIT) {
2455#if zig_little_endian
2456 byte_offset -= 128 / CHAR_BIT;
2457#endif
2458
2459 {
2460 zig_u128 val_limb;
2461
2462 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2463 limb_lz = zig_clz_u128(val_limb, 128 - skip_bits);
2464 }
2465
2466 total_lz += limb_lz;
2467 if (limb_lz < 128 - skip_bits) return total_lz;
2468 skip_bits = 0;
2469 remaining_bytes -= 128 / CHAR_BIT;
2470
2471#if zig_big_endian
2472 byte_offset += 128 / CHAR_BIT;
2473#endif
2474 }
2475
2476 while (remaining_bytes >= 64 / CHAR_BIT) {
2477#if zig_little_endian
2478 byte_offset -= 64 / CHAR_BIT;
2479#endif
2480
2481 {
2482 uint64_t val_limb;
2483
2484 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2485 limb_lz = zig_clz_u64(val_limb, 64 - skip_bits);
2486 }
2487
2488 total_lz += limb_lz;
2489 if (limb_lz < 64 - skip_bits) return total_lz;
2490 skip_bits = 0;
2491 remaining_bytes -= 64 / CHAR_BIT;
2492
2493#if zig_big_endian
2494 byte_offset += 64 / CHAR_BIT;
2495#endif
2496 }
2497
2498 while (remaining_bytes >= 32 / CHAR_BIT) {
2499#if zig_little_endian
2500 byte_offset -= 32 / CHAR_BIT;
2501#endif
2502
2503 {
2504 uint32_t val_limb;
2505
2506 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2507 limb_lz = zig_clz_u32(val_limb, 32 - skip_bits);
2508 }
2509
2510 total_lz += limb_lz;
2511 if (limb_lz < 32 - skip_bits) return total_lz;
2512 skip_bits = 0;
2513 remaining_bytes -= 32 / CHAR_BIT;
2514
2515#if zig_big_endian
2516 byte_offset += 32 / CHAR_BIT;
2517#endif
2518 }
2519
2520 while (remaining_bytes >= 16 / CHAR_BIT) {
2521#if zig_little_endian
2522 byte_offset -= 16 / CHAR_BIT;
2523#endif
2524
2525 {
2526 uint16_t val_limb;
2527
2528 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2529 limb_lz = zig_clz_u16(val_limb, 16 - skip_bits);
2530 }
2531
2532 total_lz += limb_lz;
2533 if (limb_lz < 16 - skip_bits) return total_lz;
2534 skip_bits = 0;
2535 remaining_bytes -= 16 / CHAR_BIT;
2536
2537#if zig_big_endian
2538 byte_offset += 16 / CHAR_BIT;
2539#endif
2540 }
2541
2542 while (remaining_bytes >= 8 / CHAR_BIT) {
2543#if zig_little_endian
2544 byte_offset -= 8 / CHAR_BIT;
2545#endif
2546
2547 {
2548 uint8_t val_limb;
2549
2550 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2551 limb_lz = zig_clz_u8(val_limb, 8 - skip_bits);
2552 }
2553
2554 total_lz += limb_lz;
2555 if (limb_lz < 8 - skip_bits) return total_lz;
2556 skip_bits = 0;
2557 remaining_bytes -= 8 / CHAR_BIT;
2558
2559#if zig_big_endian
2560 byte_offset += 8 / CHAR_BIT;
2561#endif
2562 }
2563
2564 return total_lz;
2565}
2566
2567static inline uint16_t zig_ctz_big(const void *val, bool is_signed, uint16_t bits) {
2568 const uint8_t *val_bytes = val;
2569 uint16_t byte_offset = 0;
2570 uint16_t remaining_bytes = zig_int_bytes(bits);
2571 uint16_t total_tz = 0;
2572 uint16_t limb_tz;
2573 (void)is_signed;
2574
2575#if zig_big_endian
2576 byte_offset = remaining_bytes;
2577#endif
2578
2579 while (remaining_bytes >= 128 / CHAR_BIT) {
2580#if zig_big_endian
2581 byte_offset -= 128 / CHAR_BIT;
2582#endif
2583
2584 {
2585 zig_u128 val_limb;
2586
2587 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2588 limb_tz = zig_ctz_u128(val_limb, 128);
2589 }
2590
2591 total_tz += limb_tz;
2592 if (limb_tz < 128) return total_tz;
2593 remaining_bytes -= 128 / CHAR_BIT;
2594
2595#if zig_little_endian
2596 byte_offset += 128 / CHAR_BIT;
2597#endif
2598 }
2599
2600 while (remaining_bytes >= 64 / CHAR_BIT) {
2601#if zig_big_endian
2602 byte_offset -= 64 / CHAR_BIT;
2603#endif
2604
2605 {
2606 uint64_t val_limb;
2607
2608 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2609 limb_tz = zig_ctz_u64(val_limb, 64);
2610 }
2611
2612 total_tz += limb_tz;
2613 if (limb_tz < 64) return total_tz;
2614 remaining_bytes -= 64 / CHAR_BIT;
2615
2616#if zig_little_endian
2617 byte_offset += 64 / CHAR_BIT;
2618#endif
2619 }
2620
2621 while (remaining_bytes >= 32 / CHAR_BIT) {
2622#if zig_big_endian
2623 byte_offset -= 32 / CHAR_BIT;
2624#endif
2625
2626 {
2627 uint32_t val_limb;
2628
2629 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2630 limb_tz = zig_ctz_u32(val_limb, 32);
2631 }
2632
2633 total_tz += limb_tz;
2634 if (limb_tz < 32) return total_tz;
2635 remaining_bytes -= 32 / CHAR_BIT;
2636
2637#if zig_little_endian
2638 byte_offset += 32 / CHAR_BIT;
2639#endif
2640 }
2641
2642 while (remaining_bytes >= 16 / CHAR_BIT) {
2643#if zig_big_endian
2644 byte_offset -= 16 / CHAR_BIT;
2645#endif
2646
2647 {
2648 uint16_t val_limb;
2649
2650 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2651 limb_tz = zig_ctz_u16(val_limb, 16);
2652 }
2653
2654 total_tz += limb_tz;
2655 if (limb_tz < 16) return total_tz;
2656 remaining_bytes -= 16 / CHAR_BIT;
2657
2658#if zig_little_endian
2659 byte_offset += 16 / CHAR_BIT;
2660#endif
2661 }
2662
2663 while (remaining_bytes >= 8 / CHAR_BIT) {
2664#if zig_big_endian
2665 byte_offset -= 8 / CHAR_BIT;
2666#endif
2667
2668 {
2669 uint8_t val_limb;
2670
2671 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2672 limb_tz = zig_ctz_u8(val_limb, 8);
2673 }
2674
2675 total_tz += limb_tz;
2676 if (limb_tz < 8) return total_tz;
2677 remaining_bytes -= 8 / CHAR_BIT;
2678
2679#if zig_little_endian
2680 byte_offset += 8 / CHAR_BIT;
2681#endif
2682 }
2683
2684 return total_tz;
2685}
2686
2687static inline uint16_t zig_popcount_big(const void *val, bool is_signed, uint16_t bits) {
2688 const uint8_t *val_bytes = val;
2689 uint16_t byte_offset = 0;
2690 uint16_t remaining_bytes = zig_int_bytes(bits);
2691 uint16_t total_pc = 0;
2692 (void)is_signed;
2693
2694#if zig_big_endian
2695 byte_offset = remaining_bytes;
2696#endif
2697
2698 while (remaining_bytes >= 128 / CHAR_BIT) {
2699#if zig_big_endian
2700 byte_offset -= 128 / CHAR_BIT;
2701#endif
2702
2703 {
2704 zig_u128 val_limb;
2705
2706 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2707 total_pc += zig_popcount_u128(val_limb, 128);
2708 }
2709
2710 remaining_bytes -= 128 / CHAR_BIT;
2711
2712#if zig_little_endian
2713 byte_offset += 128 / CHAR_BIT;
2714#endif
2715 }
2716
2717 while (remaining_bytes >= 64 / CHAR_BIT) {
2718#if zig_big_endian
2719 byte_offset -= 64 / CHAR_BIT;
2720#endif
2721
2722 {
2723 uint64_t val_limb;
2724
2725 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2726 total_pc += zig_popcount_u64(val_limb, 64);
2727 }
2728
2729 remaining_bytes -= 64 / CHAR_BIT;
2730
2731#if zig_little_endian
2732 byte_offset += 64 / CHAR_BIT;
2733#endif
2734 }
2735
2736 while (remaining_bytes >= 32 / CHAR_BIT) {
2737#if zig_big_endian
2738 byte_offset -= 32 / CHAR_BIT;
2739#endif
2740
2741 {
2742 uint32_t val_limb;
2743
2744 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2745 total_pc += zig_popcount_u32(val_limb, 32);
2746 }
2747
2748 remaining_bytes -= 32 / CHAR_BIT;
2749
2750#if zig_little_endian
2751 byte_offset += 32 / CHAR_BIT;
2752#endif
2753 }
2754
2755 while (remaining_bytes >= 16 / CHAR_BIT) {
2756#if zig_big_endian
2757 byte_offset -= 16 / CHAR_BIT;
2758#endif
2759
2760 {
2761 uint16_t val_limb;
2762
2763 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2764 total_pc = zig_popcount_u16(val_limb, 16);
2765 }
2766
2767 remaining_bytes -= 16 / CHAR_BIT;
2768
2769#if zig_little_endian
2770 byte_offset += 16 / CHAR_BIT;
2771#endif
2772 }
2773
2774 while (remaining_bytes >= 8 / CHAR_BIT) {
2775#if zig_big_endian
2776 byte_offset -= 8 / CHAR_BIT;
2777#endif
2778
2779 {
2780 uint8_t val_limb;
2781
2782 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2783 total_pc = zig_popcount_u8(val_limb, 8);
2784 }
2785
2786 remaining_bytes -= 8 / CHAR_BIT;
2787
2788#if zig_little_endian
2789 byte_offset += 8 / CHAR_BIT;
2790#endif
2791 }
2792
2793 return total_pc;
2794}
2795
1906/* ========================= Floating Point Support ========================= */2796/* ========================= Floating Point Support ========================= */
19072797
1908#if _MSC_VER2798#if _MSC_VER
...@@ -1927,7 +2817,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {...@@ -1927,7 +2817,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
1927#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg)2817#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg)
1928#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg)2818#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg)
1929#define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg)2819#define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg)
1930#define zig_make_special_c_longdouble(sign, name, arg, repr) sign zig_make_c_longdouble(__builtin_##name, )(arg)
1931#else2820#else
1932#define zig_has_float_builtins 02821#define zig_has_float_builtins 0
1933#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr)2822#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr)
...@@ -1935,13 +2824,13 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {...@@ -1935,13 +2824,13 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
1935#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)2824#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)
1936#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)2825#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)
1937#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr)2826#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr)
1938#define zig_make_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr)
1939#endif2827#endif
19402828
1941#define zig_has_f16 12829#define zig_has_f16 1
1942#define zig_bitSizeOf_f16 162830#define zig_bitSizeOf_f16 16
2831typedef int16_t zig_repr_f16;
1943#define zig_libc_name_f16(name) __##name##h2832#define zig_libc_name_f16(name) __##name##h
1944#define zig_make_special_constant_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr)2833#define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr)
1945#if FLT_MANT_DIG == 112834#if FLT_MANT_DIG == 11
1946typedef float zig_f16;2835typedef float zig_f16;
1947#define zig_make_f16(fp, repr) fp##f2836#define zig_make_f16(fp, repr) fp##f
...@@ -1950,7 +2839,9 @@ typedef double zig_f16;...@@ -1950,7 +2839,9 @@ typedef double zig_f16;
1950#define zig_make_f16(fp, repr) fp2839#define zig_make_f16(fp, repr) fp
1951#elif LDBL_MANT_DIG == 112840#elif LDBL_MANT_DIG == 11
1952#define zig_bitSizeOf_c_longdouble 162841#define zig_bitSizeOf_c_longdouble 16
1953typedef uint16_t zig_repr_c_longdouble;2842#ifndef ZIG_TARGET_ABI_MSVC
2843typedef zig_repr_f16 zig_repr_c_longdouble;
2844#endif
1954typedef long double zig_f16;2845typedef long double zig_f16;
1955#define zig_make_f16(fp, repr) fp##l2846#define zig_make_f16(fp, repr) fp##l
1956#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))2847#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))
...@@ -1967,17 +2858,18 @@ typedef int16_t zig_f16;...@@ -1967,17 +2858,18 @@ typedef int16_t zig_f16;
1967#define zig_make_f16(fp, repr) repr2858#define zig_make_f16(fp, repr) repr
1968#undef zig_make_special_f162859#undef zig_make_special_f16
1969#define zig_make_special_f16(sign, name, arg, repr) repr2860#define zig_make_special_f16(sign, name, arg, repr) repr
1970#undef zig_make_special_constant_f162861#undef zig_init_special_f16
1971#define zig_make_special_constant_f16(sign, name, arg, repr) repr2862#define zig_init_special_f16(sign, name, arg, repr) repr
1972#endif2863#endif
19732864
1974#define zig_has_f32 12865#define zig_has_f32 1
1975#define zig_bitSizeOf_f32 322866#define zig_bitSizeOf_f32 32
2867typedef int32_t zig_repr_f32;
1976#define zig_libc_name_f32(name) name##f2868#define zig_libc_name_f32(name) name##f
1977#if _MSC_VER2869#if _MSC_VER
1978#define zig_make_special_constant_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, )2870#define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, )
1979#else2871#else
1980#define zig_make_special_constant_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr)2872#define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr)
1981#endif2873#endif
1982#if FLT_MANT_DIG == 242874#if FLT_MANT_DIG == 24
1983typedef float zig_f32;2875typedef float zig_f32;
...@@ -1987,7 +2879,9 @@ typedef double zig_f32;...@@ -1987,7 +2879,9 @@ typedef double zig_f32;
1987#define zig_make_f32(fp, repr) fp2879#define zig_make_f32(fp, repr) fp
1988#elif LDBL_MANT_DIG == 242880#elif LDBL_MANT_DIG == 24
1989#define zig_bitSizeOf_c_longdouble 322881#define zig_bitSizeOf_c_longdouble 32
1990typedef uint32_t zig_repr_c_longdouble;2882#ifndef ZIG_TARGET_ABI_MSVC
2883typedef zig_repr_f32 zig_repr_c_longdouble;
2884#endif
1991typedef long double zig_f32;2885typedef long double zig_f32;
1992#define zig_make_f32(fp, repr) fp##l2886#define zig_make_f32(fp, repr) fp##l
1993#elif FLT32_MANT_DIG == 242887#elif FLT32_MANT_DIG == 24
...@@ -2001,21 +2895,24 @@ typedef int32_t zig_f32;...@@ -2001,21 +2895,24 @@ typedef int32_t zig_f32;
2001#define zig_make_f32(fp, repr) repr2895#define zig_make_f32(fp, repr) repr
2002#undef zig_make_special_f322896#undef zig_make_special_f32
2003#define zig_make_special_f32(sign, name, arg, repr) repr2897#define zig_make_special_f32(sign, name, arg, repr) repr
2004#undef zig_make_special_constant_f322898#undef zig_init_special_f32
2005#define zig_make_special_constant_f32(sign, name, arg, repr) repr2899#define zig_init_special_f32(sign, name, arg, repr) repr
2006#endif2900#endif
20072901
2008#define zig_has_f64 12902#define zig_has_f64 1
2009#define zig_bitSizeOf_f64 642903#define zig_bitSizeOf_f64 64
2904typedef int64_t zig_repr_f64;
2010#define zig_libc_name_f64(name) name2905#define zig_libc_name_f64(name) name
2011#if _MSC_VER2906#if _MSC_VER
2012#ifdef ZIG_TARGET_ABI_MSVC2907#ifdef ZIG_TARGET_ABI_MSVC
2013#define zig_bitSizeOf_c_longdouble 642908#define zig_bitSizeOf_c_longdouble 64
2014typedef uint64_t zig_repr_c_longdouble;2909#ifndef ZIG_TARGET_ABI_MSVC
2910typedef zig_repr_f64 zig_repr_c_longdouble;
2015#endif2911#endif
2016#define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, )2912#endif
2913#define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, )
2017#else /* _MSC_VER */2914#else /* _MSC_VER */
2018#define zig_make_special_constant_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr)2915#define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr)
2019#endif /* _MSC_VER */2916#endif /* _MSC_VER */
2020#if FLT_MANT_DIG == 532917#if FLT_MANT_DIG == 53
2021typedef float zig_f64;2918typedef float zig_f64;
...@@ -2025,7 +2922,9 @@ typedef double zig_f64;...@@ -2025,7 +2922,9 @@ typedef double zig_f64;
2025#define zig_make_f64(fp, repr) fp2922#define zig_make_f64(fp, repr) fp
2026#elif LDBL_MANT_DIG == 532923#elif LDBL_MANT_DIG == 53
2027#define zig_bitSizeOf_c_longdouble 642924#define zig_bitSizeOf_c_longdouble 64
2028typedef uint64_t zig_repr_c_longdouble;2925#ifndef ZIG_TARGET_ABI_MSVC
2926typedef zig_repr_f64 zig_repr_c_longdouble;
2927#endif
2029typedef long double zig_f64;2928typedef long double zig_f64;
2030#define zig_make_f64(fp, repr) fp##l2929#define zig_make_f64(fp, repr) fp##l
2031#elif FLT64_MANT_DIG == 532930#elif FLT64_MANT_DIG == 53
...@@ -2042,14 +2941,15 @@ typedef int64_t zig_f64;...@@ -2042,14 +2941,15 @@ typedef int64_t zig_f64;
2042#define zig_make_f64(fp, repr) repr2941#define zig_make_f64(fp, repr) repr
2043#undef zig_make_special_f642942#undef zig_make_special_f64
2044#define zig_make_special_f64(sign, name, arg, repr) repr2943#define zig_make_special_f64(sign, name, arg, repr) repr
2045#undef zig_make_special_constant_f642944#undef zig_init_special_f64
2046#define zig_make_special_constant_f64(sign, name, arg, repr) repr2945#define zig_init_special_f64(sign, name, arg, repr) repr
2047#endif2946#endif
20482947
2049#define zig_has_f80 12948#define zig_has_f80 1
2050#define zig_bitSizeOf_f80 802949#define zig_bitSizeOf_f80 80
2950typedef zig_i128 zig_repr_f80;
2051#define zig_libc_name_f80(name) __##name##x2951#define zig_libc_name_f80(name) __##name##x
2052#define zig_make_special_constant_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr)2952#define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr)
2053#if FLT_MANT_DIG == 642953#if FLT_MANT_DIG == 64
2054typedef float zig_f80;2954typedef float zig_f80;
2055#define zig_make_f80(fp, repr) fp##f2955#define zig_make_f80(fp, repr) fp##f
...@@ -2058,7 +2958,9 @@ typedef double zig_f80;...@@ -2058,7 +2958,9 @@ typedef double zig_f80;
2058#define zig_make_f80(fp, repr) fp2958#define zig_make_f80(fp, repr) fp
2059#elif LDBL_MANT_DIG == 642959#elif LDBL_MANT_DIG == 64
2060#define zig_bitSizeOf_c_longdouble 802960#define zig_bitSizeOf_c_longdouble 80
2061typedef zig_u128 zig_repr_c_longdouble;2961#ifndef ZIG_TARGET_ABI_MSVC
2962typedef zig_repr_f80 zig_repr_c_longdouble;
2963#endif
2062typedef long double zig_f80;2964typedef long double zig_f80;
2063#define zig_make_f80(fp, repr) fp##l2965#define zig_make_f80(fp, repr) fp##l
2064#elif FLT80_MANT_DIG == 642966#elif FLT80_MANT_DIG == 64
...@@ -2078,14 +2980,15 @@ typedef zig_i128 zig_f80;...@@ -2078,14 +2980,15 @@ typedef zig_i128 zig_f80;
2078#define zig_make_f80(fp, repr) repr2980#define zig_make_f80(fp, repr) repr
2079#undef zig_make_special_f802981#undef zig_make_special_f80
2080#define zig_make_special_f80(sign, name, arg, repr) repr2982#define zig_make_special_f80(sign, name, arg, repr) repr
2081#undef zig_make_special_constant_f802983#undef zig_init_special_f80
2082#define zig_make_special_constant_f80(sign, name, arg, repr) repr2984#define zig_init_special_f80(sign, name, arg, repr) repr
2083#endif2985#endif
20842986
2085#define zig_has_f128 12987#define zig_has_f128 1
2086#define zig_bitSizeOf_f128 1282988#define zig_bitSizeOf_f128 128
2989typedef zig_i128 zig_repr_f128;
2087#define zig_libc_name_f128(name) name##q2990#define zig_libc_name_f128(name) name##q
2088#define zig_make_special_constant_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr)2991#define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr)
2089#if FLT_MANT_DIG == 1132992#if FLT_MANT_DIG == 113
2090typedef float zig_f128;2993typedef float zig_f128;
2091#define zig_make_f128(fp, repr) fp##f2994#define zig_make_f128(fp, repr) fp##f
...@@ -2094,7 +2997,9 @@ typedef double zig_f128;...@@ -2094,7 +2997,9 @@ typedef double zig_f128;
2094#define zig_make_f128(fp, repr) fp2997#define zig_make_f128(fp, repr) fp
2095#elif LDBL_MANT_DIG == 1132998#elif LDBL_MANT_DIG == 113
2096#define zig_bitSizeOf_c_longdouble 1282999#define zig_bitSizeOf_c_longdouble 128
2097typedef zig_u128 zig_repr_c_longdouble;3000#ifndef ZIG_TARGET_ABI_MSVC
3001typedef zig_repr_f128 zig_repr_c_longdouble;
3002#endif
2098typedef long double zig_f128;3003typedef long double zig_f128;
2099#define zig_make_f128(fp, repr) fp##l3004#define zig_make_f128(fp, repr) fp##l
2100#elif FLT128_MANT_DIG == 1133005#elif FLT128_MANT_DIG == 113
...@@ -2116,63 +3021,44 @@ typedef zig_i128 zig_f128;...@@ -2116,63 +3021,44 @@ typedef zig_i128 zig_f128;
2116#define zig_make_f128(fp, repr) repr3021#define zig_make_f128(fp, repr) repr
2117#undef zig_make_special_f1283022#undef zig_make_special_f128
2118#define zig_make_special_f128(sign, name, arg, repr) repr3023#define zig_make_special_f128(sign, name, arg, repr) repr
2119#undef zig_make_special_constant_f1283024#undef zig_init_special_f128
2120#define zig_make_special_constant_f128(sign, name, arg, repr) repr3025#define zig_init_special_f128(sign, name, arg, repr) repr
2121#endif
2122
2123#define zig_has_c_longdouble 1
2124
2125#ifdef ZIG_TARGET_ABI_MSVC
2126#define zig_libc_name_c_longdouble(name) name
2127#else
2128#define zig_libc_name_c_longdouble(name) name##l
2129#endif3026#endif
21303027
2131#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr)
2132#ifdef zig_bitSizeOf_c_longdouble3028#ifdef zig_bitSizeOf_c_longdouble
21333029
3030#define zig_has_c_longdouble 1
2134#ifdef ZIG_TARGET_ABI_MSVC3031#ifdef ZIG_TARGET_ABI_MSVC
2135#undef zig_bitSizeOf_c_longdouble3032#undef zig_bitSizeOf_c_longdouble
2136#define zig_bitSizeOf_c_longdouble 643033#define zig_bitSizeOf_c_longdouble 64
2137typedef uint64_t zig_repr_c_longdouble;
2138typedef zig_f64 zig_c_longdouble;3034typedef zig_f64 zig_c_longdouble;
2139#define zig_make_c_longdouble(fp, repr) fp3035typedef zig_repr_f64 zig_repr_c_longdouble;
2140#else3036#else
2141typedef long double zig_c_longdouble;3037typedef long double zig_c_longdouble;
2142#define zig_make_c_longdouble(fp, repr) fp##l
2143#endif3038#endif
21443039
2145#else /* zig_bitSizeOf_c_longdouble */3040#else /* zig_bitSizeOf_c_longdouble */
21463041
2147#undef zig_has_c_longdouble
2148#define zig_has_c_longdouble 03042#define zig_has_c_longdouble 0
2149#define zig_bitSizeOf_c_longdouble 80
2150typedef zig_u128 zig_repr_c_longdouble;
2151#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80
2152#define zig_bitSizeOf_repr_c_longdouble 1283043#define zig_bitSizeOf_repr_c_longdouble 128
2153typedef zig_i128 zig_c_longdouble;3044typedef zig_f128 zig_c_longdouble;
2154#define zig_make_c_longdouble(fp, repr) repr3045typedef zig_repr_f128 zig_repr_c_longdouble;
2155#undef zig_make_special_c_longdouble
2156#define zig_make_special_c_longdouble(sign, name, arg, repr) repr
2157#undef zig_make_special_constant_c_longdouble
2158#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) repr
21593046
2160#endif /* zig_bitSizeOf_c_longdouble */3047#endif /* zig_bitSizeOf_c_longdouble */
21613048
2162#if !zig_has_float_builtins3049#if !zig_has_float_builtins
2163#define zig_float_from_repr(Type, ReprType) \3050#define zig_float_from_repr(Type) \
2164 static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \3051 static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \
2165 zig_##Type result; \3052 zig_##Type result; \
2166 memcpy(&result, &repr, sizeof(result)); \3053 memcpy(&result, &repr, sizeof(result)); \
2167 return result; \3054 return result; \
2168 }3055 }
21693056
2170zig_float_from_repr(f16, uint16_t)3057zig_float_from_repr(f16)
2171zig_float_from_repr(f32, uint32_t)3058zig_float_from_repr(f32)
2172zig_float_from_repr(f64, uint64_t)3059zig_float_from_repr(f64)
2173zig_float_from_repr(f80, zig_u128)3060zig_float_from_repr(f80)
2174zig_float_from_repr(f128, zig_u128)3061zig_float_from_repr(f128)
2175zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)
2176#endif3062#endif
21773063
2178#define zig_cast_f16 (zig_f16)3064#define zig_cast_f16 (zig_f16)
...@@ -2181,11 +3067,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)...@@ -2181,11 +3067,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)
21813067
2182#if _MSC_VER && !zig_has_f1283068#if _MSC_VER && !zig_has_f128
2183#define zig_cast_f803069#define zig_cast_f80
2184#define zig_cast_c_longdouble
2185#define zig_cast_f1283070#define zig_cast_f128
2186#else3071#else
2187#define zig_cast_f80 (zig_f80)3072#define zig_cast_f80 (zig_f80)
2188#define zig_cast_c_longdouble (zig_c_longdouble)
2189#define zig_cast_f128 (zig_f128)3073#define zig_cast_f128 (zig_f128)
2190#endif3074#endif
21913075
...@@ -2314,7 +3198,6 @@ zig_float_builtins(f32)...@@ -2314,7 +3198,6 @@ zig_float_builtins(f32)
2314zig_float_builtins(f64)3198zig_float_builtins(f64)
2315zig_float_builtins(f80)3199zig_float_builtins(f80)
2316zig_float_builtins(f128)3200zig_float_builtins(f128)
2317zig_float_builtins(c_longdouble)
23183201
2319#if _MSC_VER && (_M_IX86 || _M_X64)3202#if _MSC_VER && (_M_IX86 || _M_X64)
23203203
src/Air.zig+9-1
...@@ -232,7 +232,14 @@ pub const Inst = struct {...@@ -232,7 +232,14 @@ pub const Inst = struct {
232 /// Result type is always noreturn; no instructions in a block follow this one.232 /// Result type is always noreturn; no instructions in a block follow this one.
233 /// Uses the `br` field.233 /// Uses the `br` field.
234 br,234 br,
235 /// Lowers to a hardware trap instruction, or the next best thing.235 /// Lowers to a trap/jam instruction causing program abortion.
236 /// This may lower to an instruction known to be invalid.
237 /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code.
238 /// Result type is always noreturn; no instructions in a block follow this one.
239 trap,
240 /// Lowers to a trap instruction causing debuggers to break here, or the next best thing.
241 /// The debugger or something else may allow the program to resume after this point.
242 /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code.
236 /// Result type is always void.243 /// Result type is always void.
237 breakpoint,244 breakpoint,
238 /// Yields the return address of the current function.245 /// Yields the return address of the current function.
...@@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
1186 .ret,1193 .ret,
1187 .ret_load,1194 .ret_load,
1188 .unreach,1195 .unreach,
1196 .trap,
1189 => return Type.initTag(.noreturn),1197 => return Type.initTag(.noreturn),
11901198
1191 .breakpoint,1199 .breakpoint,
src/AstGen.zig+151-92
...@@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
148 };148 };
149 defer gz_instructions.deinit(gpa);149 defer gz_instructions.deinit(gpa);
150150
151 if (AstGen.structDeclInner(151 // The AST -> ZIR lowering process assumes an AST that does not have any
152 &gen_scope,152 // parse errors.
153 &gen_scope.base,153 if (tree.errors.len == 0) {
154 0,154 if (AstGen.structDeclInner(
155 tree.containerDeclRoot(),155 &gen_scope,
156 .Auto,156 &gen_scope.base,
157 0,157 0,
158 )) |struct_decl_ref| {158 tree.containerDeclRoot(),
159 assert(refToIndex(struct_decl_ref).? == 0);159 .Auto,
160 } else |err| switch (err) {160 0,
161 error.OutOfMemory => return error.OutOfMemory,161 )) |struct_decl_ref| {
162 error.AnalysisFail => {}, // Handled via compile_errors below.162 assert(refToIndex(struct_decl_ref).? == 0);
163 } else |err| switch (err) {
164 error.OutOfMemory => return error.OutOfMemory,
165 error.AnalysisFail => {}, // Handled via compile_errors below.
166 }
167 } else {
168 try lowerAstErrors(&astgen);
163 }169 }
164170
165 const err_index = @enumToInt(Zir.ExtraIndex.compile_errors);171 const err_index = @enumToInt(Zir.ExtraIndex.compile_errors);
...@@ -205,7 +211,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -205,7 +211,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
205 };211 };
206}212}
207213
208pub fn deinit(astgen: *AstGen, gpa: Allocator) void {214fn deinit(astgen: *AstGen, gpa: Allocator) void {
209 astgen.instructions.deinit(gpa);215 astgen.instructions.deinit(gpa);
210 astgen.extra.deinit(gpa);216 astgen.extra.deinit(gpa);
211 astgen.string_table.deinit(gpa);217 astgen.string_table.deinit(gpa);
...@@ -216,7 +222,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void {...@@ -216,7 +222,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void {
216 astgen.ref_table.deinit(gpa);222 astgen.ref_table.deinit(gpa);
217}223}
218224
219pub const ResultInfo = struct {225const ResultInfo = struct {
220 /// The semantics requested for the result location226 /// The semantics requested for the result location
221 rl: Loc,227 rl: Loc,
222228
...@@ -245,7 +251,7 @@ pub const ResultInfo = struct {...@@ -245,7 +251,7 @@ pub const ResultInfo = struct {
245 }251 }
246 }252 }
247253
248 pub const Loc = union(enum) {254 const Loc = union(enum) {
249 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the255 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
250 /// expression should be generated. The result instruction from the expression must256 /// expression should be generated. The result instruction from the expression must
251 /// be ignored.257 /// be ignored.
...@@ -277,11 +283,11 @@ pub const ResultInfo = struct {...@@ -277,11 +283,11 @@ pub const ResultInfo = struct {
277 src_node: ?Ast.Node.Index = null,283 src_node: ?Ast.Node.Index = null,
278 };284 };
279285
280 pub const Strategy = struct {286 const Strategy = struct {
281 elide_store_to_block_ptr_instructions: bool,287 elide_store_to_block_ptr_instructions: bool,
282 tag: Tag,288 tag: Tag,
283289
284 pub const Tag = enum {290 const Tag = enum {
285 /// Both branches will use break_void; result location is used to communicate the291 /// Both branches will use break_void; result location is used to communicate the
286 /// result instruction.292 /// result instruction.
287 break_void,293 break_void,
...@@ -331,7 +337,7 @@ pub const ResultInfo = struct {...@@ -331,7 +337,7 @@ pub const ResultInfo = struct {
331 }337 }
332 };338 };
333339
334 pub const Context = enum {340 const Context = enum {
335 /// The expression is the operand to a return expression.341 /// The expression is the operand to a return expression.
336 @"return",342 @"return",
337 /// The expression is the input to an error-handling operator (if-else, try, or catch).343 /// The expression is the input to an error-handling operator (if-else, try, or catch).
...@@ -349,11 +355,11 @@ pub const ResultInfo = struct {...@@ -349,11 +355,11 @@ pub const ResultInfo = struct {
349 };355 };
350};356};
351357
352pub const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } };358const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } };
353pub const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };359const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
354pub const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } };360const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } };
355pub const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } };361const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } };
356pub const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };362const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
357363
358fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {364fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
359 const prev_force_comptime = gz.force_comptime;365 const prev_force_comptime = gz.force_comptime;
...@@ -1960,7 +1966,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1960,7 +1966,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1960 else1966 else
1961 .@"break";1967 .@"break";
19621968
1969 block_gz.break_count += 1;
1963 if (rhs == 0) {1970 if (rhs == 0) {
1971 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
1972
1964 try genDefers(parent_gz, scope, parent_scope, .normal_only);1973 try genDefers(parent_gz, scope, parent_scope, .normal_only);
19651974
1966 // As our last action before the break, "pop" the error trace if needed1975 // As our last action before the break, "pop" the error trace if needed
...@@ -1970,7 +1979,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1970,7 +1979,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1970 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);1979 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
1971 return Zir.Inst.Ref.unreachable_value;1980 return Zir.Inst.Ref.unreachable_value;
1972 }1981 }
1973 block_gz.break_count += 1;
19741982
1975 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);1983 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
1976 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);1984 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
...@@ -2609,8 +2617,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2609,8 +2617,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2609 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {2617 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
2610 .breakpoint,2618 .breakpoint,
2611 .fence,2619 .fence,
2612 .set_align_stack,
2613 .set_float_mode,2620 .set_float_mode,
2621 .set_align_stack,
2622 .set_cold,
2614 => break :b true,2623 => break :b true,
2615 else => break :b false,2624 else => break :b false,
2616 },2625 },
...@@ -2630,6 +2639,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2630,6 +2639,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2630 .repeat_inline,2639 .repeat_inline,
2631 .panic,2640 .panic,
2632 .panic_comptime,2641 .panic_comptime,
2642 .trap,
2633 .check_comptime_control_flow,2643 .check_comptime_control_flow,
2634 => {2644 => {
2635 noreturn_src_node = statement;2645 noreturn_src_node = statement;
...@@ -2658,7 +2668,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2658,7 +2668,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2658 .validate_struct_init_comptime,2668 .validate_struct_init_comptime,
2659 .validate_array_init,2669 .validate_array_init,
2660 .validate_array_init_comptime,2670 .validate_array_init_comptime,
2661 .set_cold,
2662 .set_runtime_safety,2671 .set_runtime_safety,
2663 .closure_capture,2672 .closure_capture,
2664 .memcpy,2673 .memcpy,
...@@ -3506,7 +3515,7 @@ const WipMembers = struct {...@@ -3506,7 +3515,7 @@ const WipMembers = struct {
3506 /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )3515 /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )
3507 const max_decl_size = 11;3516 const max_decl_size = 11;
35083517
3509 pub fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self {3518 fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self {
3510 const payload_top = @intCast(u32, payload.items.len);3519 const payload_top = @intCast(u32, payload.items.len);
3511 const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;3520 const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;
3512 const field_bits_start = decls_start + decl_count * max_decl_size;3521 const field_bits_start = decls_start + decl_count * max_decl_size;
...@@ -3527,7 +3536,7 @@ const WipMembers = struct {...@@ -3527,7 +3536,7 @@ const WipMembers = struct {
3527 };3536 };
3528 }3537 }
35293538
3530 pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void {3539 fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void {
3531 const index = self.payload_top + self.decl_index / decls_per_u32;3540 const index = self.payload_top + self.decl_index / decls_per_u32;
3532 assert(index < self.decls_start);3541 assert(index < self.decls_start);
3533 const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index];3542 const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index];
...@@ -3539,7 +3548,7 @@ const WipMembers = struct {...@@ -3539,7 +3548,7 @@ const WipMembers = struct {
3539 self.decl_index += 1;3548 self.decl_index += 1;
3540 }3549 }
35413550
3542 pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {3551 fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
3543 const fields_per_u32 = 32 / bits_per_field;3552 const fields_per_u32 = 32 / bits_per_field;
3544 const index = self.field_bits_start + self.field_index / fields_per_u32;3553 const index = self.field_bits_start + self.field_index / fields_per_u32;
3545 assert(index < self.fields_start);3554 assert(index < self.fields_start);
...@@ -3553,25 +3562,25 @@ const WipMembers = struct {...@@ -3553,25 +3562,25 @@ const WipMembers = struct {
3553 self.field_index += 1;3562 self.field_index += 1;
3554 }3563 }
35553564
3556 pub fn appendToDecl(self: *Self, data: u32) void {3565 fn appendToDecl(self: *Self, data: u32) void {
3557 assert(self.decls_end < self.field_bits_start);3566 assert(self.decls_end < self.field_bits_start);
3558 self.payload.items[self.decls_end] = data;3567 self.payload.items[self.decls_end] = data;
3559 self.decls_end += 1;3568 self.decls_end += 1;
3560 }3569 }
35613570
3562 pub fn appendToDeclSlice(self: *Self, data: []const u32) void {3571 fn appendToDeclSlice(self: *Self, data: []const u32) void {
3563 assert(self.decls_end + data.len <= self.field_bits_start);3572 assert(self.decls_end + data.len <= self.field_bits_start);
3564 mem.copy(u32, self.payload.items[self.decls_end..], data);3573 mem.copy(u32, self.payload.items[self.decls_end..], data);
3565 self.decls_end += @intCast(u32, data.len);3574 self.decls_end += @intCast(u32, data.len);
3566 }3575 }
35673576
3568 pub fn appendToField(self: *Self, data: u32) void {3577 fn appendToField(self: *Self, data: u32) void {
3569 assert(self.fields_end < self.payload.items.len);3578 assert(self.fields_end < self.payload.items.len);
3570 self.payload.items[self.fields_end] = data;3579 self.payload.items[self.fields_end] = data;
3571 self.fields_end += 1;3580 self.fields_end += 1;
3572 }3581 }
35733582
3574 pub fn finishBits(self: *Self, comptime bits_per_field: u32) void {3583 fn finishBits(self: *Self, comptime bits_per_field: u32) void {
3575 const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);3584 const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);
3576 if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {3585 if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {
3577 const index = self.payload_top + self.decl_index / decls_per_u32;3586 const index = self.payload_top + self.decl_index / decls_per_u32;
...@@ -3587,15 +3596,15 @@ const WipMembers = struct {...@@ -3587,15 +3596,15 @@ const WipMembers = struct {
3587 }3596 }
3588 }3597 }
35893598
3590 pub fn declsSlice(self: *Self) []u32 {3599 fn declsSlice(self: *Self) []u32 {
3591 return self.payload.items[self.payload_top..self.decls_end];3600 return self.payload.items[self.payload_top..self.decls_end];
3592 }3601 }
35933602
3594 pub fn fieldsSlice(self: *Self) []u32 {3603 fn fieldsSlice(self: *Self) []u32 {
3595 return self.payload.items[self.field_bits_start..self.fields_end];3604 return self.payload.items[self.field_bits_start..self.fields_end];
3596 }3605 }
35973606
3598 pub fn deinit(self: *Self) void {3607 fn deinit(self: *Self) void {
3599 self.payload.items.len = self.payload_top;3608 self.payload.items.len = self.payload_top;
3600 }3609 }
3601};3610};
...@@ -6583,6 +6592,9 @@ fn forExpr(...@@ -6583,6 +6592,9 @@ fn forExpr(
6583 cond_block,6592 cond_block,
6584 break_tag,6593 break_tag,
6585 );6594 );
6595 if (ri.rl.strategy(&loop_scope).tag == .break_void and loop_scope.break_count == 0) {
6596 _ = try rvalue(parent_gz, ri, .void_value, node);
6597 }
6586 if (is_statement) {6598 if (is_statement) {
6587 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);6599 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6588 }6600 }
...@@ -7976,6 +7988,9 @@ fn builtinCall(...@@ -7976,6 +7988,9 @@ fn builtinCall(
7976 switch (node_tags[params[0]]) {7988 switch (node_tags[params[0]]) {
7977 .identifier => {7989 .identifier => {
7978 const ident_token = main_tokens[params[0]];7990 const ident_token = main_tokens[params[0]];
7991 if (isPrimitive(tree.tokenSlice(ident_token))) {
7992 return astgen.failTok(ident_token, "unable to export primitive value", .{});
7993 }
7979 decl_name = try astgen.identAsString(ident_token);7994 decl_name = try astgen.identAsString(ident_token);
79807995
7981 var s = scope;7996 var s = scope;
...@@ -8056,27 +8071,35 @@ fn builtinCall(...@@ -8056,27 +8071,35 @@ fn builtinCall(
8056 },8071 },
8057 .fence => {8072 .fence => {
8058 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]);8073 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]);
8059 const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{8074 _ = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{
8060 .node = gz.nodeIndexToRelative(node),8075 .node = gz.nodeIndexToRelative(node),
8061 .operand = order,8076 .operand = order,
8062 });8077 });
8063 return rvalue(gz, ri, result, node);8078 return rvalue(gz, ri, .void_value, node);
8064 },8079 },
8065 .set_float_mode => {8080 .set_float_mode => {
8066 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]);8081 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]);
8067 const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{8082 _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
8068 .node = gz.nodeIndexToRelative(node),8083 .node = gz.nodeIndexToRelative(node),
8069 .operand = order,8084 .operand = order,
8070 });8085 });
8071 return rvalue(gz, ri, result, node);8086 return rvalue(gz, ri, .void_value, node);
8072 },8087 },
8073 .set_align_stack => {8088 .set_align_stack => {
8074 const order = try expr(gz, scope, align_ri, params[0]);8089 const order = try expr(gz, scope, align_ri, params[0]);
8075 const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{8090 _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
8076 .node = gz.nodeIndexToRelative(node),8091 .node = gz.nodeIndexToRelative(node),
8077 .operand = order,8092 .operand = order,
8078 });8093 });
8079 return rvalue(gz, ri, result, node);8094 return rvalue(gz, ri, .void_value, node);
8095 },
8096 .set_cold => {
8097 const order = try expr(gz, scope, ri, params[0]);
8098 _ = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{
8099 .node = gz.nodeIndexToRelative(node),
8100 .operand = order,
8101 });
8102 return rvalue(gz, ri, .void_value, node);
8080 },8103 },
80818104
8082 .src => {8105 .src => {
...@@ -8097,7 +8120,7 @@ fn builtinCall(...@@ -8097,7 +8120,7 @@ fn builtinCall(
8097 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),8120 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
8098 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),8121 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
8099 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),8122 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
8100 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),8123 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
81018124
8102 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),8125 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
8103 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),8126 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
...@@ -8111,7 +8134,6 @@ fn builtinCall(...@@ -8111,7 +8134,6 @@ fn builtinCall(
8111 .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int),8134 .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int),
8112 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file),8135 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file),
8113 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name),8136 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name),
8114 .set_cold => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_cold),
8115 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety),8137 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety),
8116 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),8138 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),
8117 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),8139 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),
...@@ -8171,6 +8193,11 @@ fn builtinCall(...@@ -8171,6 +8193,11 @@ fn builtinCall(
8171 try emitDbgNode(gz, node);8193 try emitDbgNode(gz, node);
8172 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic);8194 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic);
8173 },8195 },
8196 .trap => {
8197 try emitDbgNode(gz, node);
8198 _ = try gz.addNode(.trap, node);
8199 return rvalue(gz, ri, .void_value, node);
8200 },
8174 .error_to_int => {8201 .error_to_int => {
8175 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);8202 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
8176 const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{8203 const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
...@@ -8357,14 +8384,14 @@ fn builtinCall(...@@ -8357,14 +8384,14 @@ fn builtinCall(
8357 },8384 },
8358 .atomic_store => {8385 .atomic_store => {
8359 const int_type = try typeExpr(gz, scope, params[0]);8386 const int_type = try typeExpr(gz, scope, params[0]);
8360 const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{8387 _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
8361 // zig fmt: off8388 // zig fmt: off
8362 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),8389 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
8363 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),8390 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
8364 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]),8391 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]),
8365 // zig fmt: on8392 // zig fmt: on
8366 });8393 });
8367 return rvalue(gz, ri, result, node);8394 return rvalue(gz, ri, .void_value, node);
8368 },8395 },
8369 .mul_add => {8396 .mul_add => {
8370 const float_type = try typeExpr(gz, scope, params[0]);8397 const float_type = try typeExpr(gz, scope, params[0]);
...@@ -8405,20 +8432,20 @@ fn builtinCall(...@@ -8405,20 +8432,20 @@ fn builtinCall(
8405 return rvalue(gz, ri, result, node);8432 return rvalue(gz, ri, result, node);
8406 },8433 },
8407 .memcpy => {8434 .memcpy => {
8408 const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{8435 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
8409 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),8436 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
8410 .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]),8437 .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]),
8411 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),8438 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
8412 });8439 });
8413 return rvalue(gz, ri, result, node);8440 return rvalue(gz, ri, .void_value, node);
8414 },8441 },
8415 .memset => {8442 .memset => {
8416 const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{8443 _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
8417 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),8444 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
8418 .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]),8445 .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]),
8419 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),8446 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
8420 });8447 });
8421 return rvalue(gz, ri, result, node);8448 return rvalue(gz, ri, .void_value, node);
8422 },8449 },
8423 .shuffle => {8450 .shuffle => {
8424 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{8451 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
...@@ -8459,12 +8486,12 @@ fn builtinCall(...@@ -8459,12 +8486,12 @@ fn builtinCall(
8459 .prefetch => {8486 .prefetch => {
8460 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);8487 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
8461 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]);8488 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]);
8462 const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{8489 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
8463 .node = gz.nodeIndexToRelative(node),8490 .node = gz.nodeIndexToRelative(node),
8464 .lhs = ptr,8491 .lhs = ptr,
8465 .rhs = options,8492 .rhs = options,
8466 });8493 });
8467 return rvalue(gz, ri, result, node);8494 return rvalue(gz, ri, .void_value, node);
8468 },8495 },
8469 .c_va_arg => {8496 .c_va_arg => {
8470 if (astgen.fn_block == null) {8497 if (astgen.fn_block == null) {
...@@ -8509,16 +8536,6 @@ fn builtinCall(...@@ -8509,16 +8536,6 @@ fn builtinCall(
8509 }8536 }
8510}8537}
85118538
8512fn simpleNoOpVoid(
8513 gz: *GenZir,
8514 ri: ResultInfo,
8515 node: Ast.Node.Index,
8516 tag: Zir.Inst.Tag,
8517) InnerError!Zir.Inst.Ref {
8518 _ = try gz.addNode(tag, node);
8519 return rvalue(gz, ri, .void_value, node);
8520}
8521
8522fn hasDeclOrField(8539fn hasDeclOrField(
8523 gz: *GenZir,8540 gz: *GenZir,
8524 scope: *Scope,8541 scope: *Scope,
...@@ -8988,7 +9005,7 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{...@@ -8988,7 +9005,7 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
8988});9005});
89899006
8990comptime {9007comptime {
8991 // These checks ensure that std.zig.primitives stays in synce with the primitive->Zir map.9008 // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map.
8992 const primitives = std.zig.primitives;9009 const primitives = std.zig.primitives;
8993 for (primitive_instrs.kvs) |kv| {9010 for (primitive_instrs.kvs) |kv| {
8994 if (!primitives.isPrimitive(kv.key)) {9011 if (!primitives.isPrimitive(kv.key)) {
...@@ -10369,7 +10386,7 @@ fn appendErrorTok(...@@ -10369,7 +10386,7 @@ fn appendErrorTok(
10369 comptime format: []const u8,10386 comptime format: []const u8,
10370 args: anytype,10387 args: anytype,
10371) !void {10388) !void {
10372 try astgen.appendErrorTokNotes(token, format, args, &[0]u32{});10389 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
10373}10390}
1037410391
10375fn failTokNotes(10392fn failTokNotes(
...@@ -10379,7 +10396,7 @@ fn failTokNotes(...@@ -10379,7 +10396,7 @@ fn failTokNotes(
10379 args: anytype,10396 args: anytype,
10380 notes: []const u32,10397 notes: []const u32,
10381) InnerError {10398) InnerError {
10382 try appendErrorTokNotes(astgen, token, format, args, notes);10399 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
10383 return error.AnalysisFail;10400 return error.AnalysisFail;
10384}10401}
1038510402
...@@ -10390,27 +10407,11 @@ fn appendErrorTokNotes(...@@ -10390,27 +10407,11 @@ fn appendErrorTokNotes(
10390 args: anytype,10407 args: anytype,
10391 notes: []const u32,10408 notes: []const u32,
10392) !void {10409) !void {
10393 @setCold(true);10410 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
10394 const string_bytes = &astgen.string_bytes;
10395 const msg = @intCast(u32, string_bytes.items.len);
10396 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
10397 const notes_index: u32 = if (notes.len != 0) blk: {
10398 const notes_start = astgen.extra.items.len;
10399 try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
10400 astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
10401 astgen.extra.appendSliceAssumeCapacity(notes);
10402 break :blk @intCast(u32, notes_start);
10403 } else 0;
10404 try astgen.compile_errors.append(astgen.gpa, .{
10405 .msg = msg,
10406 .node = 0,
10407 .token = token,
10408 .byte_offset = 0,
10409 .notes = notes_index,
10410 });
10411}10411}
1041210412
10413/// Same as `fail`, except given an absolute byte offset.10413/// Same as `fail`, except given a token plus an offset from its starting byte
10414/// offset.
10414fn failOff(10415fn failOff(
10415 astgen: *AstGen,10416 astgen: *AstGen,
10416 token: Ast.TokenIndex,10417 token: Ast.TokenIndex,
...@@ -10418,27 +10419,36 @@ fn failOff(...@@ -10418,27 +10419,36 @@ fn failOff(
10418 comptime format: []const u8,10419 comptime format: []const u8,
10419 args: anytype,10420 args: anytype,
10420) InnerError {10421) InnerError {
10421 try appendErrorOff(astgen, token, byte_offset, format, args);10422 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
10422 return error.AnalysisFail;10423 return error.AnalysisFail;
10423}10424}
1042410425
10425fn appendErrorOff(10426fn appendErrorTokNotesOff(
10426 astgen: *AstGen,10427 astgen: *AstGen,
10427 token: Ast.TokenIndex,10428 token: Ast.TokenIndex,
10428 byte_offset: u32,10429 byte_offset: u32,
10429 comptime format: []const u8,10430 comptime format: []const u8,
10430 args: anytype,10431 args: anytype,
10431) Allocator.Error!void {10432 notes: []const u32,
10433) !void {
10432 @setCold(true);10434 @setCold(true);
10435 const gpa = astgen.gpa;
10433 const string_bytes = &astgen.string_bytes;10436 const string_bytes = &astgen.string_bytes;
10434 const msg = @intCast(u32, string_bytes.items.len);10437 const msg = @intCast(u32, string_bytes.items.len);
10435 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);10438 try string_bytes.writer(gpa).print(format ++ "\x00", args);
10436 try astgen.compile_errors.append(astgen.gpa, .{10439 const notes_index: u32 = if (notes.len != 0) blk: {
10440 const notes_start = astgen.extra.items.len;
10441 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
10442 astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
10443 astgen.extra.appendSliceAssumeCapacity(notes);
10444 break :blk @intCast(u32, notes_start);
10445 } else 0;
10446 try astgen.compile_errors.append(gpa, .{
10437 .msg = msg,10447 .msg = msg,
10438 .node = 0,10448 .node = 0,
10439 .token = token,10449 .token = token,
10440 .byte_offset = byte_offset,10450 .byte_offset = byte_offset,
10441 .notes = 0,10451 .notes = notes_index,
10442 });10452 });
10443}10453}
1044410454
...@@ -10447,6 +10457,16 @@ fn errNoteTok(...@@ -10447,6 +10457,16 @@ fn errNoteTok(
10447 token: Ast.TokenIndex,10457 token: Ast.TokenIndex,
10448 comptime format: []const u8,10458 comptime format: []const u8,
10449 args: anytype,10459 args: anytype,
10460) Allocator.Error!u32 {
10461 return errNoteTokOff(astgen, token, 0, format, args);
10462}
10463
10464fn errNoteTokOff(
10465 astgen: *AstGen,
10466 token: Ast.TokenIndex,
10467 byte_offset: u32,
10468 comptime format: []const u8,
10469 args: anytype,
10450) Allocator.Error!u32 {10470) Allocator.Error!u32 {
10451 @setCold(true);10471 @setCold(true);
10452 const string_bytes = &astgen.string_bytes;10472 const string_bytes = &astgen.string_bytes;
...@@ -10456,7 +10476,7 @@ fn errNoteTok(...@@ -10456,7 +10476,7 @@ fn errNoteTok(
10456 .msg = msg,10476 .msg = msg,
10457 .node = 0,10477 .node = 0,
10458 .token = token,10478 .token = token,
10459 .byte_offset = 0,10479 .byte_offset = byte_offset,
10460 .notes = 0,10480 .notes = 0,
10461 });10481 });
10462}10482}
...@@ -10787,7 +10807,7 @@ const Scope = struct {...@@ -10787,7 +10807,7 @@ const Scope = struct {
10787 /// ref of the capture for decls in this namespace10807 /// ref of the capture for decls in this namespace
10788 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},10808 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
1078910809
10790 pub fn deinit(self: *Namespace, gpa: Allocator) void {10810 fn deinit(self: *Namespace, gpa: Allocator) void {
10791 self.decls.deinit(gpa);10811 self.decls.deinit(gpa);
10792 self.captures.deinit(gpa);10812 self.captures.deinit(gpa);
10793 self.* = undefined;10813 self.* = undefined;
...@@ -12623,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void {...@@ -12623,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
12623 },12643 },
12624 } });12644 } });
12625}12645}
12646
12647fn lowerAstErrors(astgen: *AstGen) !void {
12648 const tree = astgen.tree;
12649 assert(tree.errors.len > 0);
12650
12651 const gpa = astgen.gpa;
12652 const parse_err = tree.errors[0];
12653
12654 var msg: std.ArrayListUnmanaged(u8) = .{};
12655 defer msg.deinit(gpa);
12656
12657 const token_starts = tree.tokens.items(.start);
12658 const token_tags = tree.tokens.items(.tag);
12659
12660 var notes: std.ArrayListUnmanaged(u32) = .{};
12661 defer notes.deinit(gpa);
12662
12663 if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) {
12664 const tok = parse_err.token + @boolToInt(parse_err.token_is_prev);
12665 const bad_off = @intCast(u32, tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len);
12666 const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off;
12667 try notes.append(gpa, try astgen.errNoteTokOff(tok, bad_off, "invalid byte: '{'}'", .{
12668 std.zig.fmtEscapes(tree.source[byte_abs..][0..1]),
12669 }));
12670 }
12671
12672 for (tree.errors[1..]) |note| {
12673 if (!note.is_note) break;
12674
12675 msg.clearRetainingCapacity();
12676 try tree.renderError(note, msg.writer(gpa));
12677 try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
12678 }
12679
12680 const extra_offset = tree.errorOffset(parse_err);
12681 msg.clearRetainingCapacity();
12682 try tree.renderError(parse_err, msg.writer(gpa));
12683 try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
12684}
src/Autodoc.zig-1
...@@ -1338,7 +1338,6 @@ fn walkInstruction(...@@ -1338,7 +1338,6 @@ fn walkInstruction(
1338 .embed_file,1338 .embed_file,
1339 .error_name,1339 .error_name,
1340 .panic,1340 .panic,
1341 .set_cold, // @check
1342 .set_runtime_safety, // @check1341 .set_runtime_safety, // @check
1343 .sqrt,1342 .sqrt,
1344 .sin,1343 .sin,
src/BuiltinFn.zig+8
...@@ -109,6 +109,7 @@ pub const Tag = enum {...@@ -109,6 +109,7 @@ pub const Tag = enum {
109 sub_with_overflow,109 sub_with_overflow,
110 tag_name,110 tag_name,
111 This,111 This,
112 trap,
112 truncate,113 truncate,
113 Type,114 Type,
114 type_info,115 type_info,
...@@ -915,6 +916,13 @@ pub const list = list: {...@@ -915,6 +916,13 @@ pub const list = list: {
915 .param_count = 0,916 .param_count = 0,
916 },917 },
917 },918 },
919 .{
920 "@trap",
921 .{
922 .tag = .trap,
923 .param_count = 0,
924 },
925 },
918 .{926 .{
919 "@truncate",927 "@truncate",
920 .{928 .{
src/Compilation.zig+424-608
...@@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator;...@@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator;
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const log = std.log.scoped(.compilation);8const log = std.log.scoped(.compilation);
9const Target = std.Target;9const Target = std.Target;
10const ThreadPool = std.Thread.Pool;
11const WaitGroup = std.Thread.WaitGroup;
12const ErrorBundle = std.zig.ErrorBundle;
1013
11const Value = @import("value.zig").Value;14const Value = @import("value.zig").Value;
12const Type = @import("type.zig").Type;15const Type = @import("type.zig").Type;
...@@ -30,8 +33,6 @@ const Cache = std.Build.Cache;...@@ -30,8 +33,6 @@ const Cache = std.Build.Cache;
30const translate_c = @import("translate_c.zig");33const translate_c = @import("translate_c.zig");
31const clang = @import("clang.zig");34const clang = @import("clang.zig");
32const c_codegen = @import("codegen/c.zig");35const c_codegen = @import("codegen/c.zig");
33const ThreadPool = @import("ThreadPool.zig");
34const WaitGroup = @import("WaitGroup.zig");
35const libtsan = @import("libtsan.zig");36const libtsan = @import("libtsan.zig");
36const Zir = @import("Zir.zig");37const Zir = @import("Zir.zig");
37const Autodoc = @import("Autodoc.zig");38const Autodoc = @import("Autodoc.zig");
...@@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false,...@@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false,
99job_queued_compiler_rt_obj: bool = false,100job_queued_compiler_rt_obj: bool = false,
100alloc_failure_occurred: bool = false,101alloc_failure_occurred: bool = false,
101formatted_panics: bool = false,102formatted_panics: bool = false,
103last_update_was_cache_hit: bool = false,
102104
103c_source_files: []const CSourceFile,105c_source_files: []const CSourceFile,
104clang_argv: []const []const u8,106clang_argv: []const []const u8,
...@@ -334,12 +336,41 @@ pub const MiscTask = enum {...@@ -334,12 +336,41 @@ pub const MiscTask = enum {
334 libssp,336 libssp,
335 zig_libc,337 zig_libc,
336 analyze_pkg,338 analyze_pkg,
339
340 @"musl crti.o",
341 @"musl crtn.o",
342 @"musl crt1.o",
343 @"musl rcrt1.o",
344 @"musl Scrt1.o",
345 @"musl libc.a",
346 @"musl libc.so",
347
348 @"wasi crt1-reactor.o",
349 @"wasi crt1-command.o",
350 @"wasi libc.a",
351 @"libwasi-emulated-process-clocks.a",
352 @"libwasi-emulated-getpid.a",
353 @"libwasi-emulated-mman.a",
354 @"libwasi-emulated-signal.a",
355
356 @"glibc crti.o",
357 @"glibc crtn.o",
358 @"glibc Scrt1.o",
359 @"glibc libc_nonshared.a",
360 @"glibc shared object",
361
362 @"mingw-w64 crt2.o",
363 @"mingw-w64 dllcrt2.o",
364 @"mingw-w64 mingw32.lib",
365 @"mingw-w64 msvcrt-os.lib",
366 @"mingw-w64 mingwex.lib",
367 @"mingw-w64 uuid.lib",
337};368};
338369
339pub const MiscError = struct {370pub const MiscError = struct {
340 /// Allocated with gpa.371 /// Allocated with gpa.
341 msg: []u8,372 msg: []u8,
342 children: ?AllErrors = null,373 children: ?ErrorBundle = null,
343374
344 pub fn deinit(misc_err: *MiscError, gpa: Allocator) void {375 pub fn deinit(misc_err: *MiscError, gpa: Allocator) void {
345 gpa.free(misc_err.msg);376 gpa.free(misc_err.msg);
...@@ -365,448 +396,6 @@ pub const LldError = struct {...@@ -365,448 +396,6 @@ pub const LldError = struct {
365 }396 }
366};397};
367398
368/// To support incremental compilation, errors are stored in various places
369/// so that they can be created and destroyed appropriately. This structure
370/// is used to collect all the errors from the various places into one
371/// convenient place for API users to consume. It is allocated into 1 arena
372/// and freed all at once.
373pub const AllErrors = struct {
374 arena: std.heap.ArenaAllocator.State,
375 list: []const Message,
376
377 pub const Message = union(enum) {
378 src: struct {
379 msg: []const u8,
380 src_path: []const u8,
381 line: u32,
382 column: u32,
383 span: Module.SrcLoc.Span,
384 /// Usually one, but incremented for redundant messages.
385 count: u32 = 1,
386 /// Does not include the trailing newline.
387 source_line: ?[]const u8,
388 notes: []const Message = &.{},
389 reference_trace: []Message = &.{},
390
391 /// Splits the error message up into lines to properly indent them
392 /// to allow for long, good-looking error messages.
393 ///
394 /// This is used to split the message in `@compileError("hello\nworld")` for example.
395 fn writeMsg(src: @This(), stderr: anytype, indent: usize) !void {
396 var lines = mem.split(u8, src.msg, "\n");
397 while (lines.next()) |line| {
398 try stderr.writeAll(line);
399 if (lines.index == null) break;
400 try stderr.writeByte('\n');
401 try stderr.writeByteNTimes(' ', indent);
402 }
403 }
404 },
405 plain: struct {
406 msg: []const u8,
407 notes: []Message = &.{},
408 /// Usually one, but incremented for redundant messages.
409 count: u32 = 1,
410 },
411
412 pub fn incrementCount(msg: *Message) void {
413 switch (msg.*) {
414 .src => |*src| {
415 src.count += 1;
416 },
417 .plain => |*plain| {
418 plain.count += 1;
419 },
420 }
421 }
422
423 pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void {
424 std.debug.getStderrMutex().lock();
425 defer std.debug.getStderrMutex().unlock();
426 const stderr = std.io.getStdErr();
427 return msg.renderToWriter(ttyconf, stderr.writer(), "error", .Red, 0) catch return;
428 }
429
430 pub fn renderToWriter(
431 msg: Message,
432 ttyconf: std.debug.TTY.Config,
433 stderr: anytype,
434 kind: []const u8,
435 color: std.debug.TTY.Color,
436 indent: usize,
437 ) anyerror!void {
438 var counting_writer = std.io.countingWriter(stderr);
439 const counting_stderr = counting_writer.writer();
440 switch (msg) {
441 .src => |src| {
442 try counting_stderr.writeByteNTimes(' ', indent);
443 try ttyconf.setColor(stderr, .Bold);
444 try counting_stderr.print("{s}:{d}:{d}: ", .{
445 src.src_path,
446 src.line + 1,
447 src.column + 1,
448 });
449 try ttyconf.setColor(stderr, color);
450 try counting_stderr.writeAll(kind);
451 try counting_stderr.writeAll(": ");
452 // This is the length of the part before the error message:
453 // e.g. "file.zig:4:5: error: "
454 const prefix_len = @intCast(usize, counting_stderr.context.bytes_written);
455 try ttyconf.setColor(stderr, .Reset);
456 try ttyconf.setColor(stderr, .Bold);
457 if (src.count == 1) {
458 try src.writeMsg(stderr, prefix_len);
459 try stderr.writeByte('\n');
460 } else {
461 try src.writeMsg(stderr, prefix_len);
462 try ttyconf.setColor(stderr, .Dim);
463 try stderr.print(" ({d} times)\n", .{src.count});
464 }
465 try ttyconf.setColor(stderr, .Reset);
466 if (src.source_line) |line| {
467 for (line) |b| switch (b) {
468 '\t' => try stderr.writeByte(' '),
469 else => try stderr.writeByte(b),
470 };
471 try stderr.writeByte('\n');
472 // TODO basic unicode code point monospace width
473 const before_caret = src.span.main - src.span.start;
474 // -1 since span.main includes the caret
475 const after_caret = src.span.end - src.span.main -| 1;
476 try stderr.writeByteNTimes(' ', src.column - before_caret);
477 try ttyconf.setColor(stderr, .Green);
478 try stderr.writeByteNTimes('~', before_caret);
479 try stderr.writeByte('^');
480 try stderr.writeByteNTimes('~', after_caret);
481 try stderr.writeByte('\n');
482 try ttyconf.setColor(stderr, .Reset);
483 }
484 for (src.notes) |note| {
485 try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent);
486 }
487 if (src.reference_trace.len != 0) {
488 try ttyconf.setColor(stderr, .Reset);
489 try ttyconf.setColor(stderr, .Dim);
490 try stderr.print("referenced by:\n", .{});
491 for (src.reference_trace) |reference| {
492 switch (reference) {
493 .src => |ref_src| try stderr.print(" {s}: {s}:{d}:{d}\n", .{
494 ref_src.msg,
495 ref_src.src_path,
496 ref_src.line + 1,
497 ref_src.column + 1,
498 }),
499 .plain => |plain| if (plain.count != 0) {
500 try stderr.print(
501 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
502 .{ plain.count, plain.count + src.reference_trace.len - 1 },
503 );
504 } else {
505 try stderr.print(
506 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
507 .{},
508 );
509 },
510 }
511 }
512 try stderr.writeByte('\n');
513 try ttyconf.setColor(stderr, .Reset);
514 }
515 },
516 .plain => |plain| {
517 try ttyconf.setColor(stderr, color);
518 try stderr.writeByteNTimes(' ', indent);
519 try stderr.writeAll(kind);
520 try stderr.writeAll(": ");
521 try ttyconf.setColor(stderr, .Reset);
522 if (plain.count == 1) {
523 try stderr.print("{s}\n", .{plain.msg});
524 } else {
525 try stderr.print("{s}", .{plain.msg});
526 try ttyconf.setColor(stderr, .Dim);
527 try stderr.print(" ({d} times)\n", .{plain.count});
528 }
529 try ttyconf.setColor(stderr, .Reset);
530 for (plain.notes) |note| {
531 try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent + 4);
532 }
533 },
534 }
535 }
536
537 pub const HashContext = struct {
538 pub fn hash(ctx: HashContext, key: *Message) u64 {
539 _ = ctx;
540 var hasher = std.hash.Wyhash.init(0);
541
542 switch (key.*) {
543 .src => |src| {
544 hasher.update(src.msg);
545 hasher.update(src.src_path);
546 std.hash.autoHash(&hasher, src.line);
547 std.hash.autoHash(&hasher, src.column);
548 std.hash.autoHash(&hasher, src.span.main);
549 },
550 .plain => |plain| {
551 hasher.update(plain.msg);
552 },
553 }
554
555 return hasher.final();
556 }
557
558 pub fn eql(ctx: HashContext, a: *Message, b: *Message) bool {
559 _ = ctx;
560 switch (a.*) {
561 .src => |a_src| switch (b.*) {
562 .src => |b_src| {
563 return mem.eql(u8, a_src.msg, b_src.msg) and
564 mem.eql(u8, a_src.src_path, b_src.src_path) and
565 a_src.line == b_src.line and
566 a_src.column == b_src.column and
567 a_src.span.main == b_src.span.main;
568 },
569 .plain => return false,
570 },
571 .plain => |a_plain| switch (b.*) {
572 .src => return false,
573 .plain => |b_plain| {
574 return mem.eql(u8, a_plain.msg, b_plain.msg);
575 },
576 },
577 }
578 }
579 };
580 };
581
582 pub fn deinit(self: *AllErrors, gpa: Allocator) void {
583 self.arena.promote(gpa).deinit();
584 }
585
586 pub fn add(
587 module: *Module,
588 arena: *std.heap.ArenaAllocator,
589 errors: *std.ArrayList(Message),
590 module_err_msg: Module.ErrorMsg,
591 ) !void {
592 const allocator = arena.allocator();
593
594 const notes_buf = try allocator.alloc(Message, module_err_msg.notes.len);
595 var note_i: usize = 0;
596
597 // De-duplicate error notes. The main use case in mind for this is
598 // too many "note: called from here" notes when eval branch quota is reached.
599 var seen_notes = std.HashMap(
600 *Message,
601 void,
602 Message.HashContext,
603 std.hash_map.default_max_load_percentage,
604 ).init(allocator);
605 const err_source = module_err_msg.src_loc.file_scope.getSource(module.gpa) catch |err| {
606 const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator);
607 try errors.append(.{
608 .plain = .{
609 .msg = try std.fmt.allocPrint(allocator, "unable to load '{s}': {s}", .{
610 file_path, @errorName(err),
611 }),
612 },
613 });
614 return;
615 };
616 const err_span = try module_err_msg.src_loc.span(module.gpa);
617 const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main);
618
619 for (module_err_msg.notes) |module_note| {
620 const source = try module_note.src_loc.file_scope.getSource(module.gpa);
621 const span = try module_note.src_loc.span(module.gpa);
622 const loc = std.zig.findLineColumn(source.bytes, span.main);
623 const file_path = try module_note.src_loc.file_scope.fullPath(allocator);
624 const note = &notes_buf[note_i];
625 note.* = .{
626 .src = .{
627 .src_path = file_path,
628 .msg = try allocator.dupe(u8, module_note.msg),
629 .span = span,
630 .line = @intCast(u32, loc.line),
631 .column = @intCast(u32, loc.column),
632 .source_line = if (err_loc.eql(loc)) null else try allocator.dupe(u8, loc.source_line),
633 },
634 };
635 const gop = try seen_notes.getOrPut(note);
636 if (gop.found_existing) {
637 gop.key_ptr.*.incrementCount();
638 } else {
639 note_i += 1;
640 }
641 }
642
643 const reference_trace = try allocator.alloc(Message, module_err_msg.reference_trace.len);
644 for (reference_trace, 0..) |*reference, i| {
645 const module_reference = module_err_msg.reference_trace[i];
646 if (module_reference.hidden != 0) {
647 reference.* = .{ .plain = .{ .msg = undefined, .count = module_reference.hidden } };
648 break;
649 } else if (module_reference.decl == null) {
650 reference.* = .{ .plain = .{ .msg = undefined, .count = 0 } };
651 break;
652 }
653 const source = try module_reference.src_loc.file_scope.getSource(module.gpa);
654 const span = try module_reference.src_loc.span(module.gpa);
655 const loc = std.zig.findLineColumn(source.bytes, span.main);
656 const file_path = try module_reference.src_loc.file_scope.fullPath(allocator);
657 reference.* = .{
658 .src = .{
659 .src_path = file_path,
660 .msg = try allocator.dupe(u8, std.mem.sliceTo(module_reference.decl.?, 0)),
661 .span = span,
662 .line = @intCast(u32, loc.line),
663 .column = @intCast(u32, loc.column),
664 .source_line = null,
665 },
666 };
667 }
668 const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator);
669 try errors.append(.{
670 .src = .{
671 .src_path = file_path,
672 .msg = try allocator.dupe(u8, module_err_msg.msg),
673 .span = err_span,
674 .line = @intCast(u32, err_loc.line),
675 .column = @intCast(u32, err_loc.column),
676 .notes = notes_buf[0..note_i],
677 .reference_trace = reference_trace,
678 .source_line = if (module_err_msg.src_loc.lazy == .entire_file) null else try allocator.dupe(u8, err_loc.source_line),
679 },
680 });
681 }
682
683 pub fn addZir(
684 arena: Allocator,
685 errors: *std.ArrayList(Message),
686 file: *Module.File,
687 ) !void {
688 assert(file.zir_loaded);
689 assert(file.tree_loaded);
690 assert(file.source_loaded);
691 const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)];
692 assert(payload_index != 0);
693
694 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index);
695 const items_len = header.data.items_len;
696 var extra_index = header.end;
697 var item_i: usize = 0;
698 while (item_i < items_len) : (item_i += 1) {
699 const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
700 extra_index = item.end;
701 const err_span = blk: {
702 if (item.data.node != 0) {
703 break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node);
704 }
705 const token_starts = file.tree.tokens.items(.start);
706 const start = token_starts[item.data.token] + item.data.byte_offset;
707 const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset;
708 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
709 };
710 const err_loc = std.zig.findLineColumn(file.source, err_span.main);
711
712 var notes: []Message = &[0]Message{};
713 if (item.data.notes != 0) {
714 const block = file.zir.extraData(Zir.Inst.Block, item.data.notes);
715 const body = file.zir.extra[block.end..][0..block.data.body_len];
716 notes = try arena.alloc(Message, body.len);
717 for (notes, 0..) |*note, i| {
718 const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body[i]);
719 const msg = file.zir.nullTerminatedString(note_item.data.msg);
720 const span = blk: {
721 if (note_item.data.node != 0) {
722 break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node);
723 }
724 const token_starts = file.tree.tokens.items(.start);
725 const start = token_starts[note_item.data.token] + note_item.data.byte_offset;
726 const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset;
727 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
728 };
729 const loc = std.zig.findLineColumn(file.source, span.main);
730
731 note.* = .{
732 .src = .{
733 .src_path = try file.fullPath(arena),
734 .msg = try arena.dupe(u8, msg),
735 .span = span,
736 .line = @intCast(u32, loc.line),
737 .column = @intCast(u32, loc.column),
738 .notes = &.{}, // TODO rework this function to be recursive
739 .source_line = if (loc.eql(err_loc)) null else try arena.dupe(u8, loc.source_line),
740 },
741 };
742 }
743 }
744
745 const msg = file.zir.nullTerminatedString(item.data.msg);
746 try errors.append(.{
747 .src = .{
748 .src_path = try file.fullPath(arena),
749 .msg = try arena.dupe(u8, msg),
750 .span = err_span,
751 .line = @intCast(u32, err_loc.line),
752 .column = @intCast(u32, err_loc.column),
753 .notes = notes,
754 .source_line = try arena.dupe(u8, err_loc.source_line),
755 },
756 });
757 }
758 }
759
760 fn addPlain(
761 arena: *std.heap.ArenaAllocator,
762 errors: *std.ArrayList(Message),
763 msg: []const u8,
764 ) !void {
765 _ = arena;
766 try errors.append(.{ .plain = .{ .msg = msg } });
767 }
768
769 fn addPlainWithChildren(
770 arena: *std.heap.ArenaAllocator,
771 errors: *std.ArrayList(Message),
772 msg: []const u8,
773 optional_children: ?AllErrors,
774 ) !void {
775 const allocator = arena.allocator();
776 const duped_msg = try allocator.dupe(u8, msg);
777 if (optional_children) |*children| {
778 try errors.append(.{ .plain = .{
779 .msg = duped_msg,
780 .notes = try dupeList(children.list, allocator),
781 } });
782 } else {
783 try errors.append(.{ .plain = .{ .msg = duped_msg } });
784 }
785 }
786
787 fn dupeList(list: []const Message, arena: Allocator) Allocator.Error![]Message {
788 const duped_list = try arena.alloc(Message, list.len);
789 for (list, 0..) |item, i| {
790 duped_list[i] = switch (item) {
791 .src => |src| .{ .src = .{
792 .msg = try arena.dupe(u8, src.msg),
793 .src_path = try arena.dupe(u8, src.src_path),
794 .line = src.line,
795 .column = src.column,
796 .span = src.span,
797 .source_line = if (src.source_line) |s| try arena.dupe(u8, s) else null,
798 .notes = try dupeList(src.notes, arena),
799 } },
800 .plain => |plain| .{ .plain = .{
801 .msg = try arena.dupe(u8, plain.msg),
802 .notes = try dupeList(plain.notes, arena),
803 } },
804 };
805 }
806 return duped_list;
807 }
808};
809
810pub const Directory = Cache.Directory;399pub const Directory = Cache.Directory;
811400
812pub const EmitLoc = struct {401pub const EmitLoc = struct {
...@@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory(...@@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory(
2259 }1848 }
2260}1849}
22611850
1851pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void {
1852 comp.bin_file.child_pid = pid;
1853 try comp.makeBinFileWritable();
1854 try comp.update(prog_node);
1855 try comp.makeBinFileExecutable();
1856}
1857
2262/// Detect changes to source files, perform semantic analysis, and update the output files.1858/// Detect changes to source files, perform semantic analysis, and update the output files.
2263pub fn update(comp: *Compilation) !void {1859pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void {
2264 const tracy_trace = trace(@src());1860 const tracy_trace = trace(@src());
2265 defer tracy_trace.end();1861 defer tracy_trace.end();
22661862
2267 comp.clearMiscFailures();1863 comp.clearMiscFailures();
1864 comp.last_update_was_cache_hit = false;
22681865
2269 var man: Cache.Manifest = undefined;1866 var man: Cache.Manifest = undefined;
2270 defer if (comp.whole_cache_manifest != null) man.deinit();1867 defer if (comp.whole_cache_manifest != null) man.deinit();
...@@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void {...@@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void {
2292 return err;1889 return err;
2293 };1890 };
2294 if (is_hit) {1891 if (is_hit) {
1892 comp.last_update_was_cache_hit = true;
2295 log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name});1893 log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name});
2296 const digest = man.final();1894 const digest = man.final();
22971895
...@@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void {...@@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void {
2407 }2005 }
2408 }2006 }
24092007
2410 // If the terminal is dumb, we dont want to show the user all the output.
2411 var progress: std.Progress = .{ .dont_print_on_dumb = true };
2412 const main_progress_node = progress.start("", 0);
2413 defer main_progress_node.end();
2414 switch (comp.color) {
2415 .off => {
2416 progress.terminal = null;
2417 },
2418 .on => {
2419 progress.terminal = std.io.getStdErr();
2420 progress.supports_ansi_escape_codes = true;
2421 },
2422 .auto => {},
2423 }
2424
2425 try comp.performAllTheWork(main_progress_node);2008 try comp.performAllTheWork(main_progress_node);
24262009
2427 if (comp.bin_file.options.module) |module| {2010 if (comp.bin_file.options.module) |module| {
...@@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void {...@@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void {
2891}2474}
28922475
2893/// This function is temporally single-threaded.2476/// This function is temporally single-threaded.
2894pub fn totalErrorCount(self: *Compilation) usize {2477pub fn totalErrorCount(self: *Compilation) u32 {
2895 var total: usize = self.failed_c_objects.count() + self.misc_failures.count() +2478 var total: usize = self.failed_c_objects.count() + self.misc_failures.count() +
2896 @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len;2479 @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len;
28972480
...@@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize {...@@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize {
2951 }2534 }
2952 }2535 }
29532536
2954 return total;2537 return @intCast(u32, total);
2955}2538}
29562539
2957/// This function is temporally single-threaded.2540/// This function is temporally single-threaded.
2958pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {2541pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
2959 var arena = std.heap.ArenaAllocator.init(self.gpa);2542 const gpa = self.gpa;
2960 errdefer arena.deinit();
2961 const arena_allocator = arena.allocator();
29622543
2963 var errors = std.ArrayList(AllErrors.Message).init(self.gpa);2544 var bundle: ErrorBundle.Wip = undefined;
2964 defer errors.deinit();2545 try bundle.init(gpa);
2546 defer bundle.deinit();
29652547
2966 {2548 {
2967 var it = self.failed_c_objects.iterator();2549 var it = self.failed_c_objects.iterator();
...@@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
2970 const err_msg = entry.value_ptr.*;2552 const err_msg = entry.value_ptr.*;
2971 // TODO these fields will need to be adjusted when we have proper2553 // TODO these fields will need to be adjusted when we have proper
2972 // C error reporting bubbling up.2554 // C error reporting bubbling up.
2973 try errors.append(.{2555 try bundle.addRootErrorMessage(.{
2974 .src = .{2556 .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}),
2975 .src_path = try arena_allocator.dupe(u8, c_object.src.src_path),2557 .src_loc = try bundle.addSourceLocation(.{
2976 .msg = try std.fmt.allocPrint(arena_allocator, "unable to build C object: {s}", .{2558 .src_path = try bundle.addString(c_object.src.src_path),
2977 err_msg.msg,2559 .span_start = 0,
2978 }),2560 .span_main = 0,
2979 .span = .{ .start = 0, .end = 1, .main = 0 },2561 .span_end = 1,
2980 .line = err_msg.line,2562 .line = err_msg.line,
2981 .column = err_msg.column,2563 .column = err_msg.column,
2982 .source_line = null, // TODO2564 .source_line = 0, // TODO
2983 },2565 }),
2984 });2566 });
2985 }2567 }
2986 }2568 }
2569
2987 for (self.lld_errors.items) |lld_error| {2570 for (self.lld_errors.items) |lld_error| {
2988 const notes = try arena_allocator.alloc(AllErrors.Message, lld_error.context_lines.len);2571 const notes_len = @intCast(u32, lld_error.context_lines.len);
2989 for (lld_error.context_lines, 0..) |context_line, i| {
2990 notes[i] = .{ .plain = .{
2991 .msg = try arena_allocator.dupe(u8, context_line),
2992 } };
2993 }
29942572
2995 try errors.append(.{2573 try bundle.addRootErrorMessage(.{
2996 .plain = .{2574 .msg = try bundle.addString(lld_error.msg),
2997 .msg = try arena_allocator.dupe(u8, lld_error.msg),2575 .notes_len = notes_len,
2998 .notes = notes,
2999 },
3000 });2576 });
2577 const notes_start = try bundle.reserveNotes(notes_len);
2578 for (notes_start.., lld_error.context_lines) |note, context_line| {
2579 bundle.extra.items[note] = @enumToInt(bundle.addErrorMessageAssumeCapacity(.{
2580 .msg = try bundle.addString(context_line),
2581 }));
2582 }
3001 }2583 }
3002 for (self.misc_failures.values()) |*value| {2584 for (self.misc_failures.values()) |*value| {
3003 try AllErrors.addPlainWithChildren(&arena, &errors, value.msg, value.children);2585 try bundle.addRootErrorMessage(.{
2586 .msg = try bundle.addString(value.msg),
2587 .notes_len = if (value.children) |b| b.errorMessageCount() else 0,
2588 });
2589 if (value.children) |b| try bundle.addBundle(b);
3004 }2590 }
3005 if (self.alloc_failure_occurred) {2591 if (self.alloc_failure_occurred) {
3006 try AllErrors.addPlain(&arena, &errors, "memory allocation failure");2592 try bundle.addRootErrorMessage(.{
2593 .msg = try bundle.addString("memory allocation failure"),
2594 });
3007 }2595 }
3008 if (self.bin_file.options.module) |module| {2596 if (self.bin_file.options.module) |module| {
3009 {2597 {
3010 var it = module.failed_files.iterator();2598 var it = module.failed_files.iterator();
3011 while (it.next()) |entry| {2599 while (it.next()) |entry| {
3012 if (entry.value_ptr.*) |msg| {2600 if (entry.value_ptr.*) |msg| {
3013 try AllErrors.add(module, &arena, &errors, msg.*);2601 try addModuleErrorMsg(&bundle, msg.*);
3014 } else {2602 } else {
3015 // Must be ZIR errors. In order for ZIR errors to exist, the parsing2603 // Must be ZIR errors. Note that this may include AST errors.
3016 // must have completed successfully.2604 // addZirErrorMessages asserts that the tree is loaded.
3017 const tree = try entry.key_ptr.*.getTree(module.gpa);2605 _ = try entry.key_ptr.*.getTree(gpa);
3018 assert(tree.errors.len == 0);2606 try addZirErrorMessages(&bundle, entry.key_ptr.*);
3019 try AllErrors.addZir(arena_allocator, &errors, entry.key_ptr.*);
3020 }2607 }
3021 }2608 }
3022 }2609 }
...@@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
3024 var it = module.failed_embed_files.iterator();2611 var it = module.failed_embed_files.iterator();
3025 while (it.next()) |entry| {2612 while (it.next()) |entry| {
3026 const msg = entry.value_ptr.*;2613 const msg = entry.value_ptr.*;
3027 try AllErrors.add(module, &arena, &errors, msg.*);2614 try addModuleErrorMsg(&bundle, msg.*);
3028 }2615 }
3029 }2616 }
3030 {2617 {
...@@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
3034 // Skip errors for Decls within files that had a parse failure.2621 // Skip errors for Decls within files that had a parse failure.
3035 // We'll try again once parsing succeeds.2622 // We'll try again once parsing succeeds.
3036 if (decl.getFileScope().okToReportErrors()) {2623 if (decl.getFileScope().okToReportErrors()) {
3037 try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*);2624 try addModuleErrorMsg(&bundle, entry.value_ptr.*.*);
3038 if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| {2625 if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| {
3039 if (c_error.path) |some|2626 try bundle.addRootErrorMessage(.{
3040 try errors.append(.{2627 .msg = try bundle.addString(std.mem.span(c_error.msg)),
3041 .src = .{2628 .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(.{
3042 .src_path = try arena_allocator.dupe(u8, std.mem.span(some)),2629 .src_path = try bundle.addString(std.mem.span(some)),
3043 .span = .{ .start = c_error.offset, .end = c_error.offset + 1, .main = c_error.offset },2630 .span_start = c_error.offset,
3044 .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)),2631 .span_main = c_error.offset,
3045 .line = c_error.line,2632 .span_end = c_error.offset + 1,
3046 .column = c_error.column,2633 .line = c_error.line,
3047 .source_line = if (c_error.source_line) |line| try arena_allocator.dupe(u8, std.mem.span(line)) else null,2634 .column = c_error.column,
3048 },2635 .source_line = if (c_error.source_line) |line| try bundle.addString(std.mem.span(line)) else 0,
3049 })2636 }) else .none,
3050 else2637 });
3051 try errors.append(.{
3052 .plain = .{ .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)) },
3053 });
3054 };2638 };
3055 }2639 }
3056 }2640 }
...@@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
3062 // Skip errors for Decls within files that had a parse failure.2646 // Skip errors for Decls within files that had a parse failure.
3063 // We'll try again once parsing succeeds.2647 // We'll try again once parsing succeeds.
3064 if (decl.getFileScope().okToReportErrors()) {2648 if (decl.getFileScope().okToReportErrors()) {
3065 try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*);2649 try addModuleErrorMsg(&bundle, entry.value_ptr.*.*);
3066 }2650 }
3067 }2651 }
3068 }2652 }
3069 for (module.failed_exports.values()) |value| {2653 for (module.failed_exports.values()) |value| {
3070 try AllErrors.add(module, &arena, &errors, value.*);2654 try addModuleErrorMsg(&bundle, value.*);
3071 }2655 }
3072 }2656 }
30732657
3074 if (errors.items.len == 0) {2658 if (bundle.root_list.items.len == 0) {
3075 if (self.link_error_flags.no_entry_point_found) {2659 if (self.link_error_flags.no_entry_point_found) {
3076 try errors.append(.{2660 try bundle.addRootErrorMessage(.{
3077 .plain = .{2661 .msg = try bundle.addString("no entry point found"),
3078 .msg = try std.fmt.allocPrint(arena_allocator, "no entry point found", .{}),
3079 },
3080 });2662 });
3081 }2663 }
3082 }2664 }
30832665
3084 if (self.link_error_flags.missing_libc) {2666 if (self.link_error_flags.missing_libc) {
3085 const notes = try arena_allocator.create([2]AllErrors.Message);2667 try bundle.addRootErrorMessage(.{
3086 notes.* = .{2668 .msg = try bundle.addString("libc not available"),
3087 .{ .plain = .{2669 .notes_len = 2,
3088 .msg = try arena_allocator.dupe(u8, "run 'zig libc -h' to learn about libc installations"),
3089 } },
3090 .{ .plain = .{
3091 .msg = try arena_allocator.dupe(u8, "run 'zig targets' to see the targets for which zig can always provide libc"),
3092 } },
3093 };
3094 try errors.append(.{
3095 .plain = .{
3096 .msg = try std.fmt.allocPrint(arena_allocator, "libc not available", .{}),
3097 .notes = notes,
3098 },
3099 });2670 });
2671 const notes_start = try bundle.reserveNotes(2);
2672 bundle.extra.items[notes_start + 0] = @enumToInt(try bundle.addErrorMessage(.{
2673 .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"),
2674 }));
2675 bundle.extra.items[notes_start + 1] = @enumToInt(try bundle.addErrorMessage(.{
2676 .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"),
2677 }));
3100 }2678 }
31012679
3102 if (self.bin_file.options.module) |module| {2680 if (self.bin_file.options.module) |module| {
3103 if (errors.items.len == 0 and module.compile_log_decls.count() != 0) {2681 if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) {
3104 const keys = module.compile_log_decls.keys();2682 const keys = module.compile_log_decls.keys();
3105 const values = module.compile_log_decls.values();2683 const values = module.compile_log_decls.values();
3106 // First one will be the error; subsequent ones will be notes.2684 // First one will be the error; subsequent ones will be notes.
...@@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
3109 const err_msg = Module.ErrorMsg{2687 const err_msg = Module.ErrorMsg{
3110 .src_loc = src_loc,2688 .src_loc = src_loc,
3111 .msg = "found compile log statement",2689 .msg = "found compile log statement",
3112 .notes = try self.gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1),2690 .notes = try gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1),
3113 };2691 };
3114 defer self.gpa.free(err_msg.notes);2692 defer gpa.free(err_msg.notes);
31152693
3116 for (keys[1..], 0..) |key, i| {2694 for (keys[1..], 0..) |key, i| {
3117 const note_decl = module.declPtr(key);2695 const note_decl = module.declPtr(key);
...@@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
3121 };2699 };
3122 }2700 }
31232701
3124 try AllErrors.add(module, &arena, &errors, err_msg);2702 try addModuleErrorMsg(&bundle, err_msg);
2703 }
2704 }
2705
2706 assert(self.totalErrorCount() == bundle.root_list.items.len);
2707
2708 const compile_log_text = if (self.bin_file.options.module) |m| m.compile_log_text.items else "";
2709 return bundle.toOwnedBundle(compile_log_text);
2710}
2711
2712pub const ErrorNoteHashContext = struct {
2713 eb: *const ErrorBundle.Wip,
2714
2715 pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 {
2716 var hasher = std.hash.Wyhash.init(0);
2717 const eb = ctx.eb.tmpBundle();
2718
2719 hasher.update(eb.nullTerminatedString(key.msg));
2720 if (key.src_loc != .none) {
2721 const src = eb.getSourceLocation(key.src_loc);
2722 hasher.update(eb.nullTerminatedString(src.src_path));
2723 std.hash.autoHash(&hasher, src.line);
2724 std.hash.autoHash(&hasher, src.column);
2725 std.hash.autoHash(&hasher, src.span_main);
3125 }2726 }
2727
2728 return @truncate(u32, hasher.final());
3126 }2729 }
31272730
3128 assert(errors.items.len == self.totalErrorCount());2731 pub fn eql(
2732 ctx: ErrorNoteHashContext,
2733 a: ErrorBundle.ErrorMessage,
2734 b: ErrorBundle.ErrorMessage,
2735 b_index: usize,
2736 ) bool {
2737 _ = b_index;
2738 const eb = ctx.eb.tmpBundle();
2739 const msg_a = eb.nullTerminatedString(a.msg);
2740 const msg_b = eb.nullTerminatedString(b.msg);
2741 if (!std.mem.eql(u8, msg_a, msg_b)) return false;
2742
2743 if (a.src_loc == .none and b.src_loc == .none) return true;
2744 if (a.src_loc == .none or b.src_loc == .none) return false;
2745 const src_a = eb.getSourceLocation(a.src_loc);
2746 const src_b = eb.getSourceLocation(b.src_loc);
2747
2748 const src_path_a = eb.nullTerminatedString(src_a.src_path);
2749 const src_path_b = eb.nullTerminatedString(src_b.src_path);
2750
2751 return std.mem.eql(u8, src_path_a, src_path_b) and
2752 src_a.line == src_b.line and
2753 src_a.column == src_b.column and
2754 src_a.span_main == src_b.span_main;
2755 }
2756};
31292757
3130 return AllErrors{2758pub fn addModuleErrorMsg(eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void {
3131 .list = try arena_allocator.dupe(AllErrors.Message, errors.items),2759 const gpa = eb.gpa;
3132 .arena = arena.state,2760 const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| {
2761 const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa);
2762 defer gpa.free(file_path);
2763 try eb.addRootErrorMessage(.{
2764 .msg = try eb.printString("unable to load '{s}': {s}", .{
2765 file_path, @errorName(err),
2766 }),
2767 });
2768 return;
3133 };2769 };
2770 const err_span = try module_err_msg.src_loc.span(gpa);
2771 const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main);
2772 const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa);
2773 defer gpa.free(file_path);
2774
2775 var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{};
2776 defer ref_traces.deinit(gpa);
2777
2778 for (module_err_msg.reference_trace) |module_reference| {
2779 if (module_reference.hidden != 0) {
2780 try ref_traces.append(gpa, .{
2781 .decl_name = module_reference.hidden,
2782 .src_loc = .none,
2783 });
2784 break;
2785 } else if (module_reference.decl == null) {
2786 try ref_traces.append(gpa, .{
2787 .decl_name = 0,
2788 .src_loc = .none,
2789 });
2790 break;
2791 }
2792 const source = try module_reference.src_loc.file_scope.getSource(gpa);
2793 const span = try module_reference.src_loc.span(gpa);
2794 const loc = std.zig.findLineColumn(source.bytes, span.main);
2795 const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa);
2796 defer gpa.free(rt_file_path);
2797 try ref_traces.append(gpa, .{
2798 .decl_name = try eb.addString(std.mem.sliceTo(module_reference.decl.?, 0)),
2799 .src_loc = try eb.addSourceLocation(.{
2800 .src_path = try eb.addString(rt_file_path),
2801 .span_start = span.start,
2802 .span_main = span.main,
2803 .span_end = span.end,
2804 .line = @intCast(u32, loc.line),
2805 .column = @intCast(u32, loc.column),
2806 .source_line = 0,
2807 }),
2808 });
2809 }
2810
2811 const src_loc = try eb.addSourceLocation(.{
2812 .src_path = try eb.addString(file_path),
2813 .span_start = err_span.start,
2814 .span_main = err_span.main,
2815 .span_end = err_span.end,
2816 .line = @intCast(u32, err_loc.line),
2817 .column = @intCast(u32, err_loc.column),
2818 .source_line = if (module_err_msg.src_loc.lazy == .entire_file)
2819 0
2820 else
2821 try eb.addString(err_loc.source_line),
2822 .reference_trace_len = @intCast(u32, ref_traces.items.len),
2823 });
2824
2825 for (ref_traces.items) |rt| {
2826 try eb.addReferenceTrace(rt);
2827 }
2828
2829 // De-duplicate error notes. The main use case in mind for this is
2830 // too many "note: called from here" notes when eval branch quota is reached.
2831 var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .{};
2832 defer notes.deinit(gpa);
2833
2834 for (module_err_msg.notes) |module_note| {
2835 const source = try module_note.src_loc.file_scope.getSource(gpa);
2836 const span = try module_note.src_loc.span(gpa);
2837 const loc = std.zig.findLineColumn(source.bytes, span.main);
2838 const note_file_path = try module_note.src_loc.file_scope.fullPath(gpa);
2839 defer gpa.free(note_file_path);
2840
2841 const gop = try notes.getOrPutContext(gpa, .{
2842 .msg = try eb.addString(module_note.msg),
2843 .src_loc = try eb.addSourceLocation(.{
2844 .src_path = try eb.addString(note_file_path),
2845 .span_start = span.start,
2846 .span_main = span.main,
2847 .span_end = span.end,
2848 .line = @intCast(u32, loc.line),
2849 .column = @intCast(u32, loc.column),
2850 .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(loc.source_line),
2851 }),
2852 }, .{ .eb = eb });
2853 if (gop.found_existing) {
2854 gop.key_ptr.count += 1;
2855 }
2856 }
2857
2858 const notes_len = @intCast(u32, notes.entries.len);
2859
2860 try eb.addRootErrorMessage(.{
2861 .msg = try eb.addString(module_err_msg.msg),
2862 .src_loc = src_loc,
2863 .notes_len = notes_len,
2864 });
2865
2866 const notes_start = try eb.reserveNotes(notes_len);
2867
2868 for (notes_start.., notes.keys()) |i, note| {
2869 eb.extra.items[i] = @enumToInt(try eb.addErrorMessage(note));
2870 }
3134}2871}
31352872
3136pub fn getCompileLogOutput(self: *Compilation) []const u8 {2873pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {
3137 const module = self.bin_file.options.module orelse return &[0]u8{};2874 assert(file.zir_loaded);
3138 return module.compile_log_text.items;2875 assert(file.tree_loaded);
2876 assert(file.source_loaded);
2877 const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)];
2878 assert(payload_index != 0);
2879 const gpa = eb.gpa;
2880
2881 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index);
2882 const items_len = header.data.items_len;
2883 var extra_index = header.end;
2884 for (0..items_len) |_| {
2885 const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
2886 extra_index = item.end;
2887 const err_span = blk: {
2888 if (item.data.node != 0) {
2889 break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node);
2890 }
2891 const token_starts = file.tree.tokens.items(.start);
2892 const start = token_starts[item.data.token] + item.data.byte_offset;
2893 const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset;
2894 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
2895 };
2896 const err_loc = std.zig.findLineColumn(file.source, err_span.main);
2897
2898 {
2899 const msg = file.zir.nullTerminatedString(item.data.msg);
2900 const src_path = try file.fullPath(gpa);
2901 defer gpa.free(src_path);
2902 try eb.addRootErrorMessage(.{
2903 .msg = try eb.addString(msg),
2904 .src_loc = try eb.addSourceLocation(.{
2905 .src_path = try eb.addString(src_path),
2906 .span_start = err_span.start,
2907 .span_main = err_span.main,
2908 .span_end = err_span.end,
2909 .line = @intCast(u32, err_loc.line),
2910 .column = @intCast(u32, err_loc.column),
2911 .source_line = try eb.addString(err_loc.source_line),
2912 }),
2913 .notes_len = item.data.notesLen(file.zir),
2914 });
2915 }
2916
2917 if (item.data.notes != 0) {
2918 const notes_start = try eb.reserveNotes(item.data.notes);
2919 const block = file.zir.extraData(Zir.Inst.Block, item.data.notes);
2920 const body = file.zir.extra[block.end..][0..block.data.body_len];
2921 for (notes_start.., body) |note_i, body_elem| {
2922 const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem);
2923 const msg = file.zir.nullTerminatedString(note_item.data.msg);
2924 const span = blk: {
2925 if (note_item.data.node != 0) {
2926 break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node);
2927 }
2928 const token_starts = file.tree.tokens.items(.start);
2929 const start = token_starts[note_item.data.token] + note_item.data.byte_offset;
2930 const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset;
2931 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
2932 };
2933 const loc = std.zig.findLineColumn(file.source, span.main);
2934 const src_path = try file.fullPath(gpa);
2935 defer gpa.free(src_path);
2936
2937 eb.extra.items[note_i] = @enumToInt(try eb.addErrorMessage(.{
2938 .msg = try eb.addString(msg),
2939 .src_loc = try eb.addSourceLocation(.{
2940 .src_path = try eb.addString(src_path),
2941 .span_start = span.start,
2942 .span_main = span.main,
2943 .span_end = span.end,
2944 .line = @intCast(u32, loc.line),
2945 .column = @intCast(u32, loc.column),
2946 .source_line = if (loc.eql(err_loc))
2947 0
2948 else
2949 try eb.addString(loc.source_line),
2950 }),
2951 .notes_len = 0, // TODO rework this function to be recursive
2952 }));
2953 }
2954 }
2955 }
3139}2956}
31402957
3141pub fn performAllTheWork(2958pub fn performAllTheWork(
...@@ -3231,11 +3048,11 @@ pub fn performAllTheWork(...@@ -3231,11 +3048,11 @@ pub fn performAllTheWork(
3231 // backend, preventing anonymous Decls from being prematurely destroyed.3048 // backend, preventing anonymous Decls from being prematurely destroyed.
3232 while (true) {3049 while (true) {
3233 if (comp.work_queue.readItem()) |work_item| {3050 if (comp.work_queue.readItem()) |work_item| {
3234 try processOneJob(comp, work_item);3051 try processOneJob(comp, work_item, main_progress_node);
3235 continue;3052 continue;
3236 }3053 }
3237 if (comp.anon_work_queue.readItem()) |work_item| {3054 if (comp.anon_work_queue.readItem()) |work_item| {
3238 try processOneJob(comp, work_item);3055 try processOneJob(comp, work_item, main_progress_node);
3239 continue;3056 continue;
3240 }3057 }
3241 break;3058 break;
...@@ -3243,16 +3060,16 @@ pub fn performAllTheWork(...@@ -3243,16 +3060,16 @@ pub fn performAllTheWork(
32433060
3244 if (comp.job_queued_compiler_rt_lib) {3061 if (comp.job_queued_compiler_rt_lib) {
3245 comp.job_queued_compiler_rt_lib = false;3062 comp.job_queued_compiler_rt_lib = false;
3246 buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib);3063 buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node);
3247 }3064 }
32483065
3249 if (comp.job_queued_compiler_rt_obj) {3066 if (comp.job_queued_compiler_rt_obj) {
3250 comp.job_queued_compiler_rt_obj = false;3067 comp.job_queued_compiler_rt_obj = false;
3251 buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj);3068 buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node);
3252 }3069 }
3253}3070}
32543071
3255fn processOneJob(comp: *Compilation, job: Job) !void {3072fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void {
3256 switch (job) {3073 switch (job) {
3257 .codegen_decl => |decl_index| {3074 .codegen_decl => |decl_index| {
3258 const module = comp.bin_file.options.module.?;3075 const module = comp.bin_file.options.module.?;
...@@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3404 const named_frame = tracy.namedFrame("glibc_crt_file");3221 const named_frame = tracy.namedFrame("glibc_crt_file");
3405 defer named_frame.end();3222 defer named_frame.end();
34063223
3407 glibc.buildCRTFile(comp, crt_file) catch |err| {3224 glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| {
3408 // TODO Surface more error details.3225 // TODO Surface more error details.
3409 comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{3226 comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{
3410 @errorName(err),3227 @errorName(err),
...@@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3415 const named_frame = tracy.namedFrame("glibc_shared_objects");3232 const named_frame = tracy.namedFrame("glibc_shared_objects");
3416 defer named_frame.end();3233 defer named_frame.end();
34173234
3418 glibc.buildSharedObjects(comp) catch |err| {3235 glibc.buildSharedObjects(comp, prog_node) catch |err| {
3419 // TODO Surface more error details.3236 // TODO Surface more error details.
3420 comp.lockAndSetMiscFailure(3237 comp.lockAndSetMiscFailure(
3421 .glibc_shared_objects,3238 .glibc_shared_objects,
...@@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3428 const named_frame = tracy.namedFrame("musl_crt_file");3245 const named_frame = tracy.namedFrame("musl_crt_file");
3429 defer named_frame.end();3246 defer named_frame.end();
34303247
3431 musl.buildCRTFile(comp, crt_file) catch |err| {3248 musl.buildCRTFile(comp, crt_file, prog_node) catch |err| {
3432 // TODO Surface more error details.3249 // TODO Surface more error details.
3433 comp.lockAndSetMiscFailure(3250 comp.lockAndSetMiscFailure(
3434 .musl_crt_file,3251 .musl_crt_file,
...@@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3441 const named_frame = tracy.namedFrame("mingw_crt_file");3258 const named_frame = tracy.namedFrame("mingw_crt_file");
3442 defer named_frame.end();3259 defer named_frame.end();
34433260
3444 mingw.buildCRTFile(comp, crt_file) catch |err| {3261 mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| {
3445 // TODO Surface more error details.3262 // TODO Surface more error details.
3446 comp.lockAndSetMiscFailure(3263 comp.lockAndSetMiscFailure(
3447 .mingw_crt_file,3264 .mingw_crt_file,
...@@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3468 const named_frame = tracy.namedFrame("libunwind");3285 const named_frame = tracy.namedFrame("libunwind");
3469 defer named_frame.end();3286 defer named_frame.end();
34703287
3471 libunwind.buildStaticLib(comp) catch |err| {3288 libunwind.buildStaticLib(comp, prog_node) catch |err| {
3472 // TODO Surface more error details.3289 // TODO Surface more error details.
3473 comp.lockAndSetMiscFailure(3290 comp.lockAndSetMiscFailure(
3474 .libunwind,3291 .libunwind,
...@@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3481 const named_frame = tracy.namedFrame("libcxx");3298 const named_frame = tracy.namedFrame("libcxx");
3482 defer named_frame.end();3299 defer named_frame.end();
34833300
3484 libcxx.buildLibCXX(comp) catch |err| {3301 libcxx.buildLibCXX(comp, prog_node) catch |err| {
3485 // TODO Surface more error details.3302 // TODO Surface more error details.
3486 comp.lockAndSetMiscFailure(3303 comp.lockAndSetMiscFailure(
3487 .libcxx,3304 .libcxx,
...@@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3494 const named_frame = tracy.namedFrame("libcxxabi");3311 const named_frame = tracy.namedFrame("libcxxabi");
3495 defer named_frame.end();3312 defer named_frame.end();
34963313
3497 libcxx.buildLibCXXABI(comp) catch |err| {3314 libcxx.buildLibCXXABI(comp, prog_node) catch |err| {
3498 // TODO Surface more error details.3315 // TODO Surface more error details.
3499 comp.lockAndSetMiscFailure(3316 comp.lockAndSetMiscFailure(
3500 .libcxxabi,3317 .libcxxabi,
...@@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3507 const named_frame = tracy.namedFrame("libtsan");3324 const named_frame = tracy.namedFrame("libtsan");
3508 defer named_frame.end();3325 defer named_frame.end();
35093326
3510 libtsan.buildTsan(comp) catch |err| {3327 libtsan.buildTsan(comp, prog_node) catch |err| {
3511 // TODO Surface more error details.3328 // TODO Surface more error details.
3512 comp.lockAndSetMiscFailure(3329 comp.lockAndSetMiscFailure(
3513 .libtsan,3330 .libtsan,
...@@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3520 const named_frame = tracy.namedFrame("wasi_libc_crt_file");3337 const named_frame = tracy.namedFrame("wasi_libc_crt_file");
3521 defer named_frame.end();3338 defer named_frame.end();
35223339
3523 wasi_libc.buildCRTFile(comp, crt_file) catch |err| {3340 wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| {
3524 // TODO Surface more error details.3341 // TODO Surface more error details.
3525 comp.lockAndSetMiscFailure(3342 comp.lockAndSetMiscFailure(
3526 .wasi_libc_crt_file,3343 .wasi_libc_crt_file,
...@@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3538 .Lib,3355 .Lib,
3539 &comp.libssp_static_lib,3356 &comp.libssp_static_lib,
3540 .libssp,3357 .libssp,
3358 prog_node,
3541 ) catch |err| switch (err) {3359 ) catch |err| switch (err) {
3542 error.OutOfMemory => return error.OutOfMemory,3360 error.OutOfMemory => return error.OutOfMemory,
3543 error.SubCompilationFailed => return, // error reported already3361 error.SubCompilationFailed => return, // error reported already
...@@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3557 .Lib,3375 .Lib,
3558 &comp.libc_static_lib,3376 &comp.libc_static_lib,
3559 .zig_libc,3377 .zig_libc,
3378 prog_node,
3560 ) catch |err| switch (err) {3379 ) catch |err| switch (err) {
3561 error.OutOfMemory => return error.OutOfMemory,3380 error.OutOfMemory => return error.OutOfMemory,
3562 error.SubCompilationFailed => return, // error reported already3381 error.SubCompilationFailed => return, // error reported already
...@@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot(...@@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot(
3897 comp: *Compilation,3716 comp: *Compilation,
3898 output_mode: std.builtin.OutputMode,3717 output_mode: std.builtin.OutputMode,
3899 out: *?CRTFile,3718 out: *?CRTFile,
3719 prog_node: *std.Progress.Node,
3900) void {3720) void {
3901 comp.buildOutputFromZig("compiler_rt.zig", output_mode, out, .compiler_rt) catch |err| switch (err) {3721 comp.buildOutputFromZig(
3722 "compiler_rt.zig",
3723 output_mode,
3724 out,
3725 .compiler_rt,
3726 prog_node,
3727 ) catch |err| switch (err) {
3902 error.SubCompilationFailed => return, // error reported already3728 error.SubCompilationFailed => return, // error reported already
3903 else => comp.lockAndSetMiscFailure(3729 else => comp.lockAndSetMiscFailure(
3904 .compiler_rt,3730 .compiler_rt,
...@@ -5237,7 +5063,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -5237,7 +5063,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
5237 \\const std = @import("std");5063 \\const std = @import("std");
5238 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer5064 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
5239 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.5065 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
5240 \\pub const zig_version = std.SemanticVersion.parse("{s}") catch unreachable;5066 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
5067 \\pub const zig_version_string = "{s}";
5241 \\pub const zig_backend = std.builtin.CompilerBackend.{};5068 \\pub const zig_backend = std.builtin.CompilerBackend.{};
5242 \\5069 \\
5243 \\pub const output_mode = std.builtin.OutputMode.{};5070 \\pub const output_mode = std.builtin.OutputMode.{};
...@@ -5424,34 +5251,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -5424,34 +5251,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
5424 return buffer.toOwnedSliceSentinel(0);5251 return buffer.toOwnedSliceSentinel(0);
5425}5252}
54265253
5427pub fn updateSubCompilation(sub_compilation: *Compilation) !void {5254pub fn updateSubCompilation(
5428 try sub_compilation.update();5255 parent_comp: *Compilation,
54295256 sub_comp: *Compilation,
5430 // Look for compilation errors in this sub_compilation5257 misc_task: MiscTask,
5431 // TODO instead of logging these errors, handle them in the callsites5258 prog_node: *std.Progress.Node,
5432 // of updateSubCompilation and attach them as sub-errors, properly5259) !void {
5433 // surfacing the errors. You can see an example of this already5260 {
5434 // done inside buildOutputFromZig.5261 var sub_node = prog_node.start(@tagName(misc_task), 0);
5435 var errors = try sub_compilation.getAllErrorsAlloc();5262 sub_node.activate();
5436 defer errors.deinit(sub_compilation.gpa);5263 defer sub_node.end();
54375264
5438 if (errors.list.len != 0) {5265 try sub_comp.update(prog_node);
5439 for (errors.list) |full_err_msg| {5266 }
5440 switch (full_err_msg) {5267
5441 .src => |src| {5268 // Look for compilation errors in this sub compilation
5442 log.err("{s}:{d}:{d}: {s}", .{5269 const gpa = parent_comp.gpa;
5443 src.src_path,5270 var keep_errors = false;
5444 src.line + 1,5271 var errors = try sub_comp.getAllErrorsAlloc();
5445 src.column + 1,5272 defer if (!keep_errors) errors.deinit(gpa);
5446 src.msg,5273
5447 });5274 if (errors.errorMessageCount() > 0) {
5448 },5275 try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1);
5449 .plain => |plain| {5276 parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{
5450 log.err("{s}", .{plain.msg});5277 .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{
5451 },5278 @tagName(misc_task),
5452 }5279 }),
5453 }5280 .children = errors,
5454 return error.BuildingLibCObjectFailed;5281 });
5282 keep_errors = true;
5283 return error.SubCompilationFailed;
5455 }5284 }
5456}5285}
54575286
...@@ -5461,6 +5290,7 @@ fn buildOutputFromZig(...@@ -5461,6 +5290,7 @@ fn buildOutputFromZig(
5461 output_mode: std.builtin.OutputMode,5290 output_mode: std.builtin.OutputMode,
5462 out: *?CRTFile,5291 out: *?CRTFile,
5463 misc_task_tag: MiscTask,5292 misc_task_tag: MiscTask,
5293 prog_node: *std.Progress.Node,
5464) !void {5294) !void {
5465 const tracy_trace = trace(@src());5295 const tracy_trace = trace(@src());
5466 defer tracy_trace.end();5296 defer tracy_trace.end();
...@@ -5527,23 +5357,7 @@ fn buildOutputFromZig(...@@ -5527,23 +5357,7 @@ fn buildOutputFromZig(
5527 });5357 });
5528 defer sub_compilation.destroy();5358 defer sub_compilation.destroy();
55295359
5530 try sub_compilation.update();5360 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
5531 // Look for compilation errors in this sub_compilation.
5532 var keep_errors = false;
5533 var errors = try sub_compilation.getAllErrorsAlloc();
5534 defer if (!keep_errors) errors.deinit(sub_compilation.gpa);
5535
5536 if (errors.list.len != 0) {
5537 try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1);
5538 comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{
5539 .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{
5540 @tagName(misc_task_tag),
5541 }),
5542 .children = errors,
5543 });
5544 keep_errors = true;
5545 return error.SubCompilationFailed;
5546 }
55475361
5548 assert(out.* == null);5362 assert(out.* == null);
5549 out.* = Compilation.CRTFile{5363 out.* = Compilation.CRTFile{
...@@ -5558,6 +5372,8 @@ pub fn build_crt_file(...@@ -5558,6 +5372,8 @@ pub fn build_crt_file(
5558 comp: *Compilation,5372 comp: *Compilation,
5559 root_name: []const u8,5373 root_name: []const u8,
5560 output_mode: std.builtin.OutputMode,5374 output_mode: std.builtin.OutputMode,
5375 misc_task_tag: MiscTask,
5376 prog_node: *std.Progress.Node,
5561 c_source_files: []const Compilation.CSourceFile,5377 c_source_files: []const Compilation.CSourceFile,
5562) !void {5378) !void {
5563 const tracy_trace = trace(@src());5379 const tracy_trace = trace(@src());
...@@ -5618,7 +5434,7 @@ pub fn build_crt_file(...@@ -5618,7 +5434,7 @@ pub fn build_crt_file(
5618 });5434 });
5619 defer sub_compilation.destroy();5435 defer sub_compilation.destroy();
56205436
5621 try sub_compilation.updateSubCompilation();5437 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
56225438
5623 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);5439 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
56245440
src/Liveness.zig+2
...@@ -226,6 +226,7 @@ pub fn categorizeOperand(...@@ -226,6 +226,7 @@ pub fn categorizeOperand(
226 .ret_ptr,226 .ret_ptr,
227 .constant,227 .constant,
228 .const_ty,228 .const_ty,
229 .trap,
229 .breakpoint,230 .breakpoint,
230 .dbg_stmt,231 .dbg_stmt,
231 .dbg_inline_begin,232 .dbg_inline_begin,
...@@ -848,6 +849,7 @@ fn analyzeInst(...@@ -848,6 +849,7 @@ fn analyzeInst(
848 .ret_ptr,849 .ret_ptr,
849 .constant,850 .constant,
850 .const_ty,851 .const_ty,
852 .trap,
851 .breakpoint,853 .breakpoint,
852 .dbg_stmt,854 .dbg_stmt,
853 .dbg_inline_begin,855 .dbg_inline_begin,
src/Module.zig+72-125
...@@ -3528,6 +3528,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3528,6 +3528,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3528 const digest = hash: {3528 const digest = hash: {
3529 var path_hash: Cache.HashHelper = .{};3529 var path_hash: Cache.HashHelper = .{};
3530 path_hash.addBytes(build_options.version);3530 path_hash.addBytes(build_options.version);
3531 path_hash.add(builtin.zig_backend);
3531 if (!want_local_cache) {3532 if (!want_local_cache) {
3532 path_hash.addOptionalBytes(file.pkg.root_src_directory.path);3533 path_hash.addOptionalBytes(file.pkg.root_src_directory.path);
3533 }3534 }
...@@ -3537,44 +3538,66 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3537,44 +3538,66 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3537 const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache;3538 const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache;
3538 const zir_dir = cache_directory.handle;3539 const zir_dir = cache_directory.handle;
35393540
3540 var cache_file: ?std.fs.File = null;
3541 defer if (cache_file) |f| f.close();
3542
3543 // Determine whether we need to reload the file from disk and redo parsing and AstGen.3541 // Determine whether we need to reload the file from disk and redo parsing and AstGen.
3544 switch (file.status) {3542 var lock: std.fs.File.Lock = switch (file.status) {
3545 .never_loaded, .retryable_failure => cached: {3543 .never_loaded, .retryable_failure => lock: {
3546 // First, load the cached ZIR code, if any.3544 // First, load the cached ZIR code, if any.
3547 log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{3545 log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{
3548 file.sub_file_path, want_local_cache, &digest,3546 file.sub_file_path, want_local_cache, &digest,
3549 });3547 });
35503548
3551 // We ask for a lock in order to coordinate with other zig processes.3549 break :lock .Shared;
3552 // If another process is already working on this file, we will get the cached3550 },
3553 // version. Likewise if we're working on AstGen and another process asks for3551 .parse_failure, .astgen_failure, .success_zir => lock: {
3554 // the cached file, they'll get it.3552 const unchanged_metadata =
3555 cache_file = zir_dir.openFile(&digest, .{ .lock = .Shared }) catch |err| switch (err) {3553 stat.size == file.stat.size and
3556 error.PathAlreadyExists => unreachable, // opening for reading3554 stat.mtime == file.stat.mtime and
3557 error.NoSpaceLeft => unreachable, // opening for reading3555 stat.inode == file.stat.inode;
3558 error.NotDir => unreachable, // no dir components3556
3559 error.InvalidUtf8 => unreachable, // it's a hex encoded name3557 if (unchanged_metadata) {
3560 error.BadPathName => unreachable, // it's a hex encoded name3558 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
3561 error.NameTooLong => unreachable, // it's a fixed size name3559 return;
3562 error.PipeBusy => unreachable, // it's not a pipe3560 }
3563 error.WouldBlock => unreachable, // not asking for non-blocking I/O3561
35643562 log.debug("metadata changed: {s}", .{file.sub_file_path});
3565 error.SymLinkLoop,3563
3566 error.FileNotFound,3564 break :lock .Exclusive;
3567 error.Unexpected,3565 },
3568 => break :cached,3566 };
3569
3570 else => |e| return e, // Retryable errors are handled at callsite.
3571 };
35723567
3568 // We ask for a lock in order to coordinate with other zig processes.
3569 // If another process is already working on this file, we will get the cached
3570 // version. Likewise if we're working on AstGen and another process asks for
3571 // the cached file, they'll get it.
3572 const cache_file = while (true) {
3573 break zir_dir.createFile(&digest, .{
3574 .read = true,
3575 .truncate = false,
3576 .lock = lock,
3577 }) catch |err| switch (err) {
3578 error.NotDir => unreachable, // no dir components
3579 error.InvalidUtf8 => unreachable, // it's a hex encoded name
3580 error.BadPathName => unreachable, // it's a hex encoded name
3581 error.NameTooLong => unreachable, // it's a fixed size name
3582 error.PipeBusy => unreachable, // it's not a pipe
3583 error.WouldBlock => unreachable, // not asking for non-blocking I/O
3584 // There are no dir components, so you would think that this was
3585 // unreachable, however we have observed on macOS two processes racing
3586 // to do openat() with O_CREAT manifest in ENOENT.
3587 error.FileNotFound => continue,
3588
3589 else => |e| return e, // Retryable errors are handled at callsite.
3590 };
3591 };
3592 defer cache_file.close();
3593
3594 while (true) {
3595 update: {
3573 // First we read the header to determine the lengths of arrays.3596 // First we read the header to determine the lengths of arrays.
3574 const header = cache_file.?.reader().readStruct(Zir.Header) catch |err| switch (err) {3597 const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) {
3575 // This can happen if Zig bails out of this function between creating3598 // This can happen if Zig bails out of this function between creating
3576 // the cached file and writing it.3599 // the cached file and writing it.
3577 error.EndOfStream => break :cached,3600 error.EndOfStream => break :update,
3578 else => |e| return e,3601 else => |e| return e,
3579 };3602 };
3580 const unchanged_metadata =3603 const unchanged_metadata =
...@@ -3584,7 +3607,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3584,7 +3607,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
35843607
3585 if (!unchanged_metadata) {3608 if (!unchanged_metadata) {
3586 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});3609 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
3587 break :cached;3610 break :update;
3588 }3611 }
3589 log.debug("AstGen cache hit: {s} instructions_len={d}", .{3612 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
3590 file.sub_file_path, header.instructions_len,3613 file.sub_file_path, header.instructions_len,
...@@ -3636,13 +3659,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3636,13 +3659,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3636 .iov_len = header.extra_len * 4,3659 .iov_len = header.extra_len * 4,
3637 },3660 },
3638 };3661 };
3639 const amt_read = try cache_file.?.readvAll(&iovecs);3662 const amt_read = try cache_file.readvAll(&iovecs);
3640 const amt_expected = zir.instructions.len * 9 +3663 const amt_expected = zir.instructions.len * 9 +
3641 zir.string_bytes.len +3664 zir.string_bytes.len +
3642 zir.extra.len * 4;3665 zir.extra.len * 4;
3643 if (amt_read != amt_expected) {3666 if (amt_read != amt_expected) {
3644 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});3667 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
3645 break :cached;3668 break :update;
3646 }3669 }
3647 if (data_has_safety_tag) {3670 if (data_has_safety_tag) {
3648 const tags = zir.instructions.items(.tag);3671 const tags = zir.instructions.items(.tag);
...@@ -3678,42 +3701,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3678,42 +3701,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3678 return error.AnalysisFail;3701 return error.AnalysisFail;
3679 }3702 }
3680 return;3703 return;
3681 },3704 }
3682 .parse_failure, .astgen_failure, .success_zir => {
3683 const unchanged_metadata =
3684 stat.size == file.stat.size and
3685 stat.mtime == file.stat.mtime and
3686 stat.inode == file.stat.inode;
3687
3688 if (unchanged_metadata) {
3689 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
3690 return;
3691 }
36923705
3693 log.debug("metadata changed: {s}", .{file.sub_file_path});3706 // If we already have the exclusive lock then it is our job to update.
3694 },3707 if (builtin.os.tag == .wasi or lock == .Exclusive) break;
3708 // Otherwise, unlock to give someone a chance to get the exclusive lock
3709 // and then upgrade to an exclusive lock.
3710 cache_file.unlock();
3711 lock = .Exclusive;
3712 try cache_file.lock(lock);
3695 }3713 }
3696 if (cache_file) |f| {
3697 f.close();
3698 cache_file = null;
3699 }
3700 cache_file = zir_dir.createFile(&digest, .{ .lock = .Exclusive }) catch |err| switch (err) {
3701 error.NotDir => unreachable, // no dir components
3702 error.InvalidUtf8 => unreachable, // it's a hex encoded name
3703 error.BadPathName => unreachable, // it's a hex encoded name
3704 error.NameTooLong => unreachable, // it's a fixed size name
3705 error.PipeBusy => unreachable, // it's not a pipe
3706 error.WouldBlock => unreachable, // not asking for non-blocking I/O
3707 error.FileNotFound => unreachable, // no dir components
37083714
3709 else => |e| {3715 // The cache is definitely stale so delete the contents to avoid an underwrite later.
3710 const pkg_path = file.pkg.root_src_directory.path orelse ".";3716 cache_file.setEndPos(0) catch |err| switch (err) {
3711 const cache_path = cache_directory.path orelse ".";3717 error.FileTooBig => unreachable, // 0 is not too big
3712 log.warn("unable to save cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{3718
3713 pkg_path, file.sub_file_path, cache_path, &digest, @errorName(e),3719 else => |e| return e,
3714 });
3715 return;
3716 },
3717 };3720 };
37183721
3719 mod.lockAndClearFileCompileError(file);3722 mod.lockAndClearFileCompileError(file);
...@@ -3753,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3753,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3753 file.source_loaded = true;3756 file.source_loaded = true;
37543757
3755 file.tree = try Ast.parse(gpa, source, .zig);3758 file.tree = try Ast.parse(gpa, source, .zig);
3756 defer if (!file.tree_loaded) file.tree.deinit(gpa);
3757
3758 if (file.tree.errors.len != 0) {
3759 const parse_err = file.tree.errors[0];
3760
3761 var msg = std.ArrayList(u8).init(gpa);
3762 defer msg.deinit();
3763
3764 const token_starts = file.tree.tokens.items(.start);
3765 const token_tags = file.tree.tokens.items(.tag);
3766
3767 const extra_offset = file.tree.errorOffset(parse_err);
3768 try file.tree.renderError(parse_err, msg.writer());
3769 const err_msg = try gpa.create(ErrorMsg);
3770 err_msg.* = .{
3771 .src_loc = .{
3772 .file_scope = file,
3773 .parent_decl_node = 0,
3774 .lazy = if (extra_offset == 0) .{
3775 .token_abs = parse_err.token,
3776 } else .{
3777 .byte_abs = token_starts[parse_err.token] + extra_offset,
3778 },
3779 },
3780 .msg = try msg.toOwnedSlice(),
3781 };
3782 if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) {
3783 const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len);
3784 const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off;
3785 try mod.errNoteNonLazy(.{
3786 .file_scope = file,
3787 .parent_decl_node = 0,
3788 .lazy = .{ .byte_abs = byte_abs },
3789 }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])});
3790 }
3791
3792 for (file.tree.errors[1..]) |note| {
3793 if (!note.is_note) break;
3794
3795 try file.tree.renderError(note, msg.writer());
3796 err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1);
3797 err_msg.notes[err_msg.notes.len - 1] = .{
3798 .src_loc = .{
3799 .file_scope = file,
3800 .parent_decl_node = 0,
3801 .lazy = .{ .token_abs = note.token },
3802 },
3803 .msg = try msg.toOwnedSlice(),
3804 };
3805 }
3806
3807 {
3808 comp.mutex.lock();
3809 defer comp.mutex.unlock();
3810 try mod.failed_files.putNoClobber(gpa, file, err_msg);
3811 }
3812 file.status = .parse_failure;
3813 return error.AnalysisFail;
3814 }
3815 file.tree_loaded = true;3759 file.tree_loaded = true;
38163760
3761 // Any potential AST errors are converted to ZIR errors here.
3817 file.zir = try AstGen.generate(gpa, file.tree);3762 file.zir = try AstGen.generate(gpa, file.tree);
3818 file.zir_loaded = true;3763 file.zir_loaded = true;
3819 file.status = .success_zir;3764 file.status = .success_zir;
...@@ -3870,7 +3815,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3870,7 +3815,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3870 .iov_len = file.zir.extra.len * 4,3815 .iov_len = file.zir.extra.len * 4,
3871 },3816 },
3872 };3817 };
3873 cache_file.?.writevAll(&iovecs) catch |err| {3818 cache_file.writevAll(&iovecs) catch |err| {
3874 const pkg_path = file.pkg.root_src_directory.path orelse ".";3819 const pkg_path = file.pkg.root_src_directory.path orelse ".";
3875 const cache_path = cache_directory.path orelse ".";3820 const cache_path = cache_directory.path orelse ".";
3876 log.warn("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{3821 log.warn("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{
...@@ -3922,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {...@@ -3922,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
3922 const gpa = mod.gpa;3867 const gpa = mod.gpa;
3923 const new_zir = file.zir;3868 const new_zir = file.zir;
39243869
3870 // The root decl will be null if the previous ZIR had AST errors.
3871 const root_decl = file.root_decl.unwrap() orelse return;
3872
3925 // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which3873 // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which
3926 // creates a namespace, gets mapped from old to new here.3874 // creates a namespace, gets mapped from old to new here.
3927 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};3875 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
...@@ -3939,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {...@@ -3939,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
3939 var decl_stack: ArrayListUnmanaged(Decl.Index) = .{};3887 var decl_stack: ArrayListUnmanaged(Decl.Index) = .{};
3940 defer decl_stack.deinit(gpa);3888 defer decl_stack.deinit(gpa);
39413889
3942 const root_decl = file.root_decl.unwrap().?;
3943 try decl_stack.append(gpa, root_decl);3890 try decl_stack.append(gpa, root_decl);
39443891
3945 file.deleted_decls.clearRetainingCapacity();3892 file.deleted_decls.clearRetainingCapacity();
src/Package.zig+64-65
...@@ -8,11 +8,11 @@ const Allocator = mem.Allocator;...@@ -8,11 +8,11 @@ const Allocator = mem.Allocator;
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const log = std.log.scoped(.package);9const log = std.log.scoped(.package);
10const main = @import("main.zig");10const main = @import("main.zig");
11const ThreadPool = std.Thread.Pool;
12const WaitGroup = std.Thread.WaitGroup;
1113
12const Compilation = @import("Compilation.zig");14const Compilation = @import("Compilation.zig");
13const Module = @import("Module.zig");15const Module = @import("Module.zig");
14const ThreadPool = @import("ThreadPool.zig");
15const WaitGroup = @import("WaitGroup.zig");
16const Cache = std.Build.Cache;16const Cache = std.Build.Cache;
17const build_options = @import("build_options");17const build_options = @import("build_options");
18const Manifest = @import("Manifest.zig");18const Manifest = @import("Manifest.zig");
...@@ -215,6 +215,7 @@ pub const build_zig_basename = "build.zig";...@@ -215,6 +215,7 @@ pub const build_zig_basename = "build.zig";
215215
216pub fn fetchAndAddDependencies(216pub fn fetchAndAddDependencies(
217 pkg: *Package,217 pkg: *Package,
218 root_pkg: *Package,
218 arena: Allocator,219 arena: Allocator,
219 thread_pool: *ThreadPool,220 thread_pool: *ThreadPool,
220 http_client: *std.http.Client,221 http_client: *std.http.Client,
...@@ -224,7 +225,7 @@ pub fn fetchAndAddDependencies(...@@ -224,7 +225,7 @@ pub fn fetchAndAddDependencies(
224 dependencies_source: *std.ArrayList(u8),225 dependencies_source: *std.ArrayList(u8),
225 build_roots_source: *std.ArrayList(u8),226 build_roots_source: *std.ArrayList(u8),
226 name_prefix: []const u8,227 name_prefix: []const u8,
227 color: main.Color,228 error_bundle: *std.zig.ErrorBundle.Wip,
228 all_modules: *AllModules,229 all_modules: *AllModules,
229) !void {230) !void {
230 const max_bytes = 10 * 1024 * 1024;231 const max_bytes = 10 * 1024 * 1024;
...@@ -249,7 +250,7 @@ pub fn fetchAndAddDependencies(...@@ -249,7 +250,7 @@ pub fn fetchAndAddDependencies(
249250
250 if (ast.errors.len > 0) {251 if (ast.errors.len > 0) {
251 const file_path = try directory.join(arena, &.{Manifest.basename});252 const file_path = try directory.join(arena, &.{Manifest.basename});
252 try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color);253 try main.putAstErrorsIntoBundle(gpa, ast, file_path, error_bundle);
253 return error.PackageFetchFailed;254 return error.PackageFetchFailed;
254 }255 }
255256
...@@ -257,14 +258,9 @@ pub fn fetchAndAddDependencies(...@@ -257,14 +258,9 @@ pub fn fetchAndAddDependencies(
257 defer manifest.deinit(gpa);258 defer manifest.deinit(gpa);
258259
259 if (manifest.errors.len > 0) {260 if (manifest.errors.len > 0) {
260 const ttyconf: std.debug.TTY.Config = switch (color) {
261 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
262 .on => .escape_codes,
263 .off => .no_color,
264 };
265 const file_path = try directory.join(arena, &.{Manifest.basename});261 const file_path = try directory.join(arena, &.{Manifest.basename});
266 for (manifest.errors) |msg| {262 for (manifest.errors) |msg| {
267 Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{});263 try Report.addErrorMessage(ast, file_path, error_bundle, 0, msg);
268 }264 }
269 return error.PackageFetchFailed;265 return error.PackageFetchFailed;
270 }266 }
...@@ -272,8 +268,7 @@ pub fn fetchAndAddDependencies(...@@ -272,8 +268,7 @@ pub fn fetchAndAddDependencies(
272 const report: Report = .{268 const report: Report = .{
273 .ast = &ast,269 .ast = &ast,
274 .directory = directory,270 .directory = directory,
275 .color = color,271 .error_bundle = error_bundle,
276 .arena = arena,
277 };272 };
278273
279 var any_error = false;274 var any_error = false;
...@@ -295,7 +290,8 @@ pub fn fetchAndAddDependencies(...@@ -295,7 +290,8 @@ pub fn fetchAndAddDependencies(
295 all_modules,290 all_modules,
296 );291 );
297292
298 try pkg.fetchAndAddDependencies(293 try sub_pkg.fetchAndAddDependencies(
294 root_pkg,
299 arena,295 arena,
300 thread_pool,296 thread_pool,
301 http_client,297 http_client,
...@@ -305,11 +301,12 @@ pub fn fetchAndAddDependencies(...@@ -305,11 +301,12 @@ pub fn fetchAndAddDependencies(
305 dependencies_source,301 dependencies_source,
306 build_roots_source,302 build_roots_source,
307 sub_prefix,303 sub_prefix,
308 color,304 error_bundle,
309 all_modules,305 all_modules,
310 );306 );
311307
312 try add(pkg, gpa, fqn, sub_pkg);308 try pkg.add(gpa, name, sub_pkg);
309 try root_pkg.add(gpa, fqn, sub_pkg);
313310
314 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{311 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{
315 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),312 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),
...@@ -347,8 +344,7 @@ pub fn createFilePkg(...@@ -347,8 +344,7 @@ pub fn createFilePkg(
347const Report = struct {344const Report = struct {
348 ast: *const std.zig.Ast,345 ast: *const std.zig.Ast,
349 directory: Compilation.Directory,346 directory: Compilation.Directory,
350 color: main.Color,347 error_bundle: *std.zig.ErrorBundle.Wip,
351 arena: Allocator,
352348
353 fn fail(349 fn fail(
354 report: Report,350 report: Report,
...@@ -356,52 +352,46 @@ const Report = struct {...@@ -356,52 +352,46 @@ const Report = struct {
356 comptime fmt_string: []const u8,352 comptime fmt_string: []const u8,
357 fmt_args: anytype,353 fmt_args: anytype,
358 ) error{ PackageFetchFailed, OutOfMemory } {354 ) error{ PackageFetchFailed, OutOfMemory } {
359 return failWithNotes(report, &.{}, tok, fmt_string, fmt_args);355 const gpa = report.error_bundle.gpa;
360 }
361356
362 fn failWithNotes(357 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
363 report: Report,358 defer gpa.free(file_path);
364 notes: []const Compilation.AllErrors.Message,359
365 tok: std.zig.Ast.TokenIndex,360 const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args);
366 comptime fmt_string: []const u8,361 defer gpa.free(msg);
367 fmt_args: anytype,362
368 ) error{ PackageFetchFailed, OutOfMemory } {363 try addErrorMessage(report.ast.*, file_path, report.error_bundle, 0, .{
369 const ttyconf: std.debug.TTY.Config = switch (report.color) {
370 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
371 .on => .escape_codes,
372 .off => .no_color,
373 };
374 const file_path = try report.directory.join(report.arena, &.{Manifest.basename});
375 renderErrorMessage(report.ast.*, file_path, ttyconf, .{
376 .tok = tok,364 .tok = tok,
377 .off = 0,365 .off = 0,
378 .msg = try std.fmt.allocPrint(report.arena, fmt_string, fmt_args),366 .msg = msg,
379 }, notes);367 });
368
380 return error.PackageFetchFailed;369 return error.PackageFetchFailed;
381 }370 }
382371
383 fn renderErrorMessage(372 fn addErrorMessage(
384 ast: std.zig.Ast,373 ast: std.zig.Ast,
385 file_path: []const u8,374 file_path: []const u8,
386 ttyconf: std.debug.TTY.Config,375 eb: *std.zig.ErrorBundle.Wip,
376 notes_len: u32,
387 msg: Manifest.ErrorMessage,377 msg: Manifest.ErrorMessage,
388 notes: []const Compilation.AllErrors.Message,378 ) error{OutOfMemory}!void {
389 ) void {
390 const token_starts = ast.tokens.items(.start);379 const token_starts = ast.tokens.items(.start);
391 const start_loc = ast.tokenLocation(0, msg.tok);380 const start_loc = ast.tokenLocation(0, msg.tok);
392 Compilation.AllErrors.Message.renderToStdErr(.{ .src = .{381
393 .msg = msg.msg,382 try eb.addRootErrorMessage(.{
394 .src_path = file_path,383 .msg = try eb.addString(msg.msg),
395 .line = @intCast(u32, start_loc.line),384 .src_loc = try eb.addSourceLocation(.{
396 .column = @intCast(u32, start_loc.column),385 .src_path = try eb.addString(file_path),
397 .span = .{386 .span_start = token_starts[msg.tok],
398 .start = token_starts[msg.tok],387 .span_end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
399 .end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),388 .span_main = token_starts[msg.tok] + msg.off,
400 .main = token_starts[msg.tok] + msg.off,389 .line = @intCast(u32, start_loc.line),
401 },390 .column = @intCast(u32, start_loc.column),
402 .source_line = ast.source[start_loc.line_start..start_loc.line_end],391 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
403 .notes = notes,392 }),
404 } }, ttyconf);393 .notes_len = notes_len,
394 });
405 }395 }
406};396};
407397
...@@ -432,6 +422,12 @@ fn fetchAndUnpack(...@@ -432,6 +422,12 @@ fn fetchAndUnpack(
432 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});422 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
433 errdefer gpa.free(build_root);423 errdefer gpa.free(build_root);
434424
425 var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
426 error.FileNotFound => break :cached,
427 else => |e| return e,
428 };
429 errdefer pkg_dir.close();
430
435 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{431 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
436 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),432 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
437 });433 });
...@@ -444,12 +440,6 @@ fn fetchAndUnpack(...@@ -444,12 +440,6 @@ fn fetchAndUnpack(
444 return gop.value_ptr.*;440 return gop.value_ptr.*;
445 }441 }
446442
447 var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
448 error.FileNotFound => break :cached,
449 else => |e| return e,
450 };
451 errdefer pkg_dir.close();
452
453 const ptr = try gpa.create(Package);443 const ptr = try gpa.create(Package);
454 errdefer gpa.destroy(ptr);444 errdefer gpa.destroy(ptr);
455445
...@@ -501,9 +491,7 @@ fn fetchAndUnpack(...@@ -501,9 +491,7 @@ fn fetchAndUnpack(
501 // by default, so the same logic applies for buffering the reader as for gzip.491 // by default, so the same logic applies for buffering the reader as for gzip.
502 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);492 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);
503 } else {493 } else {
504 return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{494 return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{uri.path});
505 uri.path,
506 });
507 }495 }
508496
509 // TODO: delete files not included in the package prior to computing the package hash.497 // TODO: delete files not included in the package prior to computing the package hash.
...@@ -530,10 +518,21 @@ fn fetchAndUnpack(...@@ -530,10 +518,21 @@ fn fetchAndUnpack(
530 });518 });
531 }519 }
532 } else {520 } else {
533 const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{521 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
534 .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}),522 defer gpa.free(file_path);
535 } }};523
536 return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{});524 const eb = report.error_bundle;
525 const notes_len = 1;
526 try Report.addErrorMessage(report.ast.*, file_path, eb, notes_len, .{
527 .tok = dep.url_tok,
528 .off = 0,
529 .msg = "url field is missing corresponding hash field",
530 });
531 const notes_start = try eb.reserveNotes(notes_len);
532 eb.extra.items[notes_start] = @enumToInt(try eb.addErrorMessage(.{
533 .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}),
534 }));
535 return error.PackageFetchFailed;
537 }536 }
538537
539 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});538 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
src/Sema.zig+120-82
...@@ -574,11 +574,13 @@ pub const Block = struct {...@@ -574,11 +574,13 @@ pub const Block = struct {
574 });574 });
575 }575 }
576576
577 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator, vector_ty: Air.Inst.Ref) !Air.Inst.Ref {577 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref {
578 return block.addInst(.{578 return block.addInst(.{
579 .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector,579 .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector,
580 .data = .{ .ty_pl = .{580 .data = .{ .ty_pl = .{
581 .ty = vector_ty,581 .ty = try block.sema.addType(
582 try Type.vector(block.sema.arena, block.sema.typeOf(lhs).vectorLen(), Type.bool),
583 ),
582 .payload = try block.sema.addExtra(Air.VectorCmp{584 .payload = try block.sema.addExtra(Air.VectorCmp{
583 .lhs = lhs,585 .lhs = lhs,
584 .rhs = rhs,586 .rhs = rhs,
...@@ -1101,6 +1103,7 @@ fn analyzeBodyInner(...@@ -1101,6 +1103,7 @@ fn analyzeBodyInner(
1101 .@"unreachable" => break sema.zirUnreachable(block, inst),1103 .@"unreachable" => break sema.zirUnreachable(block, inst),
1102 .panic => break sema.zirPanic(block, inst, false),1104 .panic => break sema.zirPanic(block, inst, false),
1103 .panic_comptime => break sema.zirPanic(block, inst, true),1105 .panic_comptime => break sema.zirPanic(block, inst, true),
1106 .trap => break sema.zirTrap(block, inst),
1104 // zig fmt: on1107 // zig fmt: on
11051108
1106 .extended => ext: {1109 .extended => ext: {
...@@ -1167,6 +1170,11 @@ fn analyzeBodyInner(...@@ -1167,6 +1170,11 @@ fn analyzeBodyInner(
1167 i += 1;1170 i += 1;
1168 continue;1171 continue;
1169 },1172 },
1173 .set_cold => {
1174 try sema.zirSetCold(block, extended);
1175 i += 1;
1176 continue;
1177 },
1170 .breakpoint => {1178 .breakpoint => {
1171 if (!block.is_comptime) {1179 if (!block.is_comptime) {
1172 _ = try block.addNoOp(.breakpoint);1180 _ = try block.addNoOp(.breakpoint);
...@@ -1304,11 +1312,6 @@ fn analyzeBodyInner(...@@ -1304,11 +1312,6 @@ fn analyzeBodyInner(
1304 i += 1;1312 i += 1;
1305 continue;1313 continue;
1306 },1314 },
1307 .set_cold => {
1308 try sema.zirSetCold(block, inst);
1309 i += 1;
1310 continue;
1311 },
1312 .set_runtime_safety => {1315 .set_runtime_safety => {
1313 try sema.zirSetRuntimeSafety(block, inst);1316 try sema.zirSetRuntimeSafety(block, inst);
1314 i += 1;1317 i += 1;
...@@ -1609,6 +1612,12 @@ fn analyzeBodyInner(...@@ -1609,6 +1612,12 @@ fn analyzeBodyInner(
1609 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);1612 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1610 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];1613 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
1611 const err_union = try sema.resolveInst(extra.data.operand);1614 const err_union = try sema.resolveInst(extra.data.operand);
1615 const err_union_ty = sema.typeOf(err_union);
1616 if (err_union_ty.zigTypeTag() != .ErrorUnion) {
1617 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
1618 err_union_ty.fmt(sema.mod),
1619 });
1620 }
1612 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);1621 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1613 assert(is_non_err != .none);1622 assert(is_non_err != .none);
1614 const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| {1623 const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| {
...@@ -1616,7 +1625,6 @@ fn analyzeBodyInner(...@@ -1616,7 +1625,6 @@ fn analyzeBodyInner(
1616 return err;1625 return err;
1617 };1626 };
1618 if (is_non_err_tv.val.toBool()) {1627 if (is_non_err_tv.val.toBool()) {
1619 const err_union_ty = sema.typeOf(err_union);
1620 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);1628 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
1621 }1629 }
1622 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse1630 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
...@@ -2203,29 +2211,27 @@ pub fn fail(...@@ -2203,29 +2211,27 @@ pub fn fail(
22032211
2204fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {2212fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
2205 @setCold(true);2213 @setCold(true);
2214 const gpa = sema.gpa;
22062215
2207 if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) {2216 if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) {
2208 if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation;2217 if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation;
2209 var arena = std.heap.ArenaAllocator.init(sema.gpa);2218 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2210 errdefer arena.deinit();2219 wip_errors.init(gpa) catch unreachable;
2211 var errors = std.ArrayList(Compilation.AllErrors.Message).init(sema.gpa);2220 Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable;
2212 defer errors.deinit();
2213
2214 Compilation.AllErrors.add(sema.mod, &arena, &errors, err_msg.*) catch unreachable;
2215
2216 std.debug.print("compile error during Sema:\n", .{});2221 std.debug.print("compile error during Sema:\n", .{});
2217 Compilation.AllErrors.Message.renderToStdErr(errors.items[0], .no_color);2222 var error_bundle = wip_errors.toOwnedBundle("") catch unreachable;
2223 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
2218 crash_report.compilerPanic("unexpected compile error occurred", null, null);2224 crash_report.compilerPanic("unexpected compile error occurred", null, null);
2219 }2225 }
22202226
2221 const mod = sema.mod;2227 const mod = sema.mod;
2222 ref: {2228 ref: {
2223 errdefer err_msg.destroy(mod.gpa);2229 errdefer err_msg.destroy(gpa);
2224 if (err_msg.src_loc.lazy == .unneeded) {2230 if (err_msg.src_loc.lazy == .unneeded) {
2225 return error.NeededSourceLocation;2231 return error.NeededSourceLocation;
2226 }2232 }
2227 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);2233 try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
2228 try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1);2234 try mod.failed_files.ensureUnusedCapacity(gpa, 1);
22292235
2230 const max_references = blk: {2236 const max_references = blk: {
2231 if (sema.mod.comp.reference_trace) |num| break :blk num;2237 if (sema.mod.comp.reference_trace) |num| break :blk num;
...@@ -2235,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {...@@ -2235,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
2235 };2241 };
22362242
2237 var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index;2243 var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index;
2238 var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(sema.gpa);2244 var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa);
2239 defer reference_stack.deinit();2245 defer reference_stack.deinit();
22402246
2241 // Avoid infinite loops.2247 // Avoid infinite loops.
2242 var seen = std.AutoHashMap(Module.Decl.Index, void).init(sema.gpa);2248 var seen = std.AutoHashMap(Module.Decl.Index, void).init(gpa);
2243 defer seen.deinit();2249 defer seen.deinit();
22442250
2245 var cur_reference_trace: u32 = 0;2251 var cur_reference_trace: u32 = 0;
...@@ -2280,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {...@@ -2280,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
2280 if (gop.found_existing) {2286 if (gop.found_existing) {
2281 // If there are multiple errors for the same Decl, prefer the first one added.2287 // If there are multiple errors for the same Decl, prefer the first one added.
2282 sema.err = null;2288 sema.err = null;
2283 err_msg.destroy(mod.gpa);2289 err_msg.destroy(gpa);
2284 } else {2290 } else {
2285 sema.err = err_msg;2291 sema.err = err_msg;
2286 gop.value_ptr.* = err_msg;2292 gop.value_ptr.* = err_msg;
...@@ -5144,6 +5150,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo...@@ -5144,6 +5150,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo
5144 return always_noreturn;5150 return always_noreturn;
5145}5151}
51465152
5153fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
5154 const src_node = sema.code.instructions.items(.data)[inst].node;
5155 const src = LazySrcLoc.nodeOffset(src_node);
5156 sema.src = src;
5157 _ = try block.addNoOp(.trap);
5158 return always_noreturn;
5159}
5160
5147fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5161fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5148 const tracy = trace(@src());5162 const tracy = trace(@src());
5149 defer tracy.end();5163 defer tracy.end();
...@@ -5721,10 +5735,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst...@@ -5721,10 +5735,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
5721 gop.value_ptr.* = .{ .alignment = alignment, .src = src };5735 gop.value_ptr.* = .{ .alignment = alignment, .src = src };
5722}5736}
57235737
5724fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {5738fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
5725 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5739 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
5726 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };5740 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
5727 const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime-known");5741 const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known");
5728 const func = sema.func orelse return; // does nothing outside a function5742 const func = sema.func orelse return; // does nothing outside a function
5729 func.is_cold = is_cold;5743 func.is_cold = is_cold;
5730}5744}
...@@ -8766,7 +8780,7 @@ fn funcCommon(...@@ -8766,7 +8780,7 @@ fn funcCommon(
8766 };8780 };
8767 return sema.failWithOwnedErrorMsg(msg);8781 return sema.failWithOwnedErrorMsg(msg);
8768 }8782 }
8769 if (!Type.fnCallingConventionAllowsZigTypes(cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) {8783 if (!ret_poison and !Type.fnCallingConventionAllowsZigTypes(cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) {
8770 const msg = msg: {8784 const msg = msg: {
8771 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{8785 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
8772 return_type.fmt(sema.mod), @tagName(cc_resolved),8786 return_type.fmt(sema.mod), @tagName(cc_resolved),
...@@ -9403,7 +9417,7 @@ fn intCast(...@@ -9403,7 +9417,7 @@ fn intCast(
9403 const ok = if (is_vector) ok: {9417 const ok = if (is_vector) ok: {
9404 const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero);9418 const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero);
9405 const zero_inst = try sema.addConstant(sema.typeOf(operand), zeros);9419 const zero_inst = try sema.addConstant(sema.typeOf(operand), zeros);
9406 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq, try sema.addType(operand_ty));9420 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq);
9407 const all_in_range = try block.addInst(.{9421 const all_in_range = try block.addInst(.{
9408 .tag = .reduce,9422 .tag = .reduce,
9409 .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } },9423 .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } },
...@@ -9457,7 +9471,7 @@ fn intCast(...@@ -9457,7 +9471,7 @@ fn intCast(
9457 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);9471 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
94589472
9459 const ok = if (is_vector) ok: {9473 const ok = if (is_vector) ok: {
9460 const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty));9474 const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte);
9461 const all_in_range = try block.addInst(.{9475 const all_in_range = try block.addInst(.{
9462 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,9476 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
9463 .data = .{ .reduce = .{9477 .data = .{ .reduce = .{
...@@ -9474,7 +9488,7 @@ fn intCast(...@@ -9474,7 +9488,7 @@ fn intCast(
9474 try sema.addSafetyCheck(block, ok, .cast_truncated_data);9488 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
9475 } else {9489 } else {
9476 const ok = if (is_vector) ok: {9490 const ok = if (is_vector) ok: {
9477 const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty));9491 const is_in_range = try block.addCmpVector(diff, dest_max, .lte);
9478 const all_in_range = try block.addInst(.{9492 const all_in_range = try block.addInst(.{
9479 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,9493 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
9480 .data = .{ .reduce = .{9494 .data = .{ .reduce = .{
...@@ -9495,7 +9509,7 @@ fn intCast(...@@ -9495,7 +9509,7 @@ fn intCast(
9495 const ok = if (is_vector) ok: {9509 const ok = if (is_vector) ok: {
9496 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);9510 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
9497 const zero_inst = try sema.addConstant(operand_ty, zero_val);9511 const zero_inst = try sema.addConstant(operand_ty, zero_val);
9498 const is_in_range = try block.addCmpVector(operand, zero_inst, .gte, try sema.addType(operand_ty));9512 const is_in_range = try block.addCmpVector(operand, zero_inst, .gte);
9499 const all_in_range = try block.addInst(.{9513 const all_in_range = try block.addInst(.{
9500 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,9514 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
9501 .data = .{ .reduce = .{9515 .data = .{ .reduce = .{
...@@ -12007,7 +12021,7 @@ fn zirShl(...@@ -12007,7 +12021,7 @@ fn zirShl(
1200712021
12008 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {12022 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
12009 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));12023 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
12010 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));12024 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
12011 break :ok try block.addInst(.{12025 break :ok try block.addInst(.{
12012 .tag = .reduce,12026 .tag = .reduce,
12013 .data = .{ .reduce = .{12027 .data = .{ .reduce = .{
...@@ -12163,7 +12177,7 @@ fn zirShr(...@@ -12163,7 +12177,7 @@ fn zirShr(
1216312177
12164 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {12178 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
12165 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));12179 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
12166 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));12180 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
12167 break :ok try block.addInst(.{12181 break :ok try block.addInst(.{
12168 .tag = .reduce,12182 .tag = .reduce,
12169 .data = .{ .reduce = .{12183 .data = .{ .reduce = .{
...@@ -12182,7 +12196,7 @@ fn zirShr(...@@ -12182,7 +12196,7 @@ fn zirShr(
12182 const back = try block.addBinOp(.shl, result, rhs);12196 const back = try block.addBinOp(.shl, result, rhs);
1218312197
12184 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {12198 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
12185 const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));12199 const eql = try block.addCmpVector(lhs, back, .eq);
12186 break :ok try block.addInst(.{12200 break :ok try block.addInst(.{
12187 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,12201 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
12188 .data = .{ .reduce = .{12202 .data = .{ .reduce = .{
...@@ -13183,7 +13197,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13183,7 +13197,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
13183 const floored = try block.addUnOp(.floor, result);13197 const floored = try block.addUnOp(.floor, result);
1318413198
13185 if (resolved_type.zigTypeTag() == .Vector) {13199 if (resolved_type.zigTypeTag() == .Vector) {
13186 const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type));13200 const eql = try block.addCmpVector(result, floored, .eq);
13187 break :ok try block.addInst(.{13201 break :ok try block.addInst(.{
13188 .tag = switch (block.float_mode) {13202 .tag = switch (block.float_mode) {
13189 .Strict => .reduce,13203 .Strict => .reduce,
...@@ -13207,7 +13221,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13207,7 +13221,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
13207 if (resolved_type.zigTypeTag() == .Vector) {13221 if (resolved_type.zigTypeTag() == .Vector) {
13208 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);13222 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
13209 const zero = try sema.addConstant(resolved_type, zero_val);13223 const zero = try sema.addConstant(resolved_type, zero_val);
13210 const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type));13224 const eql = try block.addCmpVector(remainder, zero, .eq);
13211 break :ok try block.addInst(.{13225 break :ok try block.addInst(.{
13212 .tag = .reduce,13226 .tag = .reduce,
13213 .data = .{ .reduce = .{13227 .data = .{ .reduce = .{
...@@ -13505,14 +13519,13 @@ fn addDivIntOverflowSafety(...@@ -13505,14 +13519,13 @@ fn addDivIntOverflowSafety(
1350513519
13506 var ok: Air.Inst.Ref = .none;13520 var ok: Air.Inst.Ref = .none;
13507 if (resolved_type.zigTypeTag() == .Vector) {13521 if (resolved_type.zigTypeTag() == .Vector) {
13508 const vector_ty_ref = try sema.addType(resolved_type);
13509 if (maybe_lhs_val == null) {13522 if (maybe_lhs_val == null) {
13510 const min_int_ref = try sema.addConstant(resolved_type, min_int);13523 const min_int_ref = try sema.addConstant(resolved_type, min_int);
13511 ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref);13524 ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq);
13512 }13525 }
13513 if (maybe_rhs_val == null) {13526 if (maybe_rhs_val == null) {
13514 const neg_one_ref = try sema.addConstant(resolved_type, neg_one);13527 const neg_one_ref = try sema.addConstant(resolved_type, neg_one);
13515 const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref);13528 const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq);
13516 if (ok == .none) {13529 if (ok == .none) {
13517 ok = rhs_ok;13530 ok = rhs_ok;
13518 } else {13531 } else {
...@@ -13564,7 +13577,7 @@ fn addDivByZeroSafety(...@@ -13564,7 +13577,7 @@ fn addDivByZeroSafety(
13564 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {13577 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
13565 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);13578 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
13566 const zero = try sema.addConstant(resolved_type, zero_val);13579 const zero = try sema.addConstant(resolved_type, zero_val);
13567 const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type));13580 const ok = try block.addCmpVector(casted_rhs, zero, .neq);
13568 break :ok try block.addInst(.{13581 break :ok try block.addInst(.{
13569 .tag = if (is_int) .reduce else .reduce_optimized,13582 .tag = if (is_int) .reduce else .reduce_optimized,
13570 .data = .{ .reduce = .{13583 .data = .{ .reduce = .{
...@@ -15193,9 +15206,7 @@ fn cmpSelf(...@@ -15193,9 +15206,7 @@ fn cmpSelf(
15193 };15206 };
15194 try sema.requireRuntimeBlock(block, src, runtime_src);15207 try sema.requireRuntimeBlock(block, src, runtime_src);
15195 if (resolved_type.zigTypeTag() == .Vector) {15208 if (resolved_type.zigTypeTag() == .Vector) {
15196 const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.bool);15209 return block.addCmpVector(casted_lhs, casted_rhs, op);
15197 const result_ty_ref = try sema.addType(result_ty);
15198 return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref);
15199 }15210 }
15200 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized);15211 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized);
15201 return block.addBinOp(tag, casted_lhs, casted_rhs);15212 return block.addBinOp(tag, casted_lhs, casted_rhs);
...@@ -18360,7 +18371,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in...@@ -18360,7 +18371,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
18360 const union_val = val.cast(Value.Payload.Union).?.data;18371 const union_val = val.cast(Value.Payload.Union).?.data;
18361 const target = mod.getTarget();18372 const target = mod.getTarget();
18362 const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?;18373 const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?;
18363 if (union_val.val.anyUndef()) return sema.failWithUseOfUndef(block, src);18374 if (union_val.val.anyUndef(mod)) return sema.failWithUseOfUndef(block, src);
18364 switch (@intToEnum(std.builtin.TypeId, tag_index)) {18375 switch (@intToEnum(std.builtin.TypeId, tag_index)) {
18365 .Type => return Air.Inst.Ref.type_type,18376 .Type => return Air.Inst.Ref.type_type,
18366 .Void => return Air.Inst.Ref.void_type,18377 .Void => return Air.Inst.Ref.void_type,
...@@ -22274,7 +22285,6 @@ fn zirBuiltinExtern(...@@ -22274,7 +22285,6 @@ fn zirBuiltinExtern(
22274 extended: Zir.Inst.Extended.InstData,22285 extended: Zir.Inst.Extended.InstData,
22275) CompileError!Air.Inst.Ref {22286) CompileError!Air.Inst.Ref {
22276 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;22287 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
22277 const src = LazySrcLoc.nodeOffset(extra.node);
22278 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };22288 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
22279 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };22289 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
2228022290
...@@ -22302,39 +22312,41 @@ fn zirBuiltinExtern(...@@ -22302,39 +22312,41 @@ fn zirBuiltinExtern(
22302 const new_decl = sema.mod.declPtr(new_decl_index);22312 const new_decl = sema.mod.declPtr(new_decl_index);
22303 new_decl.name = try sema.gpa.dupeZ(u8, options.name);22313 new_decl.name = try sema.gpa.dupeZ(u8, options.name);
2230422314
22305 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);22315 {
22306 errdefer new_decl_arena.deinit();22316 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
22307 const new_decl_arena_allocator = new_decl_arena.allocator();22317 errdefer new_decl_arena.deinit();
22318 const new_decl_arena_allocator = new_decl_arena.allocator();
2230822319
22309 const new_var = try new_decl_arena_allocator.create(Module.Var);22320 const new_var = try new_decl_arena_allocator.create(Module.Var);
22310 errdefer new_decl_arena_allocator.destroy(new_var);22321 new_var.* = .{
22322 .owner_decl = sema.owner_decl_index,
22323 .init = Value.initTag(.unreachable_value),
22324 .is_extern = true,
22325 .is_mutable = false,
22326 .is_threadlocal = options.is_thread_local,
22327 .is_weak_linkage = options.linkage == .Weak,
22328 .lib_name = null,
22329 };
2231122330
22312 new_var.* = .{22331 new_decl.src_line = sema.owner_decl.src_line;
22313 .owner_decl = sema.owner_decl_index,22332 // We only access this decl through the decl_ref with the correct type created
22314 .init = Value.initTag(.unreachable_value),22333 // below, so this type doesn't matter
22315 .is_extern = true,22334 new_decl.ty = Type.Tag.init(.anyopaque);
22316 .is_mutable = false,22335 new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
22317 .is_threadlocal = options.is_thread_local,22336 new_decl.@"align" = 0;
22318 .is_weak_linkage = options.linkage == .Weak,22337 new_decl.@"linksection" = null;
22319 .lib_name = null,22338 new_decl.has_tv = true;
22320 };22339 new_decl.analysis = .complete;
22340 new_decl.generation = sema.mod.generation;
2232122341
22322 new_decl.src_line = sema.owner_decl.src_line;22342 try new_decl.finalizeNewArena(&new_decl_arena);
22323 new_decl.ty = try ty.copy(new_decl_arena_allocator);22343 }
22324 new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
22325 new_decl.@"align" = 0;
22326 new_decl.@"linksection" = null;
22327 new_decl.has_tv = true;
22328 new_decl.analysis = .complete;
22329 new_decl.generation = sema.mod.generation;
2233022344
22331 const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);22345 try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
22332 arena_state.* = new_decl_arena.state;22346 try sema.ensureDeclAnalyzed(new_decl_index);
22333 new_decl.value_arena = arena_state;
2233422347
22335 const ref = try sema.analyzeDeclRef(new_decl_index);22348 const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
22336 try sema.requireRuntimeBlock(block, src, null);22349 return sema.addConstant(ty, ref);
22337 return block.addBitCast(ty, ref);
22338}22350}
2233922351
22340fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {22352fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
...@@ -23026,7 +23038,7 @@ fn panicSentinelMismatch(...@@ -23026,7 +23038,7 @@ fn panicSentinelMismatch(
2302623038
23027 const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: {23039 const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: {
23028 const eql =23040 const eql =
23029 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq, try sema.addType(sentinel_ty));23041 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
23030 break :ok try parent_block.addInst(.{23042 break :ok try parent_block.addInst(.{
23031 .tag = .reduce,23043 .tag = .reduce,
23032 .data = .{ .reduce = .{23044 .data = .{ .reduce = .{
...@@ -23592,10 +23604,13 @@ fn fieldCallBind(...@@ -23592,10 +23604,13 @@ fn fieldCallBind(
23592 }23604 }
2359323605
23594 // If we get here, we need to look for a decl in the struct type instead.23606 // If we get here, we need to look for a decl in the struct type instead.
23595 switch (concrete_ty.zigTypeTag()) {23607 const found_decl = switch (concrete_ty.zigTypeTag()) {
23596 .Struct, .Opaque, .Union, .Enum => {23608 .Struct, .Opaque, .Union, .Enum => found_decl: {
23597 if (concrete_ty.getNamespace()) |namespace| {23609 if (concrete_ty.getNamespace()) |namespace| {
23598 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {23610 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {
23611 try sema.addReferencedBy(block, src, decl_idx);
23612 const inst = try sema.analyzeDeclRef(decl_idx);
23613
23599 const decl_val = try sema.analyzeLoad(block, src, inst, src);23614 const decl_val = try sema.analyzeLoad(block, src, inst, src);
23600 const decl_type = sema.typeOf(decl_val);23615 const decl_type = sema.typeOf(decl_val);
23601 if (decl_type.zigTypeTag() == .Fn and23616 if (decl_type.zigTypeTag() == .Fn and
...@@ -23612,7 +23627,7 @@ fn fieldCallBind(...@@ -23612,7 +23627,7 @@ fn fieldCallBind(
23612 first_param_type.ptrSize() == .C) and23627 first_param_type.ptrSize() == .C) and
23613 first_param_type.childType().eql(concrete_ty, sema.mod)))23628 first_param_type.childType().eql(concrete_ty, sema.mod)))
23614 {23629 {
23615 // zig fmt: on23630 // zig fmt: on
23616 // TODO: bound fn calls on rvalues should probably23631 // TODO: bound fn calls on rvalues should probably
23617 // generate a by-value argument somehow.23632 // generate a by-value argument somehow.
23618 const ty = Type.Tag.bound_fn.init();23633 const ty = Type.Tag.bound_fn.init();
...@@ -23651,16 +23666,22 @@ fn fieldCallBind(...@@ -23651,16 +23666,22 @@ fn fieldCallBind(
23651 return sema.addConstant(ty, value);23666 return sema.addConstant(ty, value);
23652 }23667 }
23653 }23668 }
23669 break :found_decl decl_idx;
23654 }23670 }
23655 }23671 }
23672 break :found_decl null;
23656 },23673 },
23657 else => {},23674 else => null,
23658 }23675 };
2365923676
23660 const msg = msg: {23677 const msg = msg: {
23661 const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) });23678 const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) });
23662 errdefer msg.destroy(sema.gpa);23679 errdefer msg.destroy(sema.gpa);
23663 try sema.addDeclaredHereNote(msg, concrete_ty);23680 try sema.addDeclaredHereNote(msg, concrete_ty);
23681 if (found_decl) |decl_idx| {
23682 const decl = sema.mod.declPtr(decl_idx);
23683 try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "'{s}' is not a member function", .{field_name});
23684 }
23664 break :msg msg;23685 break :msg msg;
23665 };23686 };
23666 return sema.failWithOwnedErrorMsg(msg);23687 return sema.failWithOwnedErrorMsg(msg);
...@@ -24832,6 +24853,7 @@ fn coerceExtra(...@@ -24832,6 +24853,7 @@ fn coerceExtra(
24832 const array_ty = dest_info.pointee_type;24853 const array_ty = dest_info.pointee_type;
24833 if (array_ty.zigTypeTag() != .Array) break :single_item;24854 if (array_ty.zigTypeTag() != .Array) break :single_item;
24834 const array_elem_ty = array_ty.childType();24855 const array_elem_ty = array_ty.childType();
24856 if (array_ty.arrayLen() != 1) break :single_item;
24835 const dest_is_mut = dest_info.mutable;24857 const dest_is_mut = dest_info.mutable;
24836 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {24858 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
24837 .ok => {},24859 .ok => {},
...@@ -26625,6 +26647,23 @@ fn beginComptimePtrMutation(...@@ -26625,6 +26647,23 @@ fn beginComptimePtrMutation(
26625 });26647 });
26626 }26648 }
26627 const elem_ty = parent.ty.childType();26649 const elem_ty = parent.ty.childType();
26650
26651 // We might have a pointer to multiple elements of the array (e.g. a pointer
26652 // to a sub-array). In this case, we just have to reinterpret the relevant
26653 // bytes of the whole array rather than any single element.
26654 const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty);
26655 if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) {
26656 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
26657 return .{
26658 .decl_ref_mut = parent.decl_ref_mut,
26659 .pointee = .{ .reinterpret = .{
26660 .val_ptr = val_ptr,
26661 .byte_offset = elem_abi_size * elem_ptr.index,
26662 } },
26663 .ty = parent.ty,
26664 };
26665 }
26666
26628 switch (val_ptr.tag()) {26667 switch (val_ptr.tag()) {
26629 .undef => {26668 .undef => {
26630 // An array has been initialized to undefined at comptime and now we26669 // An array has been initialized to undefined at comptime and now we
...@@ -29359,8 +29398,7 @@ fn cmpVector(...@@ -29359,8 +29398,7 @@ fn cmpVector(
29359 };29398 };
2936029399
29361 try sema.requireRuntimeBlock(block, src, runtime_src);29400 try sema.requireRuntimeBlock(block, src, runtime_src);
29362 const result_ty_inst = try sema.addType(result_ty);29401 return block.addCmpVector(lhs, rhs, op);
29363 return block.addCmpVector(lhs, rhs, op, result_ty_inst);
29364}29402}
2936529403
29366fn wrapOptional(29404fn wrapOptional(
src/ThreadPool.zig deleted-152
...@@ -1,152 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const ThreadPool = @This();
4const WaitGroup = @import("WaitGroup.zig");
5
6mutex: std.Thread.Mutex = .{},
7cond: std.Thread.Condition = .{},
8run_queue: RunQueue = .{},
9is_running: bool = true,
10allocator: std.mem.Allocator,
11threads: []std.Thread,
12
13const RunQueue = std.SinglyLinkedList(Runnable);
14const Runnable = struct {
15 runFn: RunProto,
16};
17
18const RunProto = *const fn (*Runnable) void;
19
20pub fn init(pool: *ThreadPool, allocator: std.mem.Allocator) !void {
21 pool.* = .{
22 .allocator = allocator,
23 .threads = &[_]std.Thread{},
24 };
25
26 if (builtin.single_threaded) {
27 return;
28 }
29
30 const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1);
31 pool.threads = try allocator.alloc(std.Thread, thread_count);
32 errdefer allocator.free(pool.threads);
33
34 // kill and join any threads we spawned previously on error.
35 var spawned: usize = 0;
36 errdefer pool.join(spawned);
37
38 for (pool.threads) |*thread| {
39 thread.* = try std.Thread.spawn(.{}, worker, .{pool});
40 spawned += 1;
41 }
42}
43
44pub fn deinit(pool: *ThreadPool) void {
45 pool.join(pool.threads.len); // kill and join all threads.
46 pool.* = undefined;
47}
48
49fn join(pool: *ThreadPool, spawned: usize) void {
50 if (builtin.single_threaded) {
51 return;
52 }
53
54 {
55 pool.mutex.lock();
56 defer pool.mutex.unlock();
57
58 // ensure future worker threads exit the dequeue loop
59 pool.is_running = false;
60 }
61
62 // wake up any sleeping threads (this can be done outside the mutex)
63 // then wait for all the threads we know are spawned to complete.
64 pool.cond.broadcast();
65 for (pool.threads[0..spawned]) |thread| {
66 thread.join();
67 }
68
69 pool.allocator.free(pool.threads);
70}
71
72pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void {
73 if (builtin.single_threaded) {
74 @call(.auto, func, args);
75 return;
76 }
77
78 const Args = @TypeOf(args);
79 const Closure = struct {
80 arguments: Args,
81 pool: *ThreadPool,
82 run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } },
83
84 fn runFn(runnable: *Runnable) void {
85 const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable);
86 const closure = @fieldParentPtr(@This(), "run_node", run_node);
87 @call(.auto, func, closure.arguments);
88
89 // The thread pool's allocator is protected by the mutex.
90 const mutex = &closure.pool.mutex;
91 mutex.lock();
92 defer mutex.unlock();
93
94 closure.pool.allocator.destroy(closure);
95 }
96 };
97
98 {
99 pool.mutex.lock();
100 defer pool.mutex.unlock();
101
102 const closure = try pool.allocator.create(Closure);
103 closure.* = .{
104 .arguments = args,
105 .pool = pool,
106 };
107
108 pool.run_queue.prepend(&closure.run_node);
109 }
110
111 // Notify waiting threads outside the lock to try and keep the critical section small.
112 pool.cond.signal();
113}
114
115fn worker(pool: *ThreadPool) void {
116 pool.mutex.lock();
117 defer pool.mutex.unlock();
118
119 while (true) {
120 while (pool.run_queue.popFirst()) |run_node| {
121 // Temporarily unlock the mutex in order to execute the run_node
122 pool.mutex.unlock();
123 defer pool.mutex.lock();
124
125 const runFn = run_node.data.runFn;
126 runFn(&run_node.data);
127 }
128
129 // Stop executing instead of waiting if the thread pool is no longer running.
130 if (pool.is_running) {
131 pool.cond.wait(&pool.mutex);
132 } else {
133 break;
134 }
135 }
136}
137
138pub fn waitAndWork(pool: *ThreadPool, wait_group: *WaitGroup) void {
139 while (!wait_group.isDone()) {
140 if (blk: {
141 pool.mutex.lock();
142 defer pool.mutex.unlock();
143 break :blk pool.run_queue.popFirst();
144 }) |run_node| {
145 run_node.data.runFn(&run_node.data);
146 continue;
147 }
148
149 wait_group.wait();
150 return;
151 }
152}
src/WaitGroup.zig deleted-46
...@@ -1,46 +0,0 @@
1const std = @import("std");
2const Atomic = std.atomic.Atomic;
3const assert = std.debug.assert;
4const WaitGroup = @This();
5
6const is_waiting: usize = 1 << 0;
7const one_pending: usize = 1 << 1;
8
9state: Atomic(usize) = Atomic(usize).init(0),
10event: std.Thread.ResetEvent = .{},
11
12pub fn start(self: *WaitGroup) void {
13 const state = self.state.fetchAdd(one_pending, .Monotonic);
14 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
15}
16
17pub fn finish(self: *WaitGroup) void {
18 const state = self.state.fetchSub(one_pending, .Release);
19 assert((state / one_pending) > 0);
20
21 if (state == (one_pending | is_waiting)) {
22 self.state.fence(.Acquire);
23 self.event.set();
24 }
25}
26
27pub fn wait(self: *WaitGroup) void {
28 var state = self.state.fetchAdd(is_waiting, .Acquire);
29 assert(state & is_waiting == 0);
30
31 if ((state / one_pending) > 0) {
32 self.event.wait();
33 }
34}
35
36pub fn reset(self: *WaitGroup) void {
37 self.state.store(0, .Monotonic);
38 self.event.reset();
39}
40
41pub fn isDone(wg: *WaitGroup) bool {
42 const state = wg.state.load(.Acquire);
43 assert(state & is_waiting == 0);
44
45 return (state / one_pending) == 0;
46}
src/Zir.zig+20-10
...@@ -617,7 +617,7 @@ pub const Inst = struct {...@@ -617,7 +617,7 @@ pub const Inst = struct {
617 /// Uses the `un_node` field.617 /// Uses the `un_node` field.
618 typeof_log2_int_type,618 typeof_log2_int_type,
619 /// Asserts control-flow will not reach this instruction (`unreachable`).619 /// Asserts control-flow will not reach this instruction (`unreachable`).
620 /// Uses the `unreachable` union field.620 /// Uses the `@"unreachable"` union field.
621 @"unreachable",621 @"unreachable",
622 /// Bitwise XOR. `^`622 /// Bitwise XOR. `^`
623 /// Uses the `pl_node` union field. Payload is `Bin`.623 /// Uses the `pl_node` union field. Payload is `Bin`.
...@@ -808,8 +808,9 @@ pub const Inst = struct {...@@ -808,8 +808,9 @@ pub const Inst = struct {
808 panic,808 panic,
809 /// Same as `panic` but forces comptime.809 /// Same as `panic` but forces comptime.
810 panic_comptime,810 panic_comptime,
811 /// Implement builtin `@setCold`. Uses `un_node`.811 /// Implements `@trap`.
812 set_cold,812 /// Uses the `node` field.
813 trap,
813 /// Implement builtin `@setRuntimeSafety`. Uses `un_node`.814 /// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
814 set_runtime_safety,815 set_runtime_safety,
815 /// Implement builtin `@sqrt`. Uses `un_node`.816 /// Implement builtin `@sqrt`. Uses `un_node`.
...@@ -1187,7 +1188,6 @@ pub const Inst = struct {...@@ -1187,7 +1188,6 @@ pub const Inst = struct {
1187 .bool_to_int,1188 .bool_to_int,
1188 .embed_file,1189 .embed_file,
1189 .error_name,1190 .error_name,
1190 .set_cold,
1191 .set_runtime_safety,1191 .set_runtime_safety,
1192 .sqrt,1192 .sqrt,
1193 .sin,1193 .sin,
...@@ -1277,6 +1277,7 @@ pub const Inst = struct {...@@ -1277,6 +1277,7 @@ pub const Inst = struct {
1277 .repeat_inline,1277 .repeat_inline,
1278 .panic,1278 .panic,
1279 .panic_comptime,1279 .panic_comptime,
1280 .trap,
1280 .check_comptime_control_flow,1281 .check_comptime_control_flow,
1281 => true,1282 => true,
1282 };1283 };
...@@ -1323,7 +1324,6 @@ pub const Inst = struct {...@@ -1323,7 +1324,6 @@ pub const Inst = struct {
1323 .validate_deref,1324 .validate_deref,
1324 .@"export",1325 .@"export",
1325 .export_value,1326 .export_value,
1326 .set_cold,
1327 .set_runtime_safety,1327 .set_runtime_safety,
1328 .memcpy,1328 .memcpy,
1329 .memset,1329 .memset,
...@@ -1553,6 +1553,7 @@ pub const Inst = struct {...@@ -1553,6 +1553,7 @@ pub const Inst = struct {
1553 .repeat_inline,1553 .repeat_inline,
1554 .panic,1554 .panic,
1555 .panic_comptime,1555 .panic_comptime,
1556 .trap,
1556 .for_len,1557 .for_len,
1557 .@"try",1558 .@"try",
1558 .try_ptr,1559 .try_ptr,
...@@ -1561,7 +1562,7 @@ pub const Inst = struct {...@@ -1561,7 +1562,7 @@ pub const Inst = struct {
1561 => false,1562 => false,
15621563
1563 .extended => switch (data.extended.opcode) {1564 .extended => switch (data.extended.opcode) {
1564 .breakpoint, .fence => true,1565 .fence, .set_cold, .breakpoint => true,
1565 else => false,1566 else => false,
1566 },1567 },
1567 };1568 };
...@@ -1750,7 +1751,7 @@ pub const Inst = struct {...@@ -1750,7 +1751,7 @@ pub const Inst = struct {
1750 .error_name = .un_node,1751 .error_name = .un_node,
1751 .panic = .un_node,1752 .panic = .un_node,
1752 .panic_comptime = .un_node,1753 .panic_comptime = .un_node,
1753 .set_cold = .un_node,1754 .trap = .node,
1754 .set_runtime_safety = .un_node,1755 .set_runtime_safety = .un_node,
1755 .sqrt = .un_node,1756 .sqrt = .un_node,
1756 .sin = .un_node,1757 .sin = .un_node,
...@@ -1979,11 +1980,15 @@ pub const Inst = struct {...@@ -1979,11 +1980,15 @@ pub const Inst = struct {
1979 /// Implement builtin `@setAlignStack`.1980 /// Implement builtin `@setAlignStack`.
1980 /// `operand` is payload index to `UnNode`.1981 /// `operand` is payload index to `UnNode`.
1981 set_align_stack,1982 set_align_stack,
1983 /// Implements `@setCold`.
1984 /// `operand` is payload index to `UnNode`.
1985 set_cold,
1982 /// Implements the `@errSetCast` builtin.1986 /// Implements the `@errSetCast` builtin.
1983 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.1987 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
1984 err_set_cast,1988 err_set_cast,
1985 /// `operand` is payload index to `UnNode`.1989 /// `operand` is payload index to `UnNode`.
1986 await_nosuspend,1990 await_nosuspend,
1991 /// Implements `@breakpoint`.
1987 /// `operand` is `src_node: i32`.1992 /// `operand` is `src_node: i32`.
1988 breakpoint,1993 breakpoint,
1989 /// Implements the `@select` builtin.1994 /// Implements the `@select` builtin.
...@@ -1997,7 +2002,7 @@ pub const Inst = struct {...@@ -1997,7 +2002,7 @@ pub const Inst = struct {
1997 int_to_error,2002 int_to_error,
1998 /// Implement builtin `@Type`.2003 /// Implement builtin `@Type`.
1999 /// `operand` is payload index to `UnNode`.2004 /// `operand` is payload index to `UnNode`.
2000 /// `small` contains `NameStrategy2005 /// `small` contains `NameStrategy`.
2001 reify,2006 reify,
2002 /// Implements the `@asyncCall` builtin.2007 /// Implements the `@asyncCall` builtin.
2003 /// `operand` is payload index to `AsyncCall`.2008 /// `operand` is payload index to `AsyncCall`.
...@@ -2040,8 +2045,7 @@ pub const Inst = struct {...@@ -2040,8 +2045,7 @@ pub const Inst = struct {
20402045
2041 /// A reference to a TypedValue or ZIR instruction.2046 /// A reference to a TypedValue or ZIR instruction.
2042 ///2047 ///
2043 /// If the Ref has a tag in this enum, it refers to a TypedValue which may be2048 /// If the Ref has a tag in this enum, it refers to a TypedValue.
2044 /// retrieved with Ref.toTypedValue().
2045 ///2049 ///
2046 /// If the value of a Ref does not have a tag, it refers to a ZIR instruction.2050 /// If the value of a Ref does not have a tag, it refers to a ZIR instruction.
2047 ///2051 ///
...@@ -3590,6 +3594,12 @@ pub const Inst = struct {...@@ -3590,6 +3594,12 @@ pub const Inst = struct {
3590 /// 0 or a payload index of a `Block`, each is a payload3594 /// 0 or a payload index of a `Block`, each is a payload
3591 /// index of another `Item`.3595 /// index of another `Item`.
3592 notes: u32,3596 notes: u32,
3597
3598 pub fn notesLen(item: Item, zir: Zir) u32 {
3599 if (item.notes == 0) return 0;
3600 const block = zir.extraData(Block, item.notes);
3601 return block.data.body_len;
3602 }
3593 };3603 };
3594 };3604 };
35953605
src/arch/aarch64/CodeGen.zig+30-200
...@@ -23,7 +23,7 @@ const leb128 = std.leb;...@@ -23,7 +23,7 @@ const leb128 = std.leb;
23const log = std.log.scoped(.codegen);23const log = std.log.scoped(.codegen);
24const build_options = @import("build_options");24const build_options = @import("build_options");
2525
26const GenerateSymbolError = codegen.GenerateSymbolError;26const CodeGenError = codegen.CodeGenError;
27const Result = codegen.Result;27const Result = codegen.Result;
28const DebugInfoOutput = codegen.DebugInfoOutput;28const DebugInfoOutput = codegen.DebugInfoOutput;
2929
...@@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;...@@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
41const c_abi_int_return_regs = abi.c_abi_int_return_regs;41const c_abi_int_return_regs = abi.c_abi_int_return_regs;
42const gp = abi.RegisterClass.gp;42const gp = abi.RegisterClass.gp;
4343
44const InnerError = error{44const InnerError = CodeGenError || error{OutOfRegisters};
45 OutOfMemory,
46 CodegenFail,
47 OutOfRegisters,
48};
4945
50gpa: Allocator,46gpa: Allocator,
51air: Air,47air: Air,
...@@ -337,7 +333,7 @@ pub fn generate(...@@ -337,7 +333,7 @@ pub fn generate(
337 liveness: Liveness,333 liveness: Liveness,
338 code: *std.ArrayList(u8),334 code: *std.ArrayList(u8),
339 debug_output: DebugInfoOutput,335 debug_output: DebugInfoOutput,
340) GenerateSymbolError!Result {336) CodeGenError!Result {
341 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {337 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
342 @panic("Attempted to compile for architecture that was disabled by build configuration");338 @panic("Attempted to compile for architecture that was disabled by build configuration");
343 }339 }
...@@ -737,6 +733,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -737,6 +733,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
737 .bitcast => try self.airBitCast(inst),733 .bitcast => try self.airBitCast(inst),
738 .block => try self.airBlock(inst),734 .block => try self.airBlock(inst),
739 .br => try self.airBr(inst),735 .br => try self.airBr(inst),
736 .trap => try self.airTrap(),
740 .breakpoint => try self.airBreakpoint(),737 .breakpoint => try self.airBreakpoint(),
741 .ret_addr => try self.airRetAddr(inst),738 .ret_addr => try self.airRetAddr(inst),
742 .frame_addr => try self.airFrameAddress(inst),739 .frame_addr => try self.airFrameAddress(inst),
...@@ -4198,10 +4195,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -4198,10 +4195,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4198 return self.finishAir(inst, result, .{ .none, .none, .none });4195 return self.finishAir(inst, result, .{ .none, .none, .none });
4199}4196}
42004197
4198fn airTrap(self: *Self) !void {
4199 _ = try self.addInst(.{
4200 .tag = .brk,
4201 .data = .{ .imm16 = 0x0001 },
4202 });
4203 return self.finishAirBookkeeping();
4204}
4205
4201fn airBreakpoint(self: *Self) !void {4206fn airBreakpoint(self: *Self) !void {
4202 _ = try self.addInst(.{4207 _ = try self.addInst(.{
4203 .tag = .brk,4208 .tag = .brk,
4204 .data = .{ .imm16 = 1 },4209 .data = .{ .imm16 = 0xf000 },
4205 });4210 });
4206 return self.finishAirBookkeeping();4211 return self.finishAirBookkeeping();
4207}4212}
...@@ -6137,201 +6142,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {...@@ -6137,201 +6142,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
6137 }6142 }
6138}6143}
61396144
6140fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
6141 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6142 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
6143
6144 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
6145 if (tv.ty.zigTypeTag() == .Pointer) blk: {
6146 if (tv.ty.castPtrToFn()) |_| break :blk;
6147 if (!tv.ty.elemType2().hasRuntimeBits()) {
6148 return MCValue.none;
6149 }
6150 }
6151
6152 const mod = self.bin_file.options.module.?;
6153 const decl = mod.declPtr(decl_index);
6154 mod.markDeclAlive(decl);
6155
6156 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6157 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6158 const atom = elf_file.getAtom(atom_index);
6159 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6160 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6161 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
6162 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
6163 return MCValue{ .linker_load = .{
6164 .type = .got,
6165 .sym_index = sym_index,
6166 } };
6167 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6168 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6169 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
6170 return MCValue{ .linker_load = .{
6171 .type = .got,
6172 .sym_index = sym_index,
6173 } };
6174 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6175 const decl_block_index = try p9.seeDecl(decl_index);
6176 const decl_block = p9.getDeclBlock(decl_block_index);
6177 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
6178 return MCValue{ .memory = got_addr };
6179 } else {
6180 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
6181 }
6182}
6183
6184fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6185 log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
6186 const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
6187 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6188 };
6189 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6190 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6191 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6192 return MCValue{ .linker_load = .{
6193 .type = .direct,
6194 .sym_index = local_sym_index,
6195 } };
6196 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6197 return MCValue{ .linker_load = .{
6198 .type = .direct,
6199 .sym_index = local_sym_index,
6200 } };
6201 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
6202 return self.fail("TODO lower unnamed const in Plan9", .{});
6203 } else {
6204 return self.fail("TODO lower unnamed const", .{});
6205 }
6206}
6207
6208fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {6145fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6209 var typed_value = arg_tv;6146 const mcv: MCValue = switch (try codegen.genTypedValue(
6210 if (typed_value.val.castTag(.runtime_value)) |rt| {6147 self.bin_file,
6211 typed_value.val = rt.data;6148 self.src_loc,
6212 }6149 arg_tv,
6213 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });6150 self.mod_fn.owner_decl,
6214 if (typed_value.val.isUndef())6151 )) {
6215 return MCValue{ .undef = {} };6152 .mcv => |mcv| switch (mcv) {
62166153 .none => .none,
6217 if (typed_value.val.castTag(.decl_ref)) |payload| {6154 .undef => .undef,
6218 return self.lowerDeclRef(typed_value, payload.data);6155 .linker_load => |ll| .{ .linker_load = ll },
6219 }6156 .immediate => |imm| .{ .immediate = imm },
6220 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {6157 .memory => |addr| .{ .memory = addr },
6221 return self.lowerDeclRef(typed_value, payload.data.decl_index);
6222 }
6223 const target = self.target.*;
6224
6225 switch (typed_value.ty.zigTypeTag()) {
6226 .Pointer => switch (typed_value.ty.ptrSize()) {
6227 .Slice => {},
6228 else => {
6229 switch (typed_value.val.tag()) {
6230 .int_u64 => {
6231 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
6232 },
6233 else => {},
6234 }
6235 },
6236 },
6237 .Int => {
6238 const info = typed_value.ty.intInfo(self.target.*);
6239 if (info.bits <= 64) {
6240 const unsigned = switch (info.signedness) {
6241 .signed => blk: {
6242 const signed = typed_value.val.toSignedInt(target);
6243 break :blk @bitCast(u64, signed);
6244 },
6245 .unsigned => typed_value.val.toUnsignedInt(target),
6246 };
6247
6248 return MCValue{ .immediate = unsigned };
6249 }
6250 },
6251 .Bool => {
6252 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
6253 },
6254 .Optional => {
6255 if (typed_value.ty.isPtrLikeOptional()) {
6256 if (typed_value.val.isNull())
6257 return MCValue{ .immediate = 0 };
6258
6259 var buf: Type.Payload.ElemType = undefined;
6260 return self.genTypedValue(.{
6261 .ty = typed_value.ty.optionalChild(&buf),
6262 .val = typed_value.val,
6263 });
6264 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
6265 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
6266 }
6267 },
6268 .Enum => {
6269 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
6270 switch (typed_value.ty.tag()) {
6271 .enum_simple => {
6272 return MCValue{ .immediate = field_index.data };
6273 },
6274 .enum_full, .enum_nonexhaustive => {
6275 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
6276 if (enum_full.values.count() != 0) {
6277 const tag_val = enum_full.values.keys()[field_index.data];
6278 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
6279 } else {
6280 return MCValue{ .immediate = field_index.data };
6281 }
6282 },
6283 else => unreachable,
6284 }
6285 } else {
6286 var int_tag_buffer: Type.Payload.Bits = undefined;
6287 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
6288 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
6289 }
6290 },
6291 .ErrorSet => {
6292 switch (typed_value.val.tag()) {
6293 .@"error" => {
6294 const err_name = typed_value.val.castTag(.@"error").?.data.name;
6295 const module = self.bin_file.options.module.?;
6296 const global_error_set = module.global_error_set;
6297 const error_index = global_error_set.get(err_name).?;
6298 return MCValue{ .immediate = error_index };
6299 },
6300 else => {
6301 // In this case we are rendering an error union which has a 0 bits payload.
6302 return MCValue{ .immediate = 0 };
6303 },
6304 }
6305 },6158 },
6306 .ErrorUnion => {6159 .fail => |msg| {
6307 const error_type = typed_value.ty.errorUnionSet();6160 self.err_msg = msg;
6308 const payload_type = typed_value.ty.errorUnionPayload();6161 return error.CodegenFail;
6309
6310 const is_pl = typed_value.val.errorUnionIsPayload();
6311
6312 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
6313 // We use the error type directly as the type.
6314 const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
6315 return self.genTypedValue(.{ .ty = error_type, .val = err_val });
6316 }
6317
6318 return self.lowerUnnamedConst(typed_value);
6319 },6162 },
63206163 };
6321 .ComptimeInt => unreachable, // semantic analysis prevents this6164 return mcv;
6322 .ComptimeFloat => unreachable, // semantic analysis prevents this
6323 .Type => unreachable,
6324 .EnumLiteral => unreachable,
6325 .Void => unreachable,
6326 .NoReturn => unreachable,
6327 .Undefined => unreachable,
6328 .Null => unreachable,
6329 .Opaque => unreachable,
6330
6331 else => {},
6332 }
6333
6334 return self.lowerUnnamedConst(typed_value);
6335}6165}
63366166
6337const CallMCValues = struct {6167const CallMCValues = struct {
src/arch/arm/CodeGen.zig+29-176
...@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);...@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);
24const build_options = @import("build_options");24const build_options = @import("build_options");
2525
26const Result = codegen.Result;26const Result = codegen.Result;
27const GenerateSymbolError = codegen.GenerateSymbolError;27const CodeGenError = codegen.CodeGenError;
28const DebugInfoOutput = codegen.DebugInfoOutput;28const DebugInfoOutput = codegen.DebugInfoOutput;
2929
30const bits = @import("bits.zig");30const bits = @import("bits.zig");
...@@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;...@@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
42const c_abi_int_return_regs = abi.c_abi_int_return_regs;42const c_abi_int_return_regs = abi.c_abi_int_return_regs;
43const gp = abi.RegisterClass.gp;43const gp = abi.RegisterClass.gp;
4444
45const InnerError = error{45const InnerError = CodeGenError || error{OutOfRegisters};
46 OutOfMemory,
47 CodegenFail,
48 OutOfRegisters,
49};
5046
51gpa: Allocator,47gpa: Allocator,
52air: Air,48air: Air,
...@@ -343,7 +339,7 @@ pub fn generate(...@@ -343,7 +339,7 @@ pub fn generate(
343 liveness: Liveness,339 liveness: Liveness,
344 code: *std.ArrayList(u8),340 code: *std.ArrayList(u8),
345 debug_output: DebugInfoOutput,341 debug_output: DebugInfoOutput,
346) GenerateSymbolError!Result {342) CodeGenError!Result {
347 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {343 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
348 @panic("Attempted to compile for architecture that was disabled by build configuration");344 @panic("Attempted to compile for architecture that was disabled by build configuration");
349 }345 }
...@@ -721,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -721,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
721 .bitcast => try self.airBitCast(inst),717 .bitcast => try self.airBitCast(inst),
722 .block => try self.airBlock(inst),718 .block => try self.airBlock(inst),
723 .br => try self.airBr(inst),719 .br => try self.airBr(inst),
720 .trap => try self.airTrap(),
724 .breakpoint => try self.airBreakpoint(),721 .breakpoint => try self.airBreakpoint(),
725 .ret_addr => try self.airRetAddr(inst),722 .ret_addr => try self.airRetAddr(inst),
726 .frame_addr => try self.airFrameAddress(inst),723 .frame_addr => try self.airFrameAddress(inst),
...@@ -4146,6 +4143,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -4146,6 +4143,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4146 return self.finishAir(inst, result, .{ .none, .none, .none });4143 return self.finishAir(inst, result, .{ .none, .none, .none });
4147}4144}
41484145
4146fn airTrap(self: *Self) !void {
4147 _ = try self.addInst(.{
4148 .tag = .undefined_instruction,
4149 .data = .{ .nop = {} },
4150 });
4151 return self.finishAirBookkeeping();
4152}
4153
4149fn airBreakpoint(self: *Self) !void {4154fn airBreakpoint(self: *Self) !void {
4150 _ = try self.addInst(.{4155 _ = try self.addInst(.{
4151 .tag = .bkpt,4156 .tag = .bkpt,
...@@ -6087,178 +6092,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {...@@ -6087,178 +6092,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
6087 }6092 }
6088}6093}
60896094
6090fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
6091 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6092 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
6093
6094 const mod = self.bin_file.options.module.?;
6095 const decl = mod.declPtr(decl_index);
6096 mod.markDeclAlive(decl);
6097
6098 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6099 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6100 const atom = elf_file.getAtom(atom_index);
6101 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6102 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6103 unreachable; // unsupported architecture for MachO
6104 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6105 return self.fail("TODO codegen COFF const Decl pointer", .{});
6106 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6107 const decl_block_index = try p9.seeDecl(decl_index);
6108 const decl_block = p9.getDeclBlock(decl_block_index);
6109 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
6110 return MCValue{ .memory = got_addr };
6111 } else {
6112 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
6113 }
6114
6115 _ = tv;
6116}
6117
6118fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6119 const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
6120 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6121 };
6122 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6123 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6124 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6125 unreachable;
6126 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6127 return self.fail("TODO lower unnamed const in COFF", .{});
6128 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
6129 return self.fail("TODO lower unnamed const in Plan9", .{});
6130 } else {
6131 return self.fail("TODO lower unnamed const", .{});
6132 }
6133}
6134
6135fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {6095fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6136 var typed_value = arg_tv;6096 const mcv: MCValue = switch (try codegen.genTypedValue(
6137 if (typed_value.val.castTag(.runtime_value)) |rt| {6097 self.bin_file,
6138 typed_value.val = rt.data;6098 self.src_loc,
6139 }6099 arg_tv,
6140 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });6100 self.mod_fn.owner_decl,
6141 if (typed_value.val.isUndef())6101 )) {
6142 return MCValue{ .undef = {} };6102 .mcv => |mcv| switch (mcv) {
6143 const ptr_bits = self.target.cpu.arch.ptrBitWidth();6103 .none => .none,
61446104 .undef => .undef,
6145 if (typed_value.val.castTag(.decl_ref)) |payload| {6105 .linker_load => unreachable, // TODO
6146 return self.lowerDeclRef(typed_value, payload.data);6106 .immediate => |imm| .{ .immediate = @truncate(u32, imm) },
6147 }6107 .memory => |addr| .{ .memory = addr },
6148 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
6149 return self.lowerDeclRef(typed_value, payload.data.decl_index);
6150 }
6151 const target = self.target.*;
6152
6153 switch (typed_value.ty.zigTypeTag()) {
6154 .Pointer => switch (typed_value.ty.ptrSize()) {
6155 .Slice => {},
6156 else => {
6157 switch (typed_value.val.tag()) {
6158 .int_u64 => {
6159 return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt(target)) };
6160 },
6161 else => {},
6162 }
6163 },
6164 },
6165 .Int => {
6166 const info = typed_value.ty.intInfo(self.target.*);
6167 if (info.bits <= ptr_bits) {
6168 const unsigned = switch (info.signedness) {
6169 .signed => blk: {
6170 const signed = @intCast(i32, typed_value.val.toSignedInt(target));
6171 break :blk @bitCast(u32, signed);
6172 },
6173 .unsigned => @intCast(u32, typed_value.val.toUnsignedInt(target)),
6174 };
6175
6176 return MCValue{ .immediate = unsigned };
6177 } else {
6178 return self.lowerUnnamedConst(typed_value);
6179 }
6180 },
6181 .Bool => {
6182 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
6183 },
6184 .Optional => {
6185 if (typed_value.ty.isPtrLikeOptional()) {
6186 if (typed_value.val.isNull())
6187 return MCValue{ .immediate = 0 };
6188
6189 var buf: Type.Payload.ElemType = undefined;
6190 return self.genTypedValue(.{
6191 .ty = typed_value.ty.optionalChild(&buf),
6192 .val = typed_value.val,
6193 });
6194 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
6195 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
6196 }
6197 },
6198 .Enum => {
6199 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
6200 switch (typed_value.ty.tag()) {
6201 .enum_simple => {
6202 return MCValue{ .immediate = field_index.data };
6203 },
6204 .enum_full, .enum_nonexhaustive => {
6205 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
6206 if (enum_full.values.count() != 0) {
6207 const tag_val = enum_full.values.keys()[field_index.data];
6208 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
6209 } else {
6210 return MCValue{ .immediate = field_index.data };
6211 }
6212 },
6213 else => unreachable,
6214 }
6215 } else {
6216 var int_tag_buffer: Type.Payload.Bits = undefined;
6217 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
6218 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
6219 }
6220 },6108 },
6221 .ErrorSet => {6109 .fail => |msg| {
6222 switch (typed_value.val.tag()) {6110 self.err_msg = msg;
6223 .@"error" => {6111 return error.CodegenFail;
6224 const err_name = typed_value.val.castTag(.@"error").?.data.name;
6225 const module = self.bin_file.options.module.?;
6226 const global_error_set = module.global_error_set;
6227 const error_index = global_error_set.get(err_name).?;
6228 return MCValue{ .immediate = error_index };
6229 },
6230 else => {
6231 // In this case we are rendering an error union which has a 0 bits payload.
6232 return MCValue{ .immediate = 0 };
6233 },
6234 }
6235 },
6236 .ErrorUnion => {
6237 const error_type = typed_value.ty.errorUnionSet();
6238 const payload_type = typed_value.ty.errorUnionPayload();
6239 const is_pl = typed_value.val.errorUnionIsPayload();
6240
6241 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
6242 // We use the error type directly as the type.
6243 const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
6244 return self.genTypedValue(.{ .ty = error_type, .val = err_val });
6245 }
6246 },6112 },
62476113 };
6248 .ComptimeInt => unreachable, // semantic analysis prevents this6114 return mcv;
6249 .ComptimeFloat => unreachable, // semantic analysis prevents this
6250 .Type => unreachable,
6251 .EnumLiteral => unreachable,
6252 .Void => unreachable,
6253 .NoReturn => unreachable,
6254 .Undefined => unreachable,
6255 .Null => unreachable,
6256 .Opaque => unreachable,
6257
6258 else => {},
6259 }
6260
6261 return self.lowerUnnamedConst(typed_value);
6262}6115}
62636116
6264const CallMCValues = struct {6117const CallMCValues = struct {
src/arch/arm/Emit.zig+7-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1//! This file contains the functionality for lowering AArch64 MIR into1//! This file contains the functionality for lowering AArch32 MIR into
2//! machine code2//! machine code
33
4const Emit = @This();4const Emit = @This();
...@@ -15,7 +15,7 @@ const Target = std.Target;...@@ -15,7 +15,7 @@ const Target = std.Target;
15const assert = std.debug.assert;15const assert = std.debug.assert;
16const Instruction = bits.Instruction;16const Instruction = bits.Instruction;
17const Register = bits.Register;17const Register = bits.Register;
18const log = std.log.scoped(.aarch64_emit);18const log = std.log.scoped(.aarch32_emit);
19const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;19const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
20const CodeGen = @import("CodeGen.zig");20const CodeGen = @import("CodeGen.zig");
2121
...@@ -100,6 +100,7 @@ pub fn emitMir(...@@ -100,6 +100,7 @@ pub fn emitMir(
100100
101 .b => try emit.mirBranch(inst),101 .b => try emit.mirBranch(inst),
102102
103 .undefined_instruction => try emit.mirUndefinedInstruction(),
103 .bkpt => try emit.mirExceptionGeneration(inst),104 .bkpt => try emit.mirExceptionGeneration(inst),
104105
105 .blx => try emit.mirBranchExchange(inst),106 .blx => try emit.mirBranchExchange(inst),
...@@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
494 }495 }
495}496}
496497
498fn mirUndefinedInstruction(emit: *Emit) !void {
499 try emit.writeInstruction(Instruction.undefinedInstruction());
500}
501
497fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {502fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
498 const tag = emit.mir.instructions.items(.tag)[inst];503 const tag = emit.mir.instructions.items(.tag)[inst];
499 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;504 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
src/arch/arm/Mir.zig+2
...@@ -35,6 +35,8 @@ pub const Inst = struct {...@@ -35,6 +35,8 @@ pub const Inst = struct {
35 asr,35 asr,
36 /// Branch36 /// Branch
37 b,37 b,
38 /// Undefined instruction
39 undefined_instruction,
38 /// Breakpoint40 /// Breakpoint
39 bkpt,41 bkpt,
40 /// Branch with Link and Exchange42 /// Branch with Link and Exchange
src/arch/arm/bits.zig+11
...@@ -307,6 +307,9 @@ pub const Instruction = union(enum) {...@@ -307,6 +307,9 @@ pub const Instruction = union(enum) {
307 fixed: u4 = 0b1111,307 fixed: u4 = 0b1111,
308 cond: u4,308 cond: u4,
309 },309 },
310 undefined_instruction: packed struct {
311 imm32: u32 = 0xe7ffdefe,
312 },
310 breakpoint: packed struct {313 breakpoint: packed struct {
311 imm4: u4,314 imm4: u4,
312 fixed_1: u4 = 0b0111,315 fixed_1: u4 = 0b0111,
...@@ -613,6 +616,7 @@ pub const Instruction = union(enum) {...@@ -613,6 +616,7 @@ pub const Instruction = union(enum) {
613 .branch => |v| @bitCast(u32, v),616 .branch => |v| @bitCast(u32, v),
614 .branch_exchange => |v| @bitCast(u32, v),617 .branch_exchange => |v| @bitCast(u32, v),
615 .supervisor_call => |v| @bitCast(u32, v),618 .supervisor_call => |v| @bitCast(u32, v),
619 .undefined_instruction => |v| v.imm32,
616 .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),620 .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
617 };621 };
618 }622 }
...@@ -890,6 +894,13 @@ pub const Instruction = union(enum) {...@@ -890,6 +894,13 @@ pub const Instruction = union(enum) {
890 };894 };
891 }895 }
892896
897 // This instruction has no official mnemonic equivalent so it is public as-is.
898 pub fn undefinedInstruction() Instruction {
899 return Instruction{
900 .undefined_instruction = .{},
901 };
902 }
903
893 fn breakpoint(imm: u16) Instruction {904 fn breakpoint(imm: u16) Instruction {
894 return Instruction{905 return Instruction{
895 .breakpoint = .{906 .breakpoint = .{
src/arch/riscv64/CodeGen.zig+32-145
...@@ -21,10 +21,11 @@ const DW = std.dwarf;...@@ -21,10 +21,11 @@ const DW = std.dwarf;
21const leb128 = std.leb;21const leb128 = std.leb;
22const log = std.log.scoped(.codegen);22const log = std.log.scoped(.codegen);
23const build_options = @import("build_options");23const build_options = @import("build_options");
24const codegen = @import("../../codegen.zig");
2425
25const Result = @import("../../codegen.zig").Result;26const CodeGenError = codegen.CodeGenError;
26const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;27const Result = codegen.Result;
27const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;28const DebugInfoOutput = codegen.DebugInfoOutput;
2829
29const bits = @import("bits.zig");30const bits = @import("bits.zig");
30const abi = @import("abi.zig");31const abi = @import("abi.zig");
...@@ -35,11 +36,7 @@ const Instruction = abi.Instruction;...@@ -35,11 +36,7 @@ const Instruction = abi.Instruction;
35const callee_preserved_regs = abi.callee_preserved_regs;36const callee_preserved_regs = abi.callee_preserved_regs;
36const gp = abi.RegisterClass.gp;37const gp = abi.RegisterClass.gp;
3738
38const InnerError = error{39const InnerError = CodeGenError || error{OutOfRegisters};
39 OutOfMemory,
40 CodegenFail,
41 OutOfRegisters,
42};
4340
44gpa: Allocator,41gpa: Allocator,
45air: Air,42air: Air,
...@@ -225,7 +222,7 @@ pub fn generate(...@@ -225,7 +222,7 @@ pub fn generate(
225 liveness: Liveness,222 liveness: Liveness,
226 code: *std.ArrayList(u8),223 code: *std.ArrayList(u8),
227 debug_output: DebugInfoOutput,224 debug_output: DebugInfoOutput,
228) GenerateSymbolError!Result {225) CodeGenError!Result {
229 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {226 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
230 @panic("Attempted to compile for architecture that was disabled by build configuration");227 @panic("Attempted to compile for architecture that was disabled by build configuration");
231 }228 }
...@@ -550,6 +547,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -550,6 +547,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
550 .bitcast => try self.airBitCast(inst),547 .bitcast => try self.airBitCast(inst),
551 .block => try self.airBlock(inst),548 .block => try self.airBlock(inst),
552 .br => try self.airBr(inst),549 .br => try self.airBr(inst),
550 .trap => try self.airTrap(),
553 .breakpoint => try self.airBreakpoint(),551 .breakpoint => try self.airBreakpoint(),
554 .ret_addr => try self.airRetAddr(inst),552 .ret_addr => try self.airRetAddr(inst),
555 .frame_addr => try self.airFrameAddress(inst),553 .frame_addr => try self.airFrameAddress(inst),
...@@ -1652,6 +1650,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1652,6 +1650,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1652 return self.finishAir(inst, mcv, .{ .none, .none, .none });1650 return self.finishAir(inst, mcv, .{ .none, .none, .none });
1653}1651}
16541652
1653fn airTrap(self: *Self) !void {
1654 _ = try self.addInst(.{
1655 .tag = .unimp,
1656 .data = .{ .nop = {} },
1657 });
1658 return self.finishAirBookkeeping();
1659}
1660
1655fn airBreakpoint(self: *Self) !void {1661fn airBreakpoint(self: *Self) !void {
1656 _ = try self.addInst(.{1662 _ = try self.addInst(.{
1657 .tag = .ebreak,1663 .tag = .ebreak,
...@@ -2552,145 +2558,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {...@@ -2552,145 +2558,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
2552 }2558 }
2553}2559}
25542560
2555fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
2556 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2557 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2558 const mod = self.bin_file.options.module.?;
2559 const decl = mod.declPtr(decl_index);
2560 mod.markDeclAlive(decl);
2561 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2562 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
2563 const atom = elf_file.getAtom(atom_index);
2564 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
2565 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2566 unreachable;
2567 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2568 return self.fail("TODO codegen COFF const Decl pointer", .{});
2569 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2570 const decl_block_index = try p9.seeDecl(decl_index);
2571 const decl_block = p9.getDeclBlock(decl_block_index);
2572 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
2573 return MCValue{ .memory = got_addr };
2574 } else {
2575 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
2576 }
2577 _ = tv;
2578}
2579
2580fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {2561fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
2581 if (typed_value.val.isUndef())2562 const mcv: MCValue = switch (try codegen.genTypedValue(
2582 return MCValue{ .undef = {} };2563 self.bin_file,
25832564 self.src_loc,
2584 if (typed_value.val.castTag(.decl_ref)) |payload| {2565 typed_value,
2585 return self.lowerDeclRef(typed_value, payload.data);2566 self.mod_fn.owner_decl,
2586 }2567 )) {
2587 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {2568 .mcv => |mcv| switch (mcv) {
2588 return self.lowerDeclRef(typed_value, payload.data.decl_index);2569 .none => .none,
2589 }2570 .undef => .undef,
2590 const target = self.target.*;2571 .linker_load => unreachable, // TODO
2591 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2572 .immediate => |imm| .{ .immediate = imm },
2592 switch (typed_value.ty.zigTypeTag()) {2573 .memory => |addr| .{ .memory = addr },
2593 .Pointer => switch (typed_value.ty.ptrSize()) {
2594 .Slice => {
2595 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2596 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
2597 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
2598 const mod = self.bin_file.options.module.?;
2599 const slice_len = typed_value.val.sliceLen(mod);
2600 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
2601 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
2602 const ptr_imm = ptr_mcv.memory;
2603 _ = slice_len;
2604 _ = ptr_imm;
2605 // We need more general support for const data being stored in memory to make this work.
2606 return self.fail("TODO codegen for const slices", .{});
2607 },
2608 else => {
2609 if (typed_value.val.tag() == .int_u64) {
2610 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
2611 }
2612 return self.fail("TODO codegen more kinds of const pointers", .{});
2613 },
2614 },
2615 .Int => {
2616 const info = typed_value.ty.intInfo(self.target.*);
2617 if (info.bits > ptr_bits or info.signedness == .signed) {
2618 return self.fail("TODO const int bigger than ptr and signed int", .{});
2619 }
2620 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
2621 },
2622 .Bool => {
2623 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
2624 },
2625 .ComptimeInt => unreachable, // semantic analysis prevents this
2626 .ComptimeFloat => unreachable, // semantic analysis prevents this
2627 .Optional => {
2628 if (typed_value.ty.isPtrLikeOptional()) {
2629 if (typed_value.val.isNull())
2630 return MCValue{ .immediate = 0 };
2631
2632 var buf: Type.Payload.ElemType = undefined;
2633 return self.genTypedValue(.{
2634 .ty = typed_value.ty.optionalChild(&buf),
2635 .val = typed_value.val,
2636 });
2637 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
2638 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
2639 }
2640 return self.fail("TODO non pointer optionals", .{});
2641 },2574 },
2642 .Enum => {2575 .fail => |msg| {
2643 if (typed_value.val.castTag(.enum_field_index)) |field_index| {2576 self.err_msg = msg;
2644 switch (typed_value.ty.tag()) {2577 return error.CodegenFail;
2645 .enum_simple => {
2646 return MCValue{ .immediate = field_index.data };
2647 },
2648 .enum_full, .enum_nonexhaustive => {
2649 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
2650 if (enum_full.values.count() != 0) {
2651 const tag_val = enum_full.values.keys()[field_index.data];
2652 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
2653 } else {
2654 return MCValue{ .immediate = field_index.data };
2655 }
2656 },
2657 else => unreachable,
2658 }
2659 } else {
2660 var int_tag_buffer: Type.Payload.Bits = undefined;
2661 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
2662 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
2663 }
2664 },2578 },
2665 .ErrorSet => {2579 };
2666 switch (typed_value.val.tag()) {2580 return mcv;
2667 .@"error" => {
2668 const err_name = typed_value.val.castTag(.@"error").?.data.name;
2669 const module = self.bin_file.options.module.?;
2670 const global_error_set = module.global_error_set;
2671 const error_index = global_error_set.get(err_name).?;
2672 return MCValue{ .immediate = error_index };
2673 },
2674 else => {
2675 // In this case we are rendering an error union which has a 0 bits payload.
2676 return MCValue{ .immediate = 0 };
2677 },
2678 }
2679 },
2680 .ErrorUnion => {
2681 const error_type = typed_value.ty.errorUnionSet();
2682 const payload_type = typed_value.ty.errorUnionPayload();
2683 const sub_val = typed_value.val.castTag(.eu_payload).?.data;
2684
2685 if (!payload_type.hasRuntimeBits()) {
2686 // We use the error type directly as the type.
2687 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
2688 }
2689
2690 return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty.fmtDebug()});
2691 },
2692 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
2693 }
2694}2581}
26952582
2696const CallMCValues = struct {2583const CallMCValues = struct {
src/arch/riscv64/Emit.zig+2
...@@ -51,6 +51,7 @@ pub fn emitMir(...@@ -51,6 +51,7 @@ pub fn emitMir(
5151
52 .ebreak => try emit.mirSystem(inst),52 .ebreak => try emit.mirSystem(inst),
53 .ecall => try emit.mirSystem(inst),53 .ecall => try emit.mirSystem(inst),
54 .unimp => try emit.mirSystem(inst),
5455
55 .dbg_line => try emit.mirDbgLine(inst),56 .dbg_line => try emit.mirDbgLine(inst),
5657
...@@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
153 switch (tag) {154 switch (tag) {
154 .ebreak => try emit.writeInstruction(Instruction.ebreak),155 .ebreak => try emit.writeInstruction(Instruction.ebreak),
155 .ecall => try emit.writeInstruction(Instruction.ecall),156 .ecall => try emit.writeInstruction(Instruction.ecall),
157 .unimp => try emit.writeInstruction(Instruction.unimp),
156 else => unreachable,158 else => unreachable,
157 }159 }
158}160}
src/arch/riscv64/Mir.zig+1
...@@ -32,6 +32,7 @@ pub const Inst = struct {...@@ -32,6 +32,7 @@ pub const Inst = struct {
32 dbg_epilogue_begin,32 dbg_epilogue_begin,
33 /// Pseudo-instruction: Update debug line33 /// Pseudo-instruction: Update debug line
34 dbg_line,34 dbg_line,
35 unimp,
35 ebreak,36 ebreak,
36 ecall,37 ecall,
37 jalr,38 jalr,
src/arch/riscv64/bits.zig+1
...@@ -380,6 +380,7 @@ pub const Instruction = union(enum) {...@@ -380,6 +380,7 @@ pub const Instruction = union(enum) {
380380
381 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);381 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
382 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);382 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
383 pub const unimp = iType(0, 0, .zero, .zero, 0);
383};384};
384385
385pub const Register = enum(u6) {386pub const Register = enum(u6) {
src/arch/sparc64/CodeGen.zig+36-154
...@@ -19,7 +19,7 @@ const Mir = @import("Mir.zig");...@@ -19,7 +19,7 @@ const Mir = @import("Mir.zig");
19const Emit = @import("Emit.zig");19const Emit = @import("Emit.zig");
20const Liveness = @import("../../Liveness.zig");20const Liveness = @import("../../Liveness.zig");
21const Type = @import("../../type.zig").Type;21const Type = @import("../../type.zig").Type;
22const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;22const CodeGenError = codegen.CodeGenError;
23const Result = @import("../../codegen.zig").Result;23const Result = @import("../../codegen.zig").Result;
24const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;24const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2525
...@@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp;...@@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp;
3838
39const Self = @This();39const Self = @This();
4040
41const InnerError = error{41const InnerError = CodeGenError || error{OutOfRegisters};
42 OutOfMemory,
43 CodegenFail,
44 OutOfRegisters,
45};
4642
47const RegisterView = enum(u1) {43const RegisterView = enum(u1) {
48 caller,44 caller,
...@@ -265,7 +261,7 @@ pub fn generate(...@@ -265,7 +261,7 @@ pub fn generate(
265 liveness: Liveness,261 liveness: Liveness,
266 code: *std.ArrayList(u8),262 code: *std.ArrayList(u8),
267 debug_output: DebugInfoOutput,263 debug_output: DebugInfoOutput,
268) GenerateSymbolError!Result {264) CodeGenError!Result {
269 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {265 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
270 @panic("Attempted to compile for architecture that was disabled by build configuration");266 @panic("Attempted to compile for architecture that was disabled by build configuration");
271 }267 }
...@@ -566,6 +562,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -566,6 +562,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
566 .bitcast => try self.airBitCast(inst),562 .bitcast => try self.airBitCast(inst),
567 .block => try self.airBlock(inst),563 .block => try self.airBlock(inst),
568 .br => try self.airBr(inst),564 .br => try self.airBr(inst),
565 .trap => try self.airTrap(),
569 .breakpoint => try self.airBreakpoint(),566 .breakpoint => try self.airBreakpoint(),
570 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),567 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
571 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),568 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
...@@ -1160,6 +1157,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1160,6 +1157,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
1160 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });1157 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
1161}1158}
11621159
1160fn airTrap(self: *Self) !void {
1161 // ta 0x05
1162 _ = try self.addInst(.{
1163 .tag = .tcc,
1164 .data = .{
1165 .trap = .{
1166 .is_imm = true,
1167 .cond = .al,
1168 .rs2_or_imm = .{ .imm = 0x05 },
1169 },
1170 },
1171 });
1172 return self.finishAirBookkeeping();
1173}
1174
1163fn airBreakpoint(self: *Self) !void {1175fn airBreakpoint(self: *Self) !void {
1164 // ta 0x011176 // ta 0x01
1165 _ = try self.addInst(.{1177 _ = try self.addInst(.{
...@@ -3898,133 +3910,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t...@@ -3898,133 +3910,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t
3898}3910}
38993911
3900fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {3912fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
3901 var tv = typed_value;3913 const mcv: MCValue = switch (try codegen.genTypedValue(
3902 log.debug("genTypedValue: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });3914 self.bin_file,
39033915 self.src_loc,
3904 if (tv.val.castTag(.runtime_value)) |rt| {3916 typed_value,
3905 tv.val = rt.data;3917 self.mod_fn.owner_decl,
3906 }3918 )) {
39073919 .mcv => |mcv| switch (mcv) {
3908 if (tv.val.isUndef())3920 .none => .none,
3909 return MCValue{ .undef = {} };3921 .undef => .undef,
39103922 .linker_load => unreachable, // TODO
3911 if (tv.val.castTag(.decl_ref)) |payload| {3923 .immediate => |imm| .{ .immediate = imm },
3912 return self.lowerDeclRef(tv, payload.data);3924 .memory => |addr| .{ .memory = addr },
3913 }
3914 if (tv.val.castTag(.decl_ref_mut)) |payload| {
3915 return self.lowerDeclRef(tv, payload.data.decl_index);
3916 }
3917 const target = self.target.*;
3918
3919 switch (tv.ty.zigTypeTag()) {
3920 .Pointer => switch (tv.ty.ptrSize()) {
3921 .Slice => {},
3922 else => {
3923 switch (tv.val.tag()) {
3924 .int_u64 => {
3925 return MCValue{ .immediate = tv.val.toUnsignedInt(target) };
3926 },
3927 else => {},
3928 }
3929 },
3930 },
3931 .Bool => {
3932 return MCValue{ .immediate = @boolToInt(tv.val.toBool()) };
3933 },
3934 .Int => {
3935 const info = tv.ty.intInfo(self.target.*);
3936 if (info.bits <= 64) {
3937 const unsigned = switch (info.signedness) {
3938 .signed => blk: {
3939 const signed = tv.val.toSignedInt(target);
3940 break :blk @bitCast(u64, signed);
3941 },
3942 .unsigned => tv.val.toUnsignedInt(target),
3943 };
3944
3945 return MCValue{ .immediate = unsigned };
3946 } else {
3947 return self.fail("TODO implement int genTypedValue of > 64 bits", .{});
3948 }
3949 },3925 },
3950 .Optional => {3926 .fail => |msg| {
3951 if (tv.ty.isPtrLikeOptional()) {3927 self.err_msg = msg;
3952 if (tv.val.isNull())3928 return error.CodegenFail;
3953 return MCValue{ .immediate = 0 };
3954
3955 var buf: Type.Payload.ElemType = undefined;
3956 return self.genTypedValue(.{
3957 .ty = tv.ty.optionalChild(&buf),
3958 .val = tv.val,
3959 });
3960 } else if (tv.ty.abiSize(self.target.*) == 1) {
3961 return MCValue{ .immediate = @boolToInt(tv.val.isNull()) };
3962 }
3963 },
3964 .Enum => {
3965 if (tv.val.castTag(.enum_field_index)) |field_index| {
3966 switch (tv.ty.tag()) {
3967 .enum_simple => {
3968 return MCValue{ .immediate = field_index.data };
3969 },
3970 .enum_full, .enum_nonexhaustive => {
3971 const enum_full = tv.ty.cast(Type.Payload.EnumFull).?.data;
3972 if (enum_full.values.count() != 0) {
3973 const tag_val = enum_full.values.keys()[field_index.data];
3974 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
3975 } else {
3976 return MCValue{ .immediate = field_index.data };
3977 }
3978 },
3979 else => unreachable,
3980 }
3981 } else {
3982 var int_tag_buffer: Type.Payload.Bits = undefined;
3983 const int_tag_ty = tv.ty.intTagType(&int_tag_buffer);
3984 return self.genTypedValue(.{ .ty = int_tag_ty, .val = tv.val });
3985 }
3986 },
3987 .ErrorSet => {
3988 const err_name = tv.val.castTag(.@"error").?.data.name;
3989 const module = self.bin_file.options.module.?;
3990 const global_error_set = module.global_error_set;
3991 const error_index = global_error_set.get(err_name).?;
3992 return MCValue{ .immediate = error_index };
3993 },
3994 .ErrorUnion => {
3995 const error_type = tv.ty.errorUnionSet();
3996 const payload_type = tv.ty.errorUnionPayload();
3997
3998 if (tv.val.castTag(.eu_payload)) |pl| {
3999 if (!payload_type.hasRuntimeBits()) {
4000 // We use the error type directly as the type.
4001 return MCValue{ .immediate = 0 };
4002 }
4003
4004 _ = pl;
4005 return self.fail("TODO implement error union const of type '{}' (non-error)", .{tv.ty.fmtDebug()});
4006 } else {
4007 if (!payload_type.hasRuntimeBits()) {
4008 // We use the error type directly as the type.
4009 return self.genTypedValue(.{ .ty = error_type, .val = tv.val });
4010 }
4011
4012 return self.fail("TODO implement error union const of type '{}' (error)", .{tv.ty.fmtDebug()});
4013 }
4014 },3929 },
4015 .ComptimeInt => unreachable, // semantic analysis prevents this3930 };
4016 .ComptimeFloat => unreachable, // semantic analysis prevents this3931 return mcv;
4017 .Type => unreachable,
4018 .EnumLiteral => unreachable,
4019 .Void => unreachable,
4020 .NoReturn => unreachable,
4021 .Undefined => unreachable,
4022 .Null => unreachable,
4023 .Opaque => unreachable,
4024 else => {},
4025 }
4026
4027 return self.fail("TODO implement const of type '{}'", .{tv.ty.fmtDebug()});
4028}3932}
40293933
4030fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {3934fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
...@@ -4200,28 +4104,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -4200,28 +4104,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
4200 }4104 }
4201}4105}
42024106
4203fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
4204 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
4205 if (tv.ty.zigTypeTag() == .Pointer) blk: {
4206 if (tv.ty.castPtrToFn()) |_| break :blk;
4207 if (!tv.ty.elemType2().hasRuntimeBits()) {
4208 return MCValue.none;
4209 }
4210 }
4211
4212 const mod = self.bin_file.options.module.?;
4213 const decl = mod.declPtr(decl_index);
4214
4215 mod.markDeclAlive(decl);
4216 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4217 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
4218 const atom = elf_file.getAtom(atom_index);
4219 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
4220 } else {
4221 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
4222 }
4223}
4224
4225fn minMax(4107fn minMax(
4226 self: *Self,4108 self: *Self,
4227 tag: Air.Inst.Tag,4109 tag: Air.Inst.Tag,
src/arch/wasm/CodeGen.zig+8-3
...@@ -733,8 +733,6 @@ const InnerError = error{...@@ -733,8 +733,6 @@ const InnerError = error{
733 OutOfMemory,733 OutOfMemory,
734 /// An error occurred when trying to lower AIR to MIR.734 /// An error occurred when trying to lower AIR to MIR.
735 CodegenFail,735 CodegenFail,
736 /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
737 AnalysisFail,
738 /// Compiler implementation could not handle a large integer.736 /// Compiler implementation could not handle a large integer.
739 Overflow,737 Overflow,
740};738};
...@@ -1164,7 +1162,7 @@ pub fn generate(...@@ -1164,7 +1162,7 @@ pub fn generate(
1164 liveness: Liveness,1162 liveness: Liveness,
1165 code: *std.ArrayList(u8),1163 code: *std.ArrayList(u8),
1166 debug_output: codegen.DebugInfoOutput,1164 debug_output: codegen.DebugInfoOutput,
1167) codegen.GenerateSymbolError!codegen.Result {1165) codegen.CodeGenError!codegen.Result {
1168 _ = src_loc;1166 _ = src_loc;
1169 var code_gen: CodeGen = .{1167 var code_gen: CodeGen = .{
1170 .gpa = bin_file.allocator,1168 .gpa = bin_file.allocator,
...@@ -1829,6 +1827,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1829,6 +1827,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1829 .arg => func.airArg(inst),1827 .arg => func.airArg(inst),
1830 .bitcast => func.airBitcast(inst),1828 .bitcast => func.airBitcast(inst),
1831 .block => func.airBlock(inst),1829 .block => func.airBlock(inst),
1830 .trap => func.airTrap(inst),
1832 .breakpoint => func.airBreakpoint(inst),1831 .breakpoint => func.airBreakpoint(inst),
1833 .br => func.airBr(inst),1832 .br => func.airBr(inst),
1834 .bool_to_int => func.airBoolToInt(inst),1833 .bool_to_int => func.airBoolToInt(inst),
...@@ -3289,9 +3288,15 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3289,9 +3288,15 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3289 func.finishAir(inst, result, &.{ty_op.operand});3288 func.finishAir(inst, result, &.{ty_op.operand});
3290}3289}
32913290
3291fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3292 try func.addTag(.@"unreachable");
3293 func.finishAir(inst, .none, &.{});
3294}
3295
3292fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3296fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3293 // unsupported by wasm itfunc. Can be implemented once we support DWARF3297 // unsupported by wasm itfunc. Can be implemented once we support DWARF
3294 // for wasm3298 // for wasm
3299 try func.addTag(.@"unreachable");
3295 func.finishAir(inst, .none, &.{});3300 func.finishAir(inst, .none, &.{});
3296}3301}
32973302
src/arch/x86_64/CodeGen.zig+957-1494
...@@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace;...@@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace;
1212
13const Air = @import("../../Air.zig");13const Air = @import("../../Air.zig");
14const Allocator = mem.Allocator;14const Allocator = mem.Allocator;
15const CodeGenError = codegen.CodeGenError;
15const Compilation = @import("../../Compilation.zig");16const Compilation = @import("../../Compilation.zig");
16const DebugInfoOutput = codegen.DebugInfoOutput;17const DebugInfoOutput = codegen.DebugInfoOutput;
17const DW = std.dwarf;18const DW = std.dwarf;
18const ErrorMsg = Module.ErrorMsg;19const ErrorMsg = Module.ErrorMsg;
19const Result = codegen.Result;20const Result = codegen.Result;
20const GenerateSymbolError = codegen.GenerateSymbolError;
21const Emit = @import("Emit.zig");21const Emit = @import("Emit.zig");
22const Liveness = @import("../../Liveness.zig");22const Liveness = @import("../../Liveness.zig");
23const Mir = @import("Mir.zig");23const Mir = @import("Mir.zig");
...@@ -33,18 +33,16 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset;...@@ -33,18 +33,16 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
35const Condition = bits.Condition;35const Condition = bits.Condition;
36const Immediate = bits.Immediate;
37const Memory = bits.Memory;
38const Register = bits.Register;
36const RegisterManager = abi.RegisterManager;39const RegisterManager = abi.RegisterManager;
37const RegisterLock = RegisterManager.RegisterLock;40const RegisterLock = RegisterManager.RegisterLock;
38const Register = bits.Register;
3941
40const gp = abi.RegisterClass.gp;42const gp = abi.RegisterClass.gp;
41const sse = abi.RegisterClass.sse;43const sse = abi.RegisterClass.sse;
4244
43const InnerError = error{45const InnerError = CodeGenError || error{OutOfRegisters};
44 OutOfMemory,
45 CodegenFail,
46 OutOfRegisters,
47};
4846
49gpa: Allocator,47gpa: Allocator,
50air: Air,48air: Air,
...@@ -186,8 +184,7 @@ const Branch = struct {...@@ -186,8 +184,7 @@ const Branch = struct {
186 _ = options;184 _ = options;
187 comptime assert(unused_format_string.len == 0);185 comptime assert(unused_format_string.len == 0);
188 try writer.writeAll("Branch {\n");186 try writer.writeAll("Branch {\n");
189 for (ctx.insts, 0..) |inst, i| {187 for (ctx.insts, ctx.mcvs) |inst, mcv| {
190 const mcv = ctx.mcvs[i];
191 try writer.print(" %{d} => {}\n", .{ inst, mcv });188 try writer.print(" %{d} => {}\n", .{ inst, mcv });
192 }189 }
193 try writer.writeAll("}");190 try writer.writeAll("}");
...@@ -210,7 +207,7 @@ const Branch = struct {...@@ -210,7 +207,7 @@ const Branch = struct {
210};207};
211208
212const StackAllocation = struct {209const StackAllocation = struct {
213 inst: Air.Inst.Index,210 inst: ?Air.Inst.Index,
214 /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*)211 /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*)
215 size: u32,212 size: u32,
216};213};
...@@ -257,7 +254,7 @@ pub fn generate(...@@ -257,7 +254,7 @@ pub fn generate(
257 liveness: Liveness,254 liveness: Liveness,
258 code: *std.ArrayList(u8),255 code: *std.ArrayList(u8),
259 debug_output: DebugInfoOutput,256 debug_output: DebugInfoOutput,
260) GenerateSymbolError!Result {257) CodeGenError!Result {
261 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {258 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
262 @panic("Attempted to compile for architecture that was disabled by build configuration");259 @panic("Attempted to compile for architecture that was disabled by build configuration");
263 }260 }
...@@ -307,7 +304,12 @@ pub fn generate(...@@ -307,7 +304,12 @@ pub fn generate(
307 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {304 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
308 error.CodegenFail => return Result{ .fail = function.err_msg.? },305 error.CodegenFail => return Result{ .fail = function.err_msg.? },
309 error.OutOfRegisters => return Result{306 error.OutOfRegisters => return Result{
310 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),307 .fail = try ErrorMsg.create(
308 bin_file.allocator,
309 src_loc,
310 "CodeGen ran out of registers. This is a bug in the Zig compiler.",
311 .{},
312 ),
311 },313 },
312 else => |e| return e,314 else => |e| return e,
313 };315 };
...@@ -346,6 +348,20 @@ pub fn generate(...@@ -346,6 +348,20 @@ pub fn generate(
346 defer emit.deinit();348 defer emit.deinit();
347 emit.lowerMir() catch |err| switch (err) {349 emit.lowerMir() catch |err| switch (err) {
348 error.EmitFail => return Result{ .fail = emit.err_msg.? },350 error.EmitFail => return Result{ .fail = emit.err_msg.? },
351 error.InvalidInstruction, error.CannotEncode => |e| {
352 const msg = switch (e) {
353 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
354 error.CannotEncode => "CodeGen failed to encode the instruction.",
355 };
356 return Result{
357 .fail = try ErrorMsg.create(
358 bin_file.allocator,
359 src_loc,
360 "{s} This is a bug in the Zig compiler.",
361 .{msg},
362 ),
363 };
364 },
349 else => |e| return e,365 else => |e| return e,
350 };366 };
351367
...@@ -359,52 +375,263 @@ pub fn generate(...@@ -359,52 +375,263 @@ pub fn generate(
359fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {375fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
360 const gpa = self.gpa;376 const gpa = self.gpa;
361 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);377 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
362 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);378 const result_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);
363 self.mir_instructions.appendAssumeCapacity(inst);379 self.mir_instructions.appendAssumeCapacity(inst);
364 return result_index;380 return result_index;
365}381}
366382
367pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {383fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
368 const fields = std.meta.fields(@TypeOf(extra));384 const fields = std.meta.fields(@TypeOf(extra));
369 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);385 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
370 return self.addExtraAssumeCapacity(extra);386 return self.addExtraAssumeCapacity(extra);
371}387}
372388
373pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {389fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
374 const fields = std.meta.fields(@TypeOf(extra));390 const fields = std.meta.fields(@TypeOf(extra));
375 const result = @intCast(u32, self.mir_extra.items.len);391 const result = @intCast(u32, self.mir_extra.items.len);
376 inline for (fields) |field| {392 inline for (fields) |field| {
377 self.mir_extra.appendAssumeCapacity(switch (field.type) {393 self.mir_extra.appendAssumeCapacity(switch (field.type) {
378 u32 => @field(extra, field.name),394 u32 => @field(extra, field.name),
379 i32 => @bitCast(u32, @field(extra, field.name)),395 i32 => @bitCast(u32, @field(extra, field.name)),
380 else => @compileError("bad field type"),396 else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
381 });397 });
382 }398 }
383 return result;399 return result;
384}400}
385401
402fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void {
403 _ = try self.addInst(.{
404 .tag = .setcc,
405 .ops = .r_c,
406 .data = .{ .r_c = .{
407 .r1 = reg,
408 .cc = cc,
409 } },
410 });
411}
412
413fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bits.Condition) !void {
414 _ = try self.addInst(.{
415 .tag = .cmovcc,
416 .ops = .rr_c,
417 .data = .{ .rr_c = .{
418 .r1 = reg1,
419 .r2 = reg2,
420 .cc = cc,
421 } },
422 });
423}
424
425fn asmJmpReloc(self: *Self, target: Mir.Inst.Index) !Mir.Inst.Index {
426 return self.addInst(.{
427 .tag = .jmp_reloc,
428 .ops = undefined,
429 .data = .{ .inst = target },
430 });
431}
432
433fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Inst.Index {
434 return self.addInst(.{
435 .tag = .jcc,
436 .ops = .inst_cc,
437 .data = .{ .inst_cc = .{
438 .inst = target,
439 .cc = cc,
440 } },
441 });
442}
443
444fn asmOpOnly(self: *Self, tag: Mir.Inst.Tag) !void {
445 _ = try self.addInst(.{
446 .tag = tag,
447 .ops = .none,
448 .data = undefined,
449 });
450}
451
452fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void {
453 _ = try self.addInst(.{
454 .tag = tag,
455 .ops = .r,
456 .data = .{ .r = reg },
457 });
458}
459
460fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void {
461 const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u;
462 _ = try self.addInst(.{
463 .tag = tag,
464 .ops = ops,
465 .data = .{ .imm = switch (imm) {
466 .signed => |x| @bitCast(u32, x),
467 .unsigned => |x| @intCast(u32, x),
468 } },
469 });
470}
471
472fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Register) !void {
473 _ = try self.addInst(.{
474 .tag = tag,
475 .ops = .rr,
476 .data = .{ .rr = .{
477 .r1 = reg1,
478 .r2 = reg2,
479 } },
480 });
481}
482
483fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void {
484 const ops: Mir.Inst.Ops = switch (imm) {
485 .signed => .ri_s,
486 .unsigned => |x| if (x <= math.maxInt(u32)) .ri_u else .ri64,
487 };
488 const data: Mir.Inst.Data = switch (ops) {
489 .ri_s => .{ .ri = .{
490 .r1 = reg,
491 .imm = @bitCast(u32, imm.signed),
492 } },
493 .ri_u => .{ .ri = .{
494 .r1 = reg,
495 .imm = @intCast(u32, imm.unsigned),
496 } },
497 .ri64 => .{ .rx = .{
498 .r1 = reg,
499 .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)),
500 } },
501 else => unreachable,
502 };
503 _ = try self.addInst(.{
504 .tag = tag,
505 .ops = ops,
506 .data = data,
507 });
508}
509
510fn asmRegisterRegisterImmediate(
511 self: *Self,
512 tag: Mir.Inst.Tag,
513 reg1: Register,
514 reg2: Register,
515 imm: Immediate,
516) !void {
517 const ops: Mir.Inst.Ops = switch (imm) {
518 .signed => .rri_s,
519 .unsigned => .rri_u,
520 };
521 const data: Mir.Inst.Data = switch (ops) {
522 .rri_s => .{ .rri = .{
523 .r1 = reg1,
524 .r2 = reg2,
525 .imm = @bitCast(u32, imm.signed),
526 } },
527 .rri_u => .{ .rri = .{
528 .r1 = reg1,
529 .r2 = reg2,
530 .imm = @intCast(u32, imm.unsigned),
531 } },
532 else => unreachable,
533 };
534 _ = try self.addInst(.{
535 .tag = tag,
536 .ops = ops,
537 .data = data,
538 });
539}
540
541fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void {
542 const ops: Mir.Inst.Ops = switch (m) {
543 .sib => .m_sib,
544 .rip => .m_rip,
545 else => unreachable,
546 };
547 const data: Mir.Inst.Data = .{ .payload = switch (ops) {
548 .m_sib => try self.addExtra(Mir.MemorySib.encode(m)),
549 .m_rip => try self.addExtra(Mir.MemoryRip.encode(m)),
550 else => unreachable,
551 } };
552 _ = try self.addInst(.{
553 .tag = tag,
554 .ops = ops,
555 .data = data,
556 });
557}
558
559fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.Tag, m: Memory, imm: Immediate) !void {
560 const ops: Mir.Inst.Ops = switch (m) {
561 .sib => if (imm == .signed) .mi_s_sib else .mi_u_sib,
562 .rip => if (imm == .signed) .mi_s_rip else .mi_u_rip,
563 else => unreachable,
564 };
565 const payload: u32 = switch (ops) {
566 .mi_s_sib, .mi_u_sib => try self.addExtra(Mir.MemorySib.encode(m)),
567 .mi_s_rip, .mi_u_rip => try self.addExtra(Mir.MemoryRip.encode(m)),
568 else => unreachable,
569 };
570 const data: Mir.Inst.Data = .{
571 .xi = .{ .payload = payload, .imm = switch (imm) {
572 .signed => |x| @bitCast(u32, x),
573 .unsigned => |x| @intCast(u32, x),
574 } },
575 };
576 _ = try self.addInst(.{
577 .tag = tag,
578 .ops = ops,
579 .data = data,
580 });
581}
582
583fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !void {
584 const ops: Mir.Inst.Ops = switch (m) {
585 .sib => .rm_sib,
586 .rip => .rm_rip,
587 else => unreachable,
588 };
589 const data: Mir.Inst.Data = .{
590 .rx = .{ .r1 = reg, .payload = switch (ops) {
591 .rm_sib => try self.addExtra(Mir.MemorySib.encode(m)),
592 .rm_rip => try self.addExtra(Mir.MemoryRip.encode(m)),
593 else => unreachable,
594 } },
595 };
596 _ = try self.addInst(.{
597 .tag = tag,
598 .ops = ops,
599 .data = data,
600 });
601}
602
603fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !void {
604 const ops: Mir.Inst.Ops = switch (m) {
605 .sib => .mr_sib,
606 .rip => .mr_rip,
607 else => unreachable,
608 };
609 const data: Mir.Inst.Data = .{
610 .rx = .{ .r1 = reg, .payload = switch (ops) {
611 .mr_sib => try self.addExtra(Mir.MemorySib.encode(m)),
612 .mr_rip => try self.addExtra(Mir.MemoryRip.encode(m)),
613 else => unreachable,
614 } },
615 };
616 _ = try self.addInst(.{
617 .tag = tag,
618 .ops = ops,
619 .data = data,
620 });
621}
622
386fn gen(self: *Self) InnerError!void {623fn gen(self: *Self) InnerError!void {
387 const cc = self.fn_type.fnCallingConvention();624 const cc = self.fn_type.fnCallingConvention();
388 if (cc != .Naked) {625 if (cc != .Naked) {
389 _ = try self.addInst(.{626 try self.asmRegister(.push, .rbp);
390 .tag = .push,627 try self.asmRegisterRegister(.mov, .rbp, .rsp);
391 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),628
392 .data = undefined, // unused for push reg,
393 });
394 _ = try self.addInst(.{
395 .tag = .mov,
396 .ops = Mir.Inst.Ops.encode(.{
397 .reg1 = .rbp,
398 .reg2 = .rsp,
399 }),
400 .data = undefined,
401 });
402 // We want to subtract the aligned stack frame size from rsp here, but we don't629 // We want to subtract the aligned stack frame size from rsp here, but we don't
403 // yet know how big it will be, so we leave room for a 4-byte stack size.630 // yet know how big it will be, so we leave room for a 4-byte stack size.
404 // TODO During semantic analysis, check if there are no function calls. If there631 // TODO During semantic analysis, check if there are no function calls. If there
405 // are none, here we can omit the part where we subtract and then add rsp.632 // are none, here we can omit the part where we subtract and then add rsp.
406 const backpatch_stack_sub = try self.addInst(.{633 const backpatch_stack_sub = try self.addInst(.{
407 .tag = .nop,634 .tag = .dead,
408 .ops = undefined,635 .ops = undefined,
409 .data = undefined,636 .data = undefined,
410 });637 });
...@@ -431,7 +658,7 @@ fn gen(self: *Self) InnerError!void {...@@ -431,7 +658,7 @@ fn gen(self: *Self) InnerError!void {
431658
432 // Push callee-preserved regs that were used actually in use.659 // Push callee-preserved regs that were used actually in use.
433 const backpatch_push_callee_preserved_regs = try self.addInst(.{660 const backpatch_push_callee_preserved_regs = try self.addInst(.{
434 .tag = .nop,661 .tag = .dead,
435 .ops = undefined,662 .ops = undefined,
436 .data = undefined,663 .data = undefined,
437 });664 });
...@@ -462,7 +689,7 @@ fn gen(self: *Self) InnerError!void {...@@ -462,7 +689,7 @@ fn gen(self: *Self) InnerError!void {
462689
463 // Pop saved callee-preserved regs.690 // Pop saved callee-preserved regs.
464 const backpatch_pop_callee_preserved_regs = try self.addInst(.{691 const backpatch_pop_callee_preserved_regs = try self.addInst(.{
465 .tag = .nop,692 .tag = .dead,
466 .ops = undefined,693 .ops = undefined,
467 .data = undefined,694 .data = undefined,
468 });695 });
...@@ -475,22 +702,13 @@ fn gen(self: *Self) InnerError!void {...@@ -475,22 +702,13 @@ fn gen(self: *Self) InnerError!void {
475702
476 // Maybe add rsp, x if required. This is backpatched later.703 // Maybe add rsp, x if required. This is backpatched later.
477 const backpatch_stack_add = try self.addInst(.{704 const backpatch_stack_add = try self.addInst(.{
478 .tag = .nop,705 .tag = .dead,
479 .ops = undefined,706 .ops = undefined,
480 .data = undefined,707 .data = undefined,
481 });708 });
482709
483 _ = try self.addInst(.{710 try self.asmRegister(.pop, .rbp);
484 .tag = .pop,711 try self.asmOpOnly(.ret);
485 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
486 .data = undefined,
487 });
488
489 _ = try self.addInst(.{
490 .tag = .ret,
491 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }),
492 .data = undefined,
493 });
494712
495 // Adjust the stack713 // Adjust the stack
496 if (self.max_end_stack > math.maxInt(i32)) {714 if (self.max_end_stack > math.maxInt(i32)) {
...@@ -504,27 +722,34 @@ fn gen(self: *Self) InnerError!void {...@@ -504,27 +722,34 @@ fn gen(self: *Self) InnerError!void {
504 if (aligned_stack_end > 0) {722 if (aligned_stack_end > 0) {
505 self.mir_instructions.set(backpatch_stack_sub, .{723 self.mir_instructions.set(backpatch_stack_sub, .{
506 .tag = .sub,724 .tag = .sub,
507 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),725 .ops = .ri_u,
508 .data = .{ .imm = aligned_stack_end },726 .data = .{ .ri = .{
727 .r1 = .rsp,
728 .imm = aligned_stack_end,
729 } },
509 });730 });
510 self.mir_instructions.set(backpatch_stack_add, .{731 self.mir_instructions.set(backpatch_stack_add, .{
511 .tag = .add,732 .tag = .add,
512 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),733 .ops = .ri_u,
513 .data = .{ .imm = aligned_stack_end },734 .data = .{ .ri = .{
735 .r1 = .rsp,
736 .imm = aligned_stack_end,
737 } },
514 });738 });
515739
516 const save_reg_list = try self.addExtra(Mir.SaveRegisterList{740 const save_reg_list = try self.addExtra(Mir.SaveRegisterList{
741 .base_reg = @enumToInt(Register.rbp),
517 .register_list = reg_list.asInt(),742 .register_list = reg_list.asInt(),
518 .stack_end = aligned_stack_end,743 .stack_end = aligned_stack_end,
519 });744 });
520 self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{745 self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{
521 .tag = .push_regs,746 .tag = .push_regs,
522 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),747 .ops = undefined,
523 .data = .{ .payload = save_reg_list },748 .data = .{ .payload = save_reg_list },
524 });749 });
525 self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{750 self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{
526 .tag = .pop_regs,751 .tag = .pop_regs,
527 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),752 .ops = undefined,
528 .data = .{ .payload = save_reg_list },753 .data = .{ .payload = save_reg_list },
529 });754 });
530 }755 }
...@@ -638,6 +863,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -638,6 +863,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
638 .bitcast => try self.airBitCast(inst),863 .bitcast => try self.airBitCast(inst),
639 .block => try self.airBlock(inst),864 .block => try self.airBlock(inst),
640 .br => try self.airBr(inst),865 .br => try self.airBr(inst),
866 .trap => try self.airTrap(),
641 .breakpoint => try self.airBreakpoint(),867 .breakpoint => try self.airBreakpoint(),
642 .ret_addr => try self.airRetAddr(inst),868 .ret_addr => try self.airRetAddr(inst),
643 .frame_addr => try self.airFrameAddress(inst),869 .frame_addr => try self.airFrameAddress(inst),
...@@ -811,17 +1037,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -811,17 +1037,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
811fn processDeath(self: *Self, inst: Air.Inst.Index) void {1037fn processDeath(self: *Self, inst: Air.Inst.Index) void {
812 const air_tags = self.air.instructions.items(.tag);1038 const air_tags = self.air.instructions.items(.tag);
813 if (air_tags[inst] == .constant) return; // Constants are immortal.1039 if (air_tags[inst] == .constant) return; // Constants are immortal.
814 log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } });1040 const prev_value = self.getResolvedInstValue(inst) orelse return;
1041 log.debug("%{d} => {}", .{ inst, MCValue.dead });
815 // When editing this function, note that the logic must synchronize with `reuseOperand`.1042 // When editing this function, note that the logic must synchronize with `reuseOperand`.
816 const prev_value = self.getResolvedInstValue(inst);
817 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1043 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
818 branch.inst_table.putAssumeCapacity(inst, .dead);1044 branch.inst_table.putAssumeCapacity(inst, .dead);
819 switch (prev_value) {1045 switch (prev_value) {
820 .register => |reg| {1046 .register => |reg| {
821 self.register_manager.freeReg(reg.to64());1047 self.register_manager.freeReg(reg);
822 },1048 },
823 .register_overflow => |ro| {1049 .register_overflow => |ro| {
824 self.register_manager.freeReg(ro.reg.to64());1050 self.register_manager.freeReg(ro.reg);
825 self.eflags_inst = null;1051 self.eflags_inst = null;
826 },1052 },
827 .eflags => {1053 .eflags => {
...@@ -882,7 +1108,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {...@@ -882,7 +1108,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
882 try table.ensureUnusedCapacity(self.gpa, additional_count);1108 try table.ensureUnusedCapacity(self.gpa, additional_count);
883}1109}
8841110
885fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {1111fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
886 if (abi_align > self.stack_align)1112 if (abi_align > self.stack_align)
887 self.stack_align = abi_align;1113 self.stack_align = abi_align;
888 // TODO find a free slot instead of always appending1114 // TODO find a free slot instead of always appending
...@@ -915,7 +1141,14 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {...@@ -915,7 +1141,14 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
915}1141}
9161142
917fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {1143fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
918 const elem_ty = self.air.typeOfIndex(inst);1144 return self.allocRegOrMemAdvanced(self.air.typeOfIndex(inst), inst, reg_ok);
1145}
1146
1147fn allocTempRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool) !MCValue {
1148 return self.allocRegOrMemAdvanced(elem_ty, null, reg_ok);
1149}
1150
1151fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue {
919 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {1152 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {
920 const mod = self.bin_file.options.module.?;1153 const mod = self.bin_file.options.module.?;
921 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});1154 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
...@@ -992,7 +1225,7 @@ fn revertState(self: *Self, state: State) void {...@@ -992,7 +1225,7 @@ fn revertState(self: *Self, state: State) void {
992pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {1225pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
993 const stack_mcv = try self.allocRegOrMem(inst, false);1226 const stack_mcv = try self.allocRegOrMem(inst, false);
994 log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv });1227 log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv });
995 const reg_mcv = self.getResolvedInstValue(inst);1228 const reg_mcv = self.getResolvedInstValue(inst).?;
996 switch (reg_mcv) {1229 switch (reg_mcv) {
997 .register => |other| {1230 .register => |other| {
998 assert(reg.to64() == other.to64());1231 assert(reg.to64() == other.to64());
...@@ -1009,7 +1242,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -1009,7 +1242,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
10091242
1010pub fn spillEflagsIfOccupied(self: *Self) !void {1243pub fn spillEflagsIfOccupied(self: *Self) !void {
1011 if (self.eflags_inst) |inst_to_save| {1244 if (self.eflags_inst) |inst_to_save| {
1012 const mcv = self.getResolvedInstValue(inst_to_save);1245 const mcv = self.getResolvedInstValue(inst_to_save).?;
1013 const new_mcv = switch (mcv) {1246 const new_mcv = switch (mcv) {
1014 .register_overflow => try self.allocRegOrMem(inst_to_save, false),1247 .register_overflow => try self.allocRegOrMem(inst_to_save, false),
1015 .eflags => try self.allocRegOrMem(inst_to_save, true),1248 .eflags => try self.allocRegOrMem(inst_to_save, true),
...@@ -1219,7 +1452,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1219,7 +1452,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1219 };1452 };
1220 defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);1453 defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
12211454
1222 const mask = ~@as(u64, 0);1455 const mask = switch (operand_ty.abiSize(self.target.*)) {
1456 1 => ~@as(u8, 0),
1457 2 => ~@as(u16, 0),
1458 4 => ~@as(u32, 0),
1459 8 => ~@as(u64, 0),
1460 else => unreachable,
1461 };
1223 try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask });1462 try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask });
12241463
1225 break :result dst_mcv;1464 break :result dst_mcv;
...@@ -1266,14 +1505,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void {...@@ -1266,14 +1505,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void {
1266 .unsigned => .b,1505 .unsigned => .b,
1267 .signed => .l,1506 .signed => .l,
1268 };1507 };
1269 _ = try self.addInst(.{1508 try self.asmCmovccRegisterRegister(dst_mcv.register, lhs_reg, cc);
1270 .tag = .cond_mov,
1271 .ops = Mir.Inst.Ops.encode(.{
1272 .reg1 = dst_mcv.register,
1273 .reg2 = lhs_reg,
1274 }),
1275 .data = .{ .cc = cc },
1276 });
12771509
1278 break :result dst_mcv;1510 break :result dst_mcv;
1279 };1511 };
...@@ -1473,13 +1705,7 @@ fn genSetStackTruncatedOverflowCompare(...@@ -1473,13 +1705,7 @@ fn genSetStackTruncatedOverflowCompare(
1473 .signed => .o,1705 .signed => .o,
1474 .unsigned => .c,1706 .unsigned => .c,
1475 };1707 };
1476 _ = try self.addInst(.{1708 try self.asmSetccRegister(overflow_reg.to8(), cc);
1477 .tag = .cond_set_byte,
1478 .ops = Mir.Inst.Ops.encode(.{
1479 .reg1 = overflow_reg.to8(),
1480 }),
1481 .data = .{ .cc = cc },
1482 });
14831709
1484 const scratch_reg = temp_regs[1];1710 const scratch_reg = temp_regs[1];
1485 try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg });1711 try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg });
...@@ -1492,12 +1718,7 @@ fn genSetStackTruncatedOverflowCompare(...@@ -1492,12 +1718,7 @@ fn genSetStackTruncatedOverflowCompare(
1492 );1718 );
14931719
1494 const eq_reg = temp_regs[2];1720 const eq_reg = temp_regs[2];
1495 _ = try self.addInst(.{1721 try self.asmSetccRegister(eq_reg.to8(), .ne);
1496 .tag = .cond_set_byte,
1497 .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }),
1498 .data = .{ .cc = .ne },
1499 });
1500
1501 try self.genBinOpMir(1722 try self.genBinOpMir(
1502 .@"or",1723 .@"or",
1503 Type.u8,1724 Type.u8,
...@@ -1641,23 +1862,8 @@ fn genIntMulDivOpMir(...@@ -1641,23 +1862,8 @@ fn genIntMulDivOpMir(
1641 }1862 }
16421863
1643 switch (signedness) {1864 switch (signedness) {
1644 .signed => {1865 .signed => try self.asmOpOnly(.cqo),
1645 _ = try self.addInst(.{1866 .unsigned => try self.asmRegisterRegister(.xor, .rdx, .rdx),
1646 .tag = .cwd,
1647 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }),
1648 .data = undefined,
1649 });
1650 },
1651 .unsigned => {
1652 _ = try self.addInst(.{
1653 .tag = .xor,
1654 .ops = Mir.Inst.Ops.encode(.{
1655 .reg1 = .rdx,
1656 .reg2 = .rdx,
1657 }),
1658 .data = undefined,
1659 });
1660 },
1661 }1867 }
16621868
1663 const factor = switch (rhs) {1869 const factor = switch (rhs) {
...@@ -1670,29 +1876,11 @@ fn genIntMulDivOpMir(...@@ -1670,29 +1876,11 @@ fn genIntMulDivOpMir(
1670 };1876 };
16711877
1672 switch (factor) {1878 switch (factor) {
1673 .register => |reg| {1879 .register => |reg| try self.asmRegister(tag, reg),
1674 _ = try self.addInst(.{1880 .stack_offset => |off| try self.asmMemory(tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
1675 .tag = tag,1881 .base = .rbp,
1676 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),1882 .disp = -off,
1677 .data = undefined,1883 })),
1678 });
1679 },
1680 .stack_offset => |off| {
1681 _ = try self.addInst(.{
1682 .tag = tag,
1683 .ops = Mir.Inst.Ops.encode(.{
1684 .reg2 = .rbp,
1685 .flags = switch (abi_size) {
1686 1 => 0b00,
1687 2 => 0b01,
1688 4 => 0b10,
1689 8 => 0b11,
1690 else => unreachable,
1691 },
1692 }),
1693 .data = .{ .imm = @bitCast(u32, -off) },
1694 });
1695 },
1696 else => unreachable,1884 else => unreachable,
1697 }1885 }
1698}1886}
...@@ -1720,38 +1908,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa...@@ -1720,38 +1908,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
1720 .unsigned => .div,1908 .unsigned => .div,
1721 }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor });1909 }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor });
17221910
1723 _ = try self.addInst(.{1911 try self.asmRegisterRegister(.xor, divisor.to64(), dividend.to64());
1724 .tag = .xor,1912 try self.asmRegisterImmediate(.sar, divisor.to64(), Immediate.u(63));
1725 .ops = Mir.Inst.Ops.encode(.{1913 try self.asmRegisterRegister(.@"test", .rdx, .rdx);
1726 .reg1 = divisor.to64(),1914 try self.asmCmovccRegisterRegister(divisor.to64(), .rdx, .e);
1727 .reg2 = dividend.to64(),
1728 }),
1729 .data = undefined,
1730 });
1731 _ = try self.addInst(.{
1732 .tag = .sar,
1733 .ops = Mir.Inst.Ops.encode(.{
1734 .reg1 = divisor.to64(),
1735 .flags = 0b10,
1736 }),
1737 .data = .{ .imm = 63 },
1738 });
1739 _ = try self.addInst(.{
1740 .tag = .@"test",
1741 .ops = Mir.Inst.Ops.encode(.{
1742 .reg1 = .rdx,
1743 .reg2 = .rdx,
1744 }),
1745 .data = undefined,
1746 });
1747 _ = try self.addInst(.{
1748 .tag = .cond_mov,
1749 .ops = Mir.Inst.Ops.encode(.{
1750 .reg1 = divisor.to64(),
1751 .reg2 = .rdx,
1752 }),
1753 .data = .{ .cc = .e },
1754 });
1755 try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });1915 try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });
1756 return MCValue{ .register = divisor };1916 return MCValue{ .register = divisor };
1757}1917}
...@@ -2185,18 +2345,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {...@@ -2185,18 +2345,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
21852345
2186 const addr_reg = try self.register_manager.allocReg(null, gp);2346 const addr_reg = try self.register_manager.allocReg(null, gp);
2187 switch (slice_mcv) {2347 switch (slice_mcv) {
2188 .stack_offset => |off| {2348 .stack_offset => |off| try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
2189 // mov reg, [rbp - 8]2349 .base = .rbp,
2190 _ = try self.addInst(.{2350 .disp = -off,
2191 .tag = .mov,2351 })),
2192 .ops = Mir.Inst.Ops.encode(.{
2193 .reg1 = addr_reg.to64(),
2194 .reg2 = .rbp,
2195 .flags = 0b01,
2196 }),
2197 .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
2198 });
2199 },
2200 else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}),2352 else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}),
2201 }2353 }
2202 // TODO we could allocate register here, but need to expect addr register and potentially2354 // TODO we could allocate register here, but need to expect addr register and potentially
...@@ -2271,26 +2423,16 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2271,26 +2423,16 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2271 array_ty.abiAlignment(self.target.*),2423 array_ty.abiAlignment(self.target.*),
2272 ));2424 ));
2273 try self.genSetStack(array_ty, off, array, .{});2425 try self.genSetStack(array_ty, off, array, .{});
2274 // lea reg, [rbp]2426 try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{
2275 _ = try self.addInst(.{2427 .base = .rbp,
2276 .tag = .lea,2428 .disp = -off,
2277 .ops = Mir.Inst.Ops.encode(.{2429 }));
2278 .reg1 = addr_reg.to64(),
2279 .reg2 = .rbp,
2280 }),
2281 .data = .{ .imm = @bitCast(u32, -off) },
2282 });
2283 },2430 },
2284 .stack_offset => |off| {2431 .stack_offset => |off| {
2285 // lea reg, [rbp]2432 try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{
2286 _ = try self.addInst(.{2433 .base = .rbp,
2287 .tag = .lea,2434 .disp = -off,
2288 .ops = Mir.Inst.Ops.encode(.{2435 }));
2289 .reg1 = addr_reg.to64(),
2290 .reg2 = .rbp,
2291 }),
2292 .data = .{ .imm = @bitCast(u32, -off) },
2293 });
2294 },2436 },
2295 .memory, .linker_load => {2437 .memory, .linker_load => {
2296 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);2438 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);
...@@ -2327,7 +2469,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2327,7 +2469,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
2327 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);2469 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
23282470
2329 const elem_ty = ptr_ty.elemType2();2471 const elem_ty = ptr_ty.elemType2();
2330 const elem_abi_size = elem_ty.abiSize(self.target.*);2472 const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2331 const index_ty = self.air.typeOf(bin_op.rhs);2473 const index_ty = self.air.typeOf(bin_op.rhs);
2332 const index = try self.resolveInst(bin_op.rhs);2474 const index = try self.resolveInst(bin_op.rhs);
2333 const index_lock: ?RegisterLock = switch (index) {2475 const index_lock: ?RegisterLock = switch (index) {
...@@ -2347,16 +2489,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2347,16 +2489,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
2347 if (elem_abi_size > 8) {2489 if (elem_abi_size > 8) {
2348 return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size});2490 return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size});
2349 } else {2491 } else {
2350 // mov dst_mcv, [dst_mcv]2492 try self.asmRegisterMemory(
2351 _ = try self.addInst(.{2493 .mov,
2352 .tag = .mov,2494 registerAlias(dst_mcv.register, elem_abi_size),
2353 .ops = Mir.Inst.Ops.encode(.{2495 Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{
2354 .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)),2496 .base = dst_mcv.register,
2355 .reg2 = dst_mcv.register,2497 .disp = 0,
2356 .flags = 0b01,
2357 }),2498 }),
2358 .data = .{ .imm = 0 },2499 );
2359 });
2360 break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) };2500 break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) };
2361 }2501 }
2362 };2502 };
...@@ -2583,7 +2723,7 @@ fn reuseOperand(...@@ -2583,7 +2723,7 @@ fn reuseOperand(
25832723
2584fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {2724fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
2585 const elem_ty = ptr_ty.elemType();2725 const elem_ty = ptr_ty.elemType();
2586 const abi_size = elem_ty.abiSize(self.target.*);2726 const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2587 switch (ptr) {2727 switch (ptr) {
2588 .none => unreachable,2728 .none => unreachable,
2589 .undef => unreachable,2729 .undef => unreachable,
...@@ -2610,16 +2750,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -2610,16 +2750,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
2610 .undef => unreachable,2750 .undef => unreachable,
2611 .eflags => unreachable,2751 .eflags => unreachable,
2612 .register => |dst_reg| {2752 .register => |dst_reg| {
2613 // mov dst_reg, [reg]2753 try self.asmRegisterMemory(
2614 _ = try self.addInst(.{2754 .mov,
2615 .tag = .mov,2755 registerAlias(dst_reg, abi_size),
2616 .ops = Mir.Inst.Ops.encode(.{2756 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg, .disp = 0 }),
2617 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),2757 );
2618 .reg2 = reg,
2619 .flags = 0b01,
2620 }),
2621 .data = .{ .imm = 0 },
2622 });
2623 },2758 },
2624 .stack_offset => |off| {2759 .stack_offset => |off| {
2625 if (abi_size <= 8) {2760 if (abi_size <= 8) {
...@@ -2679,23 +2814,19 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2679,23 +2814,19 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2679 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);2814 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
2680 break :blk coff_file.getAtom(atom).getSymbolIndex().?;2815 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
2681 } else unreachable;2816 } else unreachable;
2682 const flags: u2 = switch (load_struct.type) {2817 const ops: Mir.Inst.Ops = switch (load_struct.type) {
2683 .got => 0b00,2818 .got => .got_reloc,
2684 .direct => 0b01,2819 .direct => .direct_reloc,
2685 .import => 0b10,2820 .import => .import_reloc,
2686 };2821 };
2687 _ = try self.addInst(.{2822 _ = try self.addInst(.{
2688 .tag = .lea_pic,2823 .tag = .lea_linker,
2689 .ops = Mir.Inst.Ops.encode(.{2824 .ops = ops,
2690 .reg1 = registerAlias(reg, abi_size),2825 .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
2691 .flags = flags,2826 .reg = @enumToInt(registerAlias(reg, abi_size)),
2692 }),2827 .atom_index = atom_index,
2693 .data = .{2828 .sym_index = load_struct.sym_index,
2694 .relocation = .{2829 }) },
2695 .atom_index = atom_index,
2696 .sym_index = load_struct.sym_index,
2697 },
2698 },
2699 });2830 });
2700 },2831 },
2701 .memory => |addr| {2832 .memory => |addr| {
...@@ -2708,7 +2839,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2708,7 +2839,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2708}2839}
27092840
2710fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {2841fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
2711 const abi_size = value_ty.abiSize(self.target.*);2842 const abi_size = @intCast(u32, value_ty.abiSize(self.target.*));
2712 switch (ptr) {2843 switch (ptr) {
2713 .none => unreachable,2844 .none => unreachable,
2714 .undef => unreachable,2845 .undef => unreachable,
...@@ -2736,30 +2867,25 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2736,30 +2867,25 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2736 .unreach => unreachable,2867 .unreach => unreachable,
2737 .eflags => unreachable,2868 .eflags => unreachable,
2738 .undef => {2869 .undef => {
2739 try self.genSetReg(value_ty, reg, value);2870 switch (abi_size) {
2871 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty),
2872 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty),
2873 4 => try self.store(ptr, .{ .immediate = 0xaaaaaaaa }, ptr_ty, value_ty),
2874 8 => try self.store(ptr, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, ptr_ty, value_ty),
2875 else => try self.genInlineMemset(ptr, .{ .immediate = 0xaa }, .{ .immediate = abi_size }, .{}),
2876 }
2740 },2877 },
2741 .immediate => |imm| {2878 .immediate => |imm| {
2742 switch (abi_size) {2879 switch (abi_size) {
2743 1, 2, 4 => {2880 1, 2, 4 => {
2744 // TODO this is wasteful!2881 const immediate = if (value_ty.isSignedInt())
2745 // introduce new MIR tag specifically for mov [reg + 0], imm2882 Immediate.s(@intCast(i32, @bitCast(i64, imm)))
2746 const payload = try self.addExtra(Mir.ImmPair{2883 else
2747 .dest_off = 0,2884 Immediate.u(@truncate(u32, imm));
2748 .operand = @truncate(u32, imm),2885 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
2749 });2886 .base = reg.to64(),
2750 _ = try self.addInst(.{2887 .disp = 0,
2751 .tag = .mov_mem_imm,2888 }), immediate);
2752 .ops = Mir.Inst.Ops.encode(.{
2753 .reg1 = reg.to64(),
2754 .flags = switch (abi_size) {
2755 1 => 0b00,
2756 2 => 0b01,
2757 4 => 0b10,
2758 else => unreachable,
2759 },
2760 }),
2761 .data = .{ .payload = payload },
2762 });
2763 },2889 },
2764 8 => {2890 8 => {
2765 // TODO: optimization: if the imm is only using the lower2891 // TODO: optimization: if the imm is only using the lower
...@@ -2789,13 +2915,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2789,13 +2915,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2789 const overflow_bit_ty = value_ty.structFieldType(1);2915 const overflow_bit_ty = value_ty.structFieldType(1);
2790 const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*);2916 const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*);
2791 const tmp_reg = try self.register_manager.allocReg(null, gp);2917 const tmp_reg = try self.register_manager.allocReg(null, gp);
2792 _ = try self.addInst(.{2918 try self.asmSetccRegister(tmp_reg.to8(), ro.eflags);
2793 .tag = .cond_set_byte,
2794 .ops = Mir.Inst.Ops.encode(.{
2795 .reg1 = tmp_reg.to8(),
2796 }),
2797 .data = .{ .cc = ro.eflags },
2798 });
2799 try self.genInlineMemcpyRegisterRegister(2919 try self.genInlineMemcpyRegisterRegister(
2800 overflow_bit_ty,2920 overflow_bit_ty,
2801 reg,2921 reg,
...@@ -2836,17 +2956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2836,17 +2956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28362956
2837 try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr);2957 try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr);
28382958
2839 // to get the actual address of the value we want to modify we have to go through the GOT2959 // To get the actual address of the value we want to modify we have to go through the GOT
2840 // mov reg, [reg]2960 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
2841 _ = try self.addInst(.{2961 .base = addr_reg.to64(),
2842 .tag = .mov,2962 .disp = 0,
2843 .ops = Mir.Inst.Ops.encode(.{2963 }));
2844 .reg1 = addr_reg.to64(),
2845 .reg2 = addr_reg.to64(),
2846 .flags = 0b01,
2847 }),
2848 .data = .{ .imm = 0 },
2849 });
28502964
2851 const new_ptr = MCValue{ .register = addr_reg.to64() };2965 const new_ptr = MCValue{ .register = addr_reg.to64() };
28522966
...@@ -2856,19 +2970,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2856,19 +2970,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2856 return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size});2970 return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size});
2857 }2971 }
28582972
2859 const payload = try self.addExtra(Mir.ImmPair{2973 if (abi_size == 8) {
2860 .dest_off = 0,2974 // TODO
2861 // TODO check if this logic is correct
2862 .operand = @truncate(u32, imm),
2863 });
2864 const flags: u2 = switch (abi_size) {
2865 1 => 0b00,
2866 2 => 0b01,
2867 4 => 0b10,
2868 8 => 0b11,
2869 else => unreachable,
2870 };
2871 if (flags == 0b11) {
2872 const top_bits: u32 = @intCast(u32, imm >> 32);2975 const top_bits: u32 = @intCast(u32, imm >> 32);
2873 const can_extend = if (value_ty.isUnsignedInt())2976 const can_extend = if (value_ty.isUnsignedInt())
2874 (top_bits == 0) and (imm & 0x8000_0000) == 02977 (top_bits == 0) and (imm & 0x8000_0000) == 0
...@@ -2879,14 +2982,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2879,14 +2982,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2879 return self.fail("TODO imm64 would get incorrectly sign extended", .{});2982 return self.fail("TODO imm64 would get incorrectly sign extended", .{});
2880 }2983 }
2881 }2984 }
2882 _ = try self.addInst(.{2985 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
2883 .tag = .mov_mem_imm,2986 .base = addr_reg.to64(),
2884 .ops = Mir.Inst.Ops.encode(.{2987 .disp = 0,
2885 .reg1 = addr_reg.to64(),2988 }), Immediate.u(@intCast(u32, imm)));
2886 .flags = flags,
2887 }),
2888 .data = .{ .payload = payload },
2889 });
2890 },2989 },
2891 .register => {2990 .register => {
2892 return self.store(new_ptr, value, ptr_ty, value_ty);2991 return self.store(new_ptr, value, ptr_ty, value_ty);
...@@ -2898,16 +2997,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2898,16 +2997,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2898 defer self.register_manager.unlockReg(tmp_reg_lock);2997 defer self.register_manager.unlockReg(tmp_reg_lock);
28992998
2900 try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value);2999 try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value);
3000 try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib(.qword, .{
3001 .base = tmp_reg,
3002 .disp = 0,
3003 }));
29013004
2902 _ = try self.addInst(.{
2903 .tag = .mov,
2904 .ops = Mir.Inst.Ops.encode(.{
2905 .reg1 = tmp_reg,
2906 .reg2 = tmp_reg,
2907 .flags = 0b01,
2908 }),
2909 .data = .{ .imm = 0 },
2910 });
2911 return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);3005 return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
2912 }3006 }
29133007
...@@ -2959,6 +3053,9 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde...@@ -2959,6 +3053,9 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
2959 const mcv = try self.resolveInst(operand);3053 const mcv = try self.resolveInst(operand);
2960 const ptr_ty = self.air.typeOf(operand);3054 const ptr_ty = self.air.typeOf(operand);
2961 const struct_ty = ptr_ty.childType();3055 const struct_ty = ptr_ty.childType();
3056 if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) {
3057 return self.fail("TODO structFieldPtr implement packed structs", .{});
3058 }
2962 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));3059 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
29633060
2964 const dst_mcv: MCValue = result: {3061 const dst_mcv: MCValue = result: {
...@@ -3022,6 +3119,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3022,6 +3119,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30223119
3023 const mcv = try self.resolveInst(operand);3120 const mcv = try self.resolveInst(operand);
3024 const struct_ty = self.air.typeOf(operand);3121 const struct_ty = self.air.typeOf(operand);
3122 if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) {
3123 return self.fail("TODO airStructFieldVal implement packed structs", .{});
3124 }
3025 const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*);3125 const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*);
3026 const struct_field_ty = struct_ty.structFieldType(index);3126 const struct_field_ty = struct_ty.structFieldType(index);
30273127
...@@ -3055,8 +3155,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3055,8 +3155,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3055 const shift = @intCast(u8, struct_field_offset * @sizeOf(usize));3155 const shift = @intCast(u8, struct_field_offset * @sizeOf(usize));
3056 try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift });3156 try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift });
30573157
3058 // Mask with reg.size() - struct_field_size3158 // Mask with reg.bitSize() - struct_field_size
3059 const max_reg_bit_width = Register.rax.size();3159 const max_reg_bit_width = Register.rax.bitSize();
3060 const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*)));3160 const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*)));
3061 const mask = (~@as(u64, 0)) >> mask_shift;3161 const mask = (~@as(u64, 0)) >> mask_shift;
30623162
...@@ -3069,14 +3169,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3069,14 +3169,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3069 };3169 };
3070 const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));3170 const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
3071 if (signedness == .signed and field_size < 8) {3171 if (signedness == .signed and field_size < 8) {
3072 _ = try self.addInst(.{3172 try self.asmRegisterRegister(
3073 .tag = .mov_sign_extend,3173 .movsx,
3074 .ops = Mir.Inst.Ops.encode(.{3174 dst_mcv.register,
3075 .reg1 = dst_mcv.register,3175 registerAlias(dst_mcv.register, field_size),
3076 .reg2 = registerAlias(dst_mcv.register, field_size),3176 );
3077 }),
3078 .data = undefined,
3079 });
3080 }3177 }
30813178
3082 break :result dst_mcv;3179 break :result dst_mcv;
...@@ -3093,13 +3190,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3093,13 +3190,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3093 defer self.register_manager.unlockReg(reg_lock);3190 defer self.register_manager.unlockReg(reg_lock);
30943191
3095 const dst_reg = try self.register_manager.allocReg(inst, gp);3192 const dst_reg = try self.register_manager.allocReg(inst, gp);
3096 _ = try self.addInst(.{3193 try self.asmSetccRegister(dst_reg.to8(), ro.eflags);
3097 .tag = .cond_set_byte,
3098 .ops = Mir.Inst.Ops.encode(.{
3099 .reg1 = dst_reg.to8(),
3100 }),
3101 .data = .{ .cc = ro.eflags },
3102 });
3103 break :result MCValue{ .register = dst_reg.to8() };3194 break :result MCValue{ .register = dst_reg.to8() };
3104 },3195 },
3105 else => unreachable,3196 else => unreachable,
...@@ -3135,25 +3226,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi...@@ -3135,25 +3226,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
3135 switch (shift) {3226 switch (shift) {
3136 .immediate => |imm| switch (imm) {3227 .immediate => |imm| switch (imm) {
3137 0 => return,3228 0 => return,
3138 1 => {3229 else => return self.asmRegisterImmediate(tag, registerAlias(reg, abi_size), Immediate.u(imm)),
3139 _ = try self.addInst(.{
3140 .tag = tag,
3141 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }),
3142 .data = undefined,
3143 });
3144 return;
3145 },
3146 else => {
3147 _ = try self.addInst(.{
3148 .tag = tag,
3149 .ops = Mir.Inst.Ops.encode(.{
3150 .reg1 = registerAlias(reg, abi_size),
3151 .flags = 0b10,
3152 }),
3153 .data = .{ .imm = @intCast(u8, imm) },
3154 });
3155 return;
3156 },
3157 },3230 },
3158 .register => |shift_reg| {3231 .register => |shift_reg| {
3159 if (shift_reg == .rcx) break :blk;3232 if (shift_reg == .rcx) break :blk;
...@@ -3165,14 +3238,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi...@@ -3165,14 +3238,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
3165 try self.genSetReg(Type.u8, .rcx, shift);3238 try self.genSetReg(Type.u8, .rcx, shift);
3166 }3239 }
31673240
3168 _ = try self.addInst(.{3241 try self.asmRegisterRegister(tag, registerAlias(reg, abi_size), .cl);
3169 .tag = tag,
3170 .ops = Mir.Inst.Ops.encode(.{
3171 .reg1 = registerAlias(reg, abi_size),
3172 .flags = 0b01,
3173 }),
3174 .data = undefined,
3175 });
3176}3242}
31773243
3178/// Result is always a register.3244/// Result is always a register.
...@@ -3545,59 +3611,67 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3545,59 +3611,67 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3545 if (intrinsicsAllowed(self.target.*, dst_ty)) {3611 if (intrinsicsAllowed(self.target.*, dst_ty)) {
3546 const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {3612 const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
3547 .f32 => switch (mir_tag) {3613 .f32 => switch (mir_tag) {
3548 .add => if (hasAvxSupport(self.target.*))3614 .add => .addss,
3549 Mir.Inst.Tag.add_f32_avx3615 .cmp => .ucomiss,
3550 else3616 else => return self.fail(
3551 Mir.Inst.Tag.add_f32_sse,3617 "TODO genBinOpMir for f32 register-register with MIR tag {}",
3552 .cmp => if (hasAvxSupport(self.target.*))3618 .{mir_tag},
3553 Mir.Inst.Tag.cmp_f32_avx3619 ),
3554 else
3555 Mir.Inst.Tag.cmp_f32_sse,
3556 else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}),
3557 },3620 },
3558 .f64 => switch (mir_tag) {3621 .f64 => switch (mir_tag) {
3559 .add => if (hasAvxSupport(self.target.*))3622 .add => .addsd,
3560 Mir.Inst.Tag.add_f64_avx3623 .cmp => .ucomisd,
3561 else3624 else => return self.fail(
3562 Mir.Inst.Tag.add_f64_sse,3625 "TODO genBinOpMir for f64 register-register with MIR tag {}",
3563 .cmp => if (hasAvxSupport(self.target.*))3626 .{mir_tag},
3564 Mir.Inst.Tag.cmp_f64_avx3627 ),
3565 else
3566 Mir.Inst.Tag.cmp_f64_sse,
3567 else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
3568 },3628 },
3569 else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),3629 else => return self.fail(
3630 "TODO genBinOpMir for float register-register and type {}",
3631 .{dst_ty.fmtDebug()},
3632 ),
3570 };3633 };
3571 _ = try self.addInst(.{3634 return self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128());
3572 .tag = actual_tag,
3573 .ops = Mir.Inst.Ops.encode(.{
3574 .reg1 = dst_reg.to128(),
3575 .reg2 = src_reg.to128(),
3576 }),
3577 .data = undefined,
3578 });
3579 return;
3580 }3635 }
35813636
3582 return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{});3637 return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{});
3583 },3638 },
3584 else => {3639 else => try self.asmRegisterRegister(
3585 _ = try self.addInst(.{3640 mir_tag,
3586 .tag = mir_tag,3641 registerAlias(dst_reg, abi_size),
3587 .ops = Mir.Inst.Ops.encode(.{3642 registerAlias(src_reg, abi_size),
3588 .reg1 = registerAlias(dst_reg, abi_size),3643 ),
3589 .reg2 = registerAlias(src_reg, abi_size),
3590 }),
3591 .data = undefined,
3592 });
3593 },
3594 },3644 },
3595 .immediate => |imm| {3645 .immediate => |imm| {
3596 _ = try self.addInst(.{3646 switch (abi_size) {
3597 .tag = mir_tag,3647 0 => unreachable,
3598 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }),3648 1...4 => {
3599 .data = .{ .imm = @truncate(u32, imm) },3649 try self.asmRegisterImmediate(
3600 });3650 mir_tag,
3651 registerAlias(dst_reg, abi_size),
3652 Immediate.u(@intCast(u32, imm)),
3653 );
3654 },
3655 5...8 => {
3656 if (math.cast(i32, @bitCast(i64, imm))) |small| {
3657 try self.asmRegisterImmediate(
3658 mir_tag,
3659 registerAlias(dst_reg, abi_size),
3660 Immediate.s(small),
3661 );
3662 } else {
3663 const tmp_reg = try self.register_manager.allocReg(null, gp);
3664 const tmp_alias = registerAlias(tmp_reg, abi_size);
3665 try self.asmRegisterImmediate(.mov, tmp_alias, Immediate.u(imm));
3666 try self.asmRegisterRegister(
3667 mir_tag,
3668 registerAlias(dst_reg, abi_size),
3669 tmp_alias,
3670 );
3671 }
3672 },
3673 else => return self.fail("TODO getBinOpMir implement large immediate ABI", .{}),
3674 }
3601 },3675 },
3602 .memory,3676 .memory,
3603 .linker_load,3677 .linker_load,
...@@ -3614,15 +3688,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3614,15 +3688,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3614 if (off > math.maxInt(i32)) {3688 if (off > math.maxInt(i32)) {
3615 return self.fail("stack offset too large", .{});3689 return self.fail("stack offset too large", .{});
3616 }3690 }
3617 _ = try self.addInst(.{3691 try self.asmRegisterMemory(
3618 .tag = mir_tag,3692 mir_tag,
3619 .ops = Mir.Inst.Ops.encode(.{3693 registerAlias(dst_reg, abi_size),
3620 .reg1 = registerAlias(dst_reg, abi_size),3694 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
3621 .reg2 = .rbp,3695 );
3622 .flags = 0b01,
3623 }),
3624 .data = .{ .imm = @bitCast(u32, -off) },
3625 });
3626 },3696 },
3627 }3697 }
3628 },3698 },
...@@ -3640,45 +3710,17 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3640,45 +3710,17 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3640 .dead, .unreach => unreachable,3710 .dead, .unreach => unreachable,
3641 .register_overflow => unreachable,3711 .register_overflow => unreachable,
3642 .register => |src_reg| {3712 .register => |src_reg| {
3643 _ = try self.addInst(.{3713 try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
3644 .tag = mir_tag,3714 .base = .rbp,
3645 .ops = Mir.Inst.Ops.encode(.{3715 .disp = -off,
3646 .reg1 = .rbp,3716 }), registerAlias(src_reg, abi_size));
3647 .reg2 = registerAlias(src_reg, abi_size),
3648 .flags = 0b10,
3649 }),
3650 .data = .{ .imm = @bitCast(u32, -off) },
3651 });
3652 },3717 },
3653 .immediate => |imm| {3718 .immediate => |imm| {
3654 const tag: Mir.Inst.Tag = switch (mir_tag) {3719 // TODO
3655 .add => .add_mem_imm,3720 try self.asmMemoryImmediate(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
3656 .@"or" => .or_mem_imm,3721 .base = .rbp,
3657 .@"and" => .and_mem_imm,3722 .disp = -off,
3658 .sub => .sub_mem_imm,3723 }), Immediate.u(@intCast(u32, imm)));
3659 .xor => .xor_mem_imm,
3660 .cmp => .cmp_mem_imm,
3661 else => unreachable,
3662 };
3663 const flags: u2 = switch (abi_size) {
3664 1 => 0b00,
3665 2 => 0b01,
3666 4 => 0b10,
3667 8 => 0b11,
3668 else => unreachable,
3669 };
3670 const payload = try self.addExtra(Mir.ImmPair{
3671 .dest_off = @bitCast(u32, -off),
3672 .operand = @truncate(u32, imm),
3673 });
3674 _ = try self.addInst(.{
3675 .tag = tag,
3676 .ops = Mir.Inst.Ops.encode(.{
3677 .reg1 = .rbp,
3678 .flags = flags,
3679 }),
3680 .data = .{ .payload = payload },
3681 });
3682 },3724 },
3683 .memory,3725 .memory,
3684 .stack_offset,3726 .stack_offset,
...@@ -3721,30 +3763,21 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -3721,30 +3763,21 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
3721 .dead, .unreach => unreachable,3763 .dead, .unreach => unreachable,
3722 .ptr_stack_offset => unreachable,3764 .ptr_stack_offset => unreachable,
3723 .register_overflow => unreachable,3765 .register_overflow => unreachable,
3724 .register => |src_reg| {3766 .register => |src_reg| try self.asmRegisterRegister(
3725 // register, register3767 .imul,
3726 _ = try self.addInst(.{3768 registerAlias(dst_reg, abi_size),
3727 .tag = .imul_complex,3769 registerAlias(src_reg, abi_size),
3728 .ops = Mir.Inst.Ops.encode(.{3770 ),
3729 .reg1 = registerAlias(dst_reg, abi_size),
3730 .reg2 = registerAlias(src_reg, abi_size),
3731 }),
3732 .data = undefined,
3733 });
3734 },
3735 .immediate => |imm| {3771 .immediate => |imm| {
3736 // TODO take into account the type's ABI size when selecting the register alias
3737 // register, immediate
3738 if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {3772 if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {
3739 _ = try self.addInst(.{3773 // TODO take into account the type's ABI size when selecting the register alias
3740 .tag = .imul_complex,3774 // register, immediate
3741 .ops = Mir.Inst.Ops.encode(.{3775 try self.asmRegisterRegisterImmediate(
3742 .reg1 = dst_reg.to32(),3776 .imul,
3743 .reg2 = dst_reg.to32(),3777 dst_reg.to32(),
3744 .flags = 0b10,3778 dst_reg.to32(),
3745 }),3779 Immediate.u(@intCast(u32, imm)),
3746 .data = .{ .imm = @truncate(u32, imm) },3780 );
3747 });
3748 } else {3781 } else {
3749 // TODO verify we don't spill and assign to the same register as dst_mcv3782 // TODO verify we don't spill and assign to the same register as dst_mcv
3750 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);3783 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
...@@ -3752,15 +3785,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -3752,15 +3785,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
3752 }3785 }
3753 },3786 },
3754 .stack_offset => |off| {3787 .stack_offset => |off| {
3755 _ = try self.addInst(.{3788 try self.asmRegisterMemory(
3756 .tag = .imul_complex,3789 .imul,
3757 .ops = Mir.Inst.Ops.encode(.{3790 registerAlias(dst_reg, abi_size),
3758 .reg1 = registerAlias(dst_reg, abi_size),3791 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
3759 .reg2 = .rbp,3792 );
3760 .flags = 0b01,
3761 }),
3762 .data = .{ .imm = @bitCast(u32, -off) },
3763 });
3764 },3793 },
3765 .memory => {3794 .memory => {
3766 return self.fail("TODO implement x86 multiply source memory", .{});3795 return self.fail("TODO implement x86 multiply source memory", .{});
...@@ -3783,16 +3812,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -3783,16 +3812,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
3783 .register => |src_reg| {3812 .register => |src_reg| {
3784 // copy dst to a register3813 // copy dst to a register
3785 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);3814 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
3786 // multiply into dst_reg3815 try self.asmRegisterRegister(
3787 // register, register3816 .imul,
3788 _ = try self.addInst(.{3817 registerAlias(dst_reg, abi_size),
3789 .tag = .imul_complex,3818 registerAlias(src_reg, abi_size),
3790 .ops = Mir.Inst.Ops.encode(.{3819 );
3791 .reg1 = registerAlias(dst_reg, abi_size),
3792 .reg2 = registerAlias(src_reg, abi_size),
3793 }),
3794 .data = undefined,
3795 });
3796 // copy dst_reg back out3820 // copy dst_reg back out
3797 return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{});3821 return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{});
3798 },3822 },
...@@ -3917,12 +3941,13 @@ fn genVarDbgInfo(...@@ -3917,12 +3941,13 @@ fn genVarDbgInfo(
3917 }3941 }
3918}3942}
39193943
3944fn airTrap(self: *Self) !void {
3945 try self.asmOpOnly(.ud2);
3946 return self.finishAirBookkeeping();
3947}
3948
3920fn airBreakpoint(self: *Self) !void {3949fn airBreakpoint(self: *Self) !void {
3921 _ = try self.addInst(.{3950 try self.asmOpOnly(.int3);
3922 .tag = .interrupt,
3923 .ops = Mir.Inst.Ops.encode(.{}),
3924 .data = undefined,
3925 });
3926 return self.finishAirBookkeeping();3951 return self.finishAirBookkeeping();
3927}3952}
39283953
...@@ -3985,10 +4010,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -3985,10 +4010,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
3985 };4010 };
3986 defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock);4011 defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock);
39874012
3988 for (args, 0..) |arg, arg_i| {4013 for (args, info.args) |arg, info_arg| {
3989 const mc_arg = info.args[arg_i];4014 const mc_arg = info_arg;
3990 const arg_ty = self.air.typeOf(arg);4015 const arg_ty = self.air.typeOf(arg);
3991 const arg_mcv = try self.resolveInst(args[arg_i]);4016 const arg_mcv = try self.resolveInst(arg);
3992 // Here we do not use setRegOrMem even though the logic is similar, because4017 // Here we do not use setRegOrMem even though the logic is similar, because
3993 // the function call will move the stack pointer, so the offsets are different.4018 // the function call will move the stack pointer, so the offsets are different.
3994 switch (mc_arg) {4019 switch (mc_arg) {
...@@ -4017,11 +4042,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4017,11 +4042,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40174042
4018 if (info.stack_byte_count > 0) {4043 if (info.stack_byte_count > 0) {
4019 // Adjust the stack4044 // Adjust the stack
4020 _ = try self.addInst(.{4045 try self.asmRegisterImmediate(.sub, .rsp, Immediate.u(info.stack_byte_count));
4021 .tag = .sub,
4022 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
4023 .data = .{ .imm = info.stack_byte_count },
4024 });
4025 }4046 }
40264047
4027 // Due to incremental compilation, how function calls are generated depends4048 // Due to incremental compilation, how function calls are generated depends
...@@ -4034,12 +4055,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4034,12 +4055,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4034 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4055 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4035 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);4056 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4036 const atom = elf_file.getAtom(atom_index);4057 const atom = elf_file.getAtom(atom_index);
4037 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));4058 const got_addr = atom.getOffsetTableAddress(elf_file);
4038 _ = try self.addInst(.{4059 try self.asmMemory(.call, Memory.sib(.qword, .{
4039 .tag = .call,4060 .base = .ds,
4040 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),4061 .disp = @intCast(i32, got_addr),
4041 .data = .{ .imm = got_addr },4062 }));
4042 });
4043 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4063 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4044 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);4064 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4045 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;4065 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
...@@ -4049,14 +4069,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4049,14 +4069,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4049 .sym_index = sym_index,4069 .sym_index = sym_index,
4050 },4070 },
4051 });4071 });
4052 _ = try self.addInst(.{4072 try self.asmRegister(.call, .rax);
4053 .tag = .call,
4054 .ops = Mir.Inst.Ops.encode(.{
4055 .reg1 = .rax,
4056 .flags = 0b01,
4057 }),
4058 .data = undefined,
4059 });
4060 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4073 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4061 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);4074 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4062 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;4075 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
...@@ -4066,14 +4079,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4066,14 +4079,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4066 .sym_index = sym_index,4079 .sym_index = sym_index,
4067 },4080 },
4068 });4081 });
4069 _ = try self.addInst(.{4082 try self.asmRegister(.call, .rax);
4070 .tag = .call,
4071 .ops = Mir.Inst.Ops.encode(.{
4072 .reg1 = .rax,
4073 .flags = 0b01,
4074 }),
4075 .data = undefined,
4076 });
4077 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {4083 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4078 const decl_block_index = try p9.seeDecl(func.owner_decl);4084 const decl_block_index = try p9.seeDecl(func.owner_decl);
4079 const decl_block = p9.getDeclBlock(decl_block_index);4085 const decl_block = p9.getDeclBlock(decl_block_index);
...@@ -4082,11 +4088,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4082,11 +4088,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4082 const got_addr = p9.bases.data;4088 const got_addr = p9.bases.data;
4083 const got_index = decl_block.got_index.?;4089 const got_index = decl_block.got_index.?;
4084 const fn_got_addr = got_addr + got_index * ptr_bytes;4090 const fn_got_addr = got_addr + got_index * ptr_bytes;
4085 _ = try self.addInst(.{4091 try self.asmMemory(.call, Memory.sib(.qword, .{
4086 .tag = .call,4092 .base = .ds,
4087 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),4093 .disp = @intCast(i32, fn_got_addr),
4088 .data = .{ .imm = @intCast(u32, fn_got_addr) },4094 }));
4089 });
4090 } else unreachable;4095 } else unreachable;
4091 } else if (func_value.castTag(.extern_fn)) |func_payload| {4096 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4092 const extern_fn = func_payload.data;4097 const extern_fn = func_payload.data;
...@@ -4106,14 +4111,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4106,14 +4111,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4106 .sym_index = sym_index,4111 .sym_index = sym_index,
4107 },4112 },
4108 });4113 });
4109 _ = try self.addInst(.{4114 try self.asmRegister(.call, .rax);
4110 .tag = .call,
4111 .ops = Mir.Inst.Ops.encode(.{
4112 .reg1 = .rax,
4113 .flags = 0b01,
4114 }),
4115 .data = undefined,
4116 });
4117 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4115 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4118 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));4116 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4119 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);4117 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
...@@ -4136,23 +4134,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4136,23 +4134,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4136 assert(ty.zigTypeTag() == .Pointer);4134 assert(ty.zigTypeTag() == .Pointer);
4137 const mcv = try self.resolveInst(callee);4135 const mcv = try self.resolveInst(callee);
4138 try self.genSetReg(Type.initTag(.usize), .rax, mcv);4136 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4139 _ = try self.addInst(.{4137 try self.asmRegister(.call, .rax);
4140 .tag = .call,
4141 .ops = Mir.Inst.Ops.encode(.{
4142 .reg1 = .rax,
4143 .flags = 0b01,
4144 }),
4145 .data = undefined,
4146 });
4147 }4138 }
41484139
4149 if (info.stack_byte_count > 0) {4140 if (info.stack_byte_count > 0) {
4150 // Readjust the stack4141 // Readjust the stack
4151 _ = try self.addInst(.{4142 try self.asmRegisterImmediate(.add, .rsp, Immediate.u(info.stack_byte_count));
4152 .tag = .add,
4153 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
4154 .data = .{ .imm = info.stack_byte_count },
4155 });
4156 }4143 }
41574144
4158 const result: MCValue = result: {4145 const result: MCValue = result: {
...@@ -4209,11 +4196,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -4209,11 +4196,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
4209 // TODO when implementing defer, this will need to jump to the appropriate defer expression.4196 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
4210 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction4197 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
4211 // which is available if the jump is 127 bytes or less forward.4198 // which is available if the jump is 127 bytes or less forward.
4212 const jmp_reloc = try self.addInst(.{4199 const jmp_reloc = try self.asmJmpReloc(undefined);
4213 .tag = .jmp,
4214 .ops = Mir.Inst.Ops.encode(.{}),
4215 .data = .{ .inst = undefined },
4216 });
4217 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);4200 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
4218 return self.finishAir(inst, .dead, .{ un_op, .none, .none });4201 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4219}4202}
...@@ -4245,11 +4228,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -4245,11 +4228,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
4245 // TODO when implementing defer, this will need to jump to the appropriate defer expression.4228 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
4246 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction4229 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
4247 // which is available if the jump is 127 bytes or less forward.4230 // which is available if the jump is 127 bytes or less forward.
4248 const jmp_reloc = try self.addInst(.{4231 const jmp_reloc = try self.asmJmpReloc(undefined);
4249 .tag = .jmp,
4250 .ops = Mir.Inst.Ops.encode(.{}),
4251 .data = .{ .inst = undefined },
4252 });
4253 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);4232 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
4254 return self.finishAir(inst, .dead, .{ un_op, .none, .none });4233 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4255}4234}
...@@ -4424,33 +4403,13 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {...@@ -4424,33 +4403,13 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
4424 const abi_size = ty.abiSize(self.target.*);4403 const abi_size = ty.abiSize(self.target.*);
4425 switch (mcv) {4404 switch (mcv) {
4426 .eflags => |cc| {4405 .eflags => |cc| {
4427 return self.addInst(.{4406 // Here we map the opposites since the jump is to the false branch.
4428 .tag = .cond_jmp,4407 return self.asmJccReloc(undefined, cc.negate());
4429 .ops = Mir.Inst.Ops.encode(.{}),
4430 .data = .{
4431 .inst_cc = .{
4432 .inst = undefined,
4433 // Here we map the opposites since the jump is to the false branch.
4434 .cc = cc.negate(),
4435 },
4436 },
4437 });
4438 },4408 },
4439 .register => |reg| {4409 .register => |reg| {
4440 try self.spillEflagsIfOccupied();4410 try self.spillEflagsIfOccupied();
4441 _ = try self.addInst(.{4411 try self.asmRegisterImmediate(.@"test", reg, Immediate.u(1));
4442 .tag = .@"test",4412 return self.asmJccReloc(undefined, .e);
4443 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
4444 .data = .{ .imm = 1 },
4445 });
4446 return self.addInst(.{
4447 .tag = .cond_jmp,
4448 .ops = Mir.Inst.Ops.encode(.{}),
4449 .data = .{ .inst_cc = .{
4450 .inst = undefined,
4451 .cc = .e,
4452 } },
4453 });
4454 },4413 },
4455 .immediate,4414 .immediate,
4456 .stack_offset,4415 .stack_offset,
...@@ -4464,6 +4423,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {...@@ -4464,6 +4423,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
4464 },4423 },
4465 else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}),4424 else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}),
4466 }4425 }
4426 return 0; // TODO
4467}4427}
44684428
4469fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {4429fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
...@@ -4491,16 +4451,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4491,16 +4451,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4491 // Capture the state of register and stack allocation state so that we can revert to it.4451 // Capture the state of register and stack allocation state so that we can revert to it.
4492 const saved_state = try self.captureState();4452 const saved_state = try self.captureState();
44934453
4494 try self.branch_stack.append(.{});4454 {
4495 errdefer {4455 try self.branch_stack.append(.{});
4496 _ = self.branch_stack.pop();4456 errdefer _ = self.branch_stack.pop();
4497 }
44984457
4499 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);4458 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
4500 for (liveness_condbr.then_deaths) |operand| {4459 for (liveness_condbr.then_deaths) |operand| {
4501 self.processDeath(operand);4460 self.processDeath(operand);
4461 }
4462 try self.genBody(then_body);
4502 }4463 }
4503 try self.genBody(then_body);
45044464
4505 // Revert to the previous register and stack allocation state.4465 // Revert to the previous register and stack allocation state.
45064466
...@@ -4511,16 +4471,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4511,16 +4471,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
45114471
4512 try self.performReloc(reloc);4472 try self.performReloc(reloc);
45134473
4514 try self.branch_stack.append(.{});4474 {
4515 errdefer {4475 try self.branch_stack.append(.{});
4516 _ = self.branch_stack.pop();4476 errdefer _ = self.branch_stack.pop();
4517 }
45184477
4519 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);4478 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
4520 for (liveness_condbr.else_deaths) |operand| {4479 for (liveness_condbr.else_deaths) |operand| {
4521 self.processDeath(operand);4480 self.processDeath(operand);
4481 }
4482 try self.genBody(else_body);
4522 }4483 }
4523 try self.genBody(else_body);
45244484
4525 var else_branch = self.branch_stack.pop();4485 var else_branch = self.branch_stack.pop();
4526 defer else_branch.deinit(self.gpa);4486 defer else_branch.deinit(self.gpa);
...@@ -4646,18 +4606,16 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4646,18 +4606,16 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4646 };4606 };
4647 defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);4607 defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
46484608
4649 const operand: MCValue = blk: {
4650 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4651 // The MCValue that holds the pointer can be re-used as the value.
4652 break :blk operand_ptr;
4653 } else {
4654 break :blk try self.allocRegOrMem(inst, true);
4655 }
4656 };
4657 const ptr_ty = self.air.typeOf(un_op);4609 const ptr_ty = self.air.typeOf(un_op);
4610 const elem_ty = ptr_ty.childType();
4611 const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr))
4612 // The MCValue that holds the pointer can be re-used as the value.
4613 operand_ptr
4614 else
4615 try self.allocTempRegOrMem(elem_ty, true);
4658 try self.load(operand, operand_ptr, ptr_ty);4616 try self.load(operand, operand_ptr, ptr_ty);
46594617
4660 const result = try self.isNull(inst, ptr_ty.elemType(), operand);4618 const result = try self.isNull(inst, elem_ty, operand);
46614619
4662 return self.finishAir(inst, result, .{ un_op, .none, .none });4620 return self.finishAir(inst, result, .{ un_op, .none, .none });
4663}4621}
...@@ -4686,15 +4644,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4686,15 +4644,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4686 };4644 };
4687 defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);4645 defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
46884646
4689 const operand: MCValue = blk: {
4690 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4691 // The MCValue that holds the pointer can be re-used as the value.
4692 break :blk operand_ptr;
4693 } else {
4694 break :blk try self.allocRegOrMem(inst, true);
4695 }
4696 };
4697 const ptr_ty = self.air.typeOf(un_op);4647 const ptr_ty = self.air.typeOf(un_op);
4648 const elem_ty = ptr_ty.childType();
4649 const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr))
4650 // The MCValue that holds the pointer can be re-used as the value.
4651 operand_ptr
4652 else
4653 try self.allocTempRegOrMem(elem_ty, true);
4698 try self.load(operand, operand_ptr, ptr_ty);4654 try self.load(operand, operand_ptr, ptr_ty);
46994655
4700 const result = try self.isNonNull(inst, ptr_ty.elemType(), operand);4656 const result = try self.isNonNull(inst, ptr_ty.elemType(), operand);
...@@ -4789,11 +4745,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -4789,11 +4745,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
4789 const body = self.air.extra[loop.end..][0..loop.data.body_len];4745 const body = self.air.extra[loop.end..][0..loop.data.body_len];
4790 const jmp_target = @intCast(u32, self.mir_instructions.len);4746 const jmp_target = @intCast(u32, self.mir_instructions.len);
4791 try self.genBody(body);4747 try self.genBody(body);
4792 _ = try self.addInst(.{4748 _ = try self.asmJmpReloc(jmp_target);
4793 .tag = .jmp,
4794 .ops = Mir.Inst.Ops.encode(.{}),
4795 .data = .{ .inst = jmp_target },
4796 });
4797 return self.finishAirBookkeeping();4749 return self.finishAirBookkeeping();
4798}4750}
47994751
...@@ -4806,7 +4758,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4806,7 +4758,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
4806 // break instruction will choose a MCValue for the block result and overwrite4758 // break instruction will choose a MCValue for the block result and overwrite
4807 // this field. Following break instructions will use that MCValue to put their4759 // this field. Following break instructions will use that MCValue to put their
4808 // block results.4760 // block results.
4809 .mcv = MCValue{ .none = {} },4761 .mcv = .none,
4810 });4762 });
4811 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);4763 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
48124764
...@@ -4838,23 +4790,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u...@@ -4838,23 +4790,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
4838 .none => unreachable,4790 .none => unreachable,
4839 .undef => unreachable,4791 .undef => unreachable,
4840 .dead, .unreach => unreachable,4792 .dead, .unreach => unreachable,
4841 .immediate => |imm| {4793 .immediate => |imm| try self.asmRegisterImmediate(
4842 _ = try self.addInst(.{4794 .xor,
4843 .tag = .xor,4795 registerAlias(cond_reg, abi_size),
4844 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }),4796 Immediate.u(imm),
4845 .data = .{ .imm = @intCast(u32, imm) },4797 ),
4846 });4798 .register => |reg| try self.asmRegisterRegister(
4847 },4799 .xor,
4848 .register => |reg| {4800 registerAlias(cond_reg, abi_size),
4849 _ = try self.addInst(.{4801 registerAlias(reg, abi_size),
4850 .tag = .xor,4802 ),
4851 .ops = Mir.Inst.Ops.encode(.{
4852 .reg1 = registerAlias(cond_reg, abi_size),
4853 .reg2 = registerAlias(reg, abi_size),
4854 }),
4855 .data = undefined,
4856 });
4857 },
4858 .stack_offset => {4803 .stack_offset => {
4859 if (abi_size <= 8) {4804 if (abi_size <= 8) {
4860 const reg = try self.copyToTmpRegister(ty, case);4805 const reg = try self.copyToTmpRegister(ty, case);
...@@ -4868,22 +4813,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u...@@ -4868,22 +4813,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
4868 },4813 },
4869 }4814 }
48704815
4871 _ = try self.addInst(.{4816 const aliased_reg = registerAlias(cond_reg, abi_size);
4872 .tag = .@"test",4817 try self.asmRegisterRegister(.@"test", aliased_reg, aliased_reg);
4873 .ops = Mir.Inst.Ops.encode(.{4818 return self.asmJccReloc(undefined, .ne);
4874 .reg1 = registerAlias(cond_reg, abi_size),
4875 .reg2 = registerAlias(cond_reg, abi_size),
4876 }),
4877 .data = undefined,
4878 });
4879 return self.addInst(.{
4880 .tag = .cond_jmp,
4881 .ops = Mir.Inst.Ops.encode(.{}),
4882 .data = .{ .inst_cc = .{
4883 .inst = undefined,
4884 .cc = .ne,
4885 } },
4886 });
4887 },4819 },
4888 .stack_offset => {4820 .stack_offset => {
4889 try self.spillEflagsIfOccupied();4821 try self.spillEflagsIfOccupied();
...@@ -4901,6 +4833,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u...@@ -4901,6 +4833,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
4901 return self.fail("TODO implemenent switch mir when condition is {}", .{condition});4833 return self.fail("TODO implemenent switch mir when condition is {}", .{condition});
4902 },4834 },
4903 }4835 }
4836 return 0; // TODO
4904}4837}
49054838
4906fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {4839fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
...@@ -4946,25 +4879,25 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4946,25 +4879,25 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4946 var relocs = try self.gpa.alloc(u32, items.len);4879 var relocs = try self.gpa.alloc(u32, items.len);
4947 defer self.gpa.free(relocs);4880 defer self.gpa.free(relocs);
49484881
4949 for (items, 0..) |item, item_i| {4882 for (items, relocs) |item, *reloc| {
4950 const item_mcv = try self.resolveInst(item);4883 const item_mcv = try self.resolveInst(item);
4951 relocs[item_i] = try self.genCondSwitchMir(condition_ty, condition, item_mcv);4884 reloc.* = try self.genCondSwitchMir(condition_ty, condition, item_mcv);
4952 }4885 }
49534886
4954 // Capture the state of register and stack allocation state so that we can revert to it.4887 // Capture the state of register and stack allocation state so that we can revert to it.
4955 const saved_state = try self.captureState();4888 const saved_state = try self.captureState();
49564889
4957 try self.branch_stack.append(.{});4890 {
4958 errdefer {4891 try self.branch_stack.append(.{});
4959 _ = self.branch_stack.pop();4892 errdefer _ = self.branch_stack.pop();
4960 }
49614893
4962 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);4894 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
4963 for (liveness.deaths[case_i]) |operand| {4895 for (liveness.deaths[case_i]) |operand| {
4964 self.processDeath(operand);4896 self.processDeath(operand);
4965 }4897 }
49664898
4967 try self.genBody(case_body);4899 try self.genBody(case_body);
4900 }
49684901
4969 branch_stack.appendAssumeCapacity(self.branch_stack.pop());4902 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
49704903
...@@ -4982,18 +4915,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4982,18 +4915,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4982 // Capture the state of register and stack allocation state so that we can revert to it.4915 // Capture the state of register and stack allocation state so that we can revert to it.
4983 const saved_state = try self.captureState();4916 const saved_state = try self.captureState();
49844917
4985 try self.branch_stack.append(.{});4918 {
4986 errdefer {4919 try self.branch_stack.append(.{});
4987 _ = self.branch_stack.pop();4920 errdefer _ = self.branch_stack.pop();
4988 }
49894921
4990 const else_deaths = liveness.deaths.len - 1;4922 const else_deaths = liveness.deaths.len - 1;
4991 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);4923 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
4992 for (liveness.deaths[else_deaths]) |operand| {4924 for (liveness.deaths[else_deaths]) |operand| {
4993 self.processDeath(operand);4925 self.processDeath(operand);
4994 }4926 }
49954927
4996 try self.genBody(else_body);4928 try self.genBody(else_body);
4929 }
49974930
4998 branch_stack.appendAssumeCapacity(self.branch_stack.pop());4931 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
49994932
...@@ -5030,11 +4963,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran...@@ -5030,11 +4963,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
5030 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count());4963 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count());
50314964
5032 const target_slice = target_branch.inst_table.entries.slice();4965 const target_slice = target_branch.inst_table.entries.slice();
5033 const target_keys = target_slice.items(.key);4966 for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| {
5034 const target_values = target_slice.items(.value);
5035
5036 for (target_keys, 0..) |target_key, target_idx| {
5037 const target_value = target_values[target_idx];
5038 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {4967 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
5039 // The instruction's MCValue is overridden in both branches.4968 // The instruction's MCValue is overridden in both branches.
5040 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);4969 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
...@@ -5064,10 +4993,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran...@@ -5064,10 +4993,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
5064 }4993 }
5065 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());4994 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());
5066 const canon_slice = canon_branch.inst_table.entries.slice();4995 const canon_slice = canon_branch.inst_table.entries.slice();
5067 const canon_keys = canon_slice.items(.key);4996 for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| {
5068 const canon_values = canon_slice.items(.value);
5069 for (canon_keys, 0..) |canon_key, canon_idx| {
5070 const canon_value = canon_values[canon_idx];
5071 // We already deleted the items from this table that matched the target_branch.4997 // We already deleted the items from this table that matched the target_branch.
5072 // So these are all instructions that are only overridden in the canon branch.4998 // So these are all instructions that are only overridden in the canon branch.
5073 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);4999 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
...@@ -5095,10 +5021,10 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran...@@ -5095,10 +5021,10 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
5095fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {5021fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
5096 const next_inst = @intCast(u32, self.mir_instructions.len);5022 const next_inst = @intCast(u32, self.mir_instructions.len);
5097 switch (self.mir_instructions.items(.tag)[reloc]) {5023 switch (self.mir_instructions.items(.tag)[reloc]) {
5098 .cond_jmp => {5024 .jcc => {
5099 self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst;5025 self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst;
5100 },5026 },
5101 .jmp => {5027 .jmp_reloc => {
5102 self.mir_instructions.items(.data)[reloc].inst = next_inst;5028 self.mir_instructions.items(.data)[reloc].inst = next_inst;
5103 },5029 },
5104 else => unreachable,5030 else => unreachable,
...@@ -5140,11 +5066,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -5140,11 +5066,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
5140 // Emit a jump with a relocation. It will be patched up after the block ends.5066 // Emit a jump with a relocation. It will be patched up after the block ends.
5141 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);5067 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
5142 // Leave the jump offset undefined5068 // Leave the jump offset undefined
5143 const jmp_reloc = try self.addInst(.{5069 const jmp_reloc = try self.asmJmpReloc(undefined);
5144 .tag = .jmp,
5145 .ops = Mir.Inst.Ops.encode(.{}),
5146 .data = .{ .inst = undefined },
5147 });
5148 block_data.relocs.appendAssumeCapacity(jmp_reloc);5070 block_data.relocs.appendAssumeCapacity(jmp_reloc);
5149}5071}
51505072
...@@ -5217,31 +5139,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -5217,31 +5139,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5217 var iter = std.mem.tokenize(u8, asm_source, "\n\r");5139 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
5218 while (iter.next()) |ins| {5140 while (iter.next()) |ins| {
5219 if (mem.eql(u8, ins, "syscall")) {5141 if (mem.eql(u8, ins, "syscall")) {
5220 _ = try self.addInst(.{5142 try self.asmOpOnly(.syscall);
5221 .tag = .syscall,
5222 .ops = undefined,
5223 .data = undefined,
5224 });
5225 } else if (mem.indexOf(u8, ins, "push")) |_| {5143 } else if (mem.indexOf(u8, ins, "push")) |_| {
5226 const arg = ins[4..];5144 const arg = ins[4..];
5227 if (mem.indexOf(u8, arg, "$")) |l| {5145 if (mem.indexOf(u8, arg, "$")) |l| {
5228 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch {5146 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch {
5229 return self.fail("TODO implement more inline asm int parsing", .{});5147 return self.fail("TODO implement more inline asm int parsing", .{});
5230 };5148 };
5231 _ = try self.addInst(.{5149 try self.asmImmediate(.push, Immediate.u(n));
5232 .tag = .push,
5233 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b10 }),
5234 .data = .{ .imm = n },
5235 });
5236 } else if (mem.indexOf(u8, arg, "%%")) |l| {5150 } else if (mem.indexOf(u8, arg, "%%")) |l| {
5237 const reg_name = ins[4 + l + 2 ..];5151 const reg_name = ins[4 + l + 2 ..];
5238 const reg = parseRegName(reg_name) orelse5152 const reg = parseRegName(reg_name) orelse
5239 return self.fail("unrecognized register: '{s}'", .{reg_name});5153 return self.fail("unrecognized register: '{s}'", .{reg_name});
5240 _ = try self.addInst(.{5154 try self.asmRegister(.push, reg);
5241 .tag = .push,
5242 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
5243 .data = undefined,
5244 });
5245 } else return self.fail("TODO more push operands", .{});5155 } else return self.fail("TODO more push operands", .{});
5246 } else if (mem.indexOf(u8, ins, "pop")) |_| {5156 } else if (mem.indexOf(u8, ins, "pop")) |_| {
5247 const arg = ins[3..];5157 const arg = ins[3..];
...@@ -5249,11 +5159,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -5249,11 +5159,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5249 const reg_name = ins[3 + l + 2 ..];5159 const reg_name = ins[3 + l + 2 ..];
5250 const reg = parseRegName(reg_name) orelse5160 const reg = parseRegName(reg_name) orelse
5251 return self.fail("unrecognized register: '{s}'", .{reg_name});5161 return self.fail("unrecognized register: '{s}'", .{reg_name});
5252 _ = try self.addInst(.{5162 try self.asmRegister(.pop, reg);
5253 .tag = .pop,
5254 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
5255 .data = undefined,
5256 });
5257 } else return self.fail("TODO more pop operands", .{});5163 } else return self.fail("TODO more pop operands", .{});
5258 } else {5164 } else {
5259 return self.fail("TODO implement support for more x86 assembly instructions", .{});5165 return self.fail("TODO implement support for more x86 assembly instructions", .{});
...@@ -5268,9 +5174,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -5268,9 +5174,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5268 const reg_name = output[2 .. output.len - 1];5174 const reg_name = output[2 .. output.len - 1];
5269 const reg = parseRegName(reg_name) orelse5175 const reg = parseRegName(reg_name) orelse
5270 return self.fail("unrecognized register: '{s}'", .{reg_name});5176 return self.fail("unrecognized register: '{s}'", .{reg_name});
5271 break :result MCValue{ .register = reg };5177 break :result .{ .register = reg };
5272 } else {5178 } else {
5273 break :result MCValue{ .none = {} };5179 break :result .none;
5274 }5180 }
5275 };5181 };
52765182
...@@ -5326,7 +5232,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {...@@ -5326,7 +5232,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
5326}5232}
53275233
5328fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void {5234fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void {
5329 const abi_size = ty.abiSize(self.target.*);5235 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
5330 switch (mcv) {5236 switch (mcv) {
5331 .dead => unreachable,5237 .dead => unreachable,
5332 .unreach, .none => return,5238 .unreach, .none => return,
...@@ -5350,26 +5256,17 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -5350,26 +5256,17 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
5350 .immediate => |imm| {5256 .immediate => |imm| {
5351 switch (abi_size) {5257 switch (abi_size) {
5352 1, 2, 4 => {5258 1, 2, 4 => {
5259 // TODO
5353 // We have a positive stack offset value but we want a twos complement negative5260 // We have a positive stack offset value but we want a twos complement negative
5354 // offset from rbp, which is at the top of the stack frame.5261 // offset from rbp, which is at the top of the stack frame.
5355 // mov [rbp+offset], immediate5262 const immediate = if (ty.isSignedInt())
5356 const payload = try self.addExtra(Mir.ImmPair{5263 Immediate.s(@intCast(i32, @bitCast(i64, imm)))
5357 .dest_off = @bitCast(u32, -stack_offset),5264 else
5358 .operand = @truncate(u32, imm),5265 Immediate.u(@intCast(u32, imm));
5359 });5266 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5360 _ = try self.addInst(.{5267 .base = .rsp,
5361 .tag = .mov_mem_imm,5268 .disp = -stack_offset,
5362 .ops = Mir.Inst.Ops.encode(.{5269 }), immediate);
5363 .reg1 = .rsp,
5364 .flags = switch (abi_size) {
5365 1 => 0b00,
5366 2 => 0b01,
5367 4 => 0b10,
5368 else => unreachable,
5369 },
5370 }),
5371 .data = .{ .payload = payload },
5372 });
5373 },5270 },
5374 8 => {5271 8 => {
5375 const reg = try self.copyToTmpRegister(ty, mcv);5272 const reg = try self.copyToTmpRegister(ty, mcv);
...@@ -5394,44 +5291,32 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -5394,44 +5291,32 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
5394 .Float => {5291 .Float => {
5395 if (intrinsicsAllowed(self.target.*, ty)) {5292 if (intrinsicsAllowed(self.target.*, ty)) {
5396 const tag: Mir.Inst.Tag = switch (ty.tag()) {5293 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5397 .f32 => if (hasAvxSupport(self.target.*))5294 .f32 => .movss,
5398 Mir.Inst.Tag.mov_f32_avx5295 .f64 => .movsd,
5399 else5296 else => return self.fail(
5400 Mir.Inst.Tag.mov_f32_sse,5297 "TODO genSetStackArg for register for type {}",
5401 .f64 => if (hasAvxSupport(self.target.*))5298 .{ty.fmtDebug()},
5402 Mir.Inst.Tag.mov_f64_avx5299 ),
5403 else
5404 Mir.Inst.Tag.mov_f64_sse,
5405 else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}),
5406 };5300 };
5407 _ = try self.addInst(.{5301 // TODO verify this
5408 .tag = tag,5302 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5409 .ops = Mir.Inst.Ops.encode(.{5303 .f32 => .dword,
5410 .reg1 = switch (ty.tag()) {5304 .f64 => .qword,
5411 .f32 => .esp,5305 else => unreachable,
5412 .f64 => .rsp,5306 };
5413 else => unreachable,5307 return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{
5414 },5308 .base = .rsp,
5415 .reg2 = reg.to128(),5309 .disp = -stack_offset,
5416 .flags = 0b01,5310 }), reg.to128());
5417 }),
5418 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5419 });
5420 return;
5421 }5311 }
54225312
5423 return self.fail("TODO genSetStackArg for register with no intrinsics", .{});5313 return self.fail("TODO genSetStackArg for register with no intrinsics", .{});
5424 },5314 },
5425 else => {5315 else => {
5426 _ = try self.addInst(.{5316 try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5427 .tag = .mov,5317 .base = .rsp,
5428 .ops = Mir.Inst.Ops.encode(.{5318 .disp = -stack_offset,
5429 .reg1 = .rsp,5319 }), registerAlias(reg, abi_size));
5430 .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
5431 .flags = 0b10,
5432 }),
5433 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5434 });
5435 },5320 },
5436 }5321 }
5437 },5322 },
...@@ -5454,7 +5339,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -5454,7 +5339,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
5454}5339}
54555340
5456fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: InlineMemcpyOpts) InnerError!void {5341fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: InlineMemcpyOpts) InnerError!void {
5457 const abi_size = ty.abiSize(self.target.*);5342 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
5458 switch (mcv) {5343 switch (mcv) {
5459 .dead => unreachable,5344 .dead => unreachable,
5460 .unreach, .none => return, // Nothing to do.5345 .unreach, .none => return, // Nothing to do.
...@@ -5462,10 +5347,19 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -5462,10 +5347,19 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
5462 if (!self.wantSafety())5347 if (!self.wantSafety())
5463 return; // The already existing value will do just fine.5348 return; // The already existing value will do just fine.
5464 // TODO Upgrade this to a memset call when we have that available.5349 // TODO Upgrade this to a memset call when we have that available.
5465 switch (ty.abiSize(self.target.*)) {5350 switch (abi_size) {
5466 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }, opts),5351 1, 2, 4 => {
5467 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }, opts),5352 const value: u64 = switch (abi_size) {
5468 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }, opts),5353 1 => 0xaa,
5354 2 => 0xaaaa,
5355 4 => 0xaaaaaaaa,
5356 else => unreachable,
5357 };
5358 return self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5359 .base = opts.dest_stack_base orelse .rbp,
5360 .disp = -stack_offset,
5361 }), Immediate.u(value));
5362 },
5469 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts),5363 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts),
5470 else => |x| return self.genInlineMemset(5364 else => |x| return self.genInlineMemset(
5471 .{ .stack_offset = stack_offset },5365 .{ .stack_offset = stack_offset },
...@@ -5485,13 +5379,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -5485,13 +5379,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
5485 const overflow_bit_ty = ty.structFieldType(1);5379 const overflow_bit_ty = ty.structFieldType(1);
5486 const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);5380 const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);
5487 const tmp_reg = try self.register_manager.allocReg(null, gp);5381 const tmp_reg = try self.register_manager.allocReg(null, gp);
5488 _ = try self.addInst(.{5382 try self.asmSetccRegister(tmp_reg.to8(), ro.eflags);
5489 .tag = .cond_set_byte,
5490 .ops = Mir.Inst.Ops.encode(.{
5491 .reg1 = tmp_reg.to8(),
5492 }),
5493 .data = .{ .cc = ro.eflags },
5494 });
54955383
5496 return self.genSetStack(5384 return self.genSetStack(
5497 overflow_bit_ty,5385 overflow_bit_ty,
...@@ -5506,72 +5394,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -5506,72 +5394,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
5506 },5394 },
5507 .immediate => |x_big| {5395 .immediate => |x_big| {
5508 const base_reg = opts.dest_stack_base orelse .rbp;5396 const base_reg = opts.dest_stack_base orelse .rbp;
5397 // TODO
5509 switch (abi_size) {5398 switch (abi_size) {
5510 0 => {5399 0 => {
5511 assert(ty.isError());5400 assert(ty.isError());
5512 const payload = try self.addExtra(Mir.ImmPair{5401 try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{
5513 .dest_off = @bitCast(u32, -stack_offset),5402 .base = base_reg,
5514 .operand = @truncate(u32, x_big),5403 .disp = -stack_offset,
5515 });5404 }), Immediate.u(@truncate(u8, x_big)));
5516 _ = try self.addInst(.{
5517 .tag = .mov_mem_imm,
5518 .ops = Mir.Inst.Ops.encode(.{
5519 .reg1 = base_reg,
5520 .flags = 0b00,
5521 }),
5522 .data = .{ .payload = payload },
5523 });
5524 },5405 },
5525 1, 2, 4 => {5406 1, 2, 4 => {
5526 const payload = try self.addExtra(Mir.ImmPair{5407 const immediate = if (ty.isSignedInt())
5527 .dest_off = @bitCast(u32, -stack_offset),5408 Immediate.s(@truncate(i32, @bitCast(i64, x_big)))
5528 .operand = @truncate(u32, x_big),5409 else
5529 });5410 Immediate.u(@intCast(u32, x_big));
5530 _ = try self.addInst(.{5411 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5531 .tag = .mov_mem_imm,5412 .base = base_reg,
5532 .ops = Mir.Inst.Ops.encode(.{5413 .disp = -stack_offset,
5533 .reg1 = base_reg,5414 }), immediate);
5534 .flags = switch (abi_size) {
5535 1 => 0b00,
5536 2 => 0b01,
5537 4 => 0b10,
5538 else => unreachable,
5539 },
5540 }),
5541 .data = .{ .payload = payload },
5542 });
5543 },5415 },
5544 8 => {5416 8 => {
5545 // 64 bit write to memory would take two mov's anyways so we5417 // 64 bit write to memory would take two mov's anyways so we
5546 // insted just use two 32 bit writes to avoid register allocation5418 // insted just use two 32 bit writes to avoid register allocation
5547 {5419 try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{
5548 const payload = try self.addExtra(Mir.ImmPair{5420 .base = base_reg,
5549 .dest_off = @bitCast(u32, -stack_offset + 4),5421 .disp = -stack_offset + 4,
5550 .operand = @truncate(u32, x_big >> 32),5422 }), Immediate.u(@truncate(u32, x_big >> 32)));
5551 });5423 try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{
5552 _ = try self.addInst(.{5424 .base = base_reg,
5553 .tag = .mov_mem_imm,5425 .disp = -stack_offset,
5554 .ops = Mir.Inst.Ops.encode(.{5426 }), Immediate.u(@truncate(u32, x_big)));
5555 .reg1 = base_reg,
5556 .flags = 0b10,
5557 }),
5558 .data = .{ .payload = payload },
5559 });
5560 }
5561 {
5562 const payload = try self.addExtra(Mir.ImmPair{
5563 .dest_off = @bitCast(u32, -stack_offset),
5564 .operand = @truncate(u32, x_big),
5565 });
5566 _ = try self.addInst(.{
5567 .tag = .mov_mem_imm,
5568 .ops = Mir.Inst.Ops.encode(.{
5569 .reg1 = base_reg,
5570 .flags = 0b10,
5571 }),
5572 .data = .{ .payload = payload },
5573 });
5574 }
5575 },5427 },
5576 else => {5428 else => {
5577 return self.fail("TODO implement set abi_size=large stack variable with immediate", .{});5429 return self.fail("TODO implement set abi_size=large stack variable with immediate", .{});
...@@ -5589,30 +5441,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -5589,30 +5441,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
5589 .Float => {5441 .Float => {
5590 if (intrinsicsAllowed(self.target.*, ty)) {5442 if (intrinsicsAllowed(self.target.*, ty)) {
5591 const tag: Mir.Inst.Tag = switch (ty.tag()) {5443 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5592 .f32 => if (hasAvxSupport(self.target.*))5444 .f32 => .movss,
5593 Mir.Inst.Tag.mov_f32_avx5445 .f64 => .movsd,
5594 else5446 else => return self.fail(
5595 Mir.Inst.Tag.mov_f32_sse,5447 "TODO genSetStack for register for type {}",
5596 .f64 => if (hasAvxSupport(self.target.*))5448 .{ty.fmtDebug()},
5597 Mir.Inst.Tag.mov_f64_avx5449 ),
5598 else
5599 Mir.Inst.Tag.mov_f64_sse,
5600 else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
5601 };5450 };
5602 _ = try self.addInst(.{5451 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5603 .tag = tag,5452 .f32 => .dword,
5604 .ops = Mir.Inst.Ops.encode(.{5453 .f64 => .qword,
5605 .reg1 = switch (ty.tag()) {5454 else => unreachable,
5606 .f32 => base_reg.to32(),5455 };
5607 .f64 => base_reg.to64(),5456 return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{
5608 else => unreachable,5457 .base = base_reg.to64(),
5609 },5458 .disp = -stack_offset,
5610 .reg2 = reg.to128(),5459 }), reg.to128());
5611 .flags = 0b01,
5612 }),
5613 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5614 });
5615 return;
5616 }5460 }
56175461
5618 return self.fail("TODO genSetStack for register for type float with no intrinsics", .{});5462 return self.fail("TODO genSetStack for register for type float with no intrinsics", .{});
...@@ -5660,7 +5504,7 @@ fn genInlineMemcpyRegisterRegister(...@@ -5660,7 +5504,7 @@ fn genInlineMemcpyRegisterRegister(
5660 src_reg: Register,5504 src_reg: Register,
5661 offset: i32,5505 offset: i32,
5662) InnerError!void {5506) InnerError!void {
5663 assert(dst_reg.size() == 64);5507 assert(dst_reg.bitSize() == 64);
56645508
5665 const dst_reg_lock = self.register_manager.lockReg(dst_reg);5509 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
5666 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);5510 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
...@@ -5677,16 +5521,10 @@ fn genInlineMemcpyRegisterRegister(...@@ -5677,16 +5521,10 @@ fn genInlineMemcpyRegisterRegister(
5677 var remainder = abi_size;5521 var remainder = abi_size;
5678 while (remainder > 0) {5522 while (remainder > 0) {
5679 const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder));5523 const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder));
56805524 try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(nearest_power_of_two), .{
5681 _ = try self.addInst(.{5525 .base = dst_reg,
5682 .tag = .mov,5526 .disp = -next_offset,
5683 .ops = Mir.Inst.Ops.encode(.{5527 }), registerAlias(tmp_reg, nearest_power_of_two));
5684 .reg1 = dst_reg,
5685 .reg2 = registerAlias(tmp_reg, nearest_power_of_two),
5686 .flags = 0b10,
5687 }),
5688 .data = .{ .imm = @bitCast(u32, -next_offset) },
5689 });
56905528
5691 if (nearest_power_of_two > 1) {5529 if (nearest_power_of_two > 1) {
5692 try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{5530 try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{
...@@ -5698,15 +5536,10 @@ fn genInlineMemcpyRegisterRegister(...@@ -5698,15 +5536,10 @@ fn genInlineMemcpyRegisterRegister(
5698 next_offset -= nearest_power_of_two;5536 next_offset -= nearest_power_of_two;
5699 }5537 }
5700 } else {5538 } else {
5701 _ = try self.addInst(.{5539 try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5702 .tag = .mov,5540 .base = dst_reg,
5703 .ops = Mir.Inst.Ops.encode(.{5541 .disp = -offset,
5704 .reg1 = dst_reg,5542 }), registerAlias(src_reg, abi_size));
5705 .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
5706 .flags = 0b10,
5707 }),
5708 .data = .{ .imm = @bitCast(u32, -offset) },
5709 });
5710 }5543 }
5711}5544}
57125545
...@@ -5746,24 +5579,17 @@ fn genInlineMemcpy(...@@ -5746,24 +5579,17 @@ fn genInlineMemcpy(
5746 try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr);5579 try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr);
5747 },5580 },
5748 .ptr_stack_offset, .stack_offset => |off| {5581 .ptr_stack_offset, .stack_offset => |off| {
5749 _ = try self.addInst(.{5582 try self.asmRegisterMemory(.lea, dst_addr_reg.to64(), Memory.sib(.qword, .{
5750 .tag = .lea,5583 .base = opts.dest_stack_base orelse .rbp,
5751 .ops = Mir.Inst.Ops.encode(.{5584 .disp = -off,
5752 .reg1 = dst_addr_reg.to64(),5585 }));
5753 .reg2 = opts.dest_stack_base orelse .rbp,
5754 }),
5755 .data = .{ .imm = @bitCast(u32, -off) },
5756 });
5757 },5586 },
5758 .register => |reg| {5587 .register => |reg| {
5759 _ = try self.addInst(.{5588 try self.asmRegisterRegister(
5760 .tag = .mov,5589 .mov,
5761 .ops = Mir.Inst.Ops.encode(.{5590 registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))),
5762 .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)),5591 reg,
5763 .reg2 = reg,5592 );
5764 }),
5765 .data = undefined,
5766 });
5767 },5593 },
5768 else => {5594 else => {
5769 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});5595 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
...@@ -5775,24 +5601,17 @@ fn genInlineMemcpy(...@@ -5775,24 +5601,17 @@ fn genInlineMemcpy(
5775 try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr);5601 try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr);
5776 },5602 },
5777 .ptr_stack_offset, .stack_offset => |off| {5603 .ptr_stack_offset, .stack_offset => |off| {
5778 _ = try self.addInst(.{5604 try self.asmRegisterMemory(.lea, src_addr_reg.to64(), Memory.sib(.qword, .{
5779 .tag = .lea,5605 .base = opts.source_stack_base orelse .rbp,
5780 .ops = Mir.Inst.Ops.encode(.{5606 .disp = -off,
5781 .reg1 = src_addr_reg.to64(),5607 }));
5782 .reg2 = opts.source_stack_base orelse .rbp,
5783 }),
5784 .data = .{ .imm = @bitCast(u32, -off) },
5785 });
5786 },5608 },
5787 .register => |reg| {5609 .register => |reg| {
5788 _ = try self.addInst(.{5610 try self.asmRegisterRegister(
5789 .tag = .mov,5611 .mov,
5790 .ops = Mir.Inst.Ops.encode(.{5612 registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))),
5791 .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)),5613 reg,
5792 .reg2 = reg,5614 );
5793 }),
5794 .data = undefined,
5795 });
5796 },5615 },
5797 else => {5616 else => {
5798 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});5617 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
...@@ -5800,74 +5619,35 @@ fn genInlineMemcpy(...@@ -5800,74 +5619,35 @@ fn genInlineMemcpy(
5800 }5619 }
58015620
5802 try self.genSetReg(Type.usize, count_reg, len);5621 try self.genSetReg(Type.usize, count_reg, len);
58035622 try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0));
5804 // mov index_reg, 0
5805 _ = try self.addInst(.{
5806 .tag = .mov,
5807 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
5808 .data = .{ .imm = 0 },
5809 });
5810
5811 // loop:
5812 // cmp count, 0
5813 const loop_start = try self.addInst(.{5623 const loop_start = try self.addInst(.{
5814 .tag = .cmp,5624 .tag = .cmp,
5815 .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }),5625 .ops = .ri_u,
5816 .data = .{ .imm = 0 },5626 .data = .{ .ri = .{
5817 });5627 .r1 = count_reg,
58185628 .imm = 0,
5819 // je end
5820 const loop_reloc = try self.addInst(.{
5821 .tag = .cond_jmp,
5822 .ops = Mir.Inst.Ops.encode(.{}),
5823 .data = .{ .inst_cc = .{
5824 .inst = undefined,
5825 .cc = .e,
5826 } },5629 } },
5827 });5630 });
58285631 const loop_reloc = try self.asmJccReloc(undefined, .e);
5829 // mov tmp, [addr + index_reg]5632 try self.asmRegisterMemory(.mov, tmp_reg.to8(), Memory.sib(.byte, .{
5830 _ = try self.addInst(.{5633 .base = src_addr_reg,
5831 .tag = .mov_scale_src,5634 .scale_index = .{
5832 .ops = Mir.Inst.Ops.encode(.{5635 .scale = 1,
5833 .reg1 = tmp_reg.to8(),5636 .index = index_reg,
5834 .reg2 = src_addr_reg,5637 },
5835 }),5638 .disp = 0,
5836 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },5639 }));
5837 });5640 try self.asmMemoryRegister(.mov, Memory.sib(.byte, .{
58385641 .base = dst_addr_reg,
5839 // mov [stack_offset + index_reg], tmp5642 .scale_index = .{
5840 _ = try self.addInst(.{5643 .scale = 1,
5841 .tag = .mov_scale_dst,5644 .index = index_reg,
5842 .ops = Mir.Inst.Ops.encode(.{5645 },
5843 .reg1 = dst_addr_reg,5646 .disp = 0,
5844 .reg2 = tmp_reg.to8(),5647 }), tmp_reg.to8());
5845 }),5648 try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1));
5846 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },5649 try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1));
5847 });5650 _ = try self.asmJmpReloc(loop_start);
5848
5849 // add index_reg, 1
5850 _ = try self.addInst(.{
5851 .tag = .add,
5852 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
5853 .data = .{ .imm = 1 },
5854 });
5855
5856 // sub count, 1
5857 _ = try self.addInst(.{
5858 .tag = .sub,
5859 .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }),
5860 .data = .{ .imm = 1 },
5861 });
5862
5863 // jmp loop
5864 _ = try self.addInst(.{
5865 .tag = .jmp,
5866 .ops = Mir.Inst.Ops.encode(.{}),
5867 .data = .{ .inst = loop_start },
5868 });
5869
5870 // end:
5871 try self.performReloc(loop_reloc);5651 try self.performReloc(loop_reloc);
5872}5652}
58735653
...@@ -5899,24 +5679,17 @@ fn genInlineMemset(...@@ -5899,24 +5679,17 @@ fn genInlineMemset(
5899 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr);5679 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr);
5900 },5680 },
5901 .ptr_stack_offset, .stack_offset => |off| {5681 .ptr_stack_offset, .stack_offset => |off| {
5902 _ = try self.addInst(.{5682 try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{
5903 .tag = .lea,5683 .base = opts.dest_stack_base orelse .rbp,
5904 .ops = Mir.Inst.Ops.encode(.{5684 .disp = -off,
5905 .reg1 = addr_reg.to64(),5685 }));
5906 .reg2 = opts.dest_stack_base orelse .rbp,
5907 }),
5908 .data = .{ .imm = @bitCast(u32, -off) },
5909 });
5910 },5686 },
5911 .register => |reg| {5687 .register => |reg| {
5912 _ = try self.addInst(.{5688 try self.asmRegisterRegister(
5913 .tag = .mov,5689 .mov,
5914 .ops = Mir.Inst.Ops.encode(.{5690 registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))),
5915 .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)),5691 reg,
5916 .reg2 = reg,5692 );
5917 }),
5918 .data = undefined,
5919 });
5920 },5693 },
5921 else => {5694 else => {
5922 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});5695 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
...@@ -5926,54 +5699,35 @@ fn genInlineMemset(...@@ -5926,54 +5699,35 @@ fn genInlineMemset(
5926 try self.genSetReg(Type.usize, index_reg, len);5699 try self.genSetReg(Type.usize, index_reg, len);
5927 try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 });5700 try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 });
59285701
5929 // loop:
5930 // cmp index_reg, -1
5931 const loop_start = try self.addInst(.{5702 const loop_start = try self.addInst(.{
5932 .tag = .cmp,5703 .tag = .cmp,
5933 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),5704 .ops = .ri_s,
5934 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },5705 .data = .{ .ri = .{
5935 });5706 .r1 = index_reg,
59365707 .imm = @bitCast(u32, @as(i32, -1)),
5937 // je end
5938 const loop_reloc = try self.addInst(.{
5939 .tag = .cond_jmp,
5940 .ops = Mir.Inst.Ops.encode(.{}),
5941 .data = .{ .inst_cc = .{
5942 .inst = undefined,
5943 .cc = .e,
5944 } },5708 } },
5945 });5709 });
5710 const loop_reloc = try self.asmJccReloc(undefined, .e);
59465711
5947 switch (value) {5712 switch (value) {
5948 .immediate => |x| {5713 .immediate => |x| {
5949 if (x > math.maxInt(i32)) {5714 if (x > math.maxInt(i32)) {
5950 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});5715 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});
5951 }5716 }
5952 // mov byte ptr [rbp + index_reg + stack_offset], imm5717 try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{
5953 _ = try self.addInst(.{5718 .base = addr_reg,
5954 .tag = .mov_mem_index_imm,5719 .scale_index = .{
5955 .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }),5720 .scale = 1,
5956 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) },5721 .index = index_reg,
5957 });5722 },
5723 .disp = 0,
5724 }), Immediate.u(@intCast(u8, x)));
5958 },5725 },
5959 else => return self.fail("TODO inline memset for value of type {}", .{value}),5726 else => return self.fail("TODO inline memset for value of type {}", .{value}),
5960 }5727 }
59615728
5962 // sub index_reg, 15729 try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1));
5963 _ = try self.addInst(.{5730 _ = try self.asmJmpReloc(loop_start);
5964 .tag = .sub,
5965 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
5966 .data = .{ .imm = 1 },
5967 });
5968
5969 // jmp loop
5970 _ = try self.addInst(.{
5971 .tag = .jmp,
5972 .ops = Mir.Inst.Ops.encode(.{}),
5973 .data = .{ .inst = loop_start },
5974 });
5975
5976 // end:
5977 try self.performReloc(loop_reloc);5731 try self.performReloc(loop_reloc);
5978}5732}
59795733
...@@ -5986,21 +5740,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5986,21 +5740,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5986 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {5740 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
5987 return self.fail("stack offset too large", .{});5741 return self.fail("stack offset too large", .{});
5988 }5742 }
5989 _ = try self.addInst(.{5743 try self.asmRegisterMemory(
5990 .tag = .lea,5744 .lea,
5991 .ops = Mir.Inst.Ops.encode(.{5745 registerAlias(reg, abi_size),
5992 .reg1 = registerAlias(reg, abi_size),5746 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
5993 .reg2 = .rbp,5747 );
5994 }),
5995 .data = .{ .imm = @bitCast(u32, -off) },
5996 });
5997 },5748 },
5998 .unreach, .none => return, // Nothing to do.5749 .unreach, .none => return, // Nothing to do.
5999 .undef => {5750 .undef => {
6000 if (!self.wantSafety())5751 if (!self.wantSafety())
6001 return; // The already existing value will do just fine.5752 return; // The already existing value will do just fine.
6002 // Write the debug undefined value.5753 // Write the debug undefined value.
6003 switch (registerAlias(reg, abi_size).size()) {5754 switch (registerAlias(reg, abi_size).bitSize()) {
6004 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }),5755 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }),
6005 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }),5756 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }),
6006 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),5757 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
...@@ -6009,50 +5760,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6009,50 +5760,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6009 }5760 }
6010 },5761 },
6011 .eflags => |cc| {5762 .eflags => |cc| {
6012 _ = try self.addInst(.{5763 return self.asmSetccRegister(reg.to8(), cc);
6013 .tag = .cond_set_byte,
6014 .ops = Mir.Inst.Ops.encode(.{
6015 .reg1 = reg.to8(),
6016 }),
6017 .data = .{ .cc = cc },
6018 });
6019 },5764 },
6020 .immediate => |x| {5765 .immediate => |x| {
6021 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
6022 // register is the fastest way to zero a register.
6023 if (x == 0) {5766 if (x == 0) {
6024 _ = try self.addInst(.{5767 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
6025 .tag = .xor,5768 // register is the fastest way to zero a register.
6026 .ops = Mir.Inst.Ops.encode(.{5769 return self.asmRegisterRegister(.xor, reg.to32(), reg.to32());
6027 .reg1 = reg.to32(),
6028 .reg2 = reg.to32(),
6029 }),
6030 .data = undefined,
6031 });
6032 return;
6033 }5770 }
6034 if (x <= math.maxInt(i32)) {5771 if (ty.isSignedInt()) {
6035 // Next best case: if we set the lower four bytes, the upper four will be zeroed.5772 const signed_x = @bitCast(i64, x);
6036 _ = try self.addInst(.{5773 if (math.minInt(i32) <= signed_x and signed_x <= math.maxInt(i32)) {
6037 .tag = .mov,5774 return self.asmRegisterImmediate(
6038 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }),5775 .mov,
6039 .data = .{ .imm = @truncate(u32, x) },5776 registerAlias(reg, abi_size),
6040 });5777 Immediate.s(@intCast(i32, signed_x)),
6041 return;5778 );
5779 }
6042 }5780 }
6043 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls5781 return self.asmRegisterImmediate(
6044 // this `movabs`, though this is officially just a different variant of the plain `mov`5782 .mov,
6045 // instruction.5783 registerAlias(reg, abi_size),
6046 //5784 Immediate.u(x),
6047 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only5785 );
6048 // difference is that we set REX.W before the instruction, which extends the load to
6049 // 64-bit and uses the full bit-width of the register.
6050 const payload = try self.addExtra(Mir.Imm64.encode(x));
6051 _ = try self.addInst(.{
6052 .tag = .movabs,
6053 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }),
6054 .data = .{ .payload = payload },
6055 });
6056 },5786 },
6057 .register => |src_reg| {5787 .register => |src_reg| {
6058 // If the registers are the same, nothing to do.5788 // If the registers are the same, nothing to do.
...@@ -6063,69 +5793,38 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6063,69 +5793,38 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6063 .Int => switch (ty.intInfo(self.target.*).signedness) {5793 .Int => switch (ty.intInfo(self.target.*).signedness) {
6064 .signed => {5794 .signed => {
6065 if (abi_size <= 4) {5795 if (abi_size <= 4) {
6066 _ = try self.addInst(.{5796 return self.asmRegisterRegister(
6067 .tag = .mov_sign_extend,5797 .movsx,
6068 .ops = Mir.Inst.Ops.encode(.{5798 reg.to64(),
6069 .reg1 = reg.to64(),5799 registerAlias(src_reg, abi_size),
6070 .reg2 = registerAlias(src_reg, abi_size),5800 );
6071 }),
6072 .data = undefined,
6073 });
6074 return;
6075 }5801 }
6076 },5802 },
6077 .unsigned => {5803 .unsigned => {
6078 if (abi_size <= 2) {5804 if (abi_size <= 2) {
6079 _ = try self.addInst(.{5805 return self.asmRegisterRegister(
6080 .tag = .mov_zero_extend,5806 .movzx,
6081 .ops = Mir.Inst.Ops.encode(.{5807 reg.to64(),
6082 .reg1 = reg.to64(),5808 registerAlias(src_reg, abi_size),
6083 .reg2 = registerAlias(src_reg, abi_size),5809 );
6084 }),
6085 .data = undefined,
6086 });
6087 return;
6088 }5810 }
6089 },5811 },
6090 },5812 },
6091 .Float => {5813 .Float => {
6092 if (intrinsicsAllowed(self.target.*, ty)) {5814 if (intrinsicsAllowed(self.target.*, ty)) {
6093 const tag: Mir.Inst.Tag = switch (ty.tag()) {5815 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6094 .f32 => if (hasAvxSupport(self.target.*))5816 .f32 => .movss,
6095 Mir.Inst.Tag.mov_f32_avx5817 .f64 => .movsd,
6096 else
6097 Mir.Inst.Tag.mov_f32_sse,
6098 .f64 => if (hasAvxSupport(self.target.*))
6099 Mir.Inst.Tag.mov_f64_avx
6100 else
6101 Mir.Inst.Tag.mov_f64_sse,
6102 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),5818 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
6103 };5819 };
6104 _ = try self.addInst(.{5820 return self.asmRegisterRegister(tag, reg.to128(), src_reg.to128());
6105 .tag = tag,
6106 .ops = Mir.Inst.Ops.encode(.{
6107 .reg1 = reg.to128(),
6108 .reg2 = src_reg.to128(),
6109 .flags = 0b10,
6110 }),
6111 .data = undefined,
6112 });
6113 return;
6114 }5821 }
6115
6116 return self.fail("TODO genSetReg from register for float with no intrinsics", .{});5822 return self.fail("TODO genSetReg from register for float with no intrinsics", .{});
6117 },5823 },
6118 else => {},5824 else => {},
6119 }5825 }
61205826
6121 _ = try self.addInst(.{5827 try self.asmRegisterRegister(.mov, registerAlias(reg, abi_size), registerAlias(src_reg, abi_size));
6122 .tag = .mov,
6123 .ops = Mir.Inst.Ops.encode(.{
6124 .reg1 = registerAlias(reg, abi_size),
6125 .reg2 = registerAlias(src_reg, abi_size),
6126 }),
6127 .data = undefined,
6128 });
6129 },5828 },
6130 .linker_load => {5829 .linker_load => {
6131 switch (ty.zigTypeTag()) {5830 switch (ty.zigTypeTag()) {
...@@ -6135,45 +5834,30 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6135,45 +5834,30 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61355834
6136 if (intrinsicsAllowed(self.target.*, ty)) {5835 if (intrinsicsAllowed(self.target.*, ty)) {
6137 const tag: Mir.Inst.Tag = switch (ty.tag()) {5836 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6138 .f32 => if (hasAvxSupport(self.target.*))5837 .f32 => .movss,
6139 Mir.Inst.Tag.mov_f32_avx5838 .f64 => .movsd,
6140 else
6141 Mir.Inst.Tag.mov_f32_sse,
6142 .f64 => if (hasAvxSupport(self.target.*))
6143 Mir.Inst.Tag.mov_f64_avx
6144 else
6145 Mir.Inst.Tag.mov_f64_sse,
6146 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),5839 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
6147 };5840 };
61485841 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
6149 _ = try self.addInst(.{5842 .f32 => .dword,
6150 .tag = tag,5843 .f64 => .qword,
6151 .ops = Mir.Inst.Ops.encode(.{5844 else => unreachable,
6152 .reg1 = reg.to128(),5845 };
6153 .reg2 = switch (ty.tag()) {5846 return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{
6154 .f32 => base_reg.to32(),5847 .base = base_reg.to64(),
6155 .f64 => base_reg.to64(),5848 .disp = 0,
6156 else => unreachable,5849 }));
6157 },
6158 }),
6159 .data = .{ .imm = 0 },
6160 });
6161 return;
6162 }5850 }
61635851
6164 return self.fail("TODO genSetReg from memory for float with no intrinsics", .{});5852 return self.fail("TODO genSetReg from memory for float with no intrinsics", .{});
6165 },5853 },
6166 else => {5854 else => {
6167 try self.loadMemPtrIntoRegister(reg, Type.usize, mcv);5855 try self.loadMemPtrIntoRegister(reg, Type.usize, mcv);
6168 _ = try self.addInst(.{5856 try self.asmRegisterMemory(
6169 .tag = .mov,5857 .mov,
6170 .ops = Mir.Inst.Ops.encode(.{5858 registerAlias(reg, abi_size),
6171 .reg1 = registerAlias(reg, abi_size),5859 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }),
6172 .reg2 = reg.to64(),5860 );
6173 .flags = 0b01,
6174 }),
6175 .data = .{ .imm = 0 },
6176 });
6177 },5861 },
6178 }5862 }
6179 },5863 },
...@@ -6184,73 +5868,56 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6184,73 +5868,56 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61845868
6185 if (intrinsicsAllowed(self.target.*, ty)) {5869 if (intrinsicsAllowed(self.target.*, ty)) {
6186 const tag: Mir.Inst.Tag = switch (ty.tag()) {5870 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6187 .f32 => if (hasAvxSupport(self.target.*))5871 .f32 => .movss,
6188 Mir.Inst.Tag.mov_f32_avx5872 .f64 => .movsd,
6189 else
6190 Mir.Inst.Tag.mov_f32_sse,
6191 .f64 => if (hasAvxSupport(self.target.*))
6192 Mir.Inst.Tag.mov_f64_avx
6193 else
6194 Mir.Inst.Tag.mov_f64_sse,
6195 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),5873 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
6196 };5874 };
61975875 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
6198 _ = try self.addInst(.{5876 .f32 => .dword,
6199 .tag = tag,5877 .f64 => .qword,
6200 .ops = Mir.Inst.Ops.encode(.{5878 else => unreachable,
6201 .reg1 = reg.to128(),5879 };
6202 .reg2 = switch (ty.tag()) {5880 return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{
6203 .f32 => base_reg.to32(),5881 .base = base_reg.to64(),
6204 .f64 => base_reg.to64(),5882 .disp = 0,
6205 else => unreachable,5883 }));
6206 },
6207 }),
6208 .data = .{ .imm = 0 },
6209 });
6210 return;
6211 }5884 }
62125885
6213 return self.fail("TODO genSetReg from memory for float with no intrinsics", .{});5886 return self.fail("TODO genSetReg from memory for float with no intrinsics", .{});
6214 },5887 },
6215 else => {5888 else => {
6216 if (x <= math.maxInt(i32)) {5889 if (x <= math.maxInt(i32)) {
6217 // mov reg, [ds:imm32]5890 try self.asmRegisterMemory(
6218 _ = try self.addInst(.{5891 .mov,
6219 .tag = .mov,5892 registerAlias(reg, abi_size),
6220 .ops = Mir.Inst.Ops.encode(.{5893 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
6221 .reg1 = registerAlias(reg, abi_size),5894 .base = .ds,
6222 .flags = 0b01,5895 .disp = @intCast(i32, x),
6223 }),5896 }),
6224 .data = .{ .imm = @truncate(u32, x) },5897 );
6225 });
6226 } else {5898 } else {
6227 // If this is RAX, we can use a direct load.5899 if (reg.to64() == .rax) {
6228 // Otherwise, we need to load the address, then indirectly load the value.5900 // If this is RAX, we can use a direct load.
6229 if (reg.id() == 0) {5901 // Otherwise, we need to load the address, then indirectly load the value.
6230 // movabs rax, ds:moffs645902 var moffs: Mir.MemoryMoffs = .{
6231 const payload = try self.addExtra(Mir.Imm64.encode(x));5903 .seg = @enumToInt(Register.ds),
5904 .msb = undefined,
5905 .lsb = undefined,
5906 };
5907 moffs.encodeOffset(x);
6232 _ = try self.addInst(.{5908 _ = try self.addInst(.{
6233 .tag = .movabs,5909 .tag = .mov_moffs,
6234 .ops = Mir.Inst.Ops.encode(.{5910 .ops = .rax_moffs,
6235 .reg1 = .rax,5911 .data = .{ .payload = try self.addExtra(moffs) },
6236 .flags = 0b01, // imm64 will become moffs64
6237 }),
6238 .data = .{ .payload = payload },
6239 });5912 });
6240 } else {5913 } else {
6241 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.5914 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
6242 try self.genSetReg(ty, reg, MCValue{ .immediate = x });5915 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
62435916 try self.asmRegisterMemory(
6244 // mov reg, [reg + 0x0]5917 .mov,
6245 _ = try self.addInst(.{5918 registerAlias(reg, abi_size),
6246 .tag = .mov,5919 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }),
6247 .ops = Mir.Inst.Ops.encode(.{5920 );
6248 .reg1 = registerAlias(reg, abi_size),
6249 .reg2 = reg.to64(),
6250 .flags = 0b01,
6251 }),
6252 .data = .{ .imm = 0 },
6253 });
6254 }5921 }
6255 }5922 }
6256 },5923 },
...@@ -6264,85 +5931,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6264,85 +5931,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6264 .Int => switch (ty.intInfo(self.target.*).signedness) {5931 .Int => switch (ty.intInfo(self.target.*).signedness) {
6265 .signed => {5932 .signed => {
6266 if (abi_size <= 4) {5933 if (abi_size <= 4) {
6267 const flags: u2 = switch (abi_size) {5934 return self.asmRegisterMemory(
6268 1 => 0b01,5935 .movsx,
6269 2 => 0b10,5936 reg.to64(),
6270 4 => 0b11,5937 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
6271 else => unreachable,5938 .base = .rbp,
6272 };5939 .disp = -off,
6273 _ = try self.addInst(.{
6274 .tag = .mov_sign_extend,
6275 .ops = Mir.Inst.Ops.encode(.{
6276 .reg1 = reg.to64(),
6277 .reg2 = .rbp,
6278 .flags = flags,
6279 }),5940 }),
6280 .data = .{ .imm = @bitCast(u32, -off) },5941 );
6281 });
6282 return;
6283 }5942 }
6284 },5943 },
6285 .unsigned => {5944 .unsigned => {
6286 if (abi_size <= 2) {5945 if (abi_size <= 2) {
6287 const flags: u2 = switch (abi_size) {5946 return self.asmRegisterMemory(
6288 1 => 0b01,5947 .movzx,
6289 2 => 0b10,5948 reg.to64(),
6290 else => unreachable,5949 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
6291 };5950 .base = .rbp,
6292 _ = try self.addInst(.{5951 .disp = -off,
6293 .tag = .mov_zero_extend,
6294 .ops = Mir.Inst.Ops.encode(.{
6295 .reg1 = reg.to64(),
6296 .reg2 = .rbp,
6297 .flags = flags,
6298 }),5952 }),
6299 .data = .{ .imm = @bitCast(u32, -off) },5953 );
6300 });
6301 return;
6302 }5954 }
6303 },5955 },
6304 },5956 },
6305 .Float => {5957 .Float => {
6306 if (intrinsicsAllowed(self.target.*, ty)) {5958 if (intrinsicsAllowed(self.target.*, ty)) {
6307 const tag: Mir.Inst.Tag = switch (ty.tag()) {5959 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6308 .f32 => if (hasAvxSupport(self.target.*))5960 .f32 => .movss,
6309 Mir.Inst.Tag.mov_f32_avx5961 .f64 => .movsd,
6310 else5962 else => return self.fail(
6311 Mir.Inst.Tag.mov_f32_sse,5963 "TODO genSetReg from stack offset for {}",
6312 .f64 => if (hasAvxSupport(self.target.*))5964 .{ty.fmtDebug()},
6313 Mir.Inst.Tag.mov_f64_avx5965 ),
6314 else
6315 Mir.Inst.Tag.mov_f64_sse,
6316 else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
6317 };5966 };
6318 _ = try self.addInst(.{5967 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
6319 .tag = tag,5968 .f32 => .dword,
6320 .ops = Mir.Inst.Ops.encode(.{5969 .f64 => .qword,
6321 .reg1 = reg.to128(),5970 else => unreachable,
6322 .reg2 = switch (ty.tag()) {5971 };
6323 .f32 => .ebp,5972 return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{
6324 .f64 => .rbp,5973 .base = .rbp,
6325 else => unreachable,5974 .disp = -off,
6326 },5975 }));
6327 }),
6328 .data = .{ .imm = @bitCast(u32, -off) },
6329 });
6330 return;
6331 }5976 }
6332 return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{});5977 return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{});
6333 },5978 },
6334 else => {},5979 else => {},
6335 }5980 }
63365981
6337 _ = try self.addInst(.{5982 try self.asmRegisterMemory(
6338 .tag = .mov,5983 .mov,
6339 .ops = Mir.Inst.Ops.encode(.{5984 registerAlias(reg, abi_size),
6340 .reg1 = registerAlias(reg, abi_size),5985 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
6341 .reg2 = .rbp,5986 );
6342 .flags = 0b01,
6343 }),
6344 .data = .{ .imm = @bitCast(u32, -off) },
6345 });
6346 },5987 },
6347 }5988 }
6348}5989}
...@@ -6406,6 +6047,16 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {...@@ -6406,6 +6047,16 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
6406 const src_ty = self.air.typeOf(ty_op.operand);6047 const src_ty = self.air.typeOf(ty_op.operand);
6407 const dst_ty = self.air.typeOfIndex(inst);6048 const dst_ty = self.air.typeOfIndex(inst);
6408 const operand = try self.resolveInst(ty_op.operand);6049 const operand = try self.resolveInst(ty_op.operand);
6050 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
6051 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
6052
6053 switch (src_abi_size) {
6054 4, 8 => {},
6055 else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}),
6056 }
6057 if (dst_abi_size > 8) {
6058 return self.fail("TODO convert float with abiSize={}", .{dst_abi_size});
6059 }
64096060
6410 // move float src to ST(0)6061 // move float src to ST(0)
6411 const stack_offset = switch (operand) {6062 const stack_offset = switch (operand) {
...@@ -6413,41 +6064,24 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {...@@ -6413,41 +6064,24 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
6413 else => blk: {6064 else => blk: {
6414 const offset = @intCast(i32, try self.allocMem(6065 const offset = @intCast(i32, try self.allocMem(
6415 inst,6066 inst,
6416 @intCast(u32, src_ty.abiSize(self.target.*)),6067 src_abi_size,
6417 src_ty.abiAlignment(self.target.*),6068 src_ty.abiAlignment(self.target.*),
6418 ));6069 ));
6419 try self.genSetStack(src_ty, offset, operand, .{});6070 try self.genSetStack(src_ty, offset, operand, .{});
6420 break :blk offset;6071 break :blk offset;
6421 },6072 },
6422 };6073 };
6423 _ = try self.addInst(.{6074 try self.asmMemory(.fld, Memory.sib(Memory.PtrSize.fromSize(src_abi_size), .{
6424 .tag = .fld,6075 .base = .rbp,
6425 .ops = Mir.Inst.Ops.encode(.{6076 .disp = -stack_offset,
6426 .reg1 = .rbp,6077 }));
6427 .flags = switch (src_ty.abiSize(self.target.*)) {
6428 4 => 0b01,
6429 8 => 0b10,
6430 else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}),
6431 },
6432 }),
6433 .data = .{ .imm = @bitCast(u32, -stack_offset) },
6434 });
64356078
6436 // convert6079 // convert
6437 const stack_dst = try self.allocRegOrMem(inst, false);6080 const stack_dst = try self.allocRegOrMem(inst, false);
6438 _ = try self.addInst(.{6081 try self.asmMemory(.fisttp, Memory.sib(Memory.PtrSize.fromSize(dst_abi_size), .{
6439 .tag = .fisttp,6082 .base = .rbp,
6440 .ops = Mir.Inst.Ops.encode(.{6083 .disp = -stack_dst.stack_offset,
6441 .reg1 = .rbp,6084 }));
6442 .flags = switch (dst_ty.abiSize(self.target.*)) {
6443 1...2 => 0b00,
6444 3...4 => 0b01,
6445 5...8 => 0b10,
6446 else => |size| return self.fail("TODO convert float with abiSize={}", .{size}),
6447 },
6448 }),
6449 .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) },
6450 });
64516085
6452 return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none });6086 return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none });
6453}6087}
...@@ -6538,15 +6172,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {...@@ -6538,15 +6172,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
6538 .linker_load, .memory => {6172 .linker_load, .memory => {
6539 const reg = try self.register_manager.allocReg(null, gp);6173 const reg = try self.register_manager.allocReg(null, gp);
6540 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);6174 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);
6541 _ = try self.addInst(.{6175 try self.asmRegisterMemory(.mov, reg, Memory.sib(.qword, .{
6542 .tag = .mov,6176 .base = reg,
6543 .ops = Mir.Inst.Ops.encode(.{6177 .disp = 0,
6544 .reg1 = reg,6178 }));
6545 .reg2 = reg,
6546 .flags = 0b01,
6547 }),
6548 .data = .{ .imm = 0 },
6549 });
6550 break :blk MCValue{ .register = reg };6179 break :blk MCValue{ .register = reg };
6551 },6180 },
6552 else => break :blk src_ptr,6181 else => break :blk src_ptr,
...@@ -6619,6 +6248,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {...@@ -6619,6 +6248,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
6619 if (self.liveness.isUnused(inst)) break :res MCValue.dead;6248 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
6620 switch (result_ty.zigTypeTag()) {6249 switch (result_ty.zigTypeTag()) {
6621 .Struct => {6250 .Struct => {
6251 if (result_ty.containerLayout() == .Packed) {
6252 return self.fail("TODO airAggregateInit implement packed structs", .{});
6253 }
6622 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));6254 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
6623 for (elements, 0..) |elem, elem_i| {6255 for (elements, 0..) |elem, elem_i| {
6624 if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem6256 if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem
...@@ -6683,13 +6315,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {...@@ -6683,13 +6315,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
6683 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });6315 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
6684}6316}
66856317
6686pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {6318fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
6687 // First section of indexes correspond to a set number of constant values.6319 // First section of indexes correspond to a set number of constant values.
6688 const ref_int = @enumToInt(inst);6320 const ref_int = @enumToInt(inst);
6689 if (ref_int < Air.Inst.Ref.typed_value_map.len) {6321 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
6690 const tv = Air.Inst.Ref.typed_value_map[ref_int];6322 const tv = Air.Inst.Ref.typed_value_map[ref_int];
6691 if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) {6323 if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) {
6692 return MCValue{ .none = {} };6324 return .none;
6693 }6325 }
6694 return self.genTypedValue(tv);6326 return self.genTypedValue(tv);
6695 }6327 }
...@@ -6697,7 +6329,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {...@@ -6697,7 +6329,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
6697 // If the type has no codegen bits, no need to store it.6329 // If the type has no codegen bits, no need to store it.
6698 const inst_ty = self.air.typeOf(inst);6330 const inst_ty = self.air.typeOf(inst);
6699 if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError())6331 if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError())
6700 return MCValue{ .none = {} };6332 return .none;
67016333
6702 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);6334 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
6703 switch (self.air.instructions.items(.tag)[inst_index]) {6335 switch (self.air.instructions.items(.tag)[inst_index]) {
...@@ -6715,18 +6347,17 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {...@@ -6715,18 +6347,17 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
6715 return gop.value_ptr.*;6347 return gop.value_ptr.*;
6716 },6348 },
6717 .const_ty => unreachable,6349 .const_ty => unreachable,
6718 else => return self.getResolvedInstValue(inst_index),6350 else => return self.getResolvedInstValue(inst_index).?,
6719 }6351 }
6720}6352}
67216353
6722fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {6354fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue {
6723 // Treat each stack item as a "layer" on top of the previous one.6355 // Treat each stack item as a "layer" on top of the previous one.
6724 var i: usize = self.branch_stack.items.len;6356 var i: usize = self.branch_stack.items.len;
6725 while (true) {6357 while (true) {
6726 i -= 1;6358 i -= 1;
6727 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {6359 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
6728 assert(mcv != .dead);6360 return if (mcv != .dead) mcv else null;
6729 return mcv;
6730 }6361 }
6731 }6362 }
6732}6363}
...@@ -6752,200 +6383,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV...@@ -6752,200 +6383,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV
6752 return mcv;6383 return mcv;
6753}6384}
67546385
6755fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
6756 log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
6757 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6758 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
6759
6760 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
6761 if (tv.ty.zigTypeTag() == .Pointer) blk: {
6762 if (tv.ty.castPtrToFn()) |_| break :blk;
6763 if (!tv.ty.elemType2().hasRuntimeBits()) {
6764 return MCValue.none;
6765 }
6766 }
6767
6768 const module = self.bin_file.options.module.?;
6769 const decl = module.declPtr(decl_index);
6770 module.markDeclAlive(decl);
6771
6772 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6773 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6774 const atom = elf_file.getAtom(atom_index);
6775 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6776 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6777 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
6778 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
6779 return MCValue{ .linker_load = .{
6780 .type = .got,
6781 .sym_index = sym_index,
6782 } };
6783 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6784 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6785 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
6786 return MCValue{ .linker_load = .{
6787 .type = .got,
6788 .sym_index = sym_index,
6789 } };
6790 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6791 const decl_block_index = try p9.seeDecl(decl_index);
6792 const decl_block = p9.getDeclBlock(decl_block_index);
6793 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
6794 return MCValue{ .memory = got_addr };
6795 } else {
6796 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
6797 }
6798}
6799
6800fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6801 log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
6802 const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
6803 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6804 };
6805 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6806 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6807 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6808 return MCValue{ .linker_load = .{
6809 .type = .direct,
6810 .sym_index = local_sym_index,
6811 } };
6812 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6813 return MCValue{ .linker_load = .{
6814 .type = .direct,
6815 .sym_index = local_sym_index,
6816 } };
6817 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6818 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6819 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
6820 const got_index = local_sym_index; // the plan9 backend returns the got_index
6821 const got_addr = p9.bases.data + got_index * ptr_bytes;
6822 return MCValue{ .memory = got_addr };
6823 } else {
6824 return self.fail("TODO lower unnamed const", .{});
6825 }
6826}
6827
6828fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {6386fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6829 var typed_value = arg_tv;6387 const mcv: MCValue = switch (try codegen.genTypedValue(
6830 if (typed_value.val.castTag(.runtime_value)) |rt| {6388 self.bin_file,
6831 typed_value.val = rt.data;6389 self.src_loc,
6832 }6390 arg_tv,
6833 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });6391 self.mod_fn.owner_decl,
6834 if (typed_value.val.isUndef())6392 )) {
6835 return MCValue{ .undef = {} };6393 .mcv => |mcv| switch (mcv) {
6836 const ptr_bits = self.target.cpu.arch.ptrBitWidth();6394 .none => .none,
68376395 .undef => .undef,
6838 if (typed_value.val.castTag(.decl_ref)) |payload| {6396 .linker_load => |ll| .{ .linker_load = ll },
6839 return self.lowerDeclRef(typed_value, payload.data);6397 .immediate => |imm| .{ .immediate = imm },
6840 }6398 .memory => |addr| .{ .memory = addr },
6841 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
6842 return self.lowerDeclRef(typed_value, payload.data.decl_index);
6843 }
6844
6845 const target = self.target.*;
6846
6847 switch (typed_value.ty.zigTypeTag()) {
6848 .Void => return MCValue{ .none = {} },
6849 .Pointer => switch (typed_value.ty.ptrSize()) {
6850 .Slice => {},
6851 else => {
6852 switch (typed_value.val.tag()) {
6853 .int_u64 => {
6854 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
6855 },
6856 else => {},
6857 }
6858 },
6859 },
6860 .Int => {
6861 const info = typed_value.ty.intInfo(self.target.*);
6862 if (info.bits <= ptr_bits and info.signedness == .signed) {
6863 return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) };
6864 }
6865 if (!(info.bits > ptr_bits or info.signedness == .signed)) {
6866 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
6867 }
6868 },6399 },
6869 .Bool => {6400 .fail => |msg| {
6870 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };6401 self.err_msg = msg;
6402 return error.CodegenFail;
6871 },6403 },
6872 .Optional => {6404 };
6873 if (typed_value.ty.isPtrLikeOptional()) {6405 return mcv;
6874 if (typed_value.val.isNull())
6875 return MCValue{ .immediate = 0 };
6876
6877 var buf: Type.Payload.ElemType = undefined;
6878 return self.genTypedValue(.{
6879 .ty = typed_value.ty.optionalChild(&buf),
6880 .val = typed_value.val,
6881 });
6882 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
6883 return MCValue{ .immediate = @boolToInt(!typed_value.val.isNull()) };
6884 }
6885 },
6886 .Enum => {
6887 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
6888 switch (typed_value.ty.tag()) {
6889 .enum_simple => {
6890 return MCValue{ .immediate = field_index.data };
6891 },
6892 .enum_full, .enum_nonexhaustive => {
6893 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
6894 if (enum_full.values.count() != 0) {
6895 const tag_val = enum_full.values.keys()[field_index.data];
6896 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
6897 } else {
6898 return MCValue{ .immediate = field_index.data };
6899 }
6900 },
6901 else => unreachable,
6902 }
6903 } else {
6904 var int_tag_buffer: Type.Payload.Bits = undefined;
6905 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
6906 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
6907 }
6908 },
6909 .ErrorSet => {
6910 switch (typed_value.val.tag()) {
6911 .@"error" => {
6912 const err_name = typed_value.val.castTag(.@"error").?.data.name;
6913 const module = self.bin_file.options.module.?;
6914 const global_error_set = module.global_error_set;
6915 const error_index = global_error_set.get(err_name).?;
6916 return MCValue{ .immediate = error_index };
6917 },
6918 else => {
6919 // In this case we are rendering an error union which has a 0 bits payload.
6920 return MCValue{ .immediate = 0 };
6921 },
6922 }
6923 },
6924 .ErrorUnion => {
6925 const error_type = typed_value.ty.errorUnionSet();
6926 const payload_type = typed_value.ty.errorUnionPayload();
6927 const is_pl = typed_value.val.errorUnionIsPayload();
6928
6929 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
6930 // We use the error type directly as the type.
6931 const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
6932 return self.genTypedValue(.{ .ty = error_type, .val = err_val });
6933 }
6934 },
6935
6936 .ComptimeInt => unreachable,
6937 .ComptimeFloat => unreachable,
6938 .Type => unreachable,
6939 .EnumLiteral => unreachable,
6940 .NoReturn => unreachable,
6941 .Undefined => unreachable,
6942 .Null => unreachable,
6943 .Opaque => unreachable,
6944
6945 else => {},
6946 }
6947
6948 return self.lowerUnnamedConst(typed_value);
6949}6406}
69506407
6951const CallMCValues = struct {6408const CallMCValues = struct {
...@@ -6980,7 +6437,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -6980,7 +6437,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
6980 switch (cc) {6437 switch (cc) {
6981 .Naked => {6438 .Naked => {
6982 assert(result.args.len == 0);6439 assert(result.args.len == 0);
6983 result.return_value = .{ .unreach = {} };6440 result.return_value = .unreach;
6984 result.stack_byte_count = 0;6441 result.stack_byte_count = 0;
6985 result.stack_align = 1;6442 result.stack_align = 1;
6986 return result;6443 return result;
...@@ -6988,10 +6445,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -6988,10 +6445,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
6988 .C => {6445 .C => {
6989 // Return values6446 // Return values
6990 if (ret_ty.zigTypeTag() == .NoReturn) {6447 if (ret_ty.zigTypeTag() == .NoReturn) {
6991 result.return_value = .{ .unreach = {} };6448 result.return_value = .unreach;
6992 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {6449 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
6993 // TODO: is this even possible for C calling convention?6450 // TODO: is this even possible for C calling convention?
6994 result.return_value = .{ .none = {} };6451 result.return_value = .none;
6995 } else {6452 } else {
6996 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));6453 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
6997 if (ret_ty_size == 0) {6454 if (ret_ty_size == 0) {
...@@ -7012,7 +6469,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7012,7 +6469,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7012 else => 0,6469 else => 0,
7013 };6470 };
70146471
7015 for (param_types, 0..) |ty, i| {6472 for (param_types, result.args, 0..) |ty, *arg, i| {
7016 assert(ty.hasRuntimeBits());6473 assert(ty.hasRuntimeBits());
70176474
7018 const classes: []const abi.Class = switch (self.target.os.tag) {6475 const classes: []const abi.Class = switch (self.target.os.tag) {
...@@ -7025,7 +6482,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7025,7 +6482,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7025 switch (classes[0]) {6482 switch (classes[0]) {
7026 .integer => blk: {6483 .integer => blk: {
7027 if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough6484 if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough
7028 result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };6485 arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };
7029 continue;6486 continue;
7030 },6487 },
7031 .memory => {}, // fallthrough6488 .memory => {}, // fallthrough
...@@ -7037,7 +6494,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7037,7 +6494,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7037 const param_size = @intCast(u32, ty.abiSize(self.target.*));6494 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7038 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));6495 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7039 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);6496 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7040 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };6497 arg.* = .{ .stack_offset = @intCast(i32, offset) };
7041 next_stack_offset = offset;6498 next_stack_offset = offset;
7042 }6499 }
70436500
...@@ -7063,15 +6520,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7063,15 +6520,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7063 .Unspecified => {6520 .Unspecified => {
7064 // Return values6521 // Return values
7065 if (ret_ty.zigTypeTag() == .NoReturn) {6522 if (ret_ty.zigTypeTag() == .NoReturn) {
7066 result.return_value = .{ .unreach = {} };6523 result.return_value = .unreach;
7067 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {6524 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7068 result.return_value = .{ .none = {} };6525 result.return_value = .none;
7069 } else {6526 } else {
7070 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));6527 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
7071 if (ret_ty_size == 0) {6528 if (ret_ty_size == 0) {
7072 assert(ret_ty.isError());6529 assert(ret_ty.isError());
7073 result.return_value = .{ .immediate = 0 };6530 result.return_value = .{ .immediate = 0 };
7074 } else if (ret_ty_size <= 8) {6531 } else if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) {
7075 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);6532 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
7076 result.return_value = .{ .register = aliased_reg };6533 result.return_value = .{ .register = aliased_reg };
7077 } else {6534 } else {
...@@ -7088,15 +6545,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7088,15 +6545,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7088 else => 0,6545 else => 0,
7089 };6546 };
70906547
7091 for (param_types, 0..) |ty, i| {6548 for (param_types, result.args) |ty, *arg| {
7092 if (!ty.hasRuntimeBits()) {6549 if (!ty.hasRuntimeBits()) {
7093 result.args[i] = .{ .none = {} };6550 arg.* = .none;
7094 continue;6551 continue;
7095 }6552 }
7096 const param_size = @intCast(u32, ty.abiSize(self.target.*));6553 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7097 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));6554 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7098 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);6555 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7099 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };6556 arg.* = .{ .stack_offset = @intCast(i32, offset) };
7100 next_stack_offset = offset;6557 next_stack_offset = offset;
7101 }6558 }
71026559
...@@ -7146,28 +6603,34 @@ fn parseRegName(name: []const u8) ?Register {...@@ -7146,28 +6603,34 @@ fn parseRegName(name: []const u8) ?Register {
71466603
7147/// Returns register wide enough to hold at least `size_bytes`.6604/// Returns register wide enough to hold at least `size_bytes`.
7148fn registerAlias(reg: Register, size_bytes: u32) Register {6605fn registerAlias(reg: Register, size_bytes: u32) Register {
7149 if (size_bytes == 0) {6606 return switch (reg.class()) {
7150 unreachable; // should be comptime-known6607 .general_purpose => if (size_bytes == 0)
7151 } else if (size_bytes <= 1) {6608 unreachable // should be comptime-known
7152 return reg.to8();6609 else if (size_bytes <= 1)
7153 } else if (size_bytes <= 2) {6610 reg.to8()
7154 return reg.to16();6611 else if (size_bytes <= 2)
7155 } else if (size_bytes <= 4) {6612 reg.to16()
7156 return reg.to32();6613 else if (size_bytes <= 4)
7157 } else if (size_bytes <= 8) {6614 reg.to32()
7158 return reg.to64();6615 else if (size_bytes <= 8)
7159 } else if (size_bytes <= 16) {6616 reg.to64()
7160 return reg.to128();6617 else
7161 } else if (size_bytes <= 32) {6618 unreachable,
7162 return reg.to256();6619 .floating_point => if (size_bytes <= 16)
7163 } else unreachable;6620 reg.to128()
6621 else if (size_bytes <= 32)
6622 reg.to256()
6623 else
6624 unreachable,
6625 .segment => unreachable,
6626 };
7164}6627}
71656628
7166/// Truncates the value in the register in place.6629/// Truncates the value in the register in place.
7167/// Clobbers any remaining bits.6630/// Clobbers any remaining bits.
7168fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {6631fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
7169 const int_info = ty.intInfo(self.target.*);6632 const int_info = ty.intInfo(self.target.*);
7170 const max_reg_bit_width = Register.rax.size();6633 const max_reg_bit_width = Register.rax.bitSize();
7171 switch (int_info.signedness) {6634 switch (int_info.signedness) {
7172 .signed => {6635 .signed => {
7173 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);6636 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
...@@ -7177,7 +6640,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {...@@ -7177,7 +6640,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
7177 .unsigned => {6640 .unsigned => {
7178 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);6641 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
7179 const mask = (~@as(u64, 0)) >> shift;6642 const mask = (~@as(u64, 0)) >> shift;
7180 if (int_info.bits <= 32) {6643 if (int_info.bits < 32) {
7181 try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask });6644 try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask });
7182 } else {6645 } else {
7183 const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask });6646 const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask });
src/arch/x86_64/Emit.zig+385-2830
...@@ -7,6 +7,7 @@ const std = @import("std");...@@ -7,6 +7,7 @@ const std = @import("std");
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const bits = @import("bits.zig");8const bits = @import("bits.zig");
9const abi = @import("abi.zig");9const abi = @import("abi.zig");
10const encoder = @import("encoder.zig");
10const link = @import("../../link.zig");11const link = @import("../../link.zig");
11const log = std.log.scoped(.codegen);12const log = std.log.scoped(.codegen);
12const math = std.math;13const math = std.math;
...@@ -19,12 +20,14 @@ const CodeGen = @import("CodeGen.zig");...@@ -19,12 +20,14 @@ const CodeGen = @import("CodeGen.zig");
19const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;20const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
20const Encoder = bits.Encoder;21const Encoder = bits.Encoder;
21const ErrorMsg = Module.ErrorMsg;22const ErrorMsg = Module.ErrorMsg;
23const Immediate = bits.Immediate;
24const Instruction = encoder.Instruction;
22const MCValue = @import("CodeGen.zig").MCValue;25const MCValue = @import("CodeGen.zig").MCValue;
26const Memory = bits.Memory;
23const Mir = @import("Mir.zig");27const Mir = @import("Mir.zig");
24const Module = @import("../../Module.zig");28const Module = @import("../../Module.zig");
25const Instruction = bits.Instruction;
26const Type = @import("../../type.zig").Type;
27const Register = bits.Register;29const Register = bits.Register;
30const Type = @import("../../type.zig").Type;
2831
29mir: Mir,32mir: Mir,
30bin_file: *link.File,33bin_file: *link.File,
...@@ -45,6 +48,8 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{},...@@ -45,6 +48,8 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{},
45const InnerError = error{48const InnerError = error{
46 OutOfMemory,49 OutOfMemory,
47 EmitFail,50 EmitFail,
51 InvalidInstruction,
52 CannotEncode,
48};53};
4954
50const Reloc = struct {55const Reloc = struct {
...@@ -65,132 +70,66 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -65,132 +70,66 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
65 const inst = @intCast(u32, index);70 const inst = @intCast(u32, index);
66 try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);71 try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);
67 switch (tag) {72 switch (tag) {
68 // GPR instructions73 .adc,
69 .adc => try emit.mirArith(.adc, inst),74 .add,
70 .add => try emit.mirArith(.add, inst),75 .@"and",
71 .sub => try emit.mirArith(.sub, inst),76 .call,
72 .xor => try emit.mirArith(.xor, inst),77 .cbw,
73 .@"and" => try emit.mirArith(.@"and", inst),78 .cwde,
74 .@"or" => try emit.mirArith(.@"or", inst),79 .cdqe,
75 .sbb => try emit.mirArith(.sbb, inst),80 .cwd,
76 .cmp => try emit.mirArith(.cmp, inst),81 .cdq,
77 .mov => try emit.mirArith(.mov, inst),82 .cqo,
7883 .cmp,
79 .adc_mem_imm => try emit.mirArithMemImm(.adc, inst),84 .div,
80 .add_mem_imm => try emit.mirArithMemImm(.add, inst),85 .fisttp,
81 .sub_mem_imm => try emit.mirArithMemImm(.sub, inst),86 .fld,
82 .xor_mem_imm => try emit.mirArithMemImm(.xor, inst),87 .idiv,
83 .and_mem_imm => try emit.mirArithMemImm(.@"and", inst),88 .imul,
84 .or_mem_imm => try emit.mirArithMemImm(.@"or", inst),89 .int3,
85 .sbb_mem_imm => try emit.mirArithMemImm(.sbb, inst),90 .jmp,
86 .cmp_mem_imm => try emit.mirArithMemImm(.cmp, inst),91 .lea,
87 .mov_mem_imm => try emit.mirArithMemImm(.mov, inst),92 .mov,
8893 .movzx,
89 .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst),94 .mul,
90 .add_scale_src => try emit.mirArithScaleSrc(.add, inst),95 .nop,
91 .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst),96 .@"or",
92 .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst),97 .pop,
93 .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst),98 .push,
94 .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst),99 .ret,
95 .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst),100 .sal,
96 .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst),101 .sar,
97 .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst),102 .sbb,
98103 .shl,
99 .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst),104 .shr,
100 .add_scale_dst => try emit.mirArithScaleDst(.add, inst),105 .sub,
101 .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst),106 .syscall,
102 .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst),107 .@"test",
103 .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst),108 .ud2,
104 .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst),109 .xor,
105 .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst),
106 .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst),
107 .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst),
108
109 .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst),
110 .add_scale_imm => try emit.mirArithScaleImm(.add, inst),
111 .sub_scale_imm => try emit.mirArithScaleImm(.sub, inst),
112 .xor_scale_imm => try emit.mirArithScaleImm(.xor, inst),
113 .and_scale_imm => try emit.mirArithScaleImm(.@"and", inst),
114 .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst),
115 .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst),
116 .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst),
117 .mov_scale_imm => try emit.mirArithScaleImm(.mov, inst),
118
119 .adc_mem_index_imm => try emit.mirArithMemIndexImm(.adc, inst),
120 .add_mem_index_imm => try emit.mirArithMemIndexImm(.add, inst),
121 .sub_mem_index_imm => try emit.mirArithMemIndexImm(.sub, inst),
122 .xor_mem_index_imm => try emit.mirArithMemIndexImm(.xor, inst),
123 .and_mem_index_imm => try emit.mirArithMemIndexImm(.@"and", inst),
124 .or_mem_index_imm => try emit.mirArithMemIndexImm(.@"or", inst),
125 .sbb_mem_index_imm => try emit.mirArithMemIndexImm(.sbb, inst),
126 .cmp_mem_index_imm => try emit.mirArithMemIndexImm(.cmp, inst),
127 .mov_mem_index_imm => try emit.mirArithMemIndexImm(.mov, inst),
128
129 .mov_sign_extend => try emit.mirMovSignExtend(inst),
130 .mov_zero_extend => try emit.mirMovZeroExtend(inst),
131
132 .movabs => try emit.mirMovabs(inst),
133
134 .fisttp => try emit.mirFisttp(inst),
135 .fld => try emit.mirFld(inst),
136
137 .lea => try emit.mirLea(inst),
138 .lea_pic => try emit.mirLeaPic(inst),
139
140 .shl => try emit.mirShift(.shl, inst),
141 .sal => try emit.mirShift(.sal, inst),
142 .shr => try emit.mirShift(.shr, inst),
143 .sar => try emit.mirShift(.sar, inst),
144
145 .imul => try emit.mirMulDiv(.imul, inst),
146 .mul => try emit.mirMulDiv(.mul, inst),
147 .idiv => try emit.mirMulDiv(.idiv, inst),
148 .div => try emit.mirMulDiv(.div, inst),
149 .imul_complex => try emit.mirIMulComplex(inst),
150
151 .cwd => try emit.mirCwd(inst),
152
153 .push => try emit.mirPushPop(.push, inst),
154 .pop => try emit.mirPushPop(.pop, inst),
155
156 .jmp => try emit.mirJmpCall(.jmp_near, inst),
157 .call => try emit.mirJmpCall(.call_near, inst),
158
159 .cond_jmp => try emit.mirCondJmp(inst),
160 .cond_set_byte => try emit.mirCondSetByte(inst),
161 .cond_mov => try emit.mirCondMov(inst),
162
163 .ret => try emit.mirRet(inst),
164
165 .syscall => try emit.mirSyscall(),
166
167 .@"test" => try emit.mirTest(inst),
168
169 .interrupt => try emit.mirInterrupt(inst),
170 .nop => {}, // just skip it
171
172 // SSE instructions
173 .mov_f64_sse => try emit.mirMovFloatSse(.movsd, inst),
174 .mov_f32_sse => try emit.mirMovFloatSse(.movss, inst),
175110
176 .add_f64_sse => try emit.mirAddFloatSse(.addsd, inst),111 .addss,
177 .add_f32_sse => try emit.mirAddFloatSse(.addss, inst),112 .cmpss,
113 .movss,
114 .ucomiss,
115 .addsd,
116 .cmpsd,
117 .movsd,
118 .ucomisd,
119 => try emit.mirEncodeGeneric(tag, inst),
178120
179 .cmp_f64_sse => try emit.mirCmpFloatSse(.ucomisd, inst),121 .jmp_reloc => try emit.mirJmpReloc(inst),
180 .cmp_f32_sse => try emit.mirCmpFloatSse(.ucomiss, inst),
181122
182 // AVX instructions123 .call_extern => try emit.mirCallExtern(inst),
183 .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst),
184 .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst),
185124
186 .add_f64_avx => try emit.mirAddFloatAvx(.vaddsd, inst),125 .lea_linker => try emit.mirLeaLinker(inst),
187 .add_f32_avx => try emit.mirAddFloatAvx(.vaddss, inst),
188126
189 .cmp_f64_avx => try emit.mirCmpFloatAvx(.vucomisd, inst),127 .mov_moffs => try emit.mirMovMoffs(inst),
190 .cmp_f32_avx => try emit.mirCmpFloatAvx(.vucomiss, inst),
191128
192 // Pseudo-instructions129 .movsx => try emit.mirMovsx(inst),
193 .call_extern => try emit.mirCallExtern(inst),130 .cmovcc => try emit.mirCmovcc(inst),
131 .setcc => try emit.mirSetcc(inst),
132 .jcc => try emit.mirJcc(inst),
194133
195 .dbg_line => try emit.mirDbgLine(inst),134 .dbg_line => try emit.mirDbgLine(inst),
196 .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst),135 .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst),
...@@ -199,9 +138,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -199,9 +138,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
199 .push_regs => try emit.mirPushPopRegisterList(.push, inst),138 .push_regs => try emit.mirPushPopRegisterList(.push, inst),
200 .pop_regs => try emit.mirPushPopRegisterList(.pop, inst),139 .pop_regs => try emit.mirPushPopRegisterList(.pop, inst),
201140
202 else => {141 .dead => {},
203 return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag});
204 },
205 }142 }
206 }143 }
207144
...@@ -234,794 +171,385 @@ fn fixupRelocs(emit: *Emit) InnerError!void {...@@ -234,794 +171,385 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
234 }171 }
235}172}
236173
237fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {174fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct {
238 const tag = emit.mir.instructions.items(.tag)[inst];175 op1: Instruction.Operand = .none,
239 assert(tag == .interrupt);176 op2: Instruction.Operand = .none,
240 const ops = emit.mir.instructions.items(.ops)[inst].decode();177 op3: Instruction.Operand = .none,
241 switch (ops.flags) {178 op4: Instruction.Operand = .none,
242 0b00 => return lowerToZoEnc(.int3, emit.code),179}) InnerError!void {
243 else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}),180 const inst = try Instruction.new(mnemonic, .{
244 }181 .op1 = ops.op1,
182 .op2 = ops.op2,
183 .op3 = ops.op3,
184 .op4 = ops.op4,
185 });
186 return inst.encode(emit.code.writer());
245}187}
246188
247fn mirSyscall(emit: *Emit) InnerError!void {189fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
248 return lowerToZoEnc(.syscall, emit.code);190 const mnemonic = inline for (@typeInfo(Instruction.Mnemonic).Enum.fields) |field| {
249}191 if (mem.eql(u8, field.name, @tagName(tag))) break @field(Instruction.Mnemonic, field.name);
192 } else unreachable;
250193
251fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {194 const ops = emit.mir.instructions.items(.ops)[inst];
252 const ops = emit.mir.instructions.items(.ops)[inst].decode();195 const data = emit.mir.instructions.items(.data)[inst];
253 switch (ops.flags) {
254 0b00 => {
255 // PUSH/POP reg
256 return lowerToOEnc(tag, ops.reg1, emit.code);
257 },
258 0b01 => {
259 // PUSH/POP r/m64
260 const imm = emit.mir.instructions.items(.data)[inst].imm;
261 const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) {
262 16 => .word_ptr,
263 else => .qword_ptr,
264 };
265 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{
266 .disp = imm,
267 .base = ops.reg1,
268 }), emit.code);
269 },
270 0b10 => {
271 // PUSH imm32
272 assert(tag == .push);
273 const imm = emit.mir.instructions.items(.data)[inst].imm;
274 return lowerToIEnc(.push, imm, emit.code);
275 },
276 0b11 => unreachable,
277 }
278}
279196
280fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {197 var op1: Instruction.Operand = .none;
281 const ops = emit.mir.instructions.items(.ops)[inst].decode();198 var op2: Instruction.Operand = .none;
282 const payload = emit.mir.instructions.items(.data)[inst].payload;199 var op3: Instruction.Operand = .none;
283 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;200 var op4: Instruction.Operand = .none;
284 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);201
285 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);202 switch (ops) {
286 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);203 .none => {},
287 for (callee_preserved_regs) |reg| {204 .imm_s => op1 = .{ .imm = Immediate.s(@bitCast(i32, data.imm)) },
288 if (reg_list.isSet(callee_preserved_regs, reg)) {205 .imm_u => op1 = .{ .imm = Immediate.u(data.imm) },
289 switch (tag) {206 .r => op1 = .{ .reg = data.r },
290 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{207 .rr => {
291 .disp = @bitCast(u32, disp),208 op1 = .{ .reg = data.rr.r1 };
292 .base = ops.reg1,209 op2 = .{ .reg = data.rr.r2 };
293 }), reg, emit.code),210 },
294 .pop => try lowerToRmEnc(.mov, reg, RegisterOrMemory.mem(.qword_ptr, .{211 .ri_s, .ri_u => {
295 .disp = @bitCast(u32, disp),212 const imm = switch (ops) {
296 .base = ops.reg1,213 .ri_s => Immediate.s(@bitCast(i32, data.ri.imm)),
297 }), emit.code),214 .ri_u => Immediate.u(data.ri.imm),
215 else => unreachable,
216 };
217 op1 = .{ .reg = data.ri.r1 };
218 op2 = .{ .imm = imm };
219 },
220 .ri64 => {
221 const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data;
222 op1 = .{ .reg = data.rx.r1 };
223 op2 = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) };
224 },
225 .rri_s, .rri_u => {
226 const imm = switch (ops) {
227 .rri_s => Immediate.s(@bitCast(i32, data.rri.imm)),
228 .rri_u => Immediate.u(data.rri.imm),
229 else => unreachable,
230 };
231 op1 = .{ .reg = data.rri.r1 };
232 op2 = .{ .reg = data.rri.r2 };
233 op3 = .{ .imm = imm };
234 },
235 .m_sib => {
236 const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data;
237 op1 = .{ .mem = Mir.MemorySib.decode(msib) };
238 },
239 .m_rip => {
240 const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data;
241 op1 = .{ .mem = Mir.MemoryRip.decode(mrip) };
242 },
243 .mi_s_sib, .mi_u_sib => {
244 const msib = emit.mir.extraData(Mir.MemorySib, data.xi.payload).data;
245 const imm = switch (ops) {
246 .mi_s_sib => Immediate.s(@bitCast(i32, data.xi.imm)),
247 .mi_u_sib => Immediate.u(data.xi.imm),
248 else => unreachable,
249 };
250 op1 = .{ .mem = Mir.MemorySib.decode(msib) };
251 op2 = .{ .imm = imm };
252 },
253 .mi_u_rip, .mi_s_rip => {
254 const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi.payload).data;
255 const imm = switch (ops) {
256 .mi_s_rip => Immediate.s(@bitCast(i32, data.xi.imm)),
257 .mi_u_rip => Immediate.u(data.xi.imm),
258 else => unreachable,
259 };
260 op1 = .{ .mem = Mir.MemoryRip.decode(mrip) };
261 op2 = .{ .imm = imm };
262 },
263 .rm_sib, .mr_sib => {
264 const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data;
265 const op_r = .{ .reg = data.rx.r1 };
266 const op_m = .{ .mem = Mir.MemorySib.decode(msib) };
267 switch (ops) {
268 .rm_sib => {
269 op1 = op_r;
270 op2 = op_m;
271 },
272 .mr_sib => {
273 op1 = op_m;
274 op2 = op_r;
275 },
298 else => unreachable,276 else => unreachable,
299 }277 }
300 disp += 8;
301 }
302 }
303}
304
305fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
306 const ops = emit.mir.instructions.items(.ops)[inst].decode();
307 switch (ops.flags) {
308 0b00 => {
309 const target = emit.mir.instructions.items(.data)[inst].inst;
310 const source = emit.code.items.len;
311 try lowerToDEnc(tag, 0, emit.code);
312 try emit.relocs.append(emit.bin_file.allocator, .{
313 .source = source,
314 .target = target,
315 .offset = emit.code.items.len - 4,
316 .length = 5,
317 });
318 },278 },
319 0b01 => {279 .rm_rip, .mr_rip => {
320 if (ops.reg1 == .none) {280 const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data;
321 // JMP/CALL [imm]281 const op_r = .{ .reg = data.rx.r1 };
322 const imm = emit.mir.instructions.items(.data)[inst].imm;282 const op_m = .{ .mem = Mir.MemoryRip.decode(mrip) };
323 const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) {283 switch (ops) {
324 16 => .word_ptr,284 .rm_sib => {
325 else => .qword_ptr,285 op1 = op_r;
326 };286 op2 = op_m;
327 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code);287 },
288 .mr_sib => {
289 op1 = op_m;
290 op2 = op_r;
291 },
292 else => unreachable,
328 }293 }
329 // JMP/CALL reg
330 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
331 },294 },
332 0b10 => {295 else => return emit.fail("TODO handle generic encoding: {s}, {s}", .{
333 // JMP/CALL r/m64296 @tagName(mnemonic),
334 const imm = emit.mir.instructions.items(.data)[inst].imm;297 @tagName(ops),
335 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{298 }),
336 .disp = imm,
337 .base = ops.reg1,
338 }), emit.code);
339 },
340 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}),
341 }299 }
342}
343300
344fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {301 return emit.encode(mnemonic, .{
345 const mir_tag = emit.mir.instructions.items(.tag)[inst];302 .op1 = op1,
346 assert(mir_tag == .cond_jmp);303 .op2 = op2,
347 const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc;304 .op3 = op3,
348 const tag: Tag = switch (inst_cc.cc) {305 .op4 = op4,
349 .a => .ja,
350 .ae => .jae,
351 .b => .jb,
352 .be => .jbe,
353 .c => .jc,
354 .e => .je,
355 .g => .jg,
356 .ge => .jge,
357 .l => .jl,
358 .le => .jle,
359 .na => .jna,
360 .nae => .jnae,
361 .nb => .jnb,
362 .nbe => .jnbe,
363 .nc => .jnc,
364 .ne => .jne,
365 .ng => .jng,
366 .nge => .jnge,
367 .nl => .jnl,
368 .nle => .jnle,
369 .no => .jno,
370 .np => .jnp,
371 .ns => .jns,
372 .nz => .jnz,
373 .o => .jo,
374 .p => .jp,
375 .pe => .jpe,
376 .po => .jpo,
377 .s => .js,
378 .z => .jz,
379 };
380 const source = emit.code.items.len;
381 try lowerToDEnc(tag, 0, emit.code);
382 try emit.relocs.append(emit.bin_file.allocator, .{
383 .source = source,
384 .target = inst_cc.inst,
385 .offset = emit.code.items.len - 4,
386 .length = 6,
387 });306 });
388}307}
389308
390fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {309fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
391 const mir_tag = emit.mir.instructions.items(.tag)[inst];310 const ops = emit.mir.instructions.items(.ops)[inst];
392 assert(mir_tag == .cond_set_byte);311 const payload = emit.mir.instructions.items(.data)[inst].payload;
393 const ops = emit.mir.instructions.items(.ops)[inst].decode();312 const moffs = emit.mir.extraData(Mir.MemoryMoffs, payload).data;
394 const cc = emit.mir.instructions.items(.data)[inst].cc;313 const seg = @intToEnum(Register, moffs.seg);
395 const tag: Tag = switch (cc) {314 const offset = moffs.decodeOffset();
396 .a => .seta,315 switch (ops) {
397 .ae => .setae,316 .rax_moffs => {
398 .b => .setb,317 try emit.encode(.mov, .{
399 .be => .setbe,318 .op1 = .{ .reg = .rax },
400 .c => .setc,319 .op2 = .{ .mem = Memory.moffs(seg, offset) },
401 .e => .sete,320 });
402 .g => .setg,
403 .ge => .setge,
404 .l => .setl,
405 .le => .setle,
406 .na => .setna,
407 .nae => .setnae,
408 .nb => .setnb,
409 .nbe => .setnbe,
410 .nc => .setnc,
411 .ne => .setne,
412 .ng => .setng,
413 .nge => .setnge,
414 .nl => .setnl,
415 .nle => .setnle,
416 .no => .setno,
417 .np => .setnp,
418 .ns => .setns,
419 .nz => .setnz,
420 .o => .seto,
421 .p => .setp,
422 .pe => .setpe,
423 .po => .setpo,
424 .s => .sets,
425 .z => .setz,
426 };
427 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
428}
429
430fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
431 const mir_tag = emit.mir.instructions.items(.tag)[inst];
432 assert(mir_tag == .cond_mov);
433 const ops = emit.mir.instructions.items(.ops)[inst].decode();
434 const cc = emit.mir.instructions.items(.data)[inst].cc;
435 const tag: Tag = switch (cc) {
436 .a => .cmova,
437 .ae => .cmovae,
438 .b => .cmovb,
439 .be => .cmovbe,
440 .c => .cmovc,
441 .e => .cmove,
442 .g => .cmovg,
443 .ge => .cmovge,
444 .l => .cmovl,
445 .le => .cmovle,
446 .na => .cmovna,
447 .nae => .cmovnae,
448 .nb => .cmovnb,
449 .nbe => .cmovnbe,
450 .nc => .cmovnc,
451 .ne => .cmovne,
452 .ng => .cmovng,
453 .nge => .cmovnge,
454 .nl => .cmovnl,
455 .nle => .cmovnle,
456 .no => .cmovno,
457 .np => .cmovnp,
458 .ns => .cmovns,
459 .nz => .cmovnz,
460 .o => .cmovo,
461 .p => .cmovp,
462 .pe => .cmovpe,
463 .po => .cmovpo,
464 .s => .cmovs,
465 .z => .cmovz,
466 };
467
468 if (ops.flags == 0b00) {
469 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
470 }
471 const imm = emit.mir.instructions.items(.data)[inst].imm;
472 const ptr_size: Memory.PtrSize = switch (ops.flags) {
473 0b00 => unreachable,
474 0b01 => .word_ptr,
475 0b10 => .dword_ptr,
476 0b11 => .qword_ptr,
477 };
478 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ptr_size, .{
479 .disp = imm,
480 .base = ops.reg2,
481 }), emit.code);
482}
483
484fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
485 const tag = emit.mir.instructions.items(.tag)[inst];
486 assert(tag == .@"test");
487 const ops = emit.mir.instructions.items(.ops)[inst].decode();
488 switch (ops.flags) {
489 0b00 => {
490 if (ops.reg2 == .none) {
491 // TEST r/m64, imm32
492 // MI
493 const imm = emit.mir.instructions.items(.data)[inst].imm;
494 if (ops.reg1.to64() == .rax) {
495 // TEST rax, imm32
496 // I
497 return lowerToIEnc(.@"test", imm, emit.code);
498 }
499 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
500 }
501 // TEST r/m64, r64
502 return lowerToMrEnc(.@"test", RegisterOrMemory.reg(ops.reg1), ops.reg2, emit.code);
503 },
504 else => return emit.fail("TODO more TEST alternatives", .{}),
505 }
506}
507
508fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
509 const tag = emit.mir.instructions.items(.tag)[inst];
510 assert(tag == .ret);
511 const ops = emit.mir.instructions.items(.ops)[inst].decode();
512 switch (ops.flags) {
513 0b00 => {
514 // RETF imm16
515 // I
516 const imm = emit.mir.instructions.items(.data)[inst].imm;
517 return lowerToIEnc(.ret_far, imm, emit.code);
518 },
519 0b01 => {
520 return lowerToZoEnc(.ret_far, emit.code);
521 },
522 0b10 => {
523 // RET imm16
524 // I
525 const imm = emit.mir.instructions.items(.data)[inst].imm;
526 return lowerToIEnc(.ret_near, imm, emit.code);
527 },321 },
528 0b11 => {322 .moffs_rax => {
529 return lowerToZoEnc(.ret_near, emit.code);323 try emit.encode(.mov, .{
324 .op1 = .{ .mem = Memory.moffs(seg, offset) },
325 .op2 = .{ .reg = .rax },
326 });
530 },327 },
328 else => unreachable,
531 }329 }
532}330}
533331
534fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {332fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
535 const ops = emit.mir.instructions.items(.ops)[inst].decode();333 const ops = emit.mir.instructions.items(.ops)[inst];
536 switch (ops.flags) {334 const data = emit.mir.instructions.items(.data)[inst];
537 0b00 => {335
538 if (ops.reg2 == .none) {336 var op1: Instruction.Operand = .none;
539 // mov reg1, imm32337 var op2: Instruction.Operand = .none;
540 // MI338 switch (ops) {
541 const imm = emit.mir.instructions.items(.data)[inst].imm;339 .rr => {
542 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);340 op1 = .{ .reg = data.rr.r1 };
543 }341 op2 = .{ .reg = data.rr.r2 };
544 // mov reg1, reg2
545 // RM
546 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
547 },
548 0b01 => {
549 // mov reg1, [reg2 + imm32]
550 // RM
551 const imm = emit.mir.instructions.items(.data)[inst].imm;
552 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
553 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
554 .disp = imm,
555 .base = src_reg,
556 }), emit.code);
557 },342 },
558 0b10 => {343 .rm_sib => {
559 if (ops.reg2 == .none) {344 const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data;
560 return emit.fail("TODO unused variant: mov reg1, none, 0b10", .{});345 op1 = .{ .reg = data.rx.r1 };
561 }346 op2 = .{ .mem = Mir.MemorySib.decode(msib) };
562 // mov [reg1 + imm32], reg2
563 // MR
564 const imm = emit.mir.instructions.items(.data)[inst].imm;
565 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
566 .disp = imm,
567 .base = ops.reg1,
568 }), ops.reg2, emit.code);
569 },347 },
570 0b11 => {348 .rm_rip => {
571 return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{});349 const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data;
350 op1 = .{ .reg = data.rx.r1 };
351 op2 = .{ .mem = Mir.MemoryRip.decode(mrip) };
572 },352 },
353 else => unreachable, // TODO
573 }354 }
574}
575
576fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
577 const ops = emit.mir.instructions.items(.ops)[inst].decode();
578 assert(ops.reg2 == .none);
579 const payload = emit.mir.instructions.items(.data)[inst].payload;
580 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
581 const ptr_size: Memory.PtrSize = switch (ops.flags) {
582 0b00 => .byte_ptr,
583 0b01 => .word_ptr,
584 0b10 => .dword_ptr,
585 0b11 => .qword_ptr,
586 };
587 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
588 .disp = imm_pair.dest_off,
589 .base = ops.reg1,
590 }), imm_pair.operand, emit.code);
591}
592
593inline fn setRexWRegister(reg: Register) bool {
594 if (reg.size() > 64) return false;
595 if (reg.size() == 64) return true;
596 return switch (reg) {
597 .ah, .ch, .dh, .bh => true,
598 else => false,
599 };
600}
601
602inline fn immOpSize(u_imm: u32) u6 {
603 const imm = @bitCast(i32, u_imm);
604 if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) {
605 return 8;
606 }
607 if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) {
608 return 16;
609 }
610 return 32;
611}
612355
613fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {356 const mnemonic: Instruction.Mnemonic = switch (op1.bitSize()) {
614 const ops = emit.mir.instructions.items(.ops)[inst].decode();357 32, 64 => if (op2.bitSize() == 32) .movsxd else .movsx,
615 const scale = ops.flags;358 else => .movsx,
616 const payload = emit.mir.instructions.items(.data)[inst].payload;
617 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
618 // OP reg1, [reg2 + scale*index + imm32]
619 const scale_index = ScaleIndex{
620 .scale = scale,
621 .index = index_reg_disp.index,
622 };
623 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
624 .disp = index_reg_disp.disp,
625 .base = ops.reg2,
626 .scale_index = scale_index,
627 }), emit.code);
628}
629
630fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
631 const ops = emit.mir.instructions.items(.ops)[inst].decode();
632 const scale = ops.flags;
633 const payload = emit.mir.instructions.items(.data)[inst].payload;
634 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
635 const scale_index = ScaleIndex{
636 .scale = scale,
637 .index = index_reg_disp.index,
638 };359 };
639 assert(ops.reg2 != .none);
640 // OP [reg1 + scale*index + imm32], reg2
641 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
642 .disp = index_reg_disp.disp,
643 .base = ops.reg1,
644 .scale_index = scale_index,
645 }), ops.reg2, emit.code);
646}
647360
648fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {361 return emit.encode(mnemonic, .{
649 const ops = emit.mir.instructions.items(.ops)[inst].decode();362 .op1 = op1,
650 const scale = ops.flags;363 .op2 = op2,
651 const payload = emit.mir.instructions.items(.data)[inst].payload;364 });
652 const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode();
653 const scale_index = ScaleIndex{
654 .scale = scale,
655 .index = index_reg_disp_imm.index,
656 };
657 // OP qword ptr [reg1 + scale*index + imm32], imm32
658 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{
659 .disp = index_reg_disp_imm.disp,
660 .base = ops.reg1,
661 .scale_index = scale_index,
662 }), index_reg_disp_imm.imm, emit.code);
663}365}
664366
665fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {367fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic {
666 const ops = emit.mir.instructions.items(.ops)[inst].decode();368 inline for (@typeInfo(bits.Condition).Enum.fields) |field| {
667 assert(ops.reg2 == .none);369 if (mem.eql(u8, field.name, @tagName(cc)))
668 const payload = emit.mir.instructions.items(.data)[inst].payload;370 return @field(Instruction.Mnemonic, basename ++ field.name);
669 const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode();371 } else unreachable;
670 const ptr_size: Memory.PtrSize = switch (ops.flags) {
671 0b00 => .byte_ptr,
672 0b01 => .word_ptr,
673 0b10 => .dword_ptr,
674 0b11 => .qword_ptr,
675 };
676 const scale_index = ScaleIndex{
677 .scale = 0,
678 .index = index_reg_disp_imm.index,
679 };
680 // OP ptr [reg1 + index + imm32], imm32
681 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
682 .disp = index_reg_disp_imm.disp,
683 .base = ops.reg1,
684 .scale_index = scale_index,
685 }), index_reg_disp_imm.imm, emit.code);
686}372}
687373
688fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {374fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
689 const mir_tag = emit.mir.instructions.items(.tag)[inst];375 const ops = emit.mir.instructions.items(.ops)[inst];
690 assert(mir_tag == .mov_sign_extend);376 switch (ops) {
691 const ops = emit.mir.instructions.items(.ops)[inst].decode();377 .rr_c => {
692 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;378 const data = emit.mir.instructions.items(.data)[inst].rr_c;
693 switch (ops.flags) {379 const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
694 0b00 => {380 return emit.encode(mnemonic, .{
695 const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx;381 .op1 = .{ .reg = data.r1 },
696 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);382 .op2 = .{ .reg = data.r2 },
697 },383 });
698 0b01 => {
699 return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{
700 .disp = imm,
701 .base = ops.reg2,
702 }), emit.code);
703 },
704 0b10 => {
705 return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{
706 .disp = imm,
707 .base = ops.reg2,
708 }), emit.code);
709 },
710 0b11 => {
711 return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{
712 .disp = imm,
713 .base = ops.reg2,
714 }), emit.code);
715 },384 },
385 else => unreachable, // TODO
716 }386 }
717}387}
718388
719fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {389fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
720 const mir_tag = emit.mir.instructions.items(.tag)[inst];390 const ops = emit.mir.instructions.items(.ops)[inst];
721 assert(mir_tag == .mov_zero_extend);391 switch (ops) {
722 const ops = emit.mir.instructions.items(.ops)[inst].decode();392 .r_c => {
723 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;393 const data = emit.mir.instructions.items(.data)[inst].r_c;
724 switch (ops.flags) {394 const mnemonic = mnemonicFromConditionCode("set", data.cc);
725 0b00 => {395 return emit.encode(mnemonic, .{
726 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);396 .op1 = .{ .reg = data.r1 },
727 },397 });
728 0b01 => {
729 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{
730 .disp = imm,
731 .base = ops.reg2,
732 }), emit.code);
733 },
734 0b10 => {
735 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{
736 .disp = imm,
737 .base = ops.reg2,
738 }), emit.code);
739 },
740 0b11 => {
741 return emit.fail("TODO unused variant: movzx 0b11", .{});
742 },398 },
399 else => unreachable, // TODO
743 }400 }
744}401}
745402
746fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {403fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
747 const tag = emit.mir.instructions.items(.tag)[inst];404 const ops = emit.mir.instructions.items(.ops)[inst];
748 assert(tag == .movabs);405 switch (ops) {
749 const ops = emit.mir.instructions.items(.ops)[inst].decode();406 .inst_cc => {
750 switch (ops.flags) {407 const data = emit.mir.instructions.items(.data)[inst].inst_cc;
751 0b00 => {408 const mnemonic = mnemonicFromConditionCode("j", data.cc);
752 const imm: u64 = if (ops.reg1.size() == 64) blk: {409 const source = emit.code.items.len;
753 const payload = emit.mir.instructions.items(.data)[inst].payload;410 try emit.encode(mnemonic, .{
754 const imm = emit.mir.extraData(Mir.Imm64, payload).data;411 .op1 = .{ .imm = Immediate.s(0) },
755 break :blk imm.decode();412 });
756 } else emit.mir.instructions.items(.data)[inst].imm;413 try emit.relocs.append(emit.bin_file.allocator, .{
757 // movabs reg, imm64414 .source = source,
758 // OI415 .target = data.inst,
759 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code);416 .offset = emit.code.items.len - 4,
760 },417 .length = 6,
761 0b01 => {418 });
762 if (ops.reg1 == .none) {
763 const imm: u64 = if (ops.reg2.size() == 64) blk: {
764 const payload = emit.mir.instructions.items(.data)[inst].payload;
765 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
766 break :blk imm.decode();
767 } else emit.mir.instructions.items(.data)[inst].imm;
768 // movabs moffs64, rax
769 // TD
770 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code);
771 }
772 const imm: u64 = if (ops.reg1.size() == 64) blk: {
773 const payload = emit.mir.instructions.items(.data)[inst].payload;
774 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
775 break :blk imm.decode();
776 } else emit.mir.instructions.items(.data)[inst].imm;
777 // movabs rax, moffs64
778 // FD
779 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
780 },419 },
781 else => return emit.fail("TODO unused movabs variant", .{}),420 else => unreachable, // TODO
782 }421 }
783}422}
784423
785fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {424fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
786 const tag = emit.mir.instructions.items(.tag)[inst];425 const target = emit.mir.instructions.items(.data)[inst].inst;
787 assert(tag == .fisttp);426 const source = emit.code.items.len;
788 const ops = emit.mir.instructions.items(.ops)[inst].decode();427 try emit.encode(.jmp, .{
789428 .op1 = .{ .imm = Immediate.s(0) },
790 // the selecting between operand sizes for this particular `fisttp` instruction429 });
791 // is done via opcode instead of the usual prefixes.430 try emit.relocs.append(emit.bin_file.allocator, .{
792431 .source = source,
793 const opcode: Tag = switch (ops.flags) {432 .target = target,
794 0b00 => .fisttp16,433 .offset = emit.code.items.len - 4,
795 0b01 => .fisttp32,434 .length = 5,
796 0b10 => .fisttp64,435 });
797 else => unreachable,
798 };
799 const mem_or_reg = Memory{
800 .base = ops.reg1,
801 .disp = emit.mir.instructions.items(.data)[inst].imm,
802 .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used
803 };
804 return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code);
805}
806
807fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
808 const tag = emit.mir.instructions.items(.tag)[inst];
809 assert(tag == .fld);
810 const ops = emit.mir.instructions.items(.ops)[inst].decode();
811
812 // the selecting between operand sizes for this particular `fisttp` instruction
813 // is done via opcode instead of the usual prefixes.
814
815 const opcode: Tag = switch (ops.flags) {
816 0b01 => .fld32,
817 0b10 => .fld64,
818 else => unreachable,
819 };
820 const mem_or_reg = Memory{
821 .base = ops.reg1,
822 .disp = emit.mir.instructions.items(.data)[inst].imm,
823 .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used
824 };
825 return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code);
826}436}
827437
828fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {438fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
829 const ops = emit.mir.instructions.items(.ops)[inst].decode();439 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
830 switch (ops.flags) {
831 0b00 => {
832 // sal reg1, 1
833 // M1
834 return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
835 },
836 0b01 => {
837 // sal reg1, .cl
838 // MC
839 return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
840 },
841 0b10 => {
842 // sal reg1, imm8
843 // MI
844 const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm);
845 return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
846 },
847 0b11 => {
848 return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{});
849 },
850 }
851}
852440
853fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {441 const offset = blk: {
854 const ops = emit.mir.instructions.items(.ops)[inst].decode();442 try emit.encode(.call, .{
855 if (ops.reg1 != .none) {443 .op1 = .{ .imm = Immediate.s(0) },
856 assert(ops.reg2 == .none);444 });
857 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);445 break :blk @intCast(u32, emit.code.items.len) - 4;
858 }
859 assert(ops.reg2 != .none);
860 const imm = emit.mir.instructions.items(.data)[inst].imm;
861 const ptr_size: Memory.PtrSize = switch (ops.flags) {
862 0b00 => .byte_ptr,
863 0b01 => .word_ptr,
864 0b10 => .dword_ptr,
865 0b11 => .qword_ptr,
866 };446 };
867 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{
868 .disp = imm,
869 .base = ops.reg2,
870 }), emit.code);
871}
872447
873fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {448 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
874 const tag = emit.mir.instructions.items(.tag)[inst];449 // Add relocation to the decl.
875 assert(tag == .imul_complex);450 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
876 const ops = emit.mir.instructions.items(.ops)[inst].decode();451 const target = macho_file.getGlobalByIndex(relocation.sym_index);
877 switch (ops.flags) {452 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
878 0b00 => {453 .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
879 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);454 .target = target,
880 },455 .offset = offset,
881 0b01 => {456 .addend = 0,
882 const imm = emit.mir.instructions.items(.data)[inst].imm;457 .pcrel = true,
883 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;458 .length = 2,
884 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{459 });
885 .disp = imm,460 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
886 .base = src_reg,461 // Add relocation to the decl.
887 }), emit.code);462 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
888 },463 const target = coff_file.getGlobalByIndex(relocation.sym_index);
889 0b10 => {464 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
890 const imm = emit.mir.instructions.items(.data)[inst].imm;465 .type = .direct,
891 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);466 .target = target,
892 },467 .offset = offset,
893 0b11 => {468 .addend = 0,
894 const payload = emit.mir.instructions.items(.data)[inst].payload;469 .pcrel = true,
895 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;470 .length = 2,
896 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{471 });
897 .disp = imm_pair.dest_off,472 } else {
898 .base = ops.reg2,473 return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{});
899 }), imm_pair.operand, emit.code);
900 },
901 }474 }
902}475}
903476
904fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {477fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
905 const ops = emit.mir.instructions.items(.ops)[inst].decode();478 const payload = emit.mir.instructions.items(.data)[inst].payload;
906 const tag: Tag = switch (ops.flags) {479 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;
907 0b00 => .cbw,480 const base = @intToEnum(Register, save_reg_list.base_reg);
908 0b01 => .cwd,481 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
909 0b10 => .cdq,482 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
910 0b11 => .cqo,483 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
911 };484 for (callee_preserved_regs) |reg| {
912 return lowerToZoEnc(tag, emit.code);485 if (reg_list.isSet(callee_preserved_regs, reg)) {
913}486 const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{
914487 .base = base,
915fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {488 .disp = disp,
916 const tag = emit.mir.instructions.items(.tag)[inst];489 }) };
917 assert(tag == .lea);490 const op2: Instruction.Operand = .{ .reg = reg };
918 const ops = emit.mir.instructions.items(.ops)[inst].decode();491 switch (tag) {
919 switch (ops.flags) {492 .push => try emit.encode(.mov, .{
920 0b00 => {493 .op1 = op1,
921 // lea reg1, [reg2 + imm32]494 .op2 = op2,
922 // RM
923 const imm = emit.mir.instructions.items(.data)[inst].imm;
924 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
925 return lowerToRmEnc(
926 .lea,
927 ops.reg1,
928 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
929 .disp = imm,
930 .base = src_reg,
931 }),495 }),
932 emit.code,496 .pop => try emit.encode(.mov, .{
933 );497 .op1 = op2,
934 },498 .op2 = op1,
935 0b01 => {
936 // lea reg1, [rip + imm32]
937 // RM
938 const start_offset = emit.code.items.len;
939 try lowerToRmEnc(
940 .lea,
941 ops.reg1,
942 RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0),
943 emit.code,
944 );
945 const end_offset = emit.code.items.len;
946 // Backpatch the displacement
947 const payload = emit.mir.instructions.items(.data)[inst].payload;
948 const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode();
949 const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset));
950 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);
951 },
952 0b10 => {
953 // lea reg, [rbp + index + imm32]
954 const payload = emit.mir.instructions.items(.data)[inst].payload;
955 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
956 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
957 const scale_index = ScaleIndex{
958 .scale = 0,
959 .index = index_reg_disp.index,
960 };
961 return lowerToRmEnc(
962 .lea,
963 ops.reg1,
964 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
965 .disp = index_reg_disp.disp,
966 .base = src_reg,
967 .scale_index = scale_index,
968 }),499 }),
969 emit.code,500 else => unreachable,
970 );501 }
971 },502 disp += 8;
972 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}),503 }
973 }504 }
974}505}
975506
976fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {507fn mirLeaLinker(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
977 const tag = emit.mir.instructions.items(.tag)[inst];508 const ops = emit.mir.instructions.items(.ops)[inst];
978 assert(tag == .lea_pic);509 const payload = emit.mir.instructions.items(.data)[inst].payload;
979 const ops = emit.mir.instructions.items(.ops)[inst].decode();510 const metadata = emit.mir.extraData(Mir.LeaRegisterReloc, payload).data;
980 const relocation = emit.mir.instructions.items(.data)[inst].relocation;511 const reg = @intToEnum(Register, metadata.reg);
981
982 switch (ops.flags) {
983 0b00, 0b01, 0b10 => {},
984 else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}),
985 }
986512
987 // lea reg1, [rip + reloc]513 try emit.encode(.lea, .{
988 // RM514 .op1 = .{ .reg = reg },
989 try lowerToRmEnc(515 .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
990 .lea,516 });
991 ops.reg1,
992 RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0),
993 emit.code,
994 );
995517
996 const end_offset = emit.code.items.len;518 const end_offset = emit.code.items.len;
997519
998 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {520 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
999 const reloc_type = switch (ops.flags) {521 const reloc_type = switch (ops) {
1000 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),522 .got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),523 .direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1002 else => unreachable,524 else => unreachable,
1003 };525 };
1004 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;526 const atom_index = macho_file.getAtomIndexForSymbol(.{
527 .sym_index = metadata.atom_index,
528 .file = null,
529 }).?;
1005 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{530 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
1006 .type = reloc_type,531 .type = reloc_type,
1007 .target = .{ .sym_index = relocation.sym_index, .file = null },532 .target = .{ .sym_index = metadata.sym_index, .file = null },
1008 .offset = @intCast(u32, end_offset - 4),533 .offset = @intCast(u32, end_offset - 4),
1009 .addend = 0,534 .addend = 0,
1010 .pcrel = true,535 .pcrel = true,
1011 .length = 2,536 .length = 2,
1012 });537 });
1013 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {538 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1014 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;539 const atom_index = coff_file.getAtomIndexForSymbol(.{
540 .sym_index = metadata.atom_index,
541 .file = null,
542 }).?;
1015 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{543 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
1016 .type = switch (ops.flags) {544 .type = switch (ops) {
1017 0b00 => .got,545 .got_reloc => .got,
1018 0b01 => .direct,546 .direct_reloc => .direct,
1019 0b10 => .import,547 .import_reloc => .import,
1020 else => unreachable,548 else => unreachable,
1021 },549 },
1022 .target = switch (ops.flags) {550 .target = switch (ops) {
1023 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null },551 .got_reloc, .direct_reloc => .{ .sym_index = metadata.sym_index, .file = null },
1024 0b10 => coff_file.getGlobalByIndex(relocation.sym_index),552 .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index),
1025 else => unreachable,553 else => unreachable,
1026 },554 },
1027 .offset = @intCast(u32, end_offset - 4),555 .offset = @intCast(u32, end_offset - 4),
...@@ -1034,158 +562,23 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1034,158 +562,23 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1034 }562 }
1035}563}
1036564
1037// SSE instructions565fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1038566 const payload = emit.mir.instructions.items(.data)[inst].payload;
1039fn mirMovFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {567 const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data;
1040 const ops = emit.mir.instructions.items(.ops)[inst].decode();568 log.debug("mirDbgLine", .{});
1041 switch (ops.flags) {569 try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column);
1042 0b00 => {
1043 const imm = emit.mir.instructions.items(.data)[inst].imm;
1044 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
1045 .disp = imm,
1046 .base = ops.reg2,
1047 }), emit.code);
1048 },
1049 0b01 => {
1050 const imm = emit.mir.instructions.items(.data)[inst].imm;
1051 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
1052 .disp = imm,
1053 .base = ops.reg1,
1054 }), ops.reg2, emit.code);
1055 },
1056 0b10 => {
1057 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1058 },
1059 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1060 }
1061}570}
1062571
1063fn mirAddFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {572fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
1064 const ops = emit.mir.instructions.items(.ops)[inst].decode();573 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
1065 switch (ops.flags) {574 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
1066 0b00 => {575 log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc });
1067 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);576 switch (emit.debug_output) {
1068 },577 .dwarf => |dw| {
1069 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),578 try dw.advancePCAndLine(delta_line, delta_pc);
1070 }579 emit.prev_di_line = line;
1071}580 emit.prev_di_column = column;
1072581 emit.prev_di_pc = emit.code.items.len;
1073fn mirCmpFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1074 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1075 switch (ops.flags) {
1076 0b00 => {
1077 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1078 },
1079 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1080 }
1081}
1082// AVX instructions
1083
1084fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1085 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1086 switch (ops.flags) {
1087 0b00 => {
1088 const imm = emit.mir.instructions.items(.data)[inst].imm;
1089 return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
1090 .disp = imm,
1091 .base = ops.reg2,
1092 }), emit.code);
1093 },
1094 0b01 => {
1095 const imm = emit.mir.instructions.items(.data)[inst].imm;
1096 return lowerToMvEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
1097 .disp = imm,
1098 .base = ops.reg1,
1099 }), ops.reg2, emit.code);
1100 },
1101 0b10 => {
1102 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1103 },
1104 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1105 }
1106}
1107
1108fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1109 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1110 switch (ops.flags) {
1111 0b00 => {
1112 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1113 },
1114 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1115 }
1116}
1117
1118fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1119 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1120 switch (ops.flags) {
1121 0b00 => {
1122 return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1123 },
1124 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
1125 }
1126}
1127
1128// Pseudo-instructions
1129
1130fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1131 const tag = emit.mir.instructions.items(.tag)[inst];
1132 assert(tag == .call_extern);
1133 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
1134
1135 const offset = blk: {
1136 // callq
1137 try lowerToDEnc(.call_near, 0, emit.code);
1138 break :blk @intCast(u32, emit.code.items.len) - 4;
1139 };
1140
1141 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1142 // Add relocation to the decl.
1143 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1144 const target = macho_file.getGlobalByIndex(relocation.sym_index);
1145 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
1146 .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1147 .target = target,
1148 .offset = offset,
1149 .addend = 0,
1150 .pcrel = true,
1151 .length = 2,
1152 });
1153 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1154 // Add relocation to the decl.
1155 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1156 const target = coff_file.getGlobalByIndex(relocation.sym_index);
1157 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
1158 .type = .direct,
1159 .target = target,
1160 .offset = offset,
1161 .addend = 0,
1162 .pcrel = true,
1163 .length = 2,
1164 });
1165 } else {
1166 return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{});
1167 }
1168}
1169
1170fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1171 const tag = emit.mir.instructions.items(.tag)[inst];
1172 assert(tag == .dbg_line);
1173 const payload = emit.mir.instructions.items(.data)[inst].payload;
1174 const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data;
1175 log.debug("mirDbgLine", .{});
1176 try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column);
1177}
1178
1179fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
1180 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
1181 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
1182 log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc });
1183 switch (emit.debug_output) {
1184 .dwarf => |dw| {
1185 try dw.advancePCAndLine(delta_line, delta_pc);
1186 emit.prev_di_line = line;
1187 emit.prev_di_column = column;
1188 emit.prev_di_pc = emit.code.items.len;
1189 },582 },
1190 .plan9 => |dbg_out| {583 .plan9 => |dbg_out| {
1191 if (delta_pc <= 0) return; // only do this when the pc changes584 if (delta_pc <= 0) return; // only do this when the pc changes
...@@ -1227,8 +620,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {...@@ -1227,8 +620,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
1227}620}
1228621
1229fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {622fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1230 const tag = emit.mir.instructions.items(.tag)[inst];623 _ = inst;
1231 assert(tag == .dbg_prologue_end);
1232 switch (emit.debug_output) {624 switch (emit.debug_output) {
1233 .dwarf => |dw| {625 .dwarf => |dw| {
1234 try dw.setPrologueEnd();626 try dw.setPrologueEnd();
...@@ -1244,8 +636,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1244,8 +636,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1244}636}
1245637
1246fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {638fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1247 const tag = emit.mir.instructions.items(.tag)[inst];639 _ = inst;
1248 assert(tag == .dbg_epilogue_begin);
1249 switch (emit.debug_output) {640 switch (emit.debug_output) {
1250 .dwarf => |dw| {641 .dwarf => |dw| {
1251 try dw.setEpilogueBegin();642 try dw.setEpilogueBegin();
...@@ -1259,1839 +650,3 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1259,1839 +650,3 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1259 .none => {},650 .none => {},
1260 }651 }
1261}652}
1262
1263const Tag = enum {
1264 adc,
1265 add,
1266 sub,
1267 xor,
1268 @"and",
1269 @"or",
1270 sbb,
1271 cmp,
1272 mov,
1273 movsx,
1274 movsxd,
1275 movzx,
1276 lea,
1277 jmp_near,
1278 call_near,
1279 push,
1280 pop,
1281 @"test",
1282 int3,
1283 nop,
1284 imul,
1285 mul,
1286 idiv,
1287 div,
1288 syscall,
1289 ret_near,
1290 ret_far,
1291 fisttp16,
1292 fisttp32,
1293 fisttp64,
1294 fld32,
1295 fld64,
1296 jo,
1297 jno,
1298 jb,
1299 jbe,
1300 jc,
1301 jnae,
1302 jnc,
1303 jae,
1304 je,
1305 jz,
1306 jne,
1307 jnz,
1308 jna,
1309 jnb,
1310 jnbe,
1311 ja,
1312 js,
1313 jns,
1314 jpe,
1315 jp,
1316 jpo,
1317 jnp,
1318 jnge,
1319 jl,
1320 jge,
1321 jnl,
1322 jle,
1323 jng,
1324 jg,
1325 jnle,
1326 seto,
1327 setno,
1328 setb,
1329 setc,
1330 setnae,
1331 setnb,
1332 setnc,
1333 setae,
1334 sete,
1335 setz,
1336 setne,
1337 setnz,
1338 setbe,
1339 setna,
1340 seta,
1341 setnbe,
1342 sets,
1343 setns,
1344 setp,
1345 setpe,
1346 setnp,
1347 setpo,
1348 setl,
1349 setnge,
1350 setnl,
1351 setge,
1352 setle,
1353 setng,
1354 setnle,
1355 setg,
1356 cmovo,
1357 cmovno,
1358 cmovb,
1359 cmovc,
1360 cmovnae,
1361 cmovnb,
1362 cmovnc,
1363 cmovae,
1364 cmove,
1365 cmovz,
1366 cmovne,
1367 cmovnz,
1368 cmovbe,
1369 cmovna,
1370 cmova,
1371 cmovnbe,
1372 cmovs,
1373 cmovns,
1374 cmovp,
1375 cmovpe,
1376 cmovnp,
1377 cmovpo,
1378 cmovl,
1379 cmovnge,
1380 cmovnl,
1381 cmovge,
1382 cmovle,
1383 cmovng,
1384 cmovnle,
1385 cmovg,
1386 shl,
1387 sal,
1388 shr,
1389 sar,
1390 cbw,
1391 cwd,
1392 cdq,
1393 cqo,
1394 movsd,
1395 movss,
1396 addsd,
1397 addss,
1398 cmpsd,
1399 cmpss,
1400 ucomisd,
1401 ucomiss,
1402 vmovsd,
1403 vmovss,
1404 vaddsd,
1405 vaddss,
1406 vcmpsd,
1407 vcmpss,
1408 vucomisd,
1409 vucomiss,
1410
1411 fn isSse(tag: Tag) bool {
1412 return switch (tag) {
1413 .movsd,
1414 .movss,
1415 .addsd,
1416 .addss,
1417 .cmpsd,
1418 .cmpss,
1419 .ucomisd,
1420 .ucomiss,
1421 => true,
1422
1423 else => false,
1424 };
1425 }
1426
1427 fn isAvx(tag: Tag) bool {
1428 return switch (tag) {
1429 .vmovsd,
1430 .vmovss,
1431 .vaddsd,
1432 .vaddss,
1433 .vcmpsd,
1434 .vcmpss,
1435 .vucomisd,
1436 .vucomiss,
1437 => true,
1438
1439 else => false,
1440 };
1441 }
1442
1443 fn isSetCC(tag: Tag) bool {
1444 return switch (tag) {
1445 .seto,
1446 .setno,
1447 .setb,
1448 .setc,
1449 .setnae,
1450 .setnb,
1451 .setnc,
1452 .setae,
1453 .sete,
1454 .setz,
1455 .setne,
1456 .setnz,
1457 .setbe,
1458 .setna,
1459 .seta,
1460 .setnbe,
1461 .sets,
1462 .setns,
1463 .setp,
1464 .setpe,
1465 .setnp,
1466 .setpo,
1467 .setl,
1468 .setnge,
1469 .setnl,
1470 .setge,
1471 .setle,
1472 .setng,
1473 .setnle,
1474 .setg,
1475 => true,
1476 else => false,
1477 };
1478 }
1479};
1480
1481const Encoding = enum {
1482 /// OP
1483 zo,
1484
1485 /// OP rel32
1486 d,
1487
1488 /// OP r/m64
1489 m,
1490
1491 /// OP r64
1492 o,
1493
1494 /// OP imm32
1495 i,
1496
1497 /// OP r/m64, 1
1498 m1,
1499
1500 /// OP r/m64, .cl
1501 mc,
1502
1503 /// OP r/m64, imm32
1504 mi,
1505
1506 /// OP r/m64, imm8
1507 mi8,
1508
1509 /// OP r/m64, r64
1510 mr,
1511
1512 /// OP r64, r/m64
1513 rm,
1514
1515 /// OP r64, imm64
1516 oi,
1517
1518 /// OP al/ax/eax/rax, moffs
1519 fd,
1520
1521 /// OP moffs, al/ax/eax/rax
1522 td,
1523
1524 /// OP r64, r/m64, imm32
1525 rmi,
1526
1527 /// OP xmm1, xmm2/m64
1528 vm,
1529
1530 /// OP m64, xmm1
1531 mv,
1532
1533 /// OP xmm1, xmm2, xmm3/m64
1534 rvm,
1535
1536 /// OP xmm1, xmm2, xmm3/m64, imm8
1537 rvmi,
1538};
1539
1540const OpCode = struct {
1541 bytes: [3]u8,
1542 count: usize,
1543
1544 fn init(comptime in_bytes: []const u8) OpCode {
1545 comptime assert(in_bytes.len <= 3);
1546 comptime var bytes: [3]u8 = undefined;
1547 inline for (in_bytes, 0..) |x, i| {
1548 bytes[i] = x;
1549 }
1550 return .{ .bytes = bytes, .count = in_bytes.len };
1551 }
1552
1553 fn encode(opc: OpCode, encoder: Encoder) void {
1554 switch (opc.count) {
1555 1 => encoder.opcode_1byte(opc.bytes[0]),
1556 2 => encoder.opcode_2byte(opc.bytes[0], opc.bytes[1]),
1557 3 => encoder.opcode_3byte(opc.bytes[0], opc.bytes[1], opc.bytes[2]),
1558 else => unreachable,
1559 }
1560 }
1561
1562 fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void {
1563 assert(opc.count == 1);
1564 encoder.opcode_withReg(opc.bytes[0], reg.lowEnc());
1565 }
1566};
1567
1568inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
1569 // zig fmt: off
1570 switch (enc) {
1571 .zo => return switch (tag) {
1572 .ret_near => OpCode.init(&.{0xc3}),
1573 .ret_far => OpCode.init(&.{0xcb}),
1574 .int3 => OpCode.init(&.{0xcc}),
1575 .nop => OpCode.init(&.{0x90}),
1576 .syscall => OpCode.init(&.{ 0x0f, 0x05 }),
1577 .cbw => OpCode.init(&.{0x98}),
1578 .cwd,
1579 .cdq,
1580 .cqo => OpCode.init(&.{0x99}),
1581 else => unreachable,
1582 },
1583 .d => return switch (tag) {
1584 .jmp_near => OpCode.init(&.{0xe9}),
1585 .call_near => OpCode.init(&.{0xe8}),
1586
1587 .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}),
1588
1589 .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}),
1590
1591 .jb,
1592 .jc,
1593 .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}),
1594
1595 .jnb,
1596 .jnc,
1597 .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}),
1598
1599 .je,
1600 .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}),
1601
1602 .jne,
1603 .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}),
1604
1605 .jna,
1606 .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}),
1607
1608 .jnbe,
1609 .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}),
1610
1611 .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}),
1612
1613 .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}),
1614
1615 .jpe,
1616 .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}),
1617
1618 .jpo,
1619 .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}),
1620
1621 .jnge,
1622 .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}),
1623
1624 .jge,
1625 .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}),
1626
1627 .jle,
1628 .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}),
1629
1630 .jg,
1631 .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}),
1632
1633 else => unreachable,
1634 },
1635 .m => return switch (tag) {
1636 .jmp_near,
1637 .call_near,
1638 .push => OpCode.init(&.{0xff}),
1639
1640 .pop => OpCode.init(&.{0x8f}),
1641 .seto => OpCode.init(&.{0x0f,0x90}),
1642 .setno => OpCode.init(&.{0x0f,0x91}),
1643
1644 .setb,
1645 .setc,
1646 .setnae => OpCode.init(&.{0x0f,0x92}),
1647
1648 .setnb,
1649 .setnc,
1650 .setae => OpCode.init(&.{0x0f,0x93}),
1651
1652 .sete,
1653 .setz => OpCode.init(&.{0x0f,0x94}),
1654
1655 .setne,
1656 .setnz => OpCode.init(&.{0x0f,0x95}),
1657
1658 .setbe,
1659 .setna => OpCode.init(&.{0x0f,0x96}),
1660
1661 .seta,
1662 .setnbe => OpCode.init(&.{0x0f,0x97}),
1663
1664 .sets => OpCode.init(&.{0x0f,0x98}),
1665 .setns => OpCode.init(&.{0x0f,0x99}),
1666
1667 .setp,
1668 .setpe => OpCode.init(&.{0x0f,0x9a}),
1669
1670 .setnp,
1671 .setpo => OpCode.init(&.{0x0f,0x9b}),
1672
1673 .setl,
1674 .setnge => OpCode.init(&.{0x0f,0x9c}),
1675
1676 .setnl,
1677 .setge => OpCode.init(&.{0x0f,0x9d}),
1678
1679 .setle,
1680 .setng => OpCode.init(&.{0x0f,0x9e}),
1681
1682 .setnle,
1683 .setg => OpCode.init(&.{0x0f,0x9f}),
1684
1685 .idiv,
1686 .div,
1687 .imul,
1688 .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}),
1689
1690 .fisttp16 => OpCode.init(&.{0xdf}),
1691 .fisttp32 => OpCode.init(&.{0xdb}),
1692 .fisttp64 => OpCode.init(&.{0xdd}),
1693 .fld32 => OpCode.init(&.{0xd9}),
1694 .fld64 => OpCode.init(&.{0xdd}),
1695 else => unreachable,
1696 },
1697 .o => return switch (tag) {
1698 .push => OpCode.init(&.{0x50}),
1699 .pop => OpCode.init(&.{0x58}),
1700 else => unreachable,
1701 },
1702 .i => return switch (tag) {
1703 .push => if (is_one_byte) OpCode.init(&.{0x6a}) else OpCode.init(&.{0x68}),
1704 .@"test" => if (is_one_byte) OpCode.init(&.{0xa8}) else OpCode.init(&.{0xa9}),
1705 .ret_near => OpCode.init(&.{0xc2}),
1706 .ret_far => OpCode.init(&.{0xca}),
1707 else => unreachable,
1708 },
1709 .m1 => return switch (tag) {
1710 .shl, .sal,
1711 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd0}) else OpCode.init(&.{0xd1}),
1712 else => unreachable,
1713 },
1714 .mc => return switch (tag) {
1715 .shl, .sal,
1716 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd2}) else OpCode.init(&.{0xd3}),
1717 else => unreachable,
1718 },
1719 .mi => return switch (tag) {
1720 .adc, .add,
1721 .sub, .xor,
1722 .@"and", .@"or",
1723 .sbb, .cmp => if (is_one_byte) OpCode.init(&.{0x80}) else OpCode.init(&.{0x81}),
1724 .mov => if (is_one_byte) OpCode.init(&.{0xc6}) else OpCode.init(&.{0xc7}),
1725 .@"test" => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}),
1726 else => unreachable,
1727 },
1728 .mi8 => return switch (tag) {
1729 .adc, .add,
1730 .sub, .xor,
1731 .@"and", .@"or",
1732 .sbb, .cmp => OpCode.init(&.{0x83}),
1733 .shl, .sal,
1734 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xc0}) else OpCode.init(&.{0xc1}),
1735 else => unreachable,
1736 },
1737 .mr => return switch (tag) {
1738 .adc => if (is_one_byte) OpCode.init(&.{0x10}) else OpCode.init(&.{0x11}),
1739 .add => if (is_one_byte) OpCode.init(&.{0x00}) else OpCode.init(&.{0x01}),
1740 .sub => if (is_one_byte) OpCode.init(&.{0x28}) else OpCode.init(&.{0x29}),
1741 .xor => if (is_one_byte) OpCode.init(&.{0x30}) else OpCode.init(&.{0x31}),
1742 .@"and" => if (is_one_byte) OpCode.init(&.{0x20}) else OpCode.init(&.{0x21}),
1743 .@"or" => if (is_one_byte) OpCode.init(&.{0x08}) else OpCode.init(&.{0x09}),
1744 .sbb => if (is_one_byte) OpCode.init(&.{0x18}) else OpCode.init(&.{0x19}),
1745 .cmp => if (is_one_byte) OpCode.init(&.{0x38}) else OpCode.init(&.{0x39}),
1746 .mov => if (is_one_byte) OpCode.init(&.{0x88}) else OpCode.init(&.{0x89}),
1747 .@"test" => if (is_one_byte) OpCode.init(&.{0x84}) else OpCode.init(&.{0x85}),
1748 .movsd => OpCode.init(&.{0xf2,0x0f,0x11}),
1749 .movss => OpCode.init(&.{0xf3,0x0f,0x11}),
1750 else => unreachable,
1751 },
1752 .rm => return switch (tag) {
1753 .adc => if (is_one_byte) OpCode.init(&.{0x12}) else OpCode.init(&.{0x13}),
1754 .add => if (is_one_byte) OpCode.init(&.{0x02}) else OpCode.init(&.{0x03}),
1755 .sub => if (is_one_byte) OpCode.init(&.{0x2a}) else OpCode.init(&.{0x2b}),
1756 .xor => if (is_one_byte) OpCode.init(&.{0x32}) else OpCode.init(&.{0x33}),
1757 .@"and" => if (is_one_byte) OpCode.init(&.{0x22}) else OpCode.init(&.{0x23}),
1758 .@"or" => if (is_one_byte) OpCode.init(&.{0x0a}) else OpCode.init(&.{0x0b}),
1759 .sbb => if (is_one_byte) OpCode.init(&.{0x1a}) else OpCode.init(&.{0x1b}),
1760 .cmp => if (is_one_byte) OpCode.init(&.{0x3a}) else OpCode.init(&.{0x3b}),
1761 .mov => if (is_one_byte) OpCode.init(&.{0x8a}) else OpCode.init(&.{0x8b}),
1762 .movsx => if (is_one_byte) OpCode.init(&.{0x0f,0xbe}) else OpCode.init(&.{0x0f,0xbf}),
1763 .movsxd => OpCode.init(&.{0x63}),
1764 .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}),
1765 .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}),
1766 .imul => OpCode.init(&.{0x0f,0xaf}),
1767
1768 .cmova,
1769 .cmovnbe, => OpCode.init(&.{0x0f,0x47}),
1770
1771 .cmovae,
1772 .cmovnb, => OpCode.init(&.{0x0f,0x43}),
1773
1774 .cmovb,
1775 .cmovc,
1776 .cmovnae => OpCode.init(&.{0x0f,0x42}),
1777
1778 .cmovbe,
1779 .cmovna, => OpCode.init(&.{0x0f,0x46}),
1780
1781 .cmove,
1782 .cmovz, => OpCode.init(&.{0x0f,0x44}),
1783
1784 .cmovg,
1785 .cmovnle, => OpCode.init(&.{0x0f,0x4f}),
1786
1787 .cmovge,
1788 .cmovnl, => OpCode.init(&.{0x0f,0x4d}),
1789
1790 .cmovl,
1791 .cmovnge, => OpCode.init(&.{0x0f,0x4c}),
1792
1793 .cmovle,
1794 .cmovng, => OpCode.init(&.{0x0f,0x4e}),
1795
1796 .cmovne,
1797 .cmovnz, => OpCode.init(&.{0x0f,0x45}),
1798
1799 .cmovno => OpCode.init(&.{0x0f,0x41}),
1800
1801 .cmovnp,
1802 .cmovpo, => OpCode.init(&.{0x0f,0x4b}),
1803
1804 .cmovns => OpCode.init(&.{0x0f,0x49}),
1805
1806 .cmovo => OpCode.init(&.{0x0f,0x40}),
1807
1808 .cmovp,
1809 .cmovpe, => OpCode.init(&.{0x0f,0x4a}),
1810
1811 .cmovs => OpCode.init(&.{0x0f,0x48}),
1812
1813 .movsd => OpCode.init(&.{0xf2,0x0f,0x10}),
1814 .movss => OpCode.init(&.{0xf3,0x0f,0x10}),
1815 .addsd => OpCode.init(&.{0xf2,0x0f,0x58}),
1816 .addss => OpCode.init(&.{0xf3,0x0f,0x58}),
1817 .ucomisd => OpCode.init(&.{0x66,0x0f,0x2e}),
1818 .ucomiss => OpCode.init(&.{0x0f,0x2e}),
1819 else => unreachable,
1820 },
1821 .oi => return switch (tag) {
1822 .mov => if (is_one_byte) OpCode.init(&.{0xb0}) else OpCode.init(&.{0xb8}),
1823 else => unreachable,
1824 },
1825 .fd => return switch (tag) {
1826 .mov => if (is_one_byte) OpCode.init(&.{0xa0}) else OpCode.init(&.{0xa1}),
1827 else => unreachable,
1828 },
1829 .td => return switch (tag) {
1830 .mov => if (is_one_byte) OpCode.init(&.{0xa2}) else OpCode.init(&.{0xa3}),
1831 else => unreachable,
1832 },
1833 .rmi => return switch (tag) {
1834 .imul => if (is_one_byte) OpCode.init(&.{0x6b}) else OpCode.init(&.{0x69}),
1835 else => unreachable,
1836 },
1837 .mv => return switch (tag) {
1838 .vmovsd,
1839 .vmovss => OpCode.init(&.{0x11}),
1840 else => unreachable,
1841 },
1842 .vm => return switch (tag) {
1843 .vmovsd,
1844 .vmovss => OpCode.init(&.{0x10}),
1845 .vucomisd,
1846 .vucomiss => OpCode.init(&.{0x2e}),
1847 else => unreachable,
1848 },
1849 .rvm => return switch (tag) {
1850 .vaddsd,
1851 .vaddss => OpCode.init(&.{0x58}),
1852 .vmovsd,
1853 .vmovss => OpCode.init(&.{0x10}),
1854 else => unreachable,
1855 },
1856 .rvmi => return switch (tag) {
1857 .vcmpsd,
1858 .vcmpss => OpCode.init(&.{0xc2}),
1859 else => unreachable,
1860 },
1861 }
1862 // zig fmt: on
1863}
1864
1865inline fn getModRmExt(tag: Tag) u3 {
1866 return switch (tag) {
1867 .adc => 0x2,
1868 .add => 0x0,
1869 .sub => 0x5,
1870 .xor => 0x6,
1871 .@"and" => 0x4,
1872 .@"or" => 0x1,
1873 .sbb => 0x3,
1874 .cmp => 0x7,
1875 .mov => 0x0,
1876 .jmp_near => 0x4,
1877 .call_near => 0x2,
1878 .push => 0x6,
1879 .pop => 0x0,
1880 .@"test" => 0x0,
1881 .seto,
1882 .setno,
1883 .setb,
1884 .setc,
1885 .setnae,
1886 .setnb,
1887 .setnc,
1888 .setae,
1889 .sete,
1890 .setz,
1891 .setne,
1892 .setnz,
1893 .setbe,
1894 .setna,
1895 .seta,
1896 .setnbe,
1897 .sets,
1898 .setns,
1899 .setp,
1900 .setpe,
1901 .setnp,
1902 .setpo,
1903 .setl,
1904 .setnge,
1905 .setnl,
1906 .setge,
1907 .setle,
1908 .setng,
1909 .setnle,
1910 .setg,
1911 => 0x0,
1912 .shl,
1913 .sal,
1914 => 0x4,
1915 .shr => 0x5,
1916 .sar => 0x7,
1917 .mul => 0x4,
1918 .imul => 0x5,
1919 .div => 0x6,
1920 .idiv => 0x7,
1921 .fisttp16 => 0x1,
1922 .fisttp32 => 0x1,
1923 .fisttp64 => 0x1,
1924 .fld32 => 0x0,
1925 .fld64 => 0x0,
1926 else => unreachable,
1927 };
1928}
1929
1930const VexEncoding = struct {
1931 prefix: Encoder.Vex,
1932 reg: ?enum {
1933 ndd,
1934 nds,
1935 dds,
1936 },
1937};
1938
1939inline fn getVexEncoding(tag: Tag, enc: Encoding) VexEncoding {
1940 const desc: struct {
1941 reg: enum {
1942 none,
1943 ndd,
1944 nds,
1945 dds,
1946 } = .none,
1947 len_256: bool = false,
1948 wig: bool = false,
1949 lig: bool = false,
1950 lz: bool = false,
1951 lead_opc: enum {
1952 l_0f,
1953 l_0f_3a,
1954 l_0f_38,
1955 } = .l_0f,
1956 simd_prefix: enum {
1957 none,
1958 p_66,
1959 p_f2,
1960 p_f3,
1961 } = .none,
1962 } = blk: {
1963 switch (enc) {
1964 .mv => switch (tag) {
1965 .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
1966 .vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true },
1967 else => unreachable,
1968 },
1969 .vm => switch (tag) {
1970 .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
1971 .vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true },
1972 .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true },
1973 .vucomiss => break :blk .{ .lig = true, .wig = true },
1974 else => unreachable,
1975 },
1976 .rvm => switch (tag) {
1977 .vaddsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true },
1978 .vaddss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true },
1979 .vmovsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true },
1980 .vmovss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true },
1981 else => unreachable,
1982 },
1983 .rvmi => switch (tag) {
1984 .vcmpsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true },
1985 .vcmpss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true },
1986 else => unreachable,
1987 },
1988 else => unreachable,
1989 }
1990 };
1991
1992 var vex: Encoder.Vex = .{};
1993
1994 if (desc.len_256) vex.len_256();
1995 if (desc.wig) vex.wig();
1996 if (desc.lig) vex.lig();
1997 if (desc.lz) vex.lz();
1998
1999 switch (desc.lead_opc) {
2000 .l_0f => {},
2001 .l_0f_3a => vex.lead_opc_0f_3a(),
2002 .l_0f_38 => vex.lead_opc_0f_38(),
2003 }
2004
2005 switch (desc.simd_prefix) {
2006 .none => {},
2007 .p_66 => vex.simd_prefix_66(),
2008 .p_f2 => vex.simd_prefix_f2(),
2009 .p_f3 => vex.simd_prefix_f3(),
2010 }
2011
2012 return VexEncoding{ .prefix = vex, .reg = switch (desc.reg) {
2013 .none => null,
2014 .nds => .nds,
2015 .dds => .dds,
2016 .ndd => .ndd,
2017 } };
2018}
2019
2020const ScaleIndex = packed struct {
2021 scale: u2,
2022 index: Register,
2023};
2024
2025const Memory = struct {
2026 base: ?Register,
2027 rip: bool = false,
2028 disp: u32,
2029 ptr_size: PtrSize,
2030 scale_index: ?ScaleIndex = null,
2031
2032 const PtrSize = enum(u2) {
2033 byte_ptr = 0b00,
2034 word_ptr = 0b01,
2035 dword_ptr = 0b10,
2036 qword_ptr = 0b11,
2037
2038 fn new(bit_size: u64) PtrSize {
2039 return @intToEnum(PtrSize, math.log2_int(u4, @intCast(u4, @divExact(bit_size, 8))));
2040 }
2041
2042 /// Returns size in bits.
2043 fn size(ptr_size: PtrSize) u64 {
2044 return 8 * (math.powi(u8, 2, @enumToInt(ptr_size)) catch unreachable);
2045 }
2046 };
2047
2048 fn encode(mem_op: Memory, encoder: Encoder, operand: u3) void {
2049 if (mem_op.base) |base| {
2050 const dst = base.lowEnc();
2051 const src = operand;
2052 if (dst == 4 or mem_op.scale_index != null) {
2053 if (mem_op.disp == 0 and dst != 5) {
2054 encoder.modRm_SIBDisp0(src);
2055 if (mem_op.scale_index) |si| {
2056 encoder.sib_scaleIndexBase(si.scale, si.index.lowEnc(), dst);
2057 } else {
2058 encoder.sib_base(dst);
2059 }
2060 } else if (immOpSize(mem_op.disp) == 8) {
2061 encoder.modRm_SIBDisp8(src);
2062 if (mem_op.scale_index) |si| {
2063 encoder.sib_scaleIndexBaseDisp8(si.scale, si.index.lowEnc(), dst);
2064 } else {
2065 encoder.sib_baseDisp8(dst);
2066 }
2067 encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp)));
2068 } else {
2069 encoder.modRm_SIBDisp32(src);
2070 if (mem_op.scale_index) |si| {
2071 encoder.sib_scaleIndexBaseDisp32(si.scale, si.index.lowEnc(), dst);
2072 } else {
2073 encoder.sib_baseDisp32(dst);
2074 }
2075 encoder.disp32(@bitCast(i32, mem_op.disp));
2076 }
2077 } else {
2078 if (mem_op.disp == 0 and dst != 5) {
2079 encoder.modRm_indirectDisp0(src, dst);
2080 } else if (immOpSize(mem_op.disp) == 8) {
2081 encoder.modRm_indirectDisp8(src, dst);
2082 encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp)));
2083 } else {
2084 encoder.modRm_indirectDisp32(src, dst);
2085 encoder.disp32(@bitCast(i32, mem_op.disp));
2086 }
2087 }
2088 } else {
2089 if (mem_op.rip) {
2090 encoder.modRm_RIPDisp32(operand);
2091 } else {
2092 encoder.modRm_SIBDisp0(operand);
2093 if (mem_op.scale_index) |si| {
2094 encoder.sib_scaleIndexDisp32(si.scale, si.index.lowEnc());
2095 } else {
2096 encoder.sib_disp32();
2097 }
2098 }
2099 encoder.disp32(@bitCast(i32, mem_op.disp));
2100 }
2101 }
2102
2103 /// Returns size in bits.
2104 fn size(memory: Memory) u64 {
2105 return memory.ptr_size.size();
2106 }
2107};
2108
2109fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {
2110 switch (size) {
2111 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))),
2112 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))),
2113 32, 64 => encoder.imm32(@bitCast(i32, imm)),
2114 else => unreachable,
2115 }
2116}
2117
2118const RegisterOrMemory = union(enum) {
2119 register: Register,
2120 memory: Memory,
2121
2122 fn reg(register: Register) RegisterOrMemory {
2123 return .{ .register = register };
2124 }
2125
2126 fn mem(ptr_size: Memory.PtrSize, args: struct {
2127 disp: u32,
2128 base: ?Register = null,
2129 scale_index: ?ScaleIndex = null,
2130 }) RegisterOrMemory {
2131 return .{
2132 .memory = .{
2133 .base = args.base,
2134 .disp = args.disp,
2135 .ptr_size = ptr_size,
2136 .scale_index = args.scale_index,
2137 },
2138 };
2139 }
2140
2141 fn rip(ptr_size: Memory.PtrSize, disp: u32) RegisterOrMemory {
2142 return .{
2143 .memory = .{
2144 .base = null,
2145 .rip = true,
2146 .disp = disp,
2147 .ptr_size = ptr_size,
2148 },
2149 };
2150 }
2151
2152 /// Returns size in bits.
2153 fn size(reg_or_mem: RegisterOrMemory) u64 {
2154 return switch (reg_or_mem) {
2155 .register => |register| register.size(),
2156 .memory => |memory| memory.size(),
2157 };
2158 }
2159};
2160
2161fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
2162 assert(!tag.isAvx());
2163 const opc = getOpCode(tag, .zo, false);
2164 const encoder = try Encoder.init(code, 2);
2165 switch (tag) {
2166 .cqo => {
2167 encoder.rex(.{
2168 .w = true,
2169 });
2170 },
2171 else => {},
2172 }
2173 opc.encode(encoder);
2174}
2175
2176fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
2177 assert(!tag.isAvx());
2178 if (tag == .ret_far or tag == .ret_near) {
2179 const encoder = try Encoder.init(code, 3);
2180 const opc = getOpCode(tag, .i, false);
2181 opc.encode(encoder);
2182 encoder.imm16(@bitCast(i16, @truncate(u16, imm)));
2183 return;
2184 }
2185 const opc = getOpCode(tag, .i, immOpSize(imm) == 8);
2186 const encoder = try Encoder.init(code, 5);
2187 if (immOpSize(imm) == 16) {
2188 encoder.prefix16BitMode();
2189 }
2190 opc.encode(encoder);
2191 encodeImm(encoder, imm, immOpSize(imm));
2192}
2193
2194fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
2195 assert(!tag.isAvx());
2196 const opc = getOpCode(tag, .o, false);
2197 const encoder = try Encoder.init(code, 3);
2198 if (reg.size() == 16) {
2199 encoder.prefix16BitMode();
2200 }
2201 encoder.rex(.{
2202 .w = false,
2203 .b = reg.isExtended(),
2204 });
2205 opc.encodeWithReg(encoder, reg);
2206}
2207
2208fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
2209 assert(!tag.isAvx());
2210 const opc = getOpCode(tag, .d, false);
2211 const encoder = try Encoder.init(code, 6);
2212 opc.encode(encoder);
2213 encoder.imm32(@bitCast(i32, imm));
2214}
2215
2216fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void {
2217 assert(!tag.isAvx());
2218 const opc = getOpCode(tag, enc, reg_or_mem.size() == 8);
2219 const modrm_ext = getModRmExt(tag);
2220 switch (reg_or_mem) {
2221 .register => |reg| {
2222 const encoder = try Encoder.init(code, 4);
2223 if (reg.size() == 16) {
2224 encoder.prefix16BitMode();
2225 }
2226 const wide = if (tag == .jmp_near) false else setRexWRegister(reg);
2227 encoder.rex(.{
2228 .w = wide,
2229 .b = reg.isExtended(),
2230 });
2231 opc.encode(encoder);
2232 encoder.modRm_direct(modrm_ext, reg.lowEnc());
2233 },
2234 .memory => |mem_op| {
2235 const encoder = try Encoder.init(code, 8);
2236 if (mem_op.ptr_size == .word_ptr) {
2237 encoder.prefix16BitMode();
2238 }
2239 if (mem_op.base) |base| {
2240 const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr;
2241 encoder.rex(.{
2242 .w = wide,
2243 .b = base.isExtended(),
2244 .x = if (mem_op.scale_index) |si| si.index.isExtended() else false,
2245 });
2246 }
2247 opc.encode(encoder);
2248 mem_op.encode(encoder, modrm_ext);
2249 },
2250 }
2251}
2252
2253fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
2254 return lowerToMxEnc(tag, reg_or_mem, .m, code);
2255}
2256
2257fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
2258 return lowerToMxEnc(tag, reg_or_mem, .m1, code);
2259}
2260
2261fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
2262 return lowerToMxEnc(tag, reg_or_mem, .mc, code);
2263}
2264
2265fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
2266 return lowerToTdFdEnc(tag, reg, moffs, code, true);
2267}
2268
2269fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) InnerError!void {
2270 return lowerToTdFdEnc(tag, reg, moffs, code, false);
2271}
2272
2273fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void {
2274 assert(!tag.isAvx());
2275 const opc = if (td) getOpCode(tag, .td, reg.size() == 8) else getOpCode(tag, .fd, reg.size() == 8);
2276 const encoder = try Encoder.init(code, 10);
2277 if (reg.size() == 16) {
2278 encoder.prefix16BitMode();
2279 }
2280 encoder.rex(.{
2281 .w = setRexWRegister(reg),
2282 });
2283 opc.encode(encoder);
2284 switch (reg.size()) {
2285 8 => encoder.imm8(@bitCast(i8, @truncate(u8, moffs))),
2286 16 => encoder.imm16(@bitCast(i16, @truncate(u16, moffs))),
2287 32 => encoder.imm32(@bitCast(i32, @truncate(u32, moffs))),
2288 64 => encoder.imm64(moffs),
2289 else => unreachable,
2290 }
2291}
2292
2293fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void {
2294 assert(!tag.isAvx());
2295 const opc = getOpCode(tag, .oi, reg.size() == 8);
2296 const encoder = try Encoder.init(code, 10);
2297 if (reg.size() == 16) {
2298 encoder.prefix16BitMode();
2299 }
2300 encoder.rex(.{
2301 .w = setRexWRegister(reg),
2302 .b = reg.isExtended(),
2303 });
2304 opc.encodeWithReg(encoder, reg);
2305 switch (reg.size()) {
2306 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))),
2307 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))),
2308 32 => encoder.imm32(@bitCast(i32, @truncate(u32, imm))),
2309 64 => encoder.imm64(imm),
2310 else => unreachable,
2311 }
2312}
2313
2314fn lowerToMiXEnc(
2315 tag: Tag,
2316 reg_or_mem: RegisterOrMemory,
2317 imm: u32,
2318 enc: Encoding,
2319 code: *std.ArrayList(u8),
2320) InnerError!void {
2321 assert(!tag.isAvx());
2322 const modrm_ext = getModRmExt(tag);
2323 const opc = getOpCode(tag, enc, reg_or_mem.size() == 8);
2324 switch (reg_or_mem) {
2325 .register => |dst_reg| {
2326 const encoder = try Encoder.init(code, 7);
2327 if (dst_reg.size() == 16) {
2328 // 0x66 prefix switches to the non-default size; here we assume a switch from
2329 // the default 32bits to 16bits operand-size.
2330 // More info: https://www.cs.uni-potsdam.de/desn/lehre/ss15/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf#page=32&zoom=auto,-159,773
2331 encoder.prefix16BitMode();
2332 }
2333 encoder.rex(.{
2334 .w = setRexWRegister(dst_reg),
2335 .b = dst_reg.isExtended(),
2336 });
2337 opc.encode(encoder);
2338 encoder.modRm_direct(modrm_ext, dst_reg.lowEnc());
2339 encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size());
2340 },
2341 .memory => |dst_mem| {
2342 const encoder = try Encoder.init(code, 12);
2343 if (dst_mem.ptr_size == .word_ptr) {
2344 encoder.prefix16BitMode();
2345 }
2346 if (dst_mem.base) |base| {
2347 encoder.rex(.{
2348 .w = dst_mem.ptr_size == .qword_ptr,
2349 .b = base.isExtended(),
2350 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2351 });
2352 } else {
2353 encoder.rex(.{
2354 .w = dst_mem.ptr_size == .qword_ptr,
2355 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2356 });
2357 }
2358 opc.encode(encoder);
2359 dst_mem.encode(encoder, modrm_ext);
2360 encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size());
2361 },
2362 }
2363}
2364
2365fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void {
2366 return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code);
2367}
2368
2369fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
2370 return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code);
2371}
2372
2373fn lowerToRmEnc(
2374 tag: Tag,
2375 reg: Register,
2376 reg_or_mem: RegisterOrMemory,
2377 code: *std.ArrayList(u8),
2378) InnerError!void {
2379 assert(!tag.isAvx());
2380 const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8);
2381 switch (reg_or_mem) {
2382 .register => |src_reg| {
2383 const encoder = try Encoder.init(code, 5);
2384 if (reg.size() == 16) {
2385 encoder.prefix16BitMode();
2386 }
2387 encoder.rex(.{
2388 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
2389 .r = reg.isExtended(),
2390 .b = src_reg.isExtended(),
2391 });
2392 opc.encode(encoder);
2393 encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
2394 },
2395 .memory => |src_mem| {
2396 const encoder = try Encoder.init(code, 9);
2397 if (reg.size() == 16) {
2398 encoder.prefix16BitMode();
2399 }
2400 if (src_mem.base) |base| {
2401 // TODO handle 32-bit base register - requires prefix 0x67
2402 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
2403 encoder.rex(.{
2404 .w = setRexWRegister(reg),
2405 .r = reg.isExtended(),
2406 .b = base.isExtended(),
2407 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2408 });
2409 } else {
2410 encoder.rex(.{
2411 .w = setRexWRegister(reg),
2412 .r = reg.isExtended(),
2413 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2414 });
2415 }
2416 opc.encode(encoder);
2417 src_mem.encode(encoder, reg.lowEnc());
2418 },
2419 }
2420}
2421
2422fn lowerToMrEnc(
2423 tag: Tag,
2424 reg_or_mem: RegisterOrMemory,
2425 reg: Register,
2426 code: *std.ArrayList(u8),
2427) InnerError!void {
2428 assert(!tag.isAvx());
2429 const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8);
2430 switch (reg_or_mem) {
2431 .register => |dst_reg| {
2432 const encoder = try Encoder.init(code, 4);
2433 if (dst_reg.size() == 16) {
2434 encoder.prefix16BitMode();
2435 }
2436 encoder.rex(.{
2437 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),
2438 .r = reg.isExtended(),
2439 .b = dst_reg.isExtended(),
2440 });
2441 opc.encode(encoder);
2442 encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc());
2443 },
2444 .memory => |dst_mem| {
2445 const encoder = try Encoder.init(code, 9);
2446 if (reg.size() == 16) {
2447 encoder.prefix16BitMode();
2448 }
2449 if (dst_mem.base) |base| {
2450 encoder.rex(.{
2451 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2452 .r = reg.isExtended(),
2453 .b = base.isExtended(),
2454 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2455 });
2456 } else {
2457 encoder.rex(.{
2458 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2459 .r = reg.isExtended(),
2460 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2461 });
2462 }
2463 opc.encode(encoder);
2464 dst_mem.encode(encoder, reg.lowEnc());
2465 },
2466 }
2467}
2468
2469fn lowerToRmiEnc(
2470 tag: Tag,
2471 reg: Register,
2472 reg_or_mem: RegisterOrMemory,
2473 imm: u32,
2474 code: *std.ArrayList(u8),
2475) InnerError!void {
2476 assert(!tag.isAvx());
2477 const opc = getOpCode(tag, .rmi, false);
2478 const encoder = try Encoder.init(code, 13);
2479 if (reg.size() == 16) {
2480 encoder.prefix16BitMode();
2481 }
2482 switch (reg_or_mem) {
2483 .register => |src_reg| {
2484 encoder.rex(.{
2485 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
2486 .r = reg.isExtended(),
2487 .b = src_reg.isExtended(),
2488 });
2489 opc.encode(encoder);
2490 encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
2491 },
2492 .memory => |src_mem| {
2493 if (src_mem.base) |base| {
2494 // TODO handle 32-bit base register - requires prefix 0x67
2495 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
2496 encoder.rex(.{
2497 .w = setRexWRegister(reg),
2498 .r = reg.isExtended(),
2499 .b = base.isExtended(),
2500 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2501 });
2502 } else {
2503 encoder.rex(.{
2504 .w = setRexWRegister(reg),
2505 .r = reg.isExtended(),
2506 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2507 });
2508 }
2509 opc.encode(encoder);
2510 src_mem.encode(encoder, reg.lowEnc());
2511 },
2512 }
2513 encodeImm(encoder, imm, reg.size());
2514}
2515
2516/// Also referred to as XM encoding in Intel manual.
2517fn lowerToVmEnc(
2518 tag: Tag,
2519 reg: Register,
2520 reg_or_mem: RegisterOrMemory,
2521 code: *std.ArrayList(u8),
2522) InnerError!void {
2523 const opc = getOpCode(tag, .vm, false);
2524 var enc = getVexEncoding(tag, .vm);
2525 const vex = &enc.prefix;
2526 switch (reg_or_mem) {
2527 .register => |src_reg| {
2528 const encoder = try Encoder.init(code, 5);
2529 vex.rex(.{
2530 .r = reg.isExtended(),
2531 .b = src_reg.isExtended(),
2532 });
2533 encoder.vex(enc.prefix);
2534 opc.encode(encoder);
2535 encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
2536 },
2537 .memory => |src_mem| {
2538 const encoder = try Encoder.init(code, 10);
2539 if (src_mem.base) |base| {
2540 vex.rex(.{
2541 .r = reg.isExtended(),
2542 .b = base.isExtended(),
2543 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2544 });
2545 } else {
2546 vex.rex(.{
2547 .r = reg.isExtended(),
2548 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2549 });
2550 }
2551 encoder.vex(enc.prefix);
2552 opc.encode(encoder);
2553 src_mem.encode(encoder, reg.lowEnc());
2554 },
2555 }
2556}
2557
2558/// Usually referred to as MR encoding with V/V in Intel manual.
2559fn lowerToMvEnc(
2560 tag: Tag,
2561 reg_or_mem: RegisterOrMemory,
2562 reg: Register,
2563 code: *std.ArrayList(u8),
2564) InnerError!void {
2565 const opc = getOpCode(tag, .mv, false);
2566 var enc = getVexEncoding(tag, .mv);
2567 const vex = &enc.prefix;
2568 switch (reg_or_mem) {
2569 .register => |dst_reg| {
2570 const encoder = try Encoder.init(code, 4);
2571 vex.rex(.{
2572 .r = reg.isExtended(),
2573 .b = dst_reg.isExtended(),
2574 });
2575 encoder.vex(enc.prefix);
2576 opc.encode(encoder);
2577 encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc());
2578 },
2579 .memory => |dst_mem| {
2580 const encoder = try Encoder.init(code, 10);
2581 if (dst_mem.base) |base| {
2582 vex.rex(.{
2583 .r = reg.isExtended(),
2584 .b = base.isExtended(),
2585 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2586 });
2587 } else {
2588 vex.rex(.{
2589 .r = reg.isExtended(),
2590 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2591 });
2592 }
2593 encoder.vex(enc.prefix);
2594 opc.encode(encoder);
2595 dst_mem.encode(encoder, reg.lowEnc());
2596 },
2597 }
2598}
2599
2600fn lowerToRvmEnc(
2601 tag: Tag,
2602 reg1: Register,
2603 reg2: Register,
2604 reg_or_mem: RegisterOrMemory,
2605 code: *std.ArrayList(u8),
2606) InnerError!void {
2607 const opc = getOpCode(tag, .rvm, false);
2608 var enc = getVexEncoding(tag, .rvm);
2609 const vex = &enc.prefix;
2610 switch (reg_or_mem) {
2611 .register => |reg3| {
2612 if (enc.reg) |vvvv| {
2613 switch (vvvv) {
2614 .nds => vex.reg(reg2.enc()),
2615 else => unreachable, // TODO
2616 }
2617 }
2618 const encoder = try Encoder.init(code, 5);
2619 vex.rex(.{
2620 .r = reg1.isExtended(),
2621 .b = reg3.isExtended(),
2622 });
2623 encoder.vex(enc.prefix);
2624 opc.encode(encoder);
2625 encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc());
2626 },
2627 .memory => |dst_mem| {
2628 _ = dst_mem;
2629 unreachable; // TODO
2630 },
2631 }
2632}
2633
2634fn lowerToRvmiEnc(
2635 tag: Tag,
2636 reg1: Register,
2637 reg2: Register,
2638 reg_or_mem: RegisterOrMemory,
2639 imm: u32,
2640 code: *std.ArrayList(u8),
2641) InnerError!void {
2642 const opc = getOpCode(tag, .rvmi, false);
2643 var enc = getVexEncoding(tag, .rvmi);
2644 const vex = &enc.prefix;
2645 const encoder: Encoder = blk: {
2646 switch (reg_or_mem) {
2647 .register => |reg3| {
2648 if (enc.reg) |vvvv| {
2649 switch (vvvv) {
2650 .nds => vex.reg(reg2.enc()),
2651 else => unreachable, // TODO
2652 }
2653 }
2654 const encoder = try Encoder.init(code, 5);
2655 vex.rex(.{
2656 .r = reg1.isExtended(),
2657 .b = reg3.isExtended(),
2658 });
2659 encoder.vex(enc.prefix);
2660 opc.encode(encoder);
2661 encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc());
2662 break :blk encoder;
2663 },
2664 .memory => |dst_mem| {
2665 _ = dst_mem;
2666 unreachable; // TODO
2667 },
2668 }
2669 };
2670 encodeImm(encoder, imm, 8); // TODO
2671}
2672
2673fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void {
2674 assert(expected.len > 0);
2675 if (mem.eql(u8, expected, given)) return;
2676 const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)});
2677 defer testing.allocator.free(expected_fmt);
2678 const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)});
2679 defer testing.allocator.free(given_fmt);
2680 const idx = mem.indexOfDiff(u8, expected_fmt, given_fmt).?;
2681 var padding = try testing.allocator.alloc(u8, idx + 5);
2682 defer testing.allocator.free(padding);
2683 mem.set(u8, padding, ' ');
2684 std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{
2685 assembly,
2686 expected_fmt,
2687 given_fmt,
2688 padding,
2689 });
2690 return error.TestFailed;
2691}
2692
2693const TestEmit = struct {
2694 code_buffer: std.ArrayList(u8),
2695 next: usize = 0,
2696
2697 fn init() TestEmit {
2698 return .{
2699 .code_buffer = std.ArrayList(u8).init(testing.allocator),
2700 };
2701 }
2702
2703 fn deinit(emit: *TestEmit) void {
2704 emit.code_buffer.deinit();
2705 emit.next = undefined;
2706 }
2707
2708 fn code(emit: *TestEmit) *std.ArrayList(u8) {
2709 emit.next = emit.code_buffer.items.len;
2710 return &emit.code_buffer;
2711 }
2712
2713 fn lowered(emit: TestEmit) []const u8 {
2714 return emit.code_buffer.items[emit.next..];
2715 }
2716};
2717
2718test "lower MI encoding" {
2719 var emit = TestEmit.init();
2720 defer emit.deinit();
2721 try lowerToMiEnc(.mov, RegisterOrMemory.reg(.rax), 0x10, emit.code());
2722 try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", emit.lowered(), "mov rax, 0x10");
2723 try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, emit.code());
2724 try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", emit.lowered(), "mov dword ptr [r11 + 0], 0x10");
2725 try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{
2726 .disp = @bitCast(u32, @as(i32, -8)),
2727 .base = .rdx,
2728 }), 0x10, emit.code());
2729 try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", emit.lowered(), "add dword ptr [rdx - 8], 0x10");
2730 try lowerToMiEnc(.sub, RegisterOrMemory.mem(.dword_ptr, .{
2731 .disp = 0x10000000,
2732 .base = .r11,
2733 }), 0x10, emit.code());
2734 try expectEqualHexStrings(
2735 "\x41\x81\xab\x00\x00\x00\x10\x10\x00\x00\x00",
2736 emit.lowered(),
2737 "sub dword ptr [r11 + 0x10000000], 0x10",
2738 );
2739 try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), 0x10, emit.code());
2740 try expectEqualHexStrings(
2741 "\x81\x24\x25\x00\x00\x00\x10\x10\x00\x00\x00",
2742 emit.lowered(),
2743 "and dword ptr [ds:0x10000000], 0x10",
2744 );
2745 try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{
2746 .disp = 0x10000000,
2747 .base = .r12,
2748 }), 0x10, emit.code());
2749 try expectEqualHexStrings(
2750 "\x41\x81\xA4\x24\x00\x00\x00\x10\x10\x00\x00\x00",
2751 emit.lowered(),
2752 "and dword ptr [r12 + 0x10000000], 0x10",
2753 );
2754 try lowerToMiEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), 0x10, emit.code());
2755 try expectEqualHexStrings(
2756 "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
2757 emit.lowered(),
2758 "mov qword ptr [rip + 0x10], 0x10",
2759 );
2760 try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
2761 .disp = @bitCast(u32, @as(i32, -8)),
2762 .base = .rbp,
2763 }), 0x10, emit.code());
2764 try expectEqualHexStrings(
2765 "\x48\xc7\x45\xf8\x10\x00\x00\x00",
2766 emit.lowered(),
2767 "mov qword ptr [rbp - 8], 0x10",
2768 );
2769 try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{
2770 .disp = @bitCast(u32, @as(i32, -2)),
2771 .base = .rbp,
2772 }), 0x10, emit.code());
2773 try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", emit.lowered(), "mov word ptr [rbp - 2], 0x10");
2774 try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{
2775 .disp = @bitCast(u32, @as(i32, -1)),
2776 .base = .rbp,
2777 }), 0x10, emit.code());
2778 try expectEqualHexStrings("\xC6\x45\xFF\x10", emit.lowered(), "mov byte ptr [rbp - 1], 0x10");
2779 try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
2780 .disp = 0x10000000,
2781 .scale_index = .{
2782 .scale = 1,
2783 .index = .rcx,
2784 },
2785 }), 0x10, emit.code());
2786 try expectEqualHexStrings(
2787 "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
2788 emit.lowered(),
2789 "mov qword ptr [rcx*2 + 0x10000000], 0x10",
2790 );
2791
2792 try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code());
2793 try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10");
2794}
2795
2796test "lower RM encoding" {
2797 var emit = TestEmit.init();
2798 defer emit.deinit();
2799 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), emit.code());
2800 try expectEqualHexStrings("\x48\x8b\xc3", emit.lowered(), "mov rax, rbx");
2801 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code());
2802 try expectEqualHexStrings("\x49\x8b\x03", emit.lowered(), "mov rax, qword ptr [r11 + 0]");
2803 try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code());
2804 try expectEqualHexStrings(
2805 "\x4C\x03\x1C\x25\x00\x00\x00\x10",
2806 emit.lowered(),
2807 "add r11, qword ptr [ds:0x10000000]",
2808 );
2809 try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code());
2810 try expectEqualHexStrings(
2811 "\x44\x02\x24\x25\x00\x00\x00\x10",
2812 emit.lowered(),
2813 "add r11b, byte ptr [ds:0x10000000]",
2814 );
2815 try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{
2816 .disp = 0x10000000,
2817 .base = .r13,
2818 }), emit.code());
2819 try expectEqualHexStrings(
2820 "\x4D\x2B\x9D\x00\x00\x00\x10",
2821 emit.lowered(),
2822 "sub r11, qword ptr [r13 + 0x10000000]",
2823 );
2824 try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{
2825 .disp = 0x10000000,
2826 .base = .r12,
2827 }), emit.code());
2828 try expectEqualHexStrings(
2829 "\x4D\x2B\x9C\x24\x00\x00\x00\x10",
2830 emit.lowered(),
2831 "sub r11, qword ptr [r12 + 0x10000000]",
2832 );
2833 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
2834 .disp = @bitCast(u32, @as(i32, -4)),
2835 .base = .rbp,
2836 }), emit.code());
2837 try expectEqualHexStrings("\x48\x8B\x45\xFC", emit.lowered(), "mov rax, qword ptr [rbp - 4]");
2838 try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
2839 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", emit.lowered(), "lea rax, [rip + 0x10]");
2840 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
2841 .disp = @bitCast(u32, @as(i32, -8)),
2842 .base = .rbp,
2843 .scale_index = .{
2844 .scale = 0,
2845 .index = .rcx,
2846 },
2847 }), emit.code());
2848 try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]");
2849 try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{
2850 .disp = @bitCast(u32, @as(i32, -4)),
2851 .base = .rbp,
2852 .scale_index = .{
2853 .scale = 2,
2854 .index = .rdx,
2855 },
2856 }), emit.code());
2857 try expectEqualHexStrings("\x8B\x44\x95\xFC", emit.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
2858 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
2859 .disp = @bitCast(u32, @as(i32, -8)),
2860 .base = .rbp,
2861 .scale_index = .{
2862 .scale = 3,
2863 .index = .rcx,
2864 },
2865 }), emit.code());
2866 try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]");
2867 try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{
2868 .disp = @bitCast(u32, @as(i32, -24)),
2869 .base = .rsi,
2870 .scale_index = .{
2871 .scale = 0,
2872 .index = .rcx,
2873 },
2874 }), emit.code());
2875 try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", emit.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]");
2876 try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{
2877 .disp = 0,
2878 .base = .rbp,
2879 .scale_index = .{
2880 .scale = 0,
2881 .index = .rcx,
2882 },
2883 }), emit.code());
2884 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]");
2885}
2886
2887test "lower MR encoding" {
2888 var emit = TestEmit.init();
2889 defer emit.deinit();
2890 try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, emit.code());
2891 try expectEqualHexStrings("\x48\x89\xd8", emit.lowered(), "mov rax, rbx");
2892 try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
2893 .disp = @bitCast(u32, @as(i32, -4)),
2894 .base = .rbp,
2895 }), .r11, emit.code());
2896 try expectEqualHexStrings("\x4c\x89\x5d\xfc", emit.lowered(), "mov qword ptr [rbp - 4], r11");
2897 try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, emit.code());
2898 try expectEqualHexStrings(
2899 "\x44\x00\x24\x25\x00\x00\x00\x10",
2900 emit.lowered(),
2901 "add byte ptr [ds:0x10000000], r12b",
2902 );
2903 try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, emit.code());
2904 try expectEqualHexStrings(
2905 "\x44\x01\x24\x25\x00\x00\x00\x10",
2906 emit.lowered(),
2907 "add dword ptr [ds:0x10000000], r12d",
2908 );
2909 try lowerToMrEnc(.sub, RegisterOrMemory.mem(.qword_ptr, .{
2910 .disp = 0x10000000,
2911 .base = .r11,
2912 }), .r12, emit.code());
2913 try expectEqualHexStrings(
2914 "\x4D\x29\xA3\x00\x00\x00\x10",
2915 emit.lowered(),
2916 "sub qword ptr [r11 + 0x10000000], r12",
2917 );
2918 try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, emit.code());
2919 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12");
2920}
2921
2922test "lower OI encoding" {
2923 var emit = TestEmit.init();
2924 defer emit.deinit();
2925 try lowerToOiEnc(.mov, .rax, 0x1000000000000000, emit.code());
2926 try expectEqualHexStrings(
2927 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
2928 emit.lowered(),
2929 "movabs rax, 0x1000000000000000",
2930 );
2931 try lowerToOiEnc(.mov, .r11, 0x1000000000000000, emit.code());
2932 try expectEqualHexStrings(
2933 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
2934 emit.lowered(),
2935 "movabs r11, 0x1000000000000000",
2936 );
2937 try lowerToOiEnc(.mov, .r11d, 0x10000000, emit.code());
2938 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", emit.lowered(), "mov r11d, 0x10000000");
2939 try lowerToOiEnc(.mov, .r11w, 0x1000, emit.code());
2940 try expectEqualHexStrings("\x66\x41\xBB\x00\x10", emit.lowered(), "mov r11w, 0x1000");
2941 try lowerToOiEnc(.mov, .r11b, 0x10, emit.code());
2942 try expectEqualHexStrings("\x41\xB3\x10", emit.lowered(), "mov r11b, 0x10");
2943}
2944
2945test "lower FD/TD encoding" {
2946 var emit = TestEmit.init();
2947 defer emit.deinit();
2948 try lowerToFdEnc(.mov, .rax, 0x1000000000000000, emit.code());
2949 try expectEqualHexStrings(
2950 "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10",
2951 emit.lowered(),
2952 "mov rax, ds:0x1000000000000000",
2953 );
2954 try lowerToFdEnc(.mov, .eax, 0x10000000, emit.code());
2955 try expectEqualHexStrings("\xa1\x00\x00\x00\x10", emit.lowered(), "mov eax, ds:0x10000000");
2956 try lowerToFdEnc(.mov, .ax, 0x1000, emit.code());
2957 try expectEqualHexStrings("\x66\xa1\x00\x10", emit.lowered(), "mov ax, ds:0x1000");
2958 try lowerToFdEnc(.mov, .al, 0x10, emit.code());
2959 try expectEqualHexStrings("\xa0\x10", emit.lowered(), "mov al, ds:0x10");
2960}
2961
2962test "lower M encoding" {
2963 var emit = TestEmit.init();
2964 defer emit.deinit();
2965 try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12), emit.code());
2966 try expectEqualHexStrings("\x41\xFF\xE4", emit.lowered(), "jmp r12");
2967 try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12w), emit.code());
2968 try expectEqualHexStrings("\x66\x41\xFF\xE4", emit.lowered(), "jmp r12w");
2969 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r12 }), emit.code());
2970 try expectEqualHexStrings("\x41\xFF\x24\x24", emit.lowered(), "jmp qword ptr [r12]");
2971 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.word_ptr, .{ .disp = 0, .base = .r12 }), emit.code());
2972 try expectEqualHexStrings("\x66\x41\xFF\x24\x24", emit.lowered(), "jmp word ptr [r12]");
2973 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10, .base = .r12 }), emit.code());
2974 try expectEqualHexStrings("\x41\xFF\x64\x24\x10", emit.lowered(), "jmp qword ptr [r12 + 0x10]");
2975 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{
2976 .disp = 0x1000,
2977 .base = .r12,
2978 }), emit.code());
2979 try expectEqualHexStrings(
2980 "\x41\xFF\xA4\x24\x00\x10\x00\x00",
2981 emit.lowered(),
2982 "jmp qword ptr [r12 + 0x1000]",
2983 );
2984 try lowerToMEnc(.jmp_near, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
2985 try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [rip + 0x10]");
2986 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10 }), emit.code());
2987 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [ds:0x10]");
2988 try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), emit.code());
2989 try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b");
2990 try lowerToMEnc(.idiv, RegisterOrMemory.reg(.rax), emit.code());
2991 try expectEqualHexStrings("\x48\xF7\xF8", emit.lowered(), "idiv rax");
2992 try lowerToMEnc(.imul, RegisterOrMemory.reg(.al), emit.code());
2993 try expectEqualHexStrings("\xF6\xE8", emit.lowered(), "imul al");
2994}
2995
2996test "lower M1 and MC encodings" {
2997 var emit = TestEmit.init();
2998 defer emit.deinit();
2999 try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code());
3000 try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1");
3001 try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code());
3002 try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1");
3003 try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code());
3004 try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1");
3005 try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code());
3006 try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1");
3007 try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code());
3008 try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1");
3009 try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code());
3010 try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1");
3011 try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{
3012 .disp = @bitCast(u32, @as(i32, -0x10)),
3013 .base = .rbp,
3014 }), emit.code());
3015 try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1");
3016 try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{
3017 .disp = @bitCast(u32, @as(i32, -0x10)),
3018 .base = .rbp,
3019 }), emit.code());
3020 try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1");
3021
3022 try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code());
3023 try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl");
3024 try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code());
3025 try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl");
3026
3027 try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code());
3028 try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl");
3029}
3030
3031test "lower O encoding" {
3032 var emit = TestEmit.init();
3033 defer emit.deinit();
3034 try lowerToOEnc(.pop, .r12, emit.code());
3035 try expectEqualHexStrings("\x41\x5c", emit.lowered(), "pop r12");
3036 try lowerToOEnc(.push, .r12w, emit.code());
3037 try expectEqualHexStrings("\x66\x41\x54", emit.lowered(), "push r12w");
3038}
3039
3040test "lower RMI encoding" {
3041 var emit = TestEmit.init();
3042 defer emit.deinit();
3043 try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{
3044 .disp = @bitCast(u32, @as(i32, -8)),
3045 .base = .rbp,
3046 }), 0x10, emit.code());
3047 try expectEqualHexStrings(
3048 "\x48\x69\x45\xF8\x10\x00\x00\x00",
3049 emit.lowered(),
3050 "imul rax, qword ptr [rbp - 8], 0x10",
3051 );
3052 try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{
3053 .disp = @bitCast(u32, @as(i32, -4)),
3054 .base = .rbp,
3055 }), 0x10, emit.code());
3056 try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", emit.lowered(), "imul eax, dword ptr [rbp - 4], 0x10");
3057 try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{
3058 .disp = @bitCast(u32, @as(i32, -2)),
3059 .base = .rbp,
3060 }), 0x10, emit.code());
3061 try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", emit.lowered(), "imul ax, word ptr [rbp - 2], 0x10");
3062 try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, emit.code());
3063 try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", emit.lowered(), "imul r12, r12, 0x10");
3064 try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, emit.code());
3065 try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10");
3066}
3067
3068test "lower MV encoding" {
3069 var emit = TestEmit.init();
3070 defer emit.deinit();
3071 try lowerToMvEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), .xmm1, emit.code());
3072 try expectEqualHexStrings(
3073 "\xC5\xFB\x11\x0D\x10\x00\x00\x00",
3074 emit.lowered(),
3075 "vmovsd qword ptr [rip + 0x10], xmm1",
3076 );
3077}
3078
3079test "lower VM encoding" {
3080 var emit = TestEmit.init();
3081 defer emit.deinit();
3082 try lowerToVmEnc(.vmovsd, .xmm1, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
3083 try expectEqualHexStrings(
3084 "\xC5\xFB\x10\x0D\x10\x00\x00\x00",
3085 emit.lowered(),
3086 "vmovsd xmm1, qword ptr [rip + 0x10]",
3087 );
3088}
3089
3090test "lower to RVM encoding" {
3091 var emit = TestEmit.init();
3092 defer emit.deinit();
3093 try lowerToRvmEnc(.vaddsd, .xmm0, .xmm1, RegisterOrMemory.reg(.xmm2), emit.code());
3094 try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2");
3095 try lowerToRvmEnc(.vaddsd, .xmm0, .xmm0, RegisterOrMemory.reg(.xmm1), emit.code());
3096 try expectEqualHexStrings("\xC5\xFB\x58\xC1", emit.lowered(), "vaddsd xmm0, xmm0, xmm1");
3097}
src/arch/x86_64/Encoding.zig created+587
...@@ -0,0 +1,587 @@
1const Encoding = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const math = std.math;
6
7const bits = @import("bits.zig");
8const encoder = @import("encoder.zig");
9const Instruction = encoder.Instruction;
10const Register = bits.Register;
11const Rex = encoder.Rex;
12const LegacyPrefixes = encoder.LegacyPrefixes;
13
14const table = @import("encodings.zig").table;
15
16mnemonic: Mnemonic,
17op_en: OpEn,
18op1: Op,
19op2: Op,
20op3: Op,
21op4: Op,
22opc_len: u2,
23opc: [3]u8,
24modrm_ext: u3,
25mode: Mode,
26
27pub fn findByMnemonic(mnemonic: Mnemonic, args: struct {
28 op1: Instruction.Operand,
29 op2: Instruction.Operand,
30 op3: Instruction.Operand,
31 op4: Instruction.Operand,
32}) !?Encoding {
33 const input_op1 = Op.fromOperand(args.op1);
34 const input_op2 = Op.fromOperand(args.op2);
35 const input_op3 = Op.fromOperand(args.op3);
36 const input_op4 = Op.fromOperand(args.op4);
37
38 const ops = &[_]Instruction.Operand{ args.op1, args.op2, args.op3, args.op4 };
39 const rex_required = for (ops) |op| switch (op) {
40 .reg => |r| switch (r) {
41 .spl, .bpl, .sil, .dil => break true,
42 else => {},
43 },
44 else => {},
45 } else false;
46 const rex_invalid = for (ops) |op| switch (op) {
47 .reg => |r| switch (r) {
48 .ah, .bh, .ch, .dh => break true,
49 else => {},
50 },
51 else => {},
52 } else false;
53 const rex_extended = for (ops) |op| switch (op) {
54 .reg => |r| if (r.isExtended()) break true,
55 .mem => |m| {
56 if (m.base()) |base| {
57 if (base.isExtended()) break true;
58 }
59 if (m.scaleIndex()) |si| {
60 if (si.index.isExtended()) break true;
61 }
62 },
63 else => {},
64 } else false;
65
66 if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode;
67
68 // TODO work out what is the maximum number of variants we can actually find in one swoop.
69 var candidates: [10]Encoding = undefined;
70 var count: usize = 0;
71 for (table) |entry| {
72 const enc = Encoding{
73 .mnemonic = entry[0],
74 .op_en = entry[1],
75 .op1 = entry[2],
76 .op2 = entry[3],
77 .op3 = entry[4],
78 .op4 = entry[5],
79 .opc_len = entry[6],
80 .opc = .{ entry[7], entry[8], entry[9] },
81 .modrm_ext = entry[10],
82 .mode = entry[11],
83 };
84 if (enc.mnemonic == mnemonic and
85 input_op1.isSubset(enc.op1, enc.mode) and
86 input_op2.isSubset(enc.op2, enc.mode) and
87 input_op3.isSubset(enc.op3, enc.mode) and
88 input_op4.isSubset(enc.op4, enc.mode))
89 {
90 if (rex_required) {
91 switch (enc.mode) {
92 .rex, .long => {
93 candidates[count] = enc;
94 count += 1;
95 },
96 else => {},
97 }
98 } else {
99 if (enc.mode != .rex) {
100 candidates[count] = enc;
101 count += 1;
102 }
103 }
104 }
105 }
106
107 if (count == 0) return null;
108 if (count == 1) return candidates[0];
109
110 const EncodingLength = struct {
111 fn estimate(encoding: Encoding, params: struct {
112 op1: Instruction.Operand,
113 op2: Instruction.Operand,
114 op3: Instruction.Operand,
115 op4: Instruction.Operand,
116 }) usize {
117 var inst = Instruction{
118 .op1 = params.op1,
119 .op2 = params.op2,
120 .op3 = params.op3,
121 .op4 = params.op4,
122 .encoding = encoding,
123 };
124 var cwriter = std.io.countingWriter(std.io.null_writer);
125 inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM.
126 return @intCast(usize, cwriter.bytes_written);
127 }
128 };
129
130 var shortest_encoding: ?struct {
131 index: usize,
132 len: usize,
133 } = null;
134 var i: usize = 0;
135 while (i < count) : (i += 1) {
136 const candidate = candidates[i];
137 switch (candidate.mode) {
138 .long, .rex => if (rex_invalid) return error.CannotEncode,
139 else => {},
140 }
141
142 const len = EncodingLength.estimate(candidate, .{
143 .op1 = args.op1,
144 .op2 = args.op2,
145 .op3 = args.op3,
146 .op4 = args.op4,
147 });
148 const current = shortest_encoding orelse {
149 shortest_encoding = .{ .index = i, .len = len };
150 continue;
151 };
152 if (len < current.len) {
153 shortest_encoding = .{ .index = i, .len = len };
154 }
155 }
156
157 return candidates[shortest_encoding.?.index];
158}
159
160/// Returns first matching encoding by opcode.
161pub fn findByOpcode(opc: []const u8, prefixes: struct {
162 legacy: LegacyPrefixes,
163 rex: Rex,
164}, modrm_ext: ?u3) ?Encoding {
165 for (table) |entry| {
166 const enc = Encoding{
167 .mnemonic = entry[0],
168 .op_en = entry[1],
169 .op1 = entry[2],
170 .op2 = entry[3],
171 .op3 = entry[4],
172 .op4 = entry[5],
173 .opc_len = entry[6],
174 .opc = .{ entry[7], entry[8], entry[9] },
175 .modrm_ext = entry[10],
176 .mode = entry[11],
177 };
178 const match = match: {
179 if (modrm_ext) |ext| {
180 break :match ext == enc.modrm_ext and std.mem.eql(u8, enc.opcode(), opc);
181 }
182 break :match std.mem.eql(u8, enc.opcode(), opc);
183 };
184 if (match) {
185 if (prefixes.rex.w) {
186 switch (enc.mode) {
187 .fpu, .sse, .sse2, .none => {},
188 .long, .rex => return enc,
189 }
190 } else if (prefixes.rex.present and !prefixes.rex.isSet()) {
191 if (enc.mode == .rex) return enc;
192 } else if (prefixes.legacy.prefix_66) {
193 switch (enc.operandBitSize()) {
194 16 => return enc,
195 else => {},
196 }
197 } else {
198 if (enc.mode == .none) {
199 switch (enc.operandBitSize()) {
200 16 => {},
201 else => return enc,
202 }
203 }
204 }
205 }
206 }
207 return null;
208}
209
210pub fn opcode(encoding: *const Encoding) []const u8 {
211 return encoding.opc[0..encoding.opc_len];
212}
213
214pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
215 const prefix = encoding.opc[0];
216 return switch (prefix) {
217 0x66, 0xf2, 0xf3 => prefix,
218 else => null,
219 };
220}
221
222pub fn modRmExt(encoding: Encoding) u3 {
223 return switch (encoding.op_en) {
224 .m, .mi, .m1, .mc => encoding.modrm_ext,
225 else => unreachable,
226 };
227}
228
229pub fn operandBitSize(encoding: Encoding) u64 {
230 if (encoding.mode == .long) return 64;
231 const bit_size: u64 = switch (encoding.op_en) {
232 .np => switch (encoding.op1) {
233 .o16 => 16,
234 .o32 => 32,
235 .o64 => 64,
236 else => 32,
237 },
238 .td => encoding.op2.bitSize(),
239 else => encoding.op1.bitSize(),
240 };
241 return bit_size;
242}
243
244pub fn format(
245 encoding: Encoding,
246 comptime fmt: []const u8,
247 options: std.fmt.FormatOptions,
248 writer: anytype,
249) !void {
250 _ = options;
251 _ = fmt;
252 switch (encoding.mode) {
253 .long => try writer.writeAll("REX.W + "),
254 else => {},
255 }
256
257 for (encoding.opcode()) |byte| {
258 try writer.print("{x:0>2} ", .{byte});
259 }
260
261 switch (encoding.op_en) {
262 .np, .fd, .td, .i, .zi, .d => {},
263 .o, .oi => {
264 const tag = switch (encoding.op1) {
265 .r8 => "rb",
266 .r16 => "rw",
267 .r32 => "rd",
268 .r64 => "rd",
269 else => unreachable,
270 };
271 try writer.print("+{s} ", .{tag});
272 },
273 .m, .mi, .m1, .mc => try writer.print("/{d} ", .{encoding.modRmExt()}),
274 .mr, .rm, .rmi => try writer.writeAll("/r "),
275 }
276
277 switch (encoding.op_en) {
278 .i, .d, .zi, .oi, .mi, .rmi => {
279 const op = switch (encoding.op_en) {
280 .i, .d => encoding.op1,
281 .zi, .oi, .mi => encoding.op2,
282 .rmi => encoding.op3,
283 else => unreachable,
284 };
285 const tag = switch (op) {
286 .imm8, .imm8s => "ib",
287 .imm16, .imm16s => "iw",
288 .imm32, .imm32s => "id",
289 .imm64 => "io",
290 .rel8 => "cb",
291 .rel16 => "cw",
292 .rel32 => "cd",
293 else => unreachable,
294 };
295 try writer.print("{s} ", .{tag});
296 },
297 .np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm => {},
298 }
299
300 try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
301
302 const ops = &[_]Op{ encoding.op1, encoding.op2, encoding.op3, encoding.op4 };
303 for (ops) |op| switch (op) {
304 .none, .o16, .o32, .o64 => break,
305 else => try writer.print("{s} ", .{@tagName(op)}),
306 };
307
308 const op_en = switch (encoding.op_en) {
309 .zi => .i,
310 else => |op_en| op_en,
311 };
312 try writer.print("{s}", .{@tagName(op_en)});
313}
314
315pub const Mnemonic = enum {
316 // zig fmt: off
317 // General-purpose
318 adc, add, @"and",
319 call, cbw, cwde, cdqe, cwd, cdq, cqo, cmp,
320 cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna,
321 cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno,
322 cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz,
323 div,
324 fisttp, fld,
325 idiv, imul, int3,
326 ja, jae, jb, jbe, jc, jrcxz, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe,
327 jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz,
328 jmp,
329 lea,
330 mov, movsx, movsxd, movzx, mul,
331 nop,
332 @"or",
333 pop, push,
334 ret,
335 sal, sar, sbb, shl, shr, sub, syscall,
336 seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae,
337 setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns,
338 setnz, seto, setp, setpe, setpo, sets, setz,
339 @"test",
340 ud2,
341 xor,
342 // SSE
343 addss,
344 cmpss,
345 movss,
346 ucomiss,
347 // SSE2
348 addsd,
349 cmpsd,
350 movq, movsd,
351 ucomisd,
352 // zig fmt: on
353};
354
355pub const OpEn = enum {
356 // zig fmt: off
357 np,
358 o, oi,
359 i, zi,
360 d, m,
361 fd, td,
362 m1, mc, mi, mr, rm, rmi,
363 // zig fmt: on
364};
365
366pub const Op = enum {
367 // zig fmt: off
368 none,
369 o16, o32, o64,
370 unity,
371 imm8, imm16, imm32, imm64,
372 imm8s, imm16s, imm32s,
373 al, ax, eax, rax,
374 cl,
375 r8, r16, r32, r64,
376 rm8, rm16, rm32, rm64,
377 m8, m16, m32, m64, m80,
378 rel8, rel16, rel32,
379 m,
380 moffs,
381 sreg,
382 xmm, xmm_m32, xmm_m64,
383 // zig fmt: on
384
385 pub fn fromOperand(operand: Instruction.Operand) Op {
386 switch (operand) {
387 .none => return .none,
388
389 .reg => |reg| {
390 switch (reg.class()) {
391 .segment => return .sreg,
392 .floating_point => return switch (reg.bitSize()) {
393 128 => .xmm,
394 else => unreachable,
395 },
396 .general_purpose => {
397 if (reg.to64() == .rax) return switch (reg) {
398 .al => .al,
399 .ax => .ax,
400 .eax => .eax,
401 .rax => .rax,
402 else => unreachable,
403 };
404 if (reg == .cl) return .cl;
405 return switch (reg.bitSize()) {
406 8 => .r8,
407 16 => .r16,
408 32 => .r32,
409 64 => .r64,
410 else => unreachable,
411 };
412 },
413 }
414 },
415
416 .mem => |mem| switch (mem) {
417 .moffs => return .moffs,
418 .sib, .rip => {
419 const bit_size = mem.bitSize();
420 return switch (bit_size) {
421 8 => .m8,
422 16 => .m16,
423 32 => .m32,
424 64 => .m64,
425 80 => .m80,
426 else => unreachable,
427 };
428 },
429 },
430
431 .imm => |imm| {
432 switch (imm) {
433 .signed => |x| {
434 if (x == 1) return .unity;
435 if (math.cast(i8, x)) |_| return .imm8s;
436 if (math.cast(i16, x)) |_| return .imm16s;
437 return .imm32s;
438 },
439 .unsigned => |x| {
440 if (x == 1) return .unity;
441 if (math.cast(i8, x)) |_| return .imm8s;
442 if (math.cast(u8, x)) |_| return .imm8;
443 if (math.cast(i16, x)) |_| return .imm16s;
444 if (math.cast(u16, x)) |_| return .imm16;
445 if (math.cast(i32, x)) |_| return .imm32s;
446 if (math.cast(u32, x)) |_| return .imm32;
447 return .imm64;
448 },
449 }
450 },
451 }
452 }
453
454 pub fn bitSize(op: Op) u64 {
455 return switch (op) {
456 .none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
457 .unity => 1,
458 .imm8, .imm8s, .al, .cl, .r8, .m8, .rm8, .rel8 => 8,
459 .imm16, .imm16s, .ax, .r16, .m16, .rm16, .rel16 => 16,
460 .imm32, .imm32s, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32,
461 .imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64,
462 .m80 => 80,
463 .xmm => 128,
464 };
465 }
466
467 pub fn isSigned(op: Op) bool {
468 return switch (op) {
469 .unity, .imm8, .imm16, .imm32, .imm64 => false,
470 .imm8s, .imm16s, .imm32s => true,
471 else => unreachable,
472 };
473 }
474
475 pub fn isUnsigned(op: Op) bool {
476 return !op.isSigned();
477 }
478
479 pub fn isRegister(op: Op) bool {
480 // zig fmt: off
481 return switch (op) {
482 .cl,
483 .al, .ax, .eax, .rax,
484 .r8, .r16, .r32, .r64,
485 .rm8, .rm16, .rm32, .rm64,
486 .xmm, .xmm_m32, .xmm_m64,
487 => true,
488 else => false,
489 };
490 // zig fmt: on
491 }
492
493 pub fn isImmediate(op: Op) bool {
494 // zig fmt: off
495 return switch (op) {
496 .imm8, .imm16, .imm32, .imm64,
497 .imm8s, .imm16s, .imm32s,
498 .rel8, .rel16, .rel32,
499 .unity,
500 => true,
501 else => false,
502 };
503 // zig fmt: on
504 }
505
506 pub fn isMemory(op: Op) bool {
507 // zig fmt: off
508 return switch (op) {
509 .rm8, .rm16, .rm32, .rm64,
510 .m8, .m16, .m32, .m64, .m80,
511 .m,
512 .xmm_m32, .xmm_m64,
513 => true,
514 else => false,
515 };
516 // zig fmt: on
517 }
518
519 pub fn isSegmentRegister(op: Op) bool {
520 return switch (op) {
521 .moffs, .sreg => true,
522 else => false,
523 };
524 }
525
526 pub fn isFloatingPointRegister(op: Op) bool {
527 return switch (op) {
528 .xmm, .xmm_m32, .xmm_m64 => true,
529 else => false,
530 };
531 }
532
533 /// Given an operand `op` checks if `target` is a subset for the purposes of the encoding.
534 pub fn isSubset(op: Op, target: Op, mode: Mode) bool {
535 switch (op) {
536 .m, .o16, .o32, .o64 => unreachable,
537 .moffs, .sreg => return op == target,
538 .none => switch (target) {
539 .o16, .o32, .o64, .none => return true,
540 else => return false,
541 },
542 else => {
543 if (op.isRegister() and target.isRegister()) {
544 switch (mode) {
545 .sse, .sse2 => return op.isFloatingPointRegister() and target.isFloatingPointRegister(),
546 else => switch (target) {
547 .cl, .al, .ax, .eax, .rax => return op == target,
548 else => return op.bitSize() == target.bitSize(),
549 },
550 }
551 }
552 if (op.isMemory() and target.isMemory()) {
553 switch (target) {
554 .m => return true,
555 else => return op.bitSize() == target.bitSize(),
556 }
557 }
558 if (op.isImmediate() and target.isImmediate()) {
559 switch (target) {
560 .imm64 => if (op.bitSize() <= 64) return true,
561 .imm32s, .rel32 => if (op.bitSize() < 32 or (op.bitSize() == 32 and op.isSigned()))
562 return true,
563 .imm32 => if (op.bitSize() <= 32) return true,
564 .imm16s, .rel16 => if (op.bitSize() < 16 or (op.bitSize() == 16 and op.isSigned()))
565 return true,
566 .imm16 => if (op.bitSize() <= 16) return true,
567 .imm8s, .rel8 => if (op.bitSize() < 8 or (op.bitSize() == 8 and op.isSigned()))
568 return true,
569 .imm8 => if (op.bitSize() <= 8) return true,
570 else => {},
571 }
572 return op == target;
573 }
574 return false;
575 },
576 }
577 }
578};
579
580pub const Mode = enum {
581 none,
582 fpu,
583 rex,
584 long,
585 sse,
586 sse2,
587};
src/arch/x86_64/Mir.zig+358-451
...@@ -12,9 +12,12 @@ const builtin = @import("builtin");...@@ -12,9 +12,12 @@ const builtin = @import("builtin");
12const assert = std.debug.assert;12const assert = std.debug.assert;
1313
14const bits = @import("bits.zig");14const bits = @import("bits.zig");
15const encoder = @import("encoder.zig");
16
15const Air = @import("../../Air.zig");17const Air = @import("../../Air.zig");
16const CodeGen = @import("CodeGen.zig");18const CodeGen = @import("CodeGen.zig");
17const IntegerBitSet = std.bit_set.IntegerBitSet;19const IntegerBitSet = std.bit_set.IntegerBitSet;
20const Memory = bits.Memory;
18const Register = bits.Register;21const Register = bits.Register;
1922
20instructions: std.MultiArrayList(Inst).Slice,23instructions: std.MultiArrayList(Inst).Slice,
...@@ -24,428 +27,300 @@ extra: []const u32,...@@ -24,428 +27,300 @@ extra: []const u32,
24pub const Inst = struct {27pub const Inst = struct {
25 tag: Tag,28 tag: Tag,
26 ops: Ops,29 ops: Ops,
27 /// The meaning of this depends on `tag` and `ops`.
28 data: Data,30 data: Data,
2931
30 pub const Tag = enum(u16) {32 pub const Index = u32;
31 /// ops flags: form:
32 /// 0b00 reg1, reg2
33 /// 0b00 reg1, imm32
34 /// 0b01 reg1, [reg2 + imm32]
35 /// 0b01 reg1, [ds:imm32]
36 /// 0b10 [reg1 + imm32], reg2
37 /// Notes:
38 /// * If reg2 is `none` then it means Data field `imm` is used as the immediate.
39 /// * When two imm32 values are required, Data field `payload` points at `ImmPair`.
40 adc,
41
42 /// ops flags: form:
43 /// 0b00 byte ptr [reg1 + imm32], imm8
44 /// 0b01 word ptr [reg1 + imm32], imm16
45 /// 0b10 dword ptr [reg1 + imm32], imm32
46 /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)
47 /// Notes:
48 /// * Uses `ImmPair` as payload
49 adc_mem_imm,
50
51 /// form: reg1, [reg2 + scale*index + imm32]
52 /// ops flags scale
53 /// 0b00 1
54 /// 0b01 2
55 /// 0b10 4
56 /// 0b11 8
57 /// Notes:
58 /// * Uses `IndexRegisterDisp` as payload
59 adc_scale_src,
60
61 /// form: [reg1 + scale*index + imm32], reg2
62 /// ops flags scale
63 /// 0b00 1
64 /// 0b01 2
65 /// 0b10 4
66 /// 0b11 8
67 /// Notes:
68 /// * Uses `IndexRegisterDisp` payload.
69 adc_scale_dst,
70
71 /// form: [reg1 + scale*rax + imm32], imm32
72 /// ops flags scale
73 /// 0b00 1
74 /// 0b01 2
75 /// 0b10 4
76 /// 0b11 8
77 /// Notes:
78 /// * Uses `IndexRegisterDispImm` payload.
79 adc_scale_imm,
80
81 /// ops flags: form:
82 /// 0b00 byte ptr [reg1 + index + imm32], imm8
83 /// 0b01 word ptr [reg1 + index + imm32], imm16
84 /// 0b10 dword ptr [reg1 + index + imm32], imm32
85 /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64)
86 /// Notes:
87 /// * Uses `IndexRegisterDispImm` payload.
88 adc_mem_index_imm,
89
90 // The following instructions all have the same encoding as `adc`.
9133
34 pub const Tag = enum(u8) {
35 /// Add with carry
36 adc,
37 /// Add
92 add,38 add,
93 add_mem_imm,39 /// Logical and
94 add_scale_src,
95 add_scale_dst,
96 add_scale_imm,
97 add_mem_index_imm,
98 sub,
99 sub_mem_imm,
100 sub_scale_src,
101 sub_scale_dst,
102 sub_scale_imm,
103 sub_mem_index_imm,
104 xor,
105 xor_mem_imm,
106 xor_scale_src,
107 xor_scale_dst,
108 xor_scale_imm,
109 xor_mem_index_imm,
110 @"and",40 @"and",
111 and_mem_imm,41 /// Call
112 and_scale_src,42 call,
113 and_scale_dst,43 /// Convert byte to word
114 and_scale_imm,44 cbw,
115 and_mem_index_imm,45 /// Convert word to doubleword
116 @"or",46 cwde,
117 or_mem_imm,47 /// Convert doubleword to quadword
118 or_scale_src,48 cdqe,
119 or_scale_dst,49 /// Convert word to doubleword
120 or_scale_imm,50 cwd,
121 or_mem_index_imm,51 /// Convert doubleword to quadword
122 rol,52 cdq,
123 rol_mem_imm,53 /// Convert doubleword to quadword
124 rol_scale_src,54 cqo,
125 rol_scale_dst,55 /// Logical compare
126 rol_scale_imm,
127 rol_mem_index_imm,
128 ror,
129 ror_mem_imm,
130 ror_scale_src,
131 ror_scale_dst,
132 ror_scale_imm,
133 ror_mem_index_imm,
134 rcl,
135 rcl_mem_imm,
136 rcl_scale_src,
137 rcl_scale_dst,
138 rcl_scale_imm,
139 rcl_mem_index_imm,
140 rcr,
141 rcr_mem_imm,
142 rcr_scale_src,
143 rcr_scale_dst,
144 rcr_scale_imm,
145 rcr_mem_index_imm,
146 sbb,
147 sbb_mem_imm,
148 sbb_scale_src,
149 sbb_scale_dst,
150 sbb_scale_imm,
151 sbb_mem_index_imm,
152 cmp,56 cmp,
153 cmp_mem_imm,57 /// Unsigned division
154 cmp_scale_src,
155 cmp_scale_dst,
156 cmp_scale_imm,
157 cmp_mem_index_imm,
158 mov,
159 mov_mem_imm,
160 mov_scale_src,
161 mov_scale_dst,
162 mov_scale_imm,
163 mov_mem_index_imm,
164
165 /// ops flags: form:
166 /// 0b00 reg1, reg2,
167 /// 0b01 reg1, byte ptr [reg2 + imm32]
168 /// 0b10 reg1, word ptr [reg2 + imm32]
169 /// 0b11 reg1, dword ptr [reg2 + imm32]
170 mov_sign_extend,
171
172 /// ops flags: form:
173 /// 0b00 reg1, reg2
174 /// 0b01 reg1, byte ptr [reg2 + imm32]
175 /// 0b10 reg1, word ptr [reg2 + imm32]
176 mov_zero_extend,
177
178 /// ops flags: form:
179 /// 0b00 reg1, [reg2 + imm32]
180 /// 0b00 reg1, [ds:imm32]
181 /// 0b01 reg1, [rip + imm32]
182 /// 0b10 reg1, [reg2 + index + imm32]
183 /// Notes:
184 /// * 0b10 uses `IndexRegisterDisp` payload
185 lea,
186
187 /// ops flags: form:
188 /// 0b00 reg1, [rip + reloc] // via GOT PIC
189 /// 0b01 reg1, [rip + reloc] // direct load PIC
190 /// 0b10 reg1, [rip + reloc] // via imports table PIC
191 /// Notes:
192 /// * `Data` contains `relocation`
193 lea_pic,
194
195 /// ops flags: form:
196 /// 0b00 reg1, 1
197 /// 0b01 reg1, .cl
198 /// 0b10 reg1, imm8
199 /// Notes:
200 /// * If flags == 0b10, uses `imm`.
201 shl,
202 shl_mem_imm,
203 shl_scale_src,
204 shl_scale_dst,
205 shl_scale_imm,
206 shl_mem_index_imm,
207 sal,
208 sal_mem_imm,
209 sal_scale_src,
210 sal_scale_dst,
211 sal_scale_imm,
212 sal_mem_index_imm,
213 shr,
214 shr_mem_imm,
215 shr_scale_src,
216 shr_scale_dst,
217 shr_scale_imm,
218 shr_mem_index_imm,
219 sar,
220 sar_mem_imm,
221 sar_scale_src,
222 sar_scale_dst,
223 sar_scale_imm,
224 sar_mem_index_imm,
225
226 /// ops flags: form:
227 /// 0b00 reg1
228 /// 0b00 byte ptr [reg2 + imm32]
229 /// 0b01 word ptr [reg2 + imm32]
230 /// 0b10 dword ptr [reg2 + imm32]
231 /// 0b11 qword ptr [reg2 + imm32]
232 imul,
233 idiv,
234 mul,
235 div,58 div,
23659 /// Store integer with truncation
237 /// ops flags: form:
238 /// 0b00 AX <- AL
239 /// 0b01 DX:AX <- AX
240 /// 0b10 EDX:EAX <- EAX
241 /// 0b11 RDX:RAX <- RAX
242 cwd,
243
244 /// ops flags: form:
245 /// 0b00 reg1, reg2
246 /// 0b01 reg1, [reg2 + imm32]
247 /// 0b01 reg1, [imm32] if reg2 is none
248 /// 0b10 reg1, reg2, imm32
249 /// 0b11 reg1, [reg2 + imm32], imm32
250 imul_complex,
251
252 /// ops flags: form:
253 /// 0b00 reg1, imm64
254 /// 0b01 rax, moffs64
255 /// Notes:
256 /// * If reg1 is 64-bit, the immediate is 64-bit and stored
257 /// within extra data `Imm64`.
258 /// * For 0b01, reg1 (or reg2) need to be
259 /// a version of rax. If reg1 == .none, then reg2 == .rax,
260 /// or vice versa.
261 movabs,
262
263 /// ops flags: form:
264 /// 0b00 word ptr [reg1 + imm32]
265 /// 0b01 dword ptr [reg1 + imm32]
266 /// 0b10 qword ptr [reg1 + imm32]
267 /// Notes:
268 /// * source is always ST(0)
269 /// * only supports memory operands as destination
270 fisttp,60 fisttp,
27161 /// Load floating-point value
272 /// ops flags: form:
273 /// 0b01 dword ptr [reg1 + imm32]
274 /// 0b10 qword ptr [reg1 + imm32]
275 fld,62 fld,
27663 /// Signed division
277 /// ops flags: form:64 idiv,
278 /// 0b00 inst65 /// Signed multiplication
279 /// 0b01 reg166 imul,
280 /// 0b01 [imm32] if reg1 is none67 ///
281 /// 0b10 [reg1 + imm32]68 int3,
69 /// Jump
282 jmp,70 jmp,
283 call,71 /// Load effective address
28472 lea,
285 /// ops flags:73 /// Move
286 /// unused74 mov,
287 /// Notes:75 /// Move with sign extension
288 /// * uses `inst_cc` in Data.76 movsx,
289 cond_jmp,77 /// Move with zero extension
29078 movzx,
291 /// ops flags:79 /// Multiply
292 /// 0b00 reg180 mul,
293 /// Notes:81 /// No-op
294 /// * uses condition code (CC) stored as part of data82 nop,
295 cond_set_byte,83 /// Logical or
29684 @"or",
297 /// ops flags:85 /// Pop
298 /// 0b00 reg1, reg2,
299 /// 0b01 reg1, word ptr [reg2 + imm]
300 /// 0b10 reg1, dword ptr [reg2 + imm]
301 /// 0b11 reg1, qword ptr [reg2 + imm]
302 /// Notes:
303 /// * uses condition code (CC) stored as part of data
304 cond_mov,
305
306 /// ops flags: form:
307 /// 0b00 reg1
308 /// 0b01 [reg1 + imm32]
309 /// 0b10 imm32
310 /// Notes:
311 /// * If 0b10 is specified and the tag is push, pushes immediate onto the stack
312 /// using the mnemonic PUSH imm32.
313 push,
314 pop,86 pop,
31587 /// Push
316 /// ops flags: form:88 push,
317 /// 0b00 retf imm1689 /// Return
318 /// 0b01 retf
319 /// 0b10 retn imm16
320 /// 0b11 retn
321 ret,90 ret,
32291 /// Arithmetic shift left
323 /// Fast system call92 sal,
93 /// Arithmetic shift right
94 sar,
95 /// Integer subtraction with borrow
96 sbb,
97 /// Logical shift left
98 shl,
99 /// Logical shift right
100 shr,
101 /// Subtract
102 sub,
103 /// Syscall
324 syscall,104 syscall,
325105 /// Test condition
326 /// ops flags: form:
327 /// 0b00 reg1, imm32 if reg2 == .none
328 /// 0b00 reg1, reg2
329 /// TODO handle more cases
330 @"test",106 @"test",
107 /// Undefined instruction
108 ud2,
109 /// Logical exclusive-or
110 xor,
331111
332 /// Breakpoint form:112 /// Add single precision floating point
333 /// 0b00 int3113 addss,
334 interrupt,114 /// Compare scalar single-precision floating-point values
335115 cmpss,
336 /// Nop116 /// Move scalar single-precision floating-point value
337 nop,117 movss,
338118 /// Unordered compare scalar single-precision floating-point values
339 /// SSE instructions119 ucomiss,
340 /// ops flags: form:120 /// Add double precision floating point
341 /// 0b00 reg1, qword ptr [reg2 + imm32]121 addsd,
342 /// 0b01 qword ptr [reg1 + imm32], reg2122 /// Compare scalar double-precision floating-point values
343 /// 0b10 reg1, reg2123 cmpsd,
344 mov_f64_sse,124 /// Move scalar double-precision floating-point value
345 mov_f32_sse,125 movsd,
346126 /// Unordered compare scalar double-precision floating-point values
347 /// ops flags: form:127 ucomisd,
348 /// 0b00 reg1, reg2128
349 add_f64_sse,129 /// Conditional move
350 add_f32_sse,130 cmovcc,
351131 /// Conditional jump
352 /// ops flags: form:132 jcc,
353 /// 0b00 reg1, reg2133 /// Set byte on condition
354 cmp_f64_sse,134 setcc,
355 cmp_f32_sse,135
356136 /// Mov absolute to/from memory wrt segment register to/from rax
357 /// AVX instructions137 mov_moffs,
358 /// ops flags: form:138
359 /// 0b00 reg1, qword ptr [reg2 + imm32]139 /// Jump with relocation to another local MIR instruction
360 /// 0b01 qword ptr [reg1 + imm32], reg2140 /// Uses `inst` payload.
361 /// 0b10 reg1, reg1, reg2141 jmp_reloc,
362 mov_f64_avx,142
363 mov_f32_avx,143 /// Call to an extern symbol via linker relocation.
364144 /// Uses `relocation` payload.
365 /// ops flags: form:
366 /// 0b00 reg1, reg1, reg2
367 add_f64_avx,
368 add_f32_avx,
369
370 /// ops flags: form:
371 /// 0b00 reg1, reg1, reg2
372 cmp_f64_avx,
373 cmp_f32_avx,
374
375 /// Pseudo-instructions
376 /// call extern function
377 /// Notes:
378 /// * target of the call is stored as `relocation` in `Data` union.
379 call_extern,145 call_extern,
380146
381 /// end of prologue147 /// Load effective address of a symbol not yet allocated in VM.
382 dbg_prologue_end,148 lea_linker,
383149
384 /// start of epilogue150 /// End of prologue
151 dbg_prologue_end,
152 /// Start of epilogue
385 dbg_epilogue_begin,153 dbg_epilogue_begin,
386154 /// Update debug line
387 /// update debug line155 /// Uses `payload` payload with data of type `DbgLineColumn`.
388 dbg_line,156 dbg_line,
389157 /// Push registers
390 /// push registers158 /// Uses `payload` payload with data of type `SaveRegisterList`.
391 /// Uses `payload` field with `SaveRegisterList` as payload.
392 push_regs,159 push_regs,
393160 /// Pop registers
394 /// pop registers161 /// Uses `payload` payload with data of type `SaveRegisterList`.
395 /// Uses `payload` field with `SaveRegisterList` as payload.
396 pop_regs,162 pop_regs,
397 };
398 /// The position of an MIR instruction within the `Mir` instructions array.
399 pub const Index = u32;
400163
401 pub const Ops = packed struct {164 /// Tombstone
402 reg1: u7,165 /// Emitter should skip this instruction.
403 reg2: u7,166 dead,
404 flags: u2,167 };
405
406 pub fn encode(vals: struct {
407 reg1: Register = .none,
408 reg2: Register = .none,
409 flags: u2 = 0b00,
410 }) Ops {
411 return .{
412 .reg1 = @enumToInt(vals.reg1),
413 .reg2 = @enumToInt(vals.reg2),
414 .flags = vals.flags,
415 };
416 }
417168
418 pub fn decode(ops: Ops) struct {169 pub const Ops = enum(u8) {
419 reg1: Register,170 /// No data associated with this instruction (only mnemonic is used).
420 reg2: Register,171 none,
421 flags: u2,172 /// Single register operand.
422 } {173 /// Uses `r` payload.
423 return .{174 r,
424 .reg1 = @intToEnum(Register, ops.reg1),175 /// Register, register operands.
425 .reg2 = @intToEnum(Register, ops.reg2),176 /// Uses `rr` payload.
426 .flags = ops.flags,177 rr,
427 };178 /// Register, register, register operands.
428 }179 /// Uses `rrr` payload.
180 rrr,
181 /// Register, register, immediate (sign-extended) operands.
182 /// Uses `rri` payload.
183 rri_s,
184 /// Register, register, immediate (unsigned) operands.
185 /// Uses `rri` payload.
186 rri_u,
187 /// Register with condition code (CC).
188 /// Uses `r_c` payload.
189 r_c,
190 /// Register, register with condition code (CC).
191 /// Uses `rr_c` payload.
192 rr_c,
193 /// Register, immediate (sign-extended) operands.
194 /// Uses `ri` payload.
195 ri_s,
196 /// Register, immediate (unsigned) operands.
197 /// Uses `ri` payload.
198 ri_u,
199 /// Register, 64-bit unsigned immediate operands.
200 /// Uses `rx` payload with payload type `Imm64`.
201 ri64,
202 /// Immediate (sign-extended) operand.
203 /// Uses `imm` payload.
204 imm_s,
205 /// Immediate (unsigned) operand.
206 /// Uses `imm` payload.
207 imm_u,
208 /// Relative displacement operand.
209 /// Uses `imm` payload.
210 rel,
211 /// Register, memory (SIB) operands.
212 /// Uses `rx` payload.
213 rm_sib,
214 /// Register, memory (RIP) operands.
215 /// Uses `rx` payload.
216 rm_rip,
217 /// Single memory (SIB) operand.
218 /// Uses `payload` with extra data of type `MemorySib`.
219 m_sib,
220 /// Single memory (RIP) operand.
221 /// Uses `payload` with extra data of type `MemoryRip`.
222 m_rip,
223 /// Memory (SIB), immediate (unsigned) operands.
224 /// Uses `xi` payload with extra data of type `MemorySib`.
225 mi_u_sib,
226 /// Memory (RIP), immediate (unsigned) operands.
227 /// Uses `xi` payload with extra data of type `MemoryRip`.
228 mi_u_rip,
229 /// Memory (SIB), immediate (sign-extend) operands.
230 /// Uses `xi` payload with extra data of type `MemorySib`.
231 mi_s_sib,
232 /// Memory (RIP), immediate (sign-extend) operands.
233 /// Uses `xi` payload with extra data of type `MemoryRip`.
234 mi_s_rip,
235 /// Memory (SIB), register operands.
236 /// Uses `rx` payload with extra data of type `MemorySib`.
237 mr_sib,
238 /// Memory (RIP), register operands.
239 /// Uses `rx` payload with extra data of type `MemoryRip`.
240 mr_rip,
241 /// Rax, Memory moffs.
242 /// Uses `payload` with extra data of type `MemoryMoffs`.
243 rax_moffs,
244 /// Memory moffs, rax.
245 /// Uses `payload` with extra data of type `MemoryMoffs`.
246 moffs_rax,
247 /// References another Mir instruction directly.
248 /// Uses `inst` payload.
249 inst,
250 /// References another Mir instruction directly with condition code (CC).
251 /// Uses `inst_cc` payload.
252 inst_cc,
253 /// Uses `payload` payload with data of type `MemoryConditionCode`.
254 m_cc,
255 /// Uses `rx` payload with extra data of type `MemoryConditionCode`.
256 rm_cc,
257 /// Uses `reloc` payload.
258 reloc,
259 /// Linker relocation - GOT indirection.
260 /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
261 got_reloc,
262 /// Linker relocation - direct reference.
263 /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
264 direct_reloc,
265 /// Linker relocation - imports table indirection (binding).
266 /// Uses `payload` payload with extra data of type `LeaRegisterReloc`.
267 import_reloc,
429 };268 };
430269
431 /// All instructions have a 4-byte payload, which is contained within
432 /// this union. `Tag` determines which union field is active, as well as
433 /// how to interpret the data within.
434 pub const Data = union {270 pub const Data = union {
435 /// Another instruction.271 /// References another Mir instruction.
436 inst: Index,272 inst: Index,
437 /// A 32-bit immediate value.273 /// Another instruction with condition code (CC).
438 imm: u32,274 /// Used by `jcc`.
439 /// A condition code for use with EFLAGS register.
440 cc: bits.Condition,
441 /// Another instruction with condition code.
442 /// Used by `cond_jmp`.
443 inst_cc: struct {275 inst_cc: struct {
444 /// Another instruction.276 /// Another instruction.
445 inst: Index,277 inst: Index,
446 /// A condition code for use with EFLAGS register.278 /// A condition code for use with EFLAGS register.
447 cc: bits.Condition,279 cc: bits.Condition,
448 },280 },
281 /// A 32-bit immediate value.
282 imm: u32,
283 r: Register,
284 rr: struct {
285 r1: Register,
286 r2: Register,
287 },
288 rrr: struct {
289 r1: Register,
290 r2: Register,
291 r3: Register,
292 },
293 rri: struct {
294 r1: Register,
295 r2: Register,
296 imm: u32,
297 },
298 /// Register with condition code (CC).
299 r_c: struct {
300 r1: Register,
301 cc: bits.Condition,
302 },
303 /// Register, register with condition code (CC).
304 rr_c: struct {
305 r1: Register,
306 r2: Register,
307 cc: bits.Condition,
308 },
309 /// Register, immediate.
310 ri: struct {
311 r1: Register,
312 imm: u32,
313 },
314 /// Register, followed by custom payload found in extra.
315 rx: struct {
316 r1: Register,
317 payload: u32,
318 },
319 /// Custom payload followed by an immediate.
320 xi: struct {
321 payload: u32,
322 imm: u32,
323 },
449 /// Relocation for the linker where:324 /// Relocation for the linker where:
450 /// * `atom_index` is the index of the source325 /// * `atom_index` is the index of the source
451 /// * `sym_index` is the index of the target326 /// * `sym_index` is the index of the target
...@@ -468,62 +343,13 @@ pub const Inst = struct {...@@ -468,62 +343,13 @@ pub const Inst = struct {
468 }343 }
469};344};
470345
471pub const IndexRegisterDisp = struct {346pub const LeaRegisterReloc = struct {
472 /// Index register to use with SIB-based encoding347 /// Destination register.
473 index: u32,348 reg: u32,
474349 /// Index of the containing atom.
475 /// Displacement value350 atom_index: u32,
476 disp: u32,351 /// Index into the linker's symbol table.
477352 sym_index: u32,
478 pub fn encode(index: Register, disp: u32) IndexRegisterDisp {
479 return .{
480 .index = @enumToInt(index),
481 .disp = disp,
482 };
483 }
484
485 pub fn decode(this: IndexRegisterDisp) struct {
486 index: Register,
487 disp: u32,
488 } {
489 return .{
490 .index = @intToEnum(Register, this.index),
491 .disp = this.disp,
492 };
493 }
494};
495
496/// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list
497/// instead of having two structs, one a superset of the other one?
498pub const IndexRegisterDispImm = struct {
499 /// Index register to use with SIB-based encoding
500 index: u32,
501
502 /// Displacement value
503 disp: u32,
504
505 /// Immediate
506 imm: u32,
507
508 pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm {
509 return .{
510 .index = @enumToInt(index),
511 .disp = disp,
512 .imm = imm,
513 };
514 }
515
516 pub fn decode(this: IndexRegisterDispImm) struct {
517 index: Register,
518 disp: u32,
519 imm: u32,
520 } {
521 return .{
522 .index = @intToEnum(Register, this.index),
523 .disp = this.disp,
524 .imm = this.imm,
525 };
526 }
527};353};
528354
529/// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers355/// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers
...@@ -567,16 +393,13 @@ pub const RegisterList = struct {...@@ -567,16 +393,13 @@ pub const RegisterList = struct {
567};393};
568394
569pub const SaveRegisterList = struct {395pub const SaveRegisterList = struct {
396 /// Base register
397 base_reg: u32,
570 /// Use `RegisterList` to populate.398 /// Use `RegisterList` to populate.
571 register_list: u32,399 register_list: u32,
572 stack_end: u32,400 stack_end: u32,
573};401};
574402
575pub const ImmPair = struct {
576 dest_off: u32,
577 operand: u32,
578};
579
580pub const Imm64 = struct {403pub const Imm64 = struct {
581 msb: u32,404 msb: u32,
582 lsb: u32,405 lsb: u32,
...@@ -596,6 +419,90 @@ pub const Imm64 = struct {...@@ -596,6 +419,90 @@ pub const Imm64 = struct {
596 }419 }
597};420};
598421
422// TODO this can be further compacted using packed struct
423pub const MemorySib = struct {
424 /// Size of the pointer.
425 ptr_size: u32,
426 /// Base register. -1 means null, or no base register.
427 base: i32,
428 /// Scale for index register. -1 means null, or no scale.
429 /// This has to be in sync with `index` field.
430 scale: i32,
431 /// Index register. -1 means null, or no index register.
432 /// This has to be in sync with `scale` field.
433 index: i32,
434 /// Displacement value.
435 disp: i32,
436
437 pub fn encode(mem: Memory) MemorySib {
438 const sib = mem.sib;
439 return .{
440 .ptr_size = @enumToInt(sib.ptr_size),
441 .base = if (sib.base) |r| @enumToInt(r) else -1,
442 .scale = if (sib.scale_index) |si| si.scale else -1,
443 .index = if (sib.scale_index) |si| @enumToInt(si.index) else -1,
444 .disp = sib.disp,
445 };
446 }
447
448 pub fn decode(msib: MemorySib) Memory {
449 const base: ?Register = if (msib.base == -1) null else @intToEnum(Register, msib.base);
450 const scale_index: ?Memory.ScaleIndex = if (msib.index == -1) null else .{
451 .scale = @intCast(u4, msib.scale),
452 .index = @intToEnum(Register, msib.index),
453 };
454 const mem: Memory = .{ .sib = .{
455 .ptr_size = @intToEnum(Memory.PtrSize, msib.ptr_size),
456 .base = base,
457 .scale_index = scale_index,
458 .disp = msib.disp,
459 } };
460 return mem;
461 }
462};
463
464pub const MemoryRip = struct {
465 /// Size of the pointer.
466 ptr_size: u32,
467 /// Displacement value.
468 disp: i32,
469
470 pub fn encode(mem: Memory) MemoryRip {
471 return .{
472 .ptr_size = @enumToInt(mem.rip.ptr_size),
473 .disp = mem.rip.disp,
474 };
475 }
476
477 pub fn decode(mrip: MemoryRip) Memory {
478 return .{ .rip = .{
479 .ptr_size = @intToEnum(Memory.PtrSize, mrip.ptr_size),
480 .disp = mrip.disp,
481 } };
482 }
483};
484
485pub const MemoryMoffs = struct {
486 /// Segment register.
487 seg: u32,
488 /// Absolute offset wrt to the segment register split between MSB and LSB parts much like
489 /// `Imm64` payload.
490 msb: u32,
491 lsb: u32,
492
493 pub fn encodeOffset(moffs: *MemoryMoffs, v: u64) void {
494 moffs.msb = @truncate(u32, v >> 32);
495 moffs.lsb = @truncate(u32, v);
496 }
497
498 pub fn decodeOffset(moffs: *const MemoryMoffs) u64 {
499 var res: u64 = 0;
500 res |= (@intCast(u64, moffs.msb) << 32);
501 res |= @intCast(u64, moffs.lsb);
502 return res;
503 }
504};
505
599pub const DbgLineColumn = struct {506pub const DbgLineColumn = struct {
600 line: u32,507 line: u32,
601 column: u32,508 column: u32,
...@@ -607,9 +514,9 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {...@@ -607,9 +514,9 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
607 mir.* = undefined;514 mir.* = undefined;
608}515}
609516
610pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {517pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } {
611 const fields = std.meta.fields(T);518 const fields = std.meta.fields(T);
612 var i: usize = index;519 var i: u32 = index;
613 var result: T = undefined;520 var result: T = undefined;
614 inline for (fields) |field| {521 inline for (fields) |field| {
615 @field(result, field.name) = switch (field.type) {522 @field(result, field.name) = switch (field.type) {
src/arch/x86_64/bits.zig+301-845
...@@ -1,9 +1,9 @@...@@ -1,9 +1,9 @@
1const std = @import("std");1const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
4const assert = std.debug.assert;2const assert = std.debug.assert;
5const ArrayList = std.ArrayList;3const expect = std.testing.expect;
4
6const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const ArrayList = std.ArrayList;
7const DW = std.dwarf;7const DW = std.dwarf;
88
9/// EFLAGS condition codes9/// EFLAGS condition codes
...@@ -135,131 +135,199 @@ pub const Condition = enum(u5) {...@@ -135,131 +135,199 @@ pub const Condition = enum(u5) {
135 }135 }
136};136};
137137
138/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful.
139/// The registers are defined such that IDs go in descending order of 64-bit,
140/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
141/// registers. This results in some useful properties:
142///
143/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
144/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
145/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
146/// form.
147///
148/// If (register & 8) is set, the register is extended.
149///
150/// The ID can be easily determined by figuring out what range the register is
151/// in, and then subtracting the base.
152pub const Register = enum(u7) {138pub const Register = enum(u7) {
153 // zig fmt: off139 // zig fmt: off
154 // 0 through 15, 64-bit registers. 8-15 are extended.
155 // id is just the int value.
156 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,140 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
157 r8, r9, r10, r11, r12, r13, r14, r15,141 r8, r9, r10, r11, r12, r13, r14, r15,
158142
159 // 16 through 31, 32-bit registers. 24-31 are extended.
160 // id is int value - 16.
161 eax, ecx, edx, ebx, esp, ebp, esi, edi,143 eax, ecx, edx, ebx, esp, ebp, esi, edi,
162 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,144 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
163145
164 // 32-47, 16-bit registers. 40-47 are extended.
165 // id is int value - 32.
166 ax, cx, dx, bx, sp, bp, si, di,146 ax, cx, dx, bx, sp, bp, si, di,
167 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,147 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
168148
169 // 48-63, 8-bit registers. 56-63 are extended.149 al, cl, dl, bl, spl, bpl, sil, dil,
170 // id is int value - 48.
171 al, cl, dl, bl, ah, ch, dh, bh,
172 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,150 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
173151
174 // 64-79, 256-bit registers.152 ah, ch, dh, bh,
175 // id is int value - 64.153
176 ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,154 ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,
177 ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,155 ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
178156
179 // 80-95, 128-bit registers.
180 // id is int value - 80.
181 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,157 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
182 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,158 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
183159
184 // Pseudo-value for MIR instructions.160 es, cs, ss, ds, fs, gs,
161
185 none,162 none,
186 // zig fmt: on163 // zig fmt: on
187164
188 pub fn id(self: Register) u7 {165 pub const Class = enum(u2) {
189 return switch (@enumToInt(self)) {166 general_purpose,
190 0...63 => @as(u7, @truncate(u4, @enumToInt(self))),167 floating_point,
191 64...79 => @enumToInt(self),168 segment,
169 };
170
171 pub fn class(reg: Register) Class {
172 return switch (@enumToInt(reg)) {
173 // zig fmt: off
174 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => .general_purpose,
175 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => .general_purpose,
176 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => .general_purpose,
177 @enumToInt(Register.al) ... @enumToInt(Register.r15b) => .general_purpose,
178 @enumToInt(Register.ah) ... @enumToInt(Register.bh) => .general_purpose,
179
180 @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => .floating_point,
181 @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => .floating_point,
182
183 @enumToInt(Register.es) ... @enumToInt(Register.gs) => .segment,
184
192 else => unreachable,185 else => unreachable,
186 // zig fmt: on
193 };187 };
194 }188 }
195189
196 /// Returns the bit-width of the register.190 pub fn id(reg: Register) u6 {
197 pub fn size(self: Register) u9 {191 const base = switch (@enumToInt(reg)) {
198 return switch (@enumToInt(self)) {192 // zig fmt: off
199 0...15 => 64,193 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax),
200 16...31 => 32,194 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax),
201 32...47 => 16,195 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax),
202 48...63 => 8,196 @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al),
203 64...79 => 256,197 @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4,
204 80...95 => 128,198
199 @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => @enumToInt(Register.ymm0) - 16,
200 @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => @enumToInt(Register.xmm0) - 16,
201
202 @enumToInt(Register.es) ... @enumToInt(Register.gs) => @enumToInt(Register.es) - 32,
203
205 else => unreachable,204 else => unreachable,
205 // zig fmt: on
206 };206 };
207 return @intCast(u6, @enumToInt(reg) - base);
207 }208 }
208209
209 /// Returns whether the register is *extended*. Extended registers are the210 pub fn bitSize(reg: Register) u64 {
210 /// new registers added with amd64, r8 through r15. This also includes any211 return switch (@enumToInt(reg)) {
211 /// other variant of access to those registers, such as r8b, r15d, and so212 // zig fmt: off
212 /// on. This is needed because access to these registers requires special213 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => 64,
213 /// handling via the REX prefix, via the B or R bits, depending on context.214 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => 32,
214 pub fn isExtended(self: Register) bool {215 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => 16,
215 return @enumToInt(self) & 0x08 != 0;216 @enumToInt(Register.al) ... @enumToInt(Register.r15b) => 8,
217 @enumToInt(Register.ah) ... @enumToInt(Register.bh) => 8,
218
219 @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => 256,
220 @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => 128,
221
222 @enumToInt(Register.es) ... @enumToInt(Register.gs) => 16,
223
224 else => unreachable,
225 // zig fmt: on
226 };
216 }227 }
217228
218 /// This returns the 4-bit register ID, which is used in practically every229 pub fn isExtended(reg: Register) bool {
219 /// opcode. Note that bit 3 (the highest bit) is *never* used directly in230 return switch (@enumToInt(reg)) {
220 /// an instruction (@see isExtended), and requires special handling. The231 // zig fmt: off
221 /// lower three bits are often embedded directly in instructions (such as232 @enumToInt(Register.r8) ... @enumToInt(Register.r15) => true,
222 /// the B8 variant of moves), or used in R/M bytes.233 @enumToInt(Register.r8d) ... @enumToInt(Register.r15d) => true,
223 pub fn enc(self: Register) u4 {234 @enumToInt(Register.r8w) ... @enumToInt(Register.r15w) => true,
224 return @truncate(u4, @enumToInt(self));235 @enumToInt(Register.r8b) ... @enumToInt(Register.r15b) => true,
236
237 @enumToInt(Register.ymm8) ... @enumToInt(Register.ymm15) => true,
238 @enumToInt(Register.xmm8) ... @enumToInt(Register.xmm15) => true,
239
240 else => false,
241 // zig fmt: on
242 };
225 }243 }
226244
227 /// Like enc, but only returns the lower 3 bits.245 pub fn enc(reg: Register) u4 {
228 pub fn lowEnc(self: Register) u3 {246 const base = switch (@enumToInt(reg)) {
229 return @truncate(u3, @enumToInt(self));247 // zig fmt: off
248 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax),
249 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax),
250 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax),
251 @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al),
252 @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4,
253
254 @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => @enumToInt(Register.ymm0),
255 @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => @enumToInt(Register.xmm0),
256
257 @enumToInt(Register.es) ... @enumToInt(Register.gs) => @enumToInt(Register.es),
258
259 else => unreachable,
260 // zig fmt: on
261 };
262 return @truncate(u4, @enumToInt(reg) - base);
263 }
264
265 pub fn lowEnc(reg: Register) u3 {
266 return @truncate(u3, reg.enc());
267 }
268
269 pub fn toBitSize(reg: Register, bit_size: u64) Register {
270 return switch (bit_size) {
271 8 => reg.to8(),
272 16 => reg.to16(),
273 32 => reg.to32(),
274 64 => reg.to64(),
275 128 => reg.to128(),
276 256 => reg.to256(),
277 else => unreachable,
278 };
279 }
280
281 fn gpBase(reg: Register) u7 {
282 assert(reg.class() == .general_purpose);
283 return switch (@enumToInt(reg)) {
284 // zig fmt: off
285 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax),
286 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax),
287 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax),
288 @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al),
289 @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4,
290 else => unreachable,
291 // zig fmt: on
292 };
230 }293 }
231294
232 pub fn to256(self: Register) Register {295 pub fn to64(reg: Register) Register {
233 return @intToEnum(Register, @as(u8, self.enc()) + 64);296 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.rax));
234 }297 }
235298
236 pub fn to128(self: Register) Register {299 pub fn to32(reg: Register) Register {
237 return @intToEnum(Register, @as(u8, self.enc()) + 80);300 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.eax));
238 }301 }
239302
240 /// Convert from any register to its 64 bit alias.303 pub fn to16(reg: Register) Register {
241 pub fn to64(self: Register) Register {304 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.ax));
242 return @intToEnum(Register, self.enc());
243 }305 }
244306
245 /// Convert from any register to its 32 bit alias.307 pub fn to8(reg: Register) Register {
246 pub fn to32(self: Register) Register {308 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.al));
247 return @intToEnum(Register, @as(u8, self.enc()) + 16);
248 }309 }
249310
250 /// Convert from any register to its 16 bit alias.311 fn fpBase(reg: Register) u7 {
251 pub fn to16(self: Register) Register {312 assert(reg.class() == .floating_point);
252 return @intToEnum(Register, @as(u8, self.enc()) + 32);313 return switch (@enumToInt(reg)) {
314 @enumToInt(Register.ymm0)...@enumToInt(Register.ymm15) => @enumToInt(Register.ymm0),
315 @enumToInt(Register.xmm0)...@enumToInt(Register.xmm15) => @enumToInt(Register.xmm0),
316 else => unreachable,
317 };
253 }318 }
254319
255 /// Convert from any register to its 8 bit alias.320 pub fn to256(reg: Register) Register {
256 pub fn to8(self: Register) Register {321 return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.ymm0));
257 return @intToEnum(Register, @as(u8, self.enc()) + 48);
258 }322 }
259323
260 pub fn dwarfLocOp(self: Register) u8 {324 pub fn to128(reg: Register) Register {
261 switch (@enumToInt(self)) {325 return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.xmm0));
262 0...63 => return switch (self.to64()) {326 }
327
328 pub fn dwarfLocOp(reg: Register) u8 {
329 return switch (reg.class()) {
330 .general_purpose => switch (reg.to64()) {
263 .rax => DW.OP.reg0,331 .rax => DW.OP.reg0,
264 .rdx => DW.OP.reg1,332 .rdx => DW.OP.reg1,
265 .rcx => DW.OP.reg2,333 .rcx => DW.OP.reg2,
...@@ -268,31 +336,19 @@ pub const Register = enum(u7) {...@@ -268,31 +336,19 @@ pub const Register = enum(u7) {
268 .rdi => DW.OP.reg5,336 .rdi => DW.OP.reg5,
269 .rbp => DW.OP.reg6,337 .rbp => DW.OP.reg6,
270 .rsp => DW.OP.reg7,338 .rsp => DW.OP.reg7,
271339 else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.reg0,
272 .r8 => DW.OP.reg8,
273 .r9 => DW.OP.reg9,
274 .r10 => DW.OP.reg10,
275 .r11 => DW.OP.reg11,
276 .r12 => DW.OP.reg12,
277 .r13 => DW.OP.reg13,
278 .r14 => DW.OP.reg14,
279 .r15 => DW.OP.reg15,
280
281 else => unreachable,
282 },340 },
283341 .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.reg17,
284 64...79 => return @as(u8, self.enc()) + DW.OP.reg17,
285
286 else => unreachable,342 else => unreachable,
287 }343 };
288 }344 }
289345
290 /// DWARF encodings that push a value onto the DWARF stack that is either346 /// DWARF encodings that push a value onto the DWARF stack that is either
291 /// the contents of a register or the result of adding the contents a given347 /// the contents of a register or the result of adding the contents a given
292 /// register to a given signed offset.348 /// register to a given signed offset.
293 pub fn dwarfLocOpDeref(self: Register) u8 {349 pub fn dwarfLocOpDeref(reg: Register) u8 {
294 switch (@enumToInt(self)) {350 return switch (reg.class()) {
295 0...63 => return switch (self.to64()) {351 .general_purpose => switch (reg.to64()) {
296 .rax => DW.OP.breg0,352 .rax => DW.OP.breg0,
297 .rdx => DW.OP.breg1,353 .rdx => DW.OP.breg1,
298 .rcx => DW.OP.breg2,354 .rcx => DW.OP.breg2,
...@@ -300,795 +356,195 @@ pub const Register = enum(u7) {...@@ -300,795 +356,195 @@ pub const Register = enum(u7) {
300 .rsi => DW.OP.breg4,356 .rsi => DW.OP.breg4,
301 .rdi => DW.OP.breg5,357 .rdi => DW.OP.breg5,
302 .rbp => DW.OP.breg6,358 .rbp => DW.OP.breg6,
303 .rsp => DW.OP.fbreg,359 .rsp => DW.OP.breg7,
304360 else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.breg0,
305 .r8 => DW.OP.breg8,
306 .r9 => DW.OP.breg9,
307 .r10 => DW.OP.breg10,
308 .r11 => DW.OP.breg11,
309 .r12 => DW.OP.breg12,
310 .r13 => DW.OP.breg13,
311 .r14 => DW.OP.breg14,
312 .r15 => DW.OP.breg15,
313
314 else => unreachable,
315 },361 },
316362 .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.breg17,
317 64...79 => return @as(u8, self.enc()) + DW.OP.breg17,
318
319 else => unreachable,363 else => unreachable,
320 }364 };
321 }365 }
322};366};
323367
324// zig fmt: on368test "Register id - different classes" {
325369 try expect(Register.al.id() == Register.ax.id());
326/// Encoding helper functions for x86_64 instructions370 try expect(Register.ah.id() == Register.spl.id());
327///371 try expect(Register.ax.id() == Register.eax.id());
328/// Many of these helpers do very little, but they can help make things372 try expect(Register.eax.id() == Register.rax.id());
329/// slightly more readable with more descriptive field names / function names.
330///
331/// Some of them also have asserts to ensure that we aren't doing dumb things.
332/// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal,
333/// you need to encode it with an SIB byte.
334///
335/// Note that ALL of these helper functions will assume capacity,
336/// so ensure that the `code` has sufficient capacity before using them.
337/// The `init` method is the recommended way to ensure capacity.
338pub const Encoder = struct {
339 /// Non-owning reference to the code array
340 code: *ArrayList(u8),
341
342 const Self = @This();
343
344 /// Wrap `code` in Encoder to make it easier to call these helper functions
345 ///
346 /// maximum_inst_size should contain the maximum number of bytes
347 /// that the encoded instruction will take.
348 /// This is because the helper functions will assume capacity
349 /// in order to avoid bounds checking.
350 pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self {
351 try code.ensureUnusedCapacity(maximum_inst_size);
352 return Self{ .code = code };
353 }
354
355 /// Directly write a number to the code array with big endianness
356 pub fn writeIntBig(self: Self, comptime T: type, value: T) void {
357 mem.writeIntBig(
358 T,
359 self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)),
360 value,
361 );
362 }
363
364 /// Directly write a number to the code array with little endianness
365 pub fn writeIntLittle(self: Self, comptime T: type, value: T) void {
366 mem.writeIntLittle(
367 T,
368 self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)),
369 value,
370 );
371 }
372373
373 // --------374 try expect(Register.ymm0.id() == 0b10000);
374 // Prefixes375 try expect(Register.ymm0.id() != Register.rax.id());
375 // --------376 try expect(Register.xmm0.id() == Register.ymm0.id());
376
377 pub const LegacyPrefixes = packed struct {
378 /// LOCK
379 prefix_f0: bool = false,
380 /// REPNZ, REPNE, REP, Scalar Double-precision
381 prefix_f2: bool = false,
382 /// REPZ, REPE, REP, Scalar Single-precision
383 prefix_f3: bool = false,
384
385 /// CS segment override or Branch not taken
386 prefix_2e: bool = false,
387 /// DS segment override
388 prefix_36: bool = false,
389 /// ES segment override
390 prefix_26: bool = false,
391 /// FS segment override
392 prefix_64: bool = false,
393 /// GS segment override
394 prefix_65: bool = false,
395
396 /// Branch taken
397 prefix_3e: bool = false,
398
399 /// Operand size override (enables 16 bit operation)
400 prefix_66: bool = false,
401
402 /// Address size override (enables 16 bit address size)
403 prefix_67: bool = false,
404
405 padding: u5 = 0,
406 };
407377
408 /// Encodes legacy prefixes378 try expect(Register.es.id() == 0b100000);
409 pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void {379}
410 if (@bitCast(u16, prefixes) != 0) {
411 // Hopefully this path isn't taken very often, so we'll do it the slow way for now
412
413 // LOCK
414 if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0);
415 // REPNZ, REPNE, REP, Scalar Double-precision
416 if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2);
417 // REPZ, REPE, REP, Scalar Single-precision
418 if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3);
419
420 // CS segment override or Branch not taken
421 if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e);
422 // DS segment override
423 if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36);
424 // ES segment override
425 if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26);
426 // FS segment override
427 if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64);
428 // GS segment override
429 if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65);
430
431 // Branch taken
432 if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e);
433
434 // Operand size override
435 if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66);
436
437 // Address size override
438 if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67);
439 }
440 }
441
442 /// Use 16 bit operand size
443 ///
444 /// Note that this flag is overridden by REX.W, if both are present.
445 pub fn prefix16BitMode(self: Self) void {
446 self.code.appendAssumeCapacity(0x66);
447 }
448
449 pub const Vex = struct {
450 rex_prefix: Rex = .{},
451 lead_opc: u5 = 0b0_0001,
452 register: u4 = 0b1111,
453 length: u1 = 0b0,
454 simd_prefix: u2 = 0b00,
455 wig_desc: bool = false,
456 lig_desc: bool = false,
457 lz_desc: bool = false,
458
459 pub fn rex(self: *Vex, r: Rex) void {
460 self.rex_prefix = r;
461 }
462
463 pub fn lead_opc_0f(self: *Vex) void {
464 self.lead_opc = 0b0_0001;
465 }
466
467 pub fn lead_opc_0f_38(self: *Vex) void {
468 self.lead_opc = 0b0_0010;
469 }
470
471 pub fn lead_opc_0f_3a(self: *Vex) void {
472 self.lead_opc = 0b0_0011;
473 }
474
475 pub fn reg(self: *Vex, register: u4) void {
476 self.register = ~register;
477 }
478
479 pub fn len_128(self: *Vex) void {
480 self.length = 0;
481 }
482
483 pub fn len_256(self: *Vex) void {
484 assert(!self.lz_desc);
485 self.length = 1;
486 }
487380
488 pub fn simd_prefix_66(self: *Vex) void {381test "Register enc - different classes" {
489 self.simd_prefix = 0b01;382 try expect(Register.al.enc() == Register.ax.enc());
490 }383 try expect(Register.ax.enc() == Register.eax.enc());
384 try expect(Register.eax.enc() == Register.rax.enc());
385 try expect(Register.ymm0.enc() == Register.rax.enc());
386 try expect(Register.xmm0.enc() == Register.ymm0.enc());
387 try expect(Register.es.enc() == Register.rax.enc());
388}
491389
492 pub fn simd_prefix_f3(self: *Vex) void {390test "Register classes" {
493 self.simd_prefix = 0b10;391 try expect(Register.r11.class() == .general_purpose);
494 }392 try expect(Register.ymm11.class() == .floating_point);
393 try expect(Register.fs.class() == .segment);
394}
495395
496 pub fn simd_prefix_f2(self: *Vex) void {396pub const Memory = union(enum) {
497 self.simd_prefix = 0b11;397 sib: Sib,
498 }398 rip: Rip,
399 moffs: Moffs,
499400
500 pub fn wig(self: *Vex) void {401 pub const ScaleIndex = packed struct {
501 self.wig_desc = true;402 scale: u4,
502 }403 index: Register,
404 };
503405
504 pub fn lig(self: *Vex) void {406 pub const PtrSize = enum {
505 self.lig_desc = true;407 byte,
408 word,
409 dword,
410 qword,
411 tbyte,
412
413 pub fn fromSize(size: u32) PtrSize {
414 return if (size <= 1)
415 .byte
416 else if (size <= 2)
417 .word
418 else if (size <= 4)
419 .dword
420 else if (size <= 8)
421 .qword
422 else if (size == 10)
423 .tbyte
424 else
425 unreachable;
506 }426 }
507427
508 pub fn lz(self: *Vex) void {428 pub fn fromBitSize(bit_size: u64) PtrSize {
509 self.lz_desc = true;429 return switch (bit_size) {
430 8 => .byte,
431 16 => .word,
432 32 => .dword,
433 64 => .qword,
434 80 => .tbyte,
435 else => unreachable,
436 };
510 }437 }
511438
512 pub fn write(self: Vex, writer: anytype) usize {439 pub fn bitSize(s: PtrSize) u64 {
513 var buf: [3]u8 = .{0} ** 3;440 return switch (s) {
514 const form_3byte: bool = blk: {441 .byte => 8,
515 if (self.rex_prefix.w and !self.wig_desc) break :blk true;442 .word => 16,
516 if (self.rex_prefix.x or self.rex_prefix.b) break :blk true;443 .dword => 32,
517 break :blk self.lead_opc != 0b0_0001;444 .qword => 64,
445 .tbyte => 80,
518 };446 };
519
520 if (self.lz_desc) {
521 assert(self.length == 0);
522 }
523
524 if (form_3byte) {
525 // First byte
526 buf[0] = 0xc4;
527 // Second byte
528 const rxb_mask: u3 = @intCast(u3, @boolToInt(!self.rex_prefix.r)) << 2 |
529 @intCast(u2, @boolToInt(!self.rex_prefix.x)) << 1 |
530 @boolToInt(!self.rex_prefix.b);
531 buf[1] |= @intCast(u8, rxb_mask) << 5;
532 buf[1] |= self.lead_opc;
533 // Third byte
534 buf[2] |= @intCast(u8, @boolToInt(!self.rex_prefix.w)) << 7;
535 buf[2] |= @intCast(u7, self.register) << 3;
536 buf[2] |= @intCast(u3, self.length) << 2;
537 buf[2] |= self.simd_prefix;
538 } else {
539 // First byte
540 buf[0] = 0xc5;
541 // Second byte
542 buf[1] |= @intCast(u8, @boolToInt(!self.rex_prefix.r)) << 7;
543 buf[1] |= @intCast(u7, self.register) << 3;
544 buf[1] |= @intCast(u3, self.length) << 2;
545 buf[1] |= self.simd_prefix;
546 }
547
548 const count: usize = if (form_3byte) 3 else 2;
549 _ = writer.writeAll(buf[0..count]) catch unreachable;
550 return count;
551 }447 }
552 };448 };
553449
554 pub fn vex(self: Self, prefix: Vex) void {450 pub const Sib = struct {
555 _ = prefix.write(self.code.writer());451 ptr_size: PtrSize,
556 }452 base: ?Register,
557453 scale_index: ?ScaleIndex,
558 /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB454 disp: i32,
559 pub const Rex = struct {
560 /// Wide, enables 64-bit operation
561 w: bool = false,
562 /// Extends the reg field in the ModR/M byte
563 r: bool = false,
564 /// Extends the index field in the SIB byte
565 x: bool = false,
566 /// Extends the r/m field in the ModR/M byte,
567 /// or the base field in the SIB byte,
568 /// or the reg field in the Opcode byte
569 b: bool = false,
570 };455 };
571456
572 /// Encodes a REX prefix byte given all the fields457 pub const Rip = struct {
573 ///458 ptr_size: PtrSize,
574 /// Use this byte whenever you need 64 bit operation,459 disp: i32,
575 /// or one of reg, index, r/m, base, or opcode-reg might be extended.460 };
576 ///
577 /// See struct `Rex` for a description of each field.
578 ///
579 /// Does not add a prefix byte if none of the fields are set!
580 pub fn rex(self: Self, byte: Rex) void {
581 var value: u8 = 0b0100_0000;
582
583 if (byte.w) value |= 0b1000;
584 if (byte.r) value |= 0b0100;
585 if (byte.x) value |= 0b0010;
586 if (byte.b) value |= 0b0001;
587
588 if (value != 0b0100_0000) {
589 self.code.appendAssumeCapacity(value);
590 }
591 }
592
593 // ------
594 // Opcode
595 // ------
596
597 /// Encodes a 1 byte opcode
598 pub fn opcode_1byte(self: Self, opcode: u8) void {
599 self.code.appendAssumeCapacity(opcode);
600 }
601
602 /// Encodes a 2 byte opcode
603 ///
604 /// e.g. IMUL has the opcode 0x0f 0xaf, so you use
605 ///
606 /// encoder.opcode_2byte(0x0f, 0xaf);
607 pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void {
608 self.code.appendAssumeCapacity(prefix);
609 self.code.appendAssumeCapacity(opcode);
610 }
611
612 /// Encodes a 3 byte opcode
613 ///
614 /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10
615 ///
616 /// encoder.opcode_3byte(0xf2, 0x0f, 0x10);
617 pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) void {
618 self.code.appendAssumeCapacity(prefix_1);
619 self.code.appendAssumeCapacity(prefix_2);
620 self.code.appendAssumeCapacity(opcode);
621 }
622
623 /// Encodes a 1 byte opcode with a reg field
624 ///
625 /// Remember to add a REX prefix byte if reg is extended!
626 pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void {
627 assert(opcode & 0b111 == 0);
628 self.code.appendAssumeCapacity(opcode | reg);
629 }
630
631 // ------
632 // ModR/M
633 // ------
634
635 /// Construct a ModR/M byte given all the fields
636 ///
637 /// Remember to add a REX prefix byte if reg or rm are extended!
638 pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void {
639 self.code.appendAssumeCapacity(
640 @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm,
641 );
642 }
643
644 /// Construct a ModR/M byte using direct r/m addressing
645 /// r/m effective address: r/m
646 ///
647 /// Note reg's effective address is always just reg for the ModR/M byte.
648 /// Remember to add a REX prefix byte if reg or rm are extended!
649 pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void {
650 self.modRm(0b11, reg_or_opx, rm);
651 }
652
653 /// Construct a ModR/M byte using indirect r/m addressing
654 /// r/m effective address: [r/m]
655 ///
656 /// Note reg's effective address is always just reg for the ModR/M byte.
657 /// Remember to add a REX prefix byte if reg or rm are extended!
658 pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void {
659 assert(rm != 4 and rm != 5);
660 self.modRm(0b00, reg_or_opx, rm);
661 }
662
663 /// Construct a ModR/M byte using indirect SIB addressing
664 /// r/m effective address: [SIB]
665 ///
666 /// Note reg's effective address is always just reg for the ModR/M byte.
667 /// Remember to add a REX prefix byte if reg or rm are extended!
668 pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void {
669 self.modRm(0b00, reg_or_opx, 0b100);
670 }
671
672 /// Construct a ModR/M byte using RIP-relative addressing
673 /// r/m effective address: [RIP + disp32]
674 ///
675 /// Note reg's effective address is always just reg for the ModR/M byte.
676 /// Remember to add a REX prefix byte if reg or rm are extended!
677 pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void {
678 self.modRm(0b00, reg_or_opx, 0b101);
679 }
680
681 /// Construct a ModR/M byte using indirect r/m with a 8bit displacement
682 /// r/m effective address: [r/m + disp8]
683 ///
684 /// Note reg's effective address is always just reg for the ModR/M byte.
685 /// Remember to add a REX prefix byte if reg or rm are extended!
686 pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void {
687 assert(rm != 4);
688 self.modRm(0b01, reg_or_opx, rm);
689 }
690
691 /// Construct a ModR/M byte using indirect SIB with a 8bit displacement
692 /// r/m effective address: [SIB + disp8]
693 ///
694 /// Note reg's effective address is always just reg for the ModR/M byte.
695 /// Remember to add a REX prefix byte if reg or rm are extended!
696 pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void {
697 self.modRm(0b01, reg_or_opx, 0b100);
698 }
699
700 /// Construct a ModR/M byte using indirect r/m with a 32bit displacement
701 /// r/m effective address: [r/m + disp32]
702 ///
703 /// Note reg's effective address is always just reg for the ModR/M byte.
704 /// Remember to add a REX prefix byte if reg or rm are extended!
705 pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void {
706 assert(rm != 4);
707 self.modRm(0b10, reg_or_opx, rm);
708 }
709
710 /// Construct a ModR/M byte using indirect SIB with a 32bit displacement
711 /// r/m effective address: [SIB + disp32]
712 ///
713 /// Note reg's effective address is always just reg for the ModR/M byte.
714 /// Remember to add a REX prefix byte if reg or rm are extended!
715 pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void {
716 self.modRm(0b10, reg_or_opx, 0b100);
717 }
718
719 // ---
720 // SIB
721 // ---
722
723 /// Construct a SIB byte given all the fields
724 ///
725 /// Remember to add a REX prefix byte if index or base are extended!
726 pub fn sib(self: Self, scale: u2, index: u3, base: u3) void {
727 self.code.appendAssumeCapacity(
728 @as(u8, scale) << 6 | @as(u8, index) << 3 | base,
729 );
730 }
731
732 /// Construct a SIB byte with scale * index + base, no frills.
733 /// r/m effective address: [base + scale * index]
734 ///
735 /// Remember to add a REX prefix byte if index or base are extended!
736 pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void {
737 assert(base != 5);
738
739 self.sib(scale, index, base);
740 }
741
742 /// Construct a SIB byte with scale * index + disp32
743 /// r/m effective address: [scale * index + disp32]
744 ///
745 /// Remember to add a REX prefix byte if index or base are extended!
746 pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void {
747 assert(index != 4);
748
749 // scale is actually ignored
750 // index = 4 means no index
751 // base = 5 means no base, if mod == 0.
752 self.sib(scale, index, 5);
753 }
754
755 /// Construct a SIB byte with just base
756 /// r/m effective address: [base]
757 ///
758 /// Remember to add a REX prefix byte if index or base are extended!
759 pub fn sib_base(self: Self, base: u3) void {
760 assert(base != 5);
761
762 // scale is actually ignored
763 // index = 4 means no index
764 self.sib(0, 4, base);
765 }
766
767 /// Construct a SIB byte with just disp32
768 /// r/m effective address: [disp32]
769 ///
770 /// Remember to add a REX prefix byte if index or base are extended!
771 pub fn sib_disp32(self: Self) void {
772 // scale is actually ignored
773 // index = 4 means no index
774 // base = 5 means no base, if mod == 0.
775 self.sib(0, 4, 5);
776 }
777
778 /// Construct a SIB byte with scale * index + base + disp8
779 /// r/m effective address: [base + scale * index + disp8]
780 ///
781 /// Remember to add a REX prefix byte if index or base are extended!
782 pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void {
783 self.sib(scale, index, base);
784 }
785
786 /// Construct a SIB byte with base + disp8, no index
787 /// r/m effective address: [base + disp8]
788 ///
789 /// Remember to add a REX prefix byte if index or base are extended!
790 pub fn sib_baseDisp8(self: Self, base: u3) void {
791 // scale is ignored
792 // index = 4 means no index
793 self.sib(0, 4, base);
794 }
795461
796 /// Construct a SIB byte with scale * index + base + disp32462 pub const Moffs = struct {
797 /// r/m effective address: [base + scale * index + disp32]463 seg: Register,
798 ///464 offset: u64,
799 /// Remember to add a REX prefix byte if index or base are extended!465 };
800 pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void {
801 self.sib(scale, index, base);
802 }
803466
804 /// Construct a SIB byte with base + disp32, no index467 pub fn moffs(reg: Register, offset: u64) Memory {
805 /// r/m effective address: [base + disp32]468 assert(reg.class() == .segment);
806 ///469 return .{ .moffs = .{ .seg = reg, .offset = offset } };
807 /// Remember to add a REX prefix byte if index or base are extended!
808 pub fn sib_baseDisp32(self: Self, base: u3) void {
809 // scale is ignored
810 // index = 4 means no index
811 self.sib(0, 4, base);
812 }470 }
813471
814 // -------------------------472 pub fn sib(ptr_size: PtrSize, args: struct {
815 // Trivial (no bit fiddling)473 disp: i32,
816 // -------------------------474 base: ?Register = null,
817475 scale_index: ?ScaleIndex = null,
818 /// Encode an 8 bit immediate476 }) Memory {
819 ///477 return .{ .sib = .{
820 /// It is sign-extended to 64 bits by the cpu.478 .base = args.base,
821 pub fn imm8(self: Self, imm: i8) void {479 .disp = args.disp,
822 self.code.appendAssumeCapacity(@bitCast(u8, imm));480 .ptr_size = ptr_size,
481 .scale_index = args.scale_index,
482 } };
823 }483 }
824484
825 /// Encode an 8 bit displacement485 pub fn rip(ptr_size: PtrSize, disp: i32) Memory {
826 ///486 return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } };
827 /// It is sign-extended to 64 bits by the cpu.
828 pub fn disp8(self: Self, disp: i8) void {
829 self.code.appendAssumeCapacity(@bitCast(u8, disp));
830 }487 }
831488
832 /// Encode an 16 bit immediate489 pub fn isSegmentRegister(mem: Memory) bool {
833 ///490 return switch (mem) {
834 /// It is sign-extended to 64 bits by the cpu.491 .moffs => true,
835 pub fn imm16(self: Self, imm: i16) void {492 .rip => false,
836 self.writeIntLittle(i16, imm);493 .sib => |s| if (s.base) |r| r.class() == .segment else false,
494 };
837 }495 }
838496
839 /// Encode an 32 bit immediate497 pub fn base(mem: Memory) ?Register {
840 ///498 return switch (mem) {
841 /// It is sign-extended to 64 bits by the cpu.499 .moffs => |m| m.seg,
842 pub fn imm32(self: Self, imm: i32) void {500 .sib => |s| s.base,
843 self.writeIntLittle(i32, imm);501 .rip => null,
502 };
844 }503 }
845504
846 /// Encode an 32 bit displacement505 pub fn scaleIndex(mem: Memory) ?ScaleIndex {
847 ///506 return switch (mem) {
848 /// It is sign-extended to 64 bits by the cpu.507 .moffs, .rip => null,
849 pub fn disp32(self: Self, disp: i32) void {508 .sib => |s| s.scale_index,
850 self.writeIntLittle(i32, disp);509 };
851 }510 }
852511
853 /// Encode an 64 bit immediate512 pub fn bitSize(mem: Memory) u64 {
854 ///513 return switch (mem) {
855 /// It is sign-extended to 64 bits by the cpu.514 .rip => |r| r.ptr_size.bitSize(),
856 pub fn imm64(self: Self, imm: u64) void {515 .sib => |s| s.ptr_size.bitSize(),
857 self.writeIntLittle(u64, imm);516 .moffs => unreachable,
517 };
858 }518 }
859};519};
860520
861test "Encoder helpers - general purpose registers" {521pub const Immediate = union(enum) {
862 var code = ArrayList(u8).init(testing.allocator);522 signed: i32,
863 defer code.deinit();523 unsigned: u64,
864
865 // simple integer multiplication
866
867 // imul eax,edi
868 // 0faf c7
869 {
870 try code.resize(0);
871 const encoder = try Encoder.init(&code, 4);
872 encoder.rex(.{
873 .r = Register.eax.isExtended(),
874 .b = Register.edi.isExtended(),
875 });
876 encoder.opcode_2byte(0x0f, 0xaf);
877 encoder.modRm_direct(
878 Register.eax.lowEnc(),
879 Register.edi.lowEnc(),
880 );
881
882 try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items);
883 }
884
885 // simple mov
886
887 // mov eax,edi
888 // 89 f8
889 {
890 try code.resize(0);
891 const encoder = try Encoder.init(&code, 3);
892 encoder.rex(.{
893 .r = Register.edi.isExtended(),
894 .b = Register.eax.isExtended(),
895 });
896 encoder.opcode_1byte(0x89);
897 encoder.modRm_direct(
898 Register.edi.lowEnc(),
899 Register.eax.lowEnc(),
900 );
901
902 try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items);
903 }
904
905 // signed integer addition of 32-bit sign extended immediate to 64 bit register
906
907 // add rcx, 2147483647
908 //
909 // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding
910 //
911 // 48 : REX.W set for 64 bit operand (*r*cx)
912 // 81 : opcode for "<arithmetic> with immediate"
913 // c1 : id = rcx,
914 // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx)
915 // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD
916 // : 001 <-- 001 is rcx
917 // ffffff7f : 2147483647
918 {
919 try code.resize(0);
920 const encoder = try Encoder.init(&code, 7);
921 encoder.rex(.{ .w = true }); // use 64 bit operation
922 encoder.opcode_1byte(0x81);
923 encoder.modRm_direct(
924 0,
925 Register.rcx.lowEnc(),
926 );
927 encoder.imm32(2147483647);
928
929 try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items);
930 }
931}
932
933test "Encoder helpers - Vex prefix" {
934 var buf: [3]u8 = undefined;
935 var stream = std.io.fixedBufferStream(&buf);
936 const writer = stream.writer();
937
938 {
939 var vex_prefix = Encoder.Vex{};
940 vex_prefix.rex(.{
941 .r = true,
942 });
943 const nwritten = vex_prefix.write(writer);
944 try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, buf[0..nwritten]);
945 }
946
947 {
948 stream.reset();
949 var vex_prefix = Encoder.Vex{};
950 vex_prefix.reg(Register.xmm15.enc());
951 const nwritten = vex_prefix.write(writer);
952 try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]);
953 }
954524
955 {525 pub fn u(x: u64) Immediate {
956 stream.reset();526 return .{ .unsigned = x };
957 var vex_prefix = Encoder.Vex{};
958 vex_prefix.rex(.{
959 .w = true,
960 .x = true,
961 });
962 const nwritten = vex_prefix.write(writer);
963 try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, buf[0..nwritten]);
964 }527 }
965528
966 {529 pub fn s(x: i32) Immediate {
967 stream.reset();530 return .{ .signed = x };
968 var vex_prefix = Encoder.Vex{};
969 vex_prefix.rex(.{
970 .w = true,
971 .r = true,
972 });
973 vex_prefix.len_256();
974 vex_prefix.lead_opc_0f();
975 vex_prefix.simd_prefix_66();
976 const nwritten = vex_prefix.write(writer);
977 try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, buf[0..nwritten]);
978 }531 }
979532
980 var code = ArrayList(u8).init(testing.allocator);533 pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
981 defer code.deinit();534 return switch (imm) {
982535 .signed => |x| switch (bit_size) {
983 {536 1, 8 => @bitCast(u8, @intCast(i8, x)),
984 // vmovapd xmm1, xmm2537 16 => @bitCast(u16, @intCast(i16, x)),
985 const encoder = try Encoder.init(&code, 4);538 32, 64 => @bitCast(u32, x),
986 var vex = Encoder.Vex{};539 else => unreachable,
987 vex.simd_prefix_66();540 },
988 encoder.vex(vex); // use 64 bit operation541 .unsigned => |x| switch (bit_size) {
989 encoder.opcode_1byte(0x28);542 1, 8 => @intCast(u8, x),
990 encoder.modRm_direct(0, Register.xmm1.lowEnc());543 16 => @intCast(u16, x),
991 try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items);544 32 => @intCast(u32, x),
992 }545 64 => x,
993546 else => unreachable,
994 {547 },
995 try code.resize(0);548 };
996
997 // vmovhpd xmm13, xmm1, qword ptr [rip]
998 const encoder = try Encoder.init(&code, 9);
999 var vex = Encoder.Vex{};
1000 vex.len_128();
1001 vex.simd_prefix_66();
1002 vex.lead_opc_0f();
1003 vex.rex(.{ .r = true });
1004 vex.reg(Register.xmm1.enc());
1005 encoder.vex(vex);
1006 encoder.opcode_1byte(0x16);
1007 encoder.modRm_RIPDisp32(Register.xmm13.lowEnc());
1008 encoder.disp32(0);
1009 try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items);
1010 }549 }
1011}550};
1012
1013// TODO add these registers to the enum and populate dwarfLocOp
1014// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
1015// RA = (16, "RA"),
1016//
1017// XMM0 = (17, "xmm0"),
1018// XMM1 = (18, "xmm1"),
1019// XMM2 = (19, "xmm2"),
1020// XMM3 = (20, "xmm3"),
1021// XMM4 = (21, "xmm4"),
1022// XMM5 = (22, "xmm5"),
1023// XMM6 = (23, "xmm6"),
1024// XMM7 = (24, "xmm7"),
1025//
1026// XMM8 = (25, "xmm8"),
1027// XMM9 = (26, "xmm9"),
1028// XMM10 = (27, "xmm10"),
1029// XMM11 = (28, "xmm11"),
1030// XMM12 = (29, "xmm12"),
1031// XMM13 = (30, "xmm13"),
1032// XMM14 = (31, "xmm14"),
1033// XMM15 = (32, "xmm15"),
1034//
1035// ST0 = (33, "st0"),
1036// ST1 = (34, "st1"),
1037// ST2 = (35, "st2"),
1038// ST3 = (36, "st3"),
1039// ST4 = (37, "st4"),
1040// ST5 = (38, "st5"),
1041// ST6 = (39, "st6"),
1042// ST7 = (40, "st7"),
1043//
1044// MM0 = (41, "mm0"),
1045// MM1 = (42, "mm1"),
1046// MM2 = (43, "mm2"),
1047// MM3 = (44, "mm3"),
1048// MM4 = (45, "mm4"),
1049// MM5 = (46, "mm5"),
1050// MM6 = (47, "mm6"),
1051// MM7 = (48, "mm7"),
1052//
1053// RFLAGS = (49, "rFLAGS"),
1054// ES = (50, "es"),
1055// CS = (51, "cs"),
1056// SS = (52, "ss"),
1057// DS = (53, "ds"),
1058// FS = (54, "fs"),
1059// GS = (55, "gs"),
1060//
1061// FS_BASE = (58, "fs.base"),
1062// GS_BASE = (59, "gs.base"),
1063//
1064// TR = (62, "tr"),
1065// LDTR = (63, "ldtr"),
1066// MXCSR = (64, "mxcsr"),
1067// FCW = (65, "fcw"),
1068// FSW = (66, "fsw"),
1069//
1070// XMM16 = (67, "xmm16"),
1071// XMM17 = (68, "xmm17"),
1072// XMM18 = (69, "xmm18"),
1073// XMM19 = (70, "xmm19"),
1074// XMM20 = (71, "xmm20"),
1075// XMM21 = (72, "xmm21"),
1076// XMM22 = (73, "xmm22"),
1077// XMM23 = (74, "xmm23"),
1078// XMM24 = (75, "xmm24"),
1079// XMM25 = (76, "xmm25"),
1080// XMM26 = (77, "xmm26"),
1081// XMM27 = (78, "xmm27"),
1082// XMM28 = (79, "xmm28"),
1083// XMM29 = (80, "xmm29"),
1084// XMM30 = (81, "xmm30"),
1085// XMM31 = (82, "xmm31"),
1086//
1087// K0 = (118, "k0"),
1088// K1 = (119, "k1"),
1089// K2 = (120, "k2"),
1090// K3 = (121, "k3"),
1091// K4 = (122, "k4"),
1092// K5 = (123, "k5"),
1093// K6 = (124, "k6"),
1094// K7 = (125, "k7"),
src/arch/x86_64/encoder.zig created+2275
...@@ -0,0 +1,2275 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.x86_64_encoder);
4const math = std.math;
5const testing = std.testing;
6
7const bits = @import("bits.zig");
8const Encoding = @import("Encoding.zig");
9const Immediate = bits.Immediate;
10const Memory = bits.Memory;
11const Register = bits.Register;
12
13pub const Instruction = struct {
14 op1: Operand = .none,
15 op2: Operand = .none,
16 op3: Operand = .none,
17 op4: Operand = .none,
18 encoding: Encoding,
19
20 pub const Mnemonic = Encoding.Mnemonic;
21
22 pub const Operand = union(enum) {
23 none,
24 reg: Register,
25 mem: Memory,
26 imm: Immediate,
27
28 /// Returns the bitsize of the operand.
29 pub fn bitSize(op: Operand) u64 {
30 return switch (op) {
31 .none => unreachable,
32 .reg => |reg| reg.bitSize(),
33 .mem => |mem| mem.bitSize(),
34 .imm => unreachable,
35 };
36 }
37
38 /// Returns true if the operand is a segment register.
39 /// Asserts the operand is either register or memory.
40 pub fn isSegmentRegister(op: Operand) bool {
41 return switch (op) {
42 .none => unreachable,
43 .reg => |reg| reg.class() == .segment,
44 .mem => |mem| mem.isSegmentRegister(),
45 .imm => unreachable,
46 };
47 }
48
49 pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !void {
50 switch (op) {
51 .none => {},
52 .reg => |reg| try writer.writeAll(@tagName(reg)),
53 .mem => |mem| switch (mem) {
54 .rip => |rip| {
55 try writer.print("{s} ptr [rip", .{@tagName(rip.ptr_size)});
56 if (rip.disp != 0) {
57 const sign_bit = if (sign(rip.disp) < 0) "-" else "+";
58 const disp_abs = try std.math.absInt(rip.disp);
59 try writer.print(" {s} 0x{x}", .{ sign_bit, disp_abs });
60 }
61 try writer.writeByte(']');
62 },
63 .sib => |sib| {
64 try writer.print("{s} ptr ", .{@tagName(sib.ptr_size)});
65
66 if (mem.isSegmentRegister()) {
67 return writer.print("{s}:0x{x}", .{ @tagName(sib.base.?), sib.disp });
68 }
69
70 try writer.writeByte('[');
71
72 if (sib.base) |base| {
73 try writer.print("{s}", .{@tagName(base)});
74 }
75 if (sib.scale_index) |si| {
76 if (sib.base != null) {
77 try writer.writeAll(" + ");
78 }
79 try writer.print("{s} * {d}", .{ @tagName(si.index), si.scale });
80 }
81 if (sib.disp != 0) {
82 if (sib.base != null or sib.scale_index != null) {
83 try writer.writeByte(' ');
84 }
85 try writer.writeByte(if (sign(sib.disp) < 0) '-' else '+');
86 const disp_abs = try std.math.absInt(sib.disp);
87 try writer.print(" 0x{x}", .{disp_abs});
88 }
89
90 try writer.writeByte(']');
91 },
92 .moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }),
93 },
94 .imm => |imm| try writer.print("0x{x}", .{imm.asUnsigned(enc_op.bitSize())}),
95 }
96 }
97 };
98
99 pub fn new(mnemonic: Mnemonic, args: struct {
100 op1: Operand = .none,
101 op2: Operand = .none,
102 op3: Operand = .none,
103 op4: Operand = .none,
104 }) !Instruction {
105 const encoding = (try Encoding.findByMnemonic(mnemonic, .{
106 .op1 = args.op1,
107 .op2 = args.op2,
108 .op3 = args.op3,
109 .op4 = args.op4,
110 })) orelse {
111 log.debug("no encoding found for: {s} {s} {s} {s} {s}", .{
112 @tagName(mnemonic),
113 @tagName(Encoding.Op.fromOperand(args.op1)),
114 @tagName(Encoding.Op.fromOperand(args.op2)),
115 @tagName(Encoding.Op.fromOperand(args.op3)),
116 @tagName(Encoding.Op.fromOperand(args.op4)),
117 });
118 return error.InvalidInstruction;
119 };
120 log.debug("selected encoding: {}", .{encoding});
121 return .{
122 .op1 = args.op1,
123 .op2 = args.op2,
124 .op3 = args.op3,
125 .op4 = args.op4,
126 .encoding = encoding,
127 };
128 }
129
130 pub fn fmtPrint(inst: Instruction, writer: anytype) !void {
131 try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)});
132 const ops = [_]struct { Operand, Encoding.Op }{
133 .{ inst.op1, inst.encoding.op1 },
134 .{ inst.op2, inst.encoding.op2 },
135 .{ inst.op3, inst.encoding.op3 },
136 .{ inst.op4, inst.encoding.op4 },
137 };
138 for (&ops, 0..) |op, i| {
139 if (op[0] == .none) break;
140 if (i > 0) {
141 try writer.writeByte(',');
142 }
143 try writer.writeByte(' ');
144 try op[0].fmtPrint(op[1], writer);
145 }
146 }
147
148 pub fn encode(inst: Instruction, writer: anytype) !void {
149 const encoder = Encoder(@TypeOf(writer)){ .writer = writer };
150 const encoding = inst.encoding;
151
152 try inst.encodeLegacyPrefixes(encoder);
153 try inst.encodeMandatoryPrefix(encoder);
154 try inst.encodeRexPrefix(encoder);
155 try inst.encodeOpcode(encoder);
156
157 switch (encoding.op_en) {
158 .np, .o => {},
159 .i, .d => try encodeImm(inst.op1.imm, encoding.op1, encoder),
160 .zi, .oi => try encodeImm(inst.op2.imm, encoding.op2, encoder),
161 .fd => try encoder.imm64(inst.op2.mem.moffs.offset),
162 .td => try encoder.imm64(inst.op1.mem.moffs.offset),
163 else => {
164 const mem_op = switch (encoding.op_en) {
165 .m, .mi, .m1, .mc, .mr => inst.op1,
166 .rm, .rmi => inst.op2,
167 else => unreachable,
168 };
169 switch (mem_op) {
170 .reg => |reg| {
171 const rm = switch (encoding.op_en) {
172 .m, .mi, .m1, .mc => encoding.modRmExt(),
173 .mr => inst.op2.reg.lowEnc(),
174 .rm, .rmi => inst.op1.reg.lowEnc(),
175 else => unreachable,
176 };
177 try encoder.modRm_direct(rm, reg.lowEnc());
178 },
179 .mem => |mem| {
180 const op = switch (encoding.op_en) {
181 .m, .mi, .m1, .mc => .none,
182 .mr => inst.op2,
183 .rm, .rmi => inst.op1,
184 else => unreachable,
185 };
186 try encodeMemory(encoding, mem, op, encoder);
187 },
188 else => unreachable,
189 }
190
191 switch (encoding.op_en) {
192 .mi => try encodeImm(inst.op2.imm, encoding.op2, encoder),
193 .rmi => try encodeImm(inst.op3.imm, encoding.op3, encoder),
194 else => {},
195 }
196 },
197 }
198 }
199
200 fn encodeOpcode(inst: Instruction, encoder: anytype) !void {
201 const opcode = inst.encoding.opcode();
202 switch (inst.encoding.op_en) {
203 .o, .oi => try encoder.opcode_withReg(opcode[0], inst.op1.reg.lowEnc()),
204 else => {
205 const index: usize = if (inst.encoding.mandatoryPrefix()) |_| 1 else 0;
206 for (opcode[index..]) |byte| {
207 try encoder.opcode_1byte(byte);
208 }
209 },
210 }
211 }
212
213 fn encodeLegacyPrefixes(inst: Instruction, encoder: anytype) !void {
214 const enc = inst.encoding;
215 const op_en = enc.op_en;
216
217 var legacy = LegacyPrefixes{};
218 if (enc.mode == .none) {
219 const bit_size = enc.operandBitSize();
220 if (bit_size == 16) {
221 legacy.set16BitOverride();
222 }
223 }
224
225 const segment_override: ?Register = switch (op_en) {
226 .i, .zi, .o, .oi, .d, .np => null,
227 .fd => inst.op2.mem.base().?,
228 .td => inst.op1.mem.base().?,
229 .rm, .rmi => if (inst.op2.isSegmentRegister()) blk: {
230 break :blk switch (inst.op2) {
231 .reg => |r| r,
232 .mem => |m| m.base().?,
233 else => unreachable,
234 };
235 } else null,
236 .m, .mi, .m1, .mc, .mr => if (inst.op1.isSegmentRegister()) blk: {
237 break :blk switch (inst.op1) {
238 .reg => |r| r,
239 .mem => |m| m.base().?,
240 else => unreachable,
241 };
242 } else null,
243 };
244 if (segment_override) |seg| {
245 legacy.setSegmentOverride(seg);
246 }
247
248 try encoder.legacyPrefixes(legacy);
249 }
250
251 fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void {
252 const op_en = inst.encoding.op_en;
253
254 var rex = Rex{};
255 rex.present = inst.encoding.mode == .rex;
256 rex.w = inst.encoding.mode == .long;
257
258 switch (op_en) {
259 .np, .i, .zi, .fd, .td, .d => {},
260 .o, .oi => {
261 rex.b = inst.op1.reg.isExtended();
262 },
263 .m, .mi, .m1, .mc, .mr, .rm, .rmi => {
264 const r_op = switch (op_en) {
265 .rm, .rmi => inst.op1,
266 .mr => inst.op2,
267 else => null,
268 };
269 if (r_op) |op| {
270 rex.r = op.reg.isExtended();
271 }
272
273 const b_x_op = switch (op_en) {
274 .rm, .rmi => inst.op2,
275 .m, .mi, .m1, .mc, .mr => inst.op1,
276 else => unreachable,
277 };
278 switch (b_x_op) {
279 .reg => |r| {
280 rex.b = r.isExtended();
281 },
282 .mem => |mem| {
283 rex.b = if (mem.base()) |base| base.isExtended() else false;
284 rex.x = if (mem.scaleIndex()) |si| si.index.isExtended() else false;
285 },
286 else => unreachable,
287 }
288 },
289 }
290
291 try encoder.rex(rex);
292 }
293
294 fn encodeMandatoryPrefix(inst: Instruction, encoder: anytype) !void {
295 const prefix = inst.encoding.mandatoryPrefix() orelse return;
296 try encoder.opcode_1byte(prefix);
297 }
298
299 fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void {
300 const operand_enc = switch (operand) {
301 .reg => |reg| reg.lowEnc(),
302 .none => encoding.modRmExt(),
303 else => unreachable,
304 };
305
306 switch (mem) {
307 .moffs => unreachable,
308 .sib => |sib| {
309 if (sib.base) |base| {
310 if (base.class() == .segment) {
311 // TODO audit this wrt SIB
312 try encoder.modRm_SIBDisp0(operand_enc);
313 if (sib.scale_index) |si| {
314 const scale = math.log2_int(u4, si.scale);
315 try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
316 } else {
317 try encoder.sib_disp32();
318 }
319 try encoder.disp32(sib.disp);
320 } else {
321 assert(base.class() == .general_purpose);
322 const dst = base.lowEnc();
323 const src = operand_enc;
324 if (dst == 4 or sib.scale_index != null) {
325 if (sib.disp == 0 and dst != 5) {
326 try encoder.modRm_SIBDisp0(src);
327 if (sib.scale_index) |si| {
328 const scale = math.log2_int(u4, si.scale);
329 try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
330 } else {
331 try encoder.sib_base(dst);
332 }
333 } else if (math.cast(i8, sib.disp)) |_| {
334 try encoder.modRm_SIBDisp8(src);
335 if (sib.scale_index) |si| {
336 const scale = math.log2_int(u4, si.scale);
337 try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
338 } else {
339 try encoder.sib_baseDisp8(dst);
340 }
341 try encoder.disp8(@truncate(i8, sib.disp));
342 } else {
343 try encoder.modRm_SIBDisp32(src);
344 if (sib.scale_index) |si| {
345 const scale = math.log2_int(u4, si.scale);
346 try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
347 } else {
348 try encoder.sib_baseDisp32(dst);
349 }
350 try encoder.disp32(sib.disp);
351 }
352 } else {
353 if (sib.disp == 0 and dst != 5) {
354 try encoder.modRm_indirectDisp0(src, dst);
355 } else if (math.cast(i8, sib.disp)) |_| {
356 try encoder.modRm_indirectDisp8(src, dst);
357 try encoder.disp8(@truncate(i8, sib.disp));
358 } else {
359 try encoder.modRm_indirectDisp32(src, dst);
360 try encoder.disp32(sib.disp);
361 }
362 }
363 }
364 } else {
365 try encoder.modRm_SIBDisp0(operand_enc);
366 if (sib.scale_index) |si| {
367 const scale = math.log2_int(u4, si.scale);
368 try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
369 } else {
370 try encoder.sib_disp32();
371 }
372 try encoder.disp32(sib.disp);
373 }
374 },
375 .rip => |rip| {
376 try encoder.modRm_RIPDisp32(operand_enc);
377 try encoder.disp32(rip.disp);
378 },
379 }
380 }
381
382 fn encodeImm(imm: Immediate, kind: Encoding.Op, encoder: anytype) !void {
383 const raw = imm.asUnsigned(kind.bitSize());
384 switch (kind.bitSize()) {
385 8 => try encoder.imm8(@intCast(u8, raw)),
386 16 => try encoder.imm16(@intCast(u16, raw)),
387 32 => try encoder.imm32(@intCast(u32, raw)),
388 64 => try encoder.imm64(raw),
389 else => unreachable,
390 }
391 }
392};
393
394inline fn sign(i: anytype) @TypeOf(i) {
395 return @as(@TypeOf(i), @boolToInt(i > 0)) - @boolToInt(i < 0);
396}
397
398pub const LegacyPrefixes = packed struct {
399 /// LOCK
400 prefix_f0: bool = false,
401 /// REPNZ, REPNE, REP, Scalar Double-precision
402 prefix_f2: bool = false,
403 /// REPZ, REPE, REP, Scalar Single-precision
404 prefix_f3: bool = false,
405
406 /// CS segment override or Branch not taken
407 prefix_2e: bool = false,
408 /// SS segment override
409 prefix_36: bool = false,
410 /// ES segment override
411 prefix_26: bool = false,
412 /// FS segment override
413 prefix_64: bool = false,
414 /// GS segment override
415 prefix_65: bool = false,
416
417 /// Branch taken
418 prefix_3e: bool = false,
419
420 /// Address size override (enables 16 bit address size)
421 prefix_67: bool = false,
422
423 /// Operand size override (enables 16 bit operation)
424 prefix_66: bool = false,
425
426 padding: u5 = 0,
427
428 pub fn setSegmentOverride(self: *LegacyPrefixes, reg: Register) void {
429 assert(reg.class() == .segment);
430 switch (reg) {
431 .cs => self.prefix_2e = true,
432 .ss => self.prefix_36 = true,
433 .es => self.prefix_26 = true,
434 .fs => self.prefix_64 = true,
435 .gs => self.prefix_65 = true,
436 .ds => {},
437 else => unreachable,
438 }
439 }
440
441 pub fn set16BitOverride(self: *LegacyPrefixes) void {
442 self.prefix_66 = true;
443 }
444};
445
446fn Encoder(comptime T: type) type {
447 return struct {
448 writer: T,
449
450 const Self = @This();
451
452 // --------
453 // Prefixes
454 // --------
455
456 /// Encodes legacy prefixes
457 pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) !void {
458 if (@bitCast(u16, prefixes) != 0) {
459 // Hopefully this path isn't taken very often, so we'll do it the slow way for now
460
461 // LOCK
462 if (prefixes.prefix_f0) try self.writer.writeByte(0xf0);
463 // REPNZ, REPNE, REP, Scalar Double-precision
464 if (prefixes.prefix_f2) try self.writer.writeByte(0xf2);
465 // REPZ, REPE, REP, Scalar Single-precision
466 if (prefixes.prefix_f3) try self.writer.writeByte(0xf3);
467
468 // CS segment override or Branch not taken
469 if (prefixes.prefix_2e) try self.writer.writeByte(0x2e);
470 // DS segment override
471 if (prefixes.prefix_36) try self.writer.writeByte(0x36);
472 // ES segment override
473 if (prefixes.prefix_26) try self.writer.writeByte(0x26);
474 // FS segment override
475 if (prefixes.prefix_64) try self.writer.writeByte(0x64);
476 // GS segment override
477 if (prefixes.prefix_65) try self.writer.writeByte(0x65);
478
479 // Branch taken
480 if (prefixes.prefix_3e) try self.writer.writeByte(0x3e);
481
482 // Operand size override
483 if (prefixes.prefix_66) try self.writer.writeByte(0x66);
484
485 // Address size override
486 if (prefixes.prefix_67) try self.writer.writeByte(0x67);
487 }
488 }
489
490 /// Use 16 bit operand size
491 ///
492 /// Note that this flag is overridden by REX.W, if both are present.
493 pub fn prefix16BitMode(self: Self) !void {
494 try self.writer.writeByte(0x66);
495 }
496
497 /// Encodes a REX prefix byte given all the fields
498 ///
499 /// Use this byte whenever you need 64 bit operation,
500 /// or one of reg, index, r/m, base, or opcode-reg might be extended.
501 ///
502 /// See struct `Rex` for a description of each field.
503 pub fn rex(self: Self, byte: Rex) !void {
504 if (!byte.present and !byte.isSet()) return;
505
506 var value: u8 = 0b0100_0000;
507
508 if (byte.w) value |= 0b1000;
509 if (byte.r) value |= 0b0100;
510 if (byte.x) value |= 0b0010;
511 if (byte.b) value |= 0b0001;
512
513 try self.writer.writeByte(value);
514 }
515
516 // ------
517 // Opcode
518 // ------
519
520 /// Encodes a 1 byte opcode
521 pub fn opcode_1byte(self: Self, opcode: u8) !void {
522 try self.writer.writeByte(opcode);
523 }
524
525 /// Encodes a 2 byte opcode
526 ///
527 /// e.g. IMUL has the opcode 0x0f 0xaf, so you use
528 ///
529 /// encoder.opcode_2byte(0x0f, 0xaf);
530 pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) !void {
531 try self.writer.writeAll(&.{ prefix, opcode });
532 }
533
534 /// Encodes a 3 byte opcode
535 ///
536 /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10
537 ///
538 /// encoder.opcode_3byte(0xf2, 0x0f, 0x10);
539 pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) !void {
540 try self.writer.writeAll(&.{ prefix_1, prefix_2, opcode });
541 }
542
543 /// Encodes a 1 byte opcode with a reg field
544 ///
545 /// Remember to add a REX prefix byte if reg is extended!
546 pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) !void {
547 assert(opcode & 0b111 == 0);
548 try self.writer.writeByte(opcode | reg);
549 }
550
551 // ------
552 // ModR/M
553 // ------
554
555 /// Construct a ModR/M byte given all the fields
556 ///
557 /// Remember to add a REX prefix byte if reg or rm are extended!
558 pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) !void {
559 try self.writer.writeByte(@as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm);
560 }
561
562 /// Construct a ModR/M byte using direct r/m addressing
563 /// r/m effective address: r/m
564 ///
565 /// Note reg's effective address is always just reg for the ModR/M byte.
566 /// Remember to add a REX prefix byte if reg or rm are extended!
567 pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) !void {
568 try self.modRm(0b11, reg_or_opx, rm);
569 }
570
571 /// Construct a ModR/M byte using indirect r/m addressing
572 /// r/m effective address: [r/m]
573 ///
574 /// Note reg's effective address is always just reg for the ModR/M byte.
575 /// Remember to add a REX prefix byte if reg or rm are extended!
576 pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) !void {
577 assert(rm != 4 and rm != 5);
578 try self.modRm(0b00, reg_or_opx, rm);
579 }
580
581 /// Construct a ModR/M byte using indirect SIB addressing
582 /// r/m effective address: [SIB]
583 ///
584 /// Note reg's effective address is always just reg for the ModR/M byte.
585 /// Remember to add a REX prefix byte if reg or rm are extended!
586 pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) !void {
587 try self.modRm(0b00, reg_or_opx, 0b100);
588 }
589
590 /// Construct a ModR/M byte using RIP-relative addressing
591 /// r/m effective address: [RIP + disp32]
592 ///
593 /// Note reg's effective address is always just reg for the ModR/M byte.
594 /// Remember to add a REX prefix byte if reg or rm are extended!
595 pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) !void {
596 try self.modRm(0b00, reg_or_opx, 0b101);
597 }
598
599 /// Construct a ModR/M byte using indirect r/m with a 8bit displacement
600 /// r/m effective address: [r/m + disp8]
601 ///
602 /// Note reg's effective address is always just reg for the ModR/M byte.
603 /// Remember to add a REX prefix byte if reg or rm are extended!
604 pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) !void {
605 assert(rm != 4);
606 try self.modRm(0b01, reg_or_opx, rm);
607 }
608
609 /// Construct a ModR/M byte using indirect SIB with a 8bit displacement
610 /// r/m effective address: [SIB + disp8]
611 ///
612 /// Note reg's effective address is always just reg for the ModR/M byte.
613 /// Remember to add a REX prefix byte if reg or rm are extended!
614 pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) !void {
615 try self.modRm(0b01, reg_or_opx, 0b100);
616 }
617
618 /// Construct a ModR/M byte using indirect r/m with a 32bit displacement
619 /// r/m effective address: [r/m + disp32]
620 ///
621 /// Note reg's effective address is always just reg for the ModR/M byte.
622 /// Remember to add a REX prefix byte if reg or rm are extended!
623 pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) !void {
624 assert(rm != 4);
625 try self.modRm(0b10, reg_or_opx, rm);
626 }
627
628 /// Construct a ModR/M byte using indirect SIB with a 32bit displacement
629 /// r/m effective address: [SIB + disp32]
630 ///
631 /// Note reg's effective address is always just reg for the ModR/M byte.
632 /// Remember to add a REX prefix byte if reg or rm are extended!
633 pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) !void {
634 try self.modRm(0b10, reg_or_opx, 0b100);
635 }
636
637 // ---
638 // SIB
639 // ---
640
641 /// Construct a SIB byte given all the fields
642 ///
643 /// Remember to add a REX prefix byte if index or base are extended!
644 pub fn sib(self: Self, scale: u2, index: u3, base: u3) !void {
645 try self.writer.writeByte(@as(u8, scale) << 6 | @as(u8, index) << 3 | base);
646 }
647
648 /// Construct a SIB byte with scale * index + base, no frills.
649 /// r/m effective address: [base + scale * index]
650 ///
651 /// Remember to add a REX prefix byte if index or base are extended!
652 pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) !void {
653 assert(base != 5);
654
655 try self.sib(scale, index, base);
656 }
657
658 /// Construct a SIB byte with scale * index + disp32
659 /// r/m effective address: [scale * index + disp32]
660 ///
661 /// Remember to add a REX prefix byte if index or base are extended!
662 pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) !void {
663 // scale is actually ignored
664 // index = 4 means no index if and only if we haven't extended the register
665 // TODO enforce this
666 // base = 5 means no base, if mod == 0.
667 try self.sib(scale, index, 5);
668 }
669
670 /// Construct a SIB byte with just base
671 /// r/m effective address: [base]
672 ///
673 /// Remember to add a REX prefix byte if index or base are extended!
674 pub fn sib_base(self: Self, base: u3) !void {
675 assert(base != 5);
676
677 // scale is actually ignored
678 // index = 4 means no index
679 try self.sib(0, 4, base);
680 }
681
682 /// Construct a SIB byte with just disp32
683 /// r/m effective address: [disp32]
684 ///
685 /// Remember to add a REX prefix byte if index or base are extended!
686 pub fn sib_disp32(self: Self) !void {
687 // scale is actually ignored
688 // index = 4 means no index
689 // base = 5 means no base, if mod == 0.
690 try self.sib(0, 4, 5);
691 }
692
693 /// Construct a SIB byte with scale * index + base + disp8
694 /// r/m effective address: [base + scale * index + disp8]
695 ///
696 /// Remember to add a REX prefix byte if index or base are extended!
697 pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) !void {
698 try self.sib(scale, index, base);
699 }
700
701 /// Construct a SIB byte with base + disp8, no index
702 /// r/m effective address: [base + disp8]
703 ///
704 /// Remember to add a REX prefix byte if index or base are extended!
705 pub fn sib_baseDisp8(self: Self, base: u3) !void {
706 // scale is ignored
707 // index = 4 means no index
708 try self.sib(0, 4, base);
709 }
710
711 /// Construct a SIB byte with scale * index + base + disp32
712 /// r/m effective address: [base + scale * index + disp32]
713 ///
714 /// Remember to add a REX prefix byte if index or base are extended!
715 pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) !void {
716 try self.sib(scale, index, base);
717 }
718
719 /// Construct a SIB byte with base + disp32, no index
720 /// r/m effective address: [base + disp32]
721 ///
722 /// Remember to add a REX prefix byte if index or base are extended!
723 pub fn sib_baseDisp32(self: Self, base: u3) !void {
724 // scale is ignored
725 // index = 4 means no index
726 try self.sib(0, 4, base);
727 }
728
729 // -------------------------
730 // Trivial (no bit fiddling)
731 // -------------------------
732
733 /// Encode an 8 bit displacement
734 ///
735 /// It is sign-extended to 64 bits by the cpu.
736 pub fn disp8(self: Self, disp: i8) !void {
737 try self.writer.writeByte(@bitCast(u8, disp));
738 }
739
740 /// Encode an 32 bit displacement
741 ///
742 /// It is sign-extended to 64 bits by the cpu.
743 pub fn disp32(self: Self, disp: i32) !void {
744 try self.writer.writeIntLittle(i32, disp);
745 }
746
747 /// Encode an 8 bit immediate
748 ///
749 /// It is sign-extended to 64 bits by the cpu.
750 pub fn imm8(self: Self, imm: u8) !void {
751 try self.writer.writeByte(imm);
752 }
753
754 /// Encode an 16 bit immediate
755 ///
756 /// It is sign-extended to 64 bits by the cpu.
757 pub fn imm16(self: Self, imm: u16) !void {
758 try self.writer.writeIntLittle(u16, imm);
759 }
760
761 /// Encode an 32 bit immediate
762 ///
763 /// It is sign-extended to 64 bits by the cpu.
764 pub fn imm32(self: Self, imm: u32) !void {
765 try self.writer.writeIntLittle(u32, imm);
766 }
767
768 /// Encode an 64 bit immediate
769 ///
770 /// It is sign-extended to 64 bits by the cpu.
771 pub fn imm64(self: Self, imm: u64) !void {
772 try self.writer.writeIntLittle(u64, imm);
773 }
774 };
775}
776
777pub const Rex = struct {
778 w: bool = false,
779 r: bool = false,
780 x: bool = false,
781 b: bool = false,
782 present: bool = false,
783
784 pub fn isSet(rex: Rex) bool {
785 return rex.w or rex.r or rex.x or rex.b;
786 }
787};
788
789// Tests
790fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void {
791 assert(expected.len > 0);
792 if (std.mem.eql(u8, expected, given)) return;
793 const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)});
794 defer testing.allocator.free(expected_fmt);
795 const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)});
796 defer testing.allocator.free(given_fmt);
797 const idx = std.mem.indexOfDiff(u8, expected_fmt, given_fmt).?;
798 var padding = try testing.allocator.alloc(u8, idx + 5);
799 defer testing.allocator.free(padding);
800 std.mem.set(u8, padding, ' ');
801 std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{
802 assembly,
803 expected_fmt,
804 given_fmt,
805 padding,
806 });
807 return error.TestFailed;
808}
809
810const TestEncode = struct {
811 buffer: [32]u8 = undefined,
812 index: usize = 0,
813
814 fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: struct {
815 op1: Instruction.Operand = .none,
816 op2: Instruction.Operand = .none,
817 op3: Instruction.Operand = .none,
818 op4: Instruction.Operand = .none,
819 }) !void {
820 var stream = std.io.fixedBufferStream(&enc.buffer);
821 var count_writer = std.io.countingWriter(stream.writer());
822 const inst = try Instruction.new(mnemonic, .{
823 .op1 = args.op1,
824 .op2 = args.op2,
825 .op3 = args.op3,
826 .op4 = args.op4,
827 });
828 try inst.encode(count_writer.writer());
829 enc.index = count_writer.bytes_written;
830 }
831
832 fn code(enc: TestEncode) []const u8 {
833 return enc.buffer[0..enc.index];
834 }
835};
836
837test "encode" {
838 var buf = std.ArrayList(u8).init(testing.allocator);
839 defer buf.deinit();
840
841 const inst = try Instruction.new(.mov, .{
842 .op1 = .{ .reg = .rbx },
843 .op2 = .{ .imm = Immediate.u(4) },
844 });
845 try inst.encode(buf.writer());
846 try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
847}
848
849test "lower I encoding" {
850 var enc = TestEncode{};
851
852 try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10) } });
853 try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
854
855 try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000) } });
856 try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
857
858 try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10000000) } });
859 try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000");
860
861 try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10000000) } });
862 try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000");
863
864 try enc.encode(.add, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } });
865 try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
866
867 try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
868 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
869
870 try enc.encode(.sbb, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .imm = Immediate.u(0x10) } });
871 try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
872
873 try enc.encode(.xor, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } });
874 try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
875}
876
877test "lower MI encoding" {
878 var enc = TestEncode{};
879
880 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } });
881 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
882
883 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
884 .base = .r12,
885 .disp = 0,
886 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
887 try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
888
889 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } });
890 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
891
892 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } });
893 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
894
895 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
896 try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
897
898 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
899 .base = .r11,
900 .disp = 0,
901 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
902 try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
903
904 try enc.encode(.mov, .{
905 .op1 = .{ .mem = Memory.rip(.qword, 0x10) },
906 .op2 = .{ .imm = Immediate.u(0x10) },
907 });
908 try expectEqualHexStrings(
909 "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
910 enc.code(),
911 "mov QWORD PTR [rip + 0x10], 0x10",
912 );
913
914 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
915 .base = .rbp,
916 .disp = -8,
917 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
918 try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
919
920 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{
921 .base = .rbp,
922 .disp = -2,
923 }) }, .op2 = .{ .imm = Immediate.s(-16) } });
924 try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
925
926 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
927 .base = .rbp,
928 .disp = -1,
929 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
930 try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
931
932 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
933 .base = .ds,
934 .disp = 0x10000000,
935 .scale_index = .{
936 .scale = 2,
937 .index = .rcx,
938 },
939 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
940 try expectEqualHexStrings(
941 "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
942 enc.code(),
943 "mov QWORD PTR [rcx*2 + 0x10000000], 0x10",
944 );
945
946 try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
947 .base = .rbp,
948 .disp = -0x10,
949 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
950 try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
951
952 try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) }, .op2 = .{ .imm = Immediate.u(0x10) } });
953 try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
954
955 try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
956 try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
957
958 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
959 .base = .rdx,
960 .disp = -8,
961 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
962 try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
963
964 try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
965 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
966
967 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
968 .base = .rbp,
969 .disp = -0x10,
970 }) }, .op2 = .{ .imm = Immediate.s(-0x10) } });
971 try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
972
973 try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{
974 .base = .ds,
975 .disp = 0x10000000,
976 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
977 try expectEqualHexStrings(
978 "\x83\x24\x25\x00\x00\x00\x10\x10",
979 enc.code(),
980 "and DWORD PTR ds:0x10000000, 0x10",
981 );
982
983 try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{
984 .base = .es,
985 .disp = 0x10000000,
986 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
987 try expectEqualHexStrings(
988 "\x26\x83\x24\x25\x00\x00\x00\x10\x10",
989 enc.code(),
990 "and DWORD PTR es:0x10000000, 0x10",
991 );
992
993 try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{
994 .base = .r12,
995 .disp = 0x10000000,
996 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
997 try expectEqualHexStrings(
998 "\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
999 enc.code(),
1000 "and DWORD PTR [r12 + 0x10000000], 0x10",
1001 );
1002
1003 try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
1004 .base = .r11,
1005 .disp = 0x10000000,
1006 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
1007 try expectEqualHexStrings(
1008 "\x41\x83\xAB\x00\x00\x00\x10\x10",
1009 enc.code(),
1010 "sub DWORD PTR [r11 + 0x10000000], 0x10",
1011 );
1012}
1013
1014test "lower RM encoding" {
1015 var enc = TestEncode{};
1016
1017 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1018 .base = .r11,
1019 .disp = 0,
1020 }) } });
1021 try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
1022
1023 try enc.encode(.mov, .{ .op1 = .{ .reg = .rbx }, .op2 = .{ .mem = Memory.sib(.qword, .{
1024 .base = .ds,
1025 .disp = 0x10,
1026 }) } });
1027 try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
1028
1029 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1030 .base = .rbp,
1031 .disp = -4,
1032 }) } });
1033 try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
1034
1035 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1036 .base = .rbp,
1037 .scale_index = .{
1038 .scale = 1,
1039 .index = .rcx,
1040 },
1041 .disp = -8,
1042 }) } });
1043 try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*1 - 8]");
1044
1045 try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.dword, .{
1046 .base = .rbp,
1047 .scale_index = .{
1048 .scale = 4,
1049 .index = .rdx,
1050 },
1051 .disp = -4,
1052 }) } });
1053 try expectEqualHexStrings("\x8B\x44\x95\xFC", enc.code(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
1054
1055 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1056 .base = .rbp,
1057 .scale_index = .{
1058 .scale = 8,
1059 .index = .rcx,
1060 },
1061 .disp = -8,
1062 }) } });
1063 try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*8 - 8]");
1064
1065 try enc.encode(.mov, .{ .op1 = .{ .reg = .r8b }, .op2 = .{ .mem = Memory.sib(.byte, .{
1066 .base = .rsi,
1067 .scale_index = .{
1068 .scale = 1,
1069 .index = .rcx,
1070 },
1071 .disp = -24,
1072 }) } });
1073 try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", enc.code(), "mov r8b, BYTE PTR [rsi + rcx*1 - 24]");
1074
1075 // TODO this mnemonic needs cleanup as some prefixes are obsolete.
1076 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .cs } });
1077 try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
1078
1079 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1080 .base = .rbp,
1081 .disp = -16,
1082 }) }, .op2 = .{ .reg = .fs } });
1083 try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs");
1084
1085 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12w }, .op2 = .{ .reg = .cs } });
1086 try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
1087
1088 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{
1089 .base = .rbp,
1090 .disp = -16,
1091 }) }, .op2 = .{ .reg = .fs } });
1092 try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
1093
1094 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bx } });
1095 try expectEqualHexStrings("\x0F\xBF\xC3", enc.code(), "movsx eax, bx");
1096
1097 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bl } });
1098 try expectEqualHexStrings("\x0F\xBE\xC3", enc.code(), "movsx eax, bl");
1099
1100 try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .reg = .bl } });
1101 try expectEqualHexStrings("\x66\x0F\xBE\xC3", enc.code(), "movsx ax, bl");
1102
1103 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.word, .{
1104 .base = .rbp,
1105 .disp = 0,
1106 }) } });
1107 try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
1108
1109 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.byte, .{
1110 .base = null,
1111 .scale_index = .{
1112 .index = .rax,
1113 .scale = 2,
1114 },
1115 .disp = 0,
1116 }) } });
1117 try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]");
1118
1119 try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } });
1120 try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]");
1121
1122 try enc.encode(.movsx, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .bx } });
1123 try expectEqualHexStrings("\x48\x0F\xBF\xC3", enc.code(), "movsx rax, bx");
1124
1125 try enc.encode(.movsxd, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .ebx } });
1126 try expectEqualHexStrings("\x48\x63\xC3", enc.code(), "movsxd rax, ebx");
1127
1128 try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.qword, 0x10) } });
1129 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]");
1130
1131 try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } });
1132 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]");
1133
1134 try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } });
1135 try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]");
1136
1137 try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.word, 0x10) } });
1138 try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]");
1139
1140 try enc.encode(.lea, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } });
1141 try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]");
1142
1143 try enc.encode(.lea, .{ .op1 = .{ .reg = .rsi }, .op2 = .{ .mem = Memory.sib(.qword, .{
1144 .base = .rbp,
1145 .scale_index = .{
1146 .scale = 1,
1147 .index = .rcx,
1148 },
1149 .disp = 0,
1150 }) } });
1151 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", enc.code(), "lea rsi, QWORD PTR [rbp + rcx*1 + 0]");
1152
1153 try enc.encode(.add, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{
1154 .base = .ds,
1155 .disp = 0x10000000,
1156 }) } });
1157 try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
1158
1159 try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{
1160 .base = .ds,
1161 .disp = 0x10000000,
1162 }) } });
1163 try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
1164
1165 try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{
1166 .base = .fs,
1167 .disp = 0x10000000,
1168 }) } });
1169 try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
1170
1171 try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{
1172 .base = .r13,
1173 .disp = 0x10000000,
1174 }) } });
1175 try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
1176
1177 try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{
1178 .base = .r12,
1179 .disp = 0x10000000,
1180 }) } });
1181 try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
1182
1183 try enc.encode(.imul, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .reg = .r12 } });
1184 try expectEqualHexStrings("\x4D\x0F\xAF\xDC", enc.code(), "mov r11, r12");
1185}
1186
1187test "lower RMI encoding" {
1188 var enc = TestEncode{};
1189
1190 try enc.encode(.imul, .{
1191 .op1 = .{ .reg = .r11 },
1192 .op2 = .{ .reg = .r12 },
1193 .op3 = .{ .imm = Immediate.s(-2) },
1194 });
1195 try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
1196
1197 try enc.encode(.imul, .{
1198 .op1 = .{ .reg = .r11 },
1199 .op2 = .{ .mem = Memory.rip(.qword, -16) },
1200 .op3 = .{ .imm = Immediate.s(-1024) },
1201 });
1202 try expectEqualHexStrings(
1203 "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
1204 enc.code(),
1205 "imul r11, QWORD PTR [rip - 16], -1024",
1206 );
1207
1208 try enc.encode(.imul, .{
1209 .op1 = .{ .reg = .bx },
1210 .op2 = .{ .mem = Memory.sib(.word, .{
1211 .base = .rbp,
1212 .disp = -16,
1213 }) },
1214 .op3 = .{ .imm = Immediate.s(-1024) },
1215 });
1216 try expectEqualHexStrings(
1217 "\x66\x69\x5D\xF0\x00\xFC",
1218 enc.code(),
1219 "imul bx, WORD PTR [rbp - 16], -1024",
1220 );
1221
1222 try enc.encode(.imul, .{
1223 .op1 = .{ .reg = .bx },
1224 .op2 = .{ .mem = Memory.sib(.word, .{
1225 .base = .rbp,
1226 .disp = -16,
1227 }) },
1228 .op3 = .{ .imm = Immediate.u(1024) },
1229 });
1230 try expectEqualHexStrings(
1231 "\x66\x69\x5D\xF0\x00\x04",
1232 enc.code(),
1233 "imul bx, WORD PTR [rbp - 16], 1024",
1234 );
1235}
1236
1237test "lower MR encoding" {
1238 var enc = TestEncode{};
1239
1240 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } });
1241 try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
1242
1243 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1244 .base = .rbp,
1245 .disp = -4,
1246 }) }, .op2 = .{ .reg = .r11 } });
1247 try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
1248
1249 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, .op2 = .{ .reg = .r12 } });
1250 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12");
1251
1252 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1253 .base = .r11,
1254 .scale_index = .{
1255 .scale = 2,
1256 .index = .r12,
1257 },
1258 .disp = 0x10,
1259 }) }, .op2 = .{ .reg = .r13 } });
1260 try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13");
1261
1262 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, -0x10) }, .op2 = .{ .reg = .r12w } });
1263 try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w");
1264
1265 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
1266 .base = .r11,
1267 .scale_index = .{
1268 .scale = 2,
1269 .index = .r12,
1270 },
1271 .disp = 0x10,
1272 }) }, .op2 = .{ .reg = .r13b } });
1273 try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
1274
1275 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
1276 .base = .ds,
1277 .disp = 0x10000000,
1278 }) }, .op2 = .{ .reg = .r12b } });
1279 try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
1280
1281 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
1282 .base = .ds,
1283 .disp = 0x10000000,
1284 }) }, .op2 = .{ .reg = .r12d } });
1285 try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
1286
1287 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
1288 .base = .gs,
1289 .disp = 0x10000000,
1290 }) }, .op2 = .{ .reg = .r12d } });
1291 try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
1292
1293 try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1294 .base = .r11,
1295 .disp = 0x10000000,
1296 }) }, .op2 = .{ .reg = .r12 } });
1297 try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12");
1298}
1299
1300test "lower M encoding" {
1301 var enc = TestEncode{};
1302
1303 try enc.encode(.call, .{ .op1 = .{ .reg = .r12 } });
1304 try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
1305
1306 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1307 .base = .r12,
1308 .disp = 0,
1309 }) } });
1310 try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
1311
1312 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1313 .base = null,
1314 .scale_index = .{
1315 .index = .r11,
1316 .scale = 2,
1317 },
1318 .disp = 0,
1319 }) } });
1320 try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]");
1321
1322 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1323 .base = null,
1324 .scale_index = .{
1325 .index = .r12,
1326 .scale = 2,
1327 },
1328 .disp = 0,
1329 }) } });
1330 try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
1331
1332 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1333 .base = .gs,
1334 .disp = 0,
1335 }) } });
1336 try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
1337
1338 try enc.encode(.call, .{ .op1 = .{ .imm = Immediate.s(0) } });
1339 try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
1340
1341 try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1342 .base = .rbp,
1343 .disp = 0,
1344 }) } });
1345 try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
1346
1347 try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.word, .{
1348 .base = .rbp,
1349 .disp = 0,
1350 }) } });
1351 try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
1352
1353 try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) } });
1354 try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]");
1355
1356 try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } });
1357 try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]");
1358
1359 try enc.encode(.imul, .{ .op1 = .{ .reg = .rax } });
1360 try expectEqualHexStrings("\x48\xF7\xE8", enc.code(), "imul rax");
1361
1362 try enc.encode(.imul, .{ .op1 = .{ .reg = .r12 } });
1363 try expectEqualHexStrings("\x49\xF7\xEC", enc.code(), "imul r12");
1364}
1365
1366test "lower O encoding" {
1367 var enc = TestEncode{};
1368
1369 try enc.encode(.push, .{ .op1 = .{ .reg = .rax } });
1370 try expectEqualHexStrings("\x50", enc.code(), "push rax");
1371
1372 try enc.encode(.push, .{ .op1 = .{ .reg = .r12w } });
1373 try expectEqualHexStrings("\x66\x41\x54", enc.code(), "push r12w");
1374
1375 try enc.encode(.pop, .{ .op1 = .{ .reg = .r12 } });
1376 try expectEqualHexStrings("\x41\x5c", enc.code(), "pop r12");
1377}
1378
1379test "lower OI encoding" {
1380 var enc = TestEncode{};
1381
1382 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } });
1383 try expectEqualHexStrings(
1384 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
1385 enc.code(),
1386 "movabs rax, 0x1000000000000000",
1387 );
1388
1389 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } });
1390 try expectEqualHexStrings(
1391 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
1392 enc.code(),
1393 "movabs r11, 0x1000000000000000",
1394 );
1395
1396 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11d }, .op2 = .{ .imm = Immediate.u(0x10000000) } });
1397 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000");
1398
1399 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11w }, .op2 = .{ .imm = Immediate.u(0x1000) } });
1400 try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
1401
1402 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11b }, .op2 = .{ .imm = Immediate.u(0x10) } });
1403 try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
1404}
1405
1406test "lower FD/TD encoding" {
1407 var enc = TestEncode{};
1408
1409 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.moffs(.cs, 0x10) } });
1410 try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10");
1411
1412 try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.moffs(.fs, 0x10) } });
1413 try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10");
1414
1415 try enc.encode(.mov, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.moffs(.gs, 0x10) } });
1416 try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10");
1417
1418 try enc.encode(.mov, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.moffs(.ds, 0x10) } });
1419 try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10");
1420
1421 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.cs, 0x10) }, .op2 = .{ .reg = .rax } });
1422 try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax");
1423
1424 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.fs, 0x10) }, .op2 = .{ .reg = .eax } });
1425 try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax");
1426
1427 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.gs, 0x10) }, .op2 = .{ .reg = .ax } });
1428 try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax");
1429
1430 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.ds, 0x10) }, .op2 = .{ .reg = .al } });
1431 try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al");
1432}
1433
1434test "lower NP encoding" {
1435 var enc = TestEncode{};
1436
1437 try enc.encode(.int3, .{});
1438 try expectEqualHexStrings("\xCC", enc.code(), "int3");
1439
1440 try enc.encode(.nop, .{});
1441 try expectEqualHexStrings("\x90", enc.code(), "nop");
1442
1443 try enc.encode(.ret, .{});
1444 try expectEqualHexStrings("\xC3", enc.code(), "ret");
1445
1446 try enc.encode(.syscall, .{});
1447 try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall");
1448}
1449
1450fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: struct {
1451 op1: Instruction.Operand = .none,
1452 op2: Instruction.Operand = .none,
1453 op3: Instruction.Operand = .none,
1454 op4: Instruction.Operand = .none,
1455}) !void {
1456 const err = Instruction.new(mnemonic, .{
1457 .op1 = args.op1,
1458 .op2 = args.op2,
1459 .op3 = args.op3,
1460 .op4 = args.op4,
1461 });
1462 try testing.expectError(error.InvalidInstruction, err);
1463}
1464
1465test "invalid instruction" {
1466 try invalidInstruction(.call, .{ .op1 = .{ .reg = .eax } });
1467 try invalidInstruction(.call, .{ .op1 = .{ .reg = .ax } });
1468 try invalidInstruction(.call, .{ .op1 = .{ .reg = .al } });
1469 try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.dword, 0) } });
1470 try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } });
1471 try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.byte, 0) } });
1472 try invalidInstruction(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, 0x10) }, .op2 = .{ .reg = .r12 } });
1473 try invalidInstruction(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } });
1474 try invalidInstruction(.lea, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.rip(.byte, 0) } });
1475 try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12b } });
1476 try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12d } });
1477 try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12b } });
1478 try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12d } });
1479 try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } });
1480}
1481
1482fn cannotEncode(mnemonic: Instruction.Mnemonic, args: struct {
1483 op1: Instruction.Operand = .none,
1484 op2: Instruction.Operand = .none,
1485 op3: Instruction.Operand = .none,
1486 op4: Instruction.Operand = .none,
1487}) !void {
1488 try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, .{
1489 .op1 = args.op1,
1490 .op2 = args.op2,
1491 .op3 = args.op3,
1492 .op4 = args.op4,
1493 }));
1494}
1495
1496test "cannot encode" {
1497 try cannotEncode(.@"test", .{
1498 .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12, .disp = 0 }) },
1499 .op2 = .{ .reg = .ah },
1500 });
1501 try cannotEncode(.@"test", .{
1502 .op1 = .{ .reg = .r11b },
1503 .op2 = .{ .reg = .bh },
1504 });
1505 try cannotEncode(.mov, .{
1506 .op1 = .{ .reg = .sil },
1507 .op2 = .{ .reg = .ah },
1508 });
1509}
1510
1511const Assembler = struct {
1512 it: Tokenizer,
1513
1514 const Tokenizer = struct {
1515 input: []const u8,
1516 pos: usize = 0,
1517
1518 const Error = error{InvalidToken};
1519
1520 const Token = struct {
1521 id: Id,
1522 start: usize,
1523 end: usize,
1524
1525 const Id = enum {
1526 eof,
1527
1528 space,
1529 new_line,
1530
1531 colon,
1532 comma,
1533 open_br,
1534 close_br,
1535 plus,
1536 minus,
1537 star,
1538
1539 string,
1540 numeral,
1541 };
1542 };
1543
1544 const Iterator = struct {};
1545
1546 fn next(it: *Tokenizer) !Token {
1547 var result = Token{
1548 .id = .eof,
1549 .start = it.pos,
1550 .end = it.pos,
1551 };
1552
1553 var state: enum {
1554 start,
1555 space,
1556 new_line,
1557 string,
1558 numeral,
1559 numeral_hex,
1560 } = .start;
1561
1562 while (it.pos < it.input.len) : (it.pos += 1) {
1563 const ch = it.input[it.pos];
1564 switch (state) {
1565 .start => switch (ch) {
1566 ',' => {
1567 result.id = .comma;
1568 it.pos += 1;
1569 break;
1570 },
1571 ':' => {
1572 result.id = .colon;
1573 it.pos += 1;
1574 break;
1575 },
1576 '[' => {
1577 result.id = .open_br;
1578 it.pos += 1;
1579 break;
1580 },
1581 ']' => {
1582 result.id = .close_br;
1583 it.pos += 1;
1584 break;
1585 },
1586 '+' => {
1587 result.id = .plus;
1588 it.pos += 1;
1589 break;
1590 },
1591 '-' => {
1592 result.id = .minus;
1593 it.pos += 1;
1594 break;
1595 },
1596 '*' => {
1597 result.id = .star;
1598 it.pos += 1;
1599 break;
1600 },
1601 ' ', '\t' => state = .space,
1602 '\n', '\r' => state = .new_line,
1603 'a'...'z', 'A'...'Z' => state = .string,
1604 '0'...'9' => state = .numeral,
1605 else => return error.InvalidToken,
1606 },
1607
1608 .space => switch (ch) {
1609 ' ', '\t' => {},
1610 else => {
1611 result.id = .space;
1612 break;
1613 },
1614 },
1615
1616 .new_line => switch (ch) {
1617 '\n', '\r', ' ', '\t' => {},
1618 else => {
1619 result.id = .new_line;
1620 break;
1621 },
1622 },
1623
1624 .string => switch (ch) {
1625 'a'...'z', 'A'...'Z', '0'...'9' => {},
1626 else => {
1627 result.id = .string;
1628 break;
1629 },
1630 },
1631
1632 .numeral => switch (ch) {
1633 'x' => state = .numeral_hex,
1634 '0'...'9' => {},
1635 else => {
1636 result.id = .numeral;
1637 break;
1638 },
1639 },
1640
1641 .numeral_hex => switch (ch) {
1642 'a'...'f' => {},
1643 '0'...'9' => {},
1644 else => {
1645 result.id = .numeral;
1646 break;
1647 },
1648 },
1649 }
1650 }
1651
1652 if (it.pos >= it.input.len) {
1653 switch (state) {
1654 .string => result.id = .string,
1655 .numeral, .numeral_hex => result.id = .numeral,
1656 else => {},
1657 }
1658 }
1659
1660 result.end = it.pos;
1661 return result;
1662 }
1663
1664 fn seekTo(it: *Tokenizer, pos: usize) void {
1665 it.pos = pos;
1666 }
1667 };
1668
1669 pub fn init(input: []const u8) Assembler {
1670 return .{
1671 .it = Tokenizer{ .input = input },
1672 };
1673 }
1674
1675 pub fn assemble(as: *Assembler, writer: anytype) !void {
1676 while (try as.next()) |parsed_inst| {
1677 const inst = try Instruction.new(parsed_inst.mnemonic, .{
1678 .op1 = parsed_inst.ops[0],
1679 .op2 = parsed_inst.ops[1],
1680 .op3 = parsed_inst.ops[2],
1681 .op4 = parsed_inst.ops[3],
1682 });
1683 try inst.encode(writer);
1684 }
1685 }
1686
1687 const ParseResult = struct {
1688 mnemonic: Instruction.Mnemonic,
1689 ops: [4]Instruction.Operand,
1690 };
1691
1692 const ParseError = error{
1693 UnexpectedToken,
1694 InvalidMnemonic,
1695 InvalidOperand,
1696 InvalidRegister,
1697 InvalidPtrSize,
1698 InvalidMemoryOperand,
1699 InvalidScaleIndex,
1700 } || Tokenizer.Error || std.fmt.ParseIntError;
1701
1702 fn next(as: *Assembler) ParseError!?ParseResult {
1703 try as.skip(2, .{ .space, .new_line });
1704 const mnemonic_tok = as.expect(.string) catch |err| switch (err) {
1705 error.UnexpectedToken => return if (try as.peek() == .eof) null else err,
1706 else => return err,
1707 };
1708 const mnemonic = mnemonicFromString(as.source(mnemonic_tok)) orelse
1709 return error.InvalidMnemonic;
1710 try as.skip(1, .{.space});
1711
1712 const rules = .{
1713 .{},
1714 .{.register},
1715 .{.memory},
1716 .{.immediate},
1717 .{ .register, .register },
1718 .{ .register, .memory },
1719 .{ .memory, .register },
1720 .{ .register, .immediate },
1721 .{ .memory, .immediate },
1722 .{ .register, .register, .immediate },
1723 .{ .register, .memory, .immediate },
1724 };
1725
1726 const pos = as.it.pos;
1727 inline for (rules) |rule| {
1728 var ops = [4]Instruction.Operand{ .none, .none, .none, .none };
1729 if (as.parseOperandRule(rule, &ops)) {
1730 return .{
1731 .mnemonic = mnemonic,
1732 .ops = ops,
1733 };
1734 } else |_| {
1735 as.it.seekTo(pos);
1736 }
1737 }
1738
1739 return error.InvalidOperand;
1740 }
1741
1742 fn source(as: *Assembler, token: Tokenizer.Token) []const u8 {
1743 return as.it.input[token.start..token.end];
1744 }
1745
1746 fn peek(as: *Assembler) Tokenizer.Error!Tokenizer.Token.Id {
1747 const pos = as.it.pos;
1748 const next_tok = try as.it.next();
1749 const id = next_tok.id;
1750 as.it.seekTo(pos);
1751 return id;
1752 }
1753
1754 fn expect(as: *Assembler, id: Tokenizer.Token.Id) ParseError!Tokenizer.Token {
1755 const next_tok_id = try as.peek();
1756 if (next_tok_id == id) return as.it.next();
1757 return error.UnexpectedToken;
1758 }
1759
1760 fn skip(as: *Assembler, comptime num: comptime_int, tok_ids: [num]Tokenizer.Token.Id) Tokenizer.Error!void {
1761 outer: while (true) {
1762 const pos = as.it.pos;
1763 const next_tok = try as.it.next();
1764 inline for (tok_ids) |tok_id| {
1765 if (next_tok.id == tok_id) continue :outer;
1766 }
1767 as.it.seekTo(pos);
1768 break;
1769 }
1770 }
1771
1772 fn mnemonicFromString(bytes: []const u8) ?Instruction.Mnemonic {
1773 const ti = @typeInfo(Instruction.Mnemonic).Enum;
1774 inline for (ti.fields) |field| {
1775 if (std.mem.eql(u8, bytes, field.name)) {
1776 return @field(Instruction.Mnemonic, field.name);
1777 }
1778 }
1779 return null;
1780 }
1781
1782 fn parseOperandRule(as: *Assembler, rule: anytype, ops: *[4]Instruction.Operand) ParseError!void {
1783 inline for (rule, 0..) |cond, i| {
1784 comptime assert(i < 4);
1785 if (i > 0) {
1786 _ = try as.expect(.comma);
1787 try as.skip(1, .{.space});
1788 }
1789 if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
1790 @compileError("invalid condition in the rule: " ++ @typeName(@TypeOf(cond)));
1791 }
1792 switch (cond) {
1793 .register => {
1794 const reg_tok = try as.expect(.string);
1795 const reg = registerFromString(as.source(reg_tok)) orelse
1796 return error.InvalidOperand;
1797 ops[i] = .{ .reg = reg };
1798 },
1799 .memory => {
1800 const mem = try as.parseMemory();
1801 ops[i] = .{ .mem = mem };
1802 },
1803 .immediate => {
1804 const is_neg = if (as.expect(.minus)) |_| true else |_| false;
1805 const imm_tok = try as.expect(.numeral);
1806 const imm: Immediate = if (is_neg) blk: {
1807 const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0);
1808 break :blk .{ .signed = imm * -1 };
1809 } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) };
1810 ops[i] = .{ .imm = imm };
1811 },
1812 else => @compileError("unhandled enum literal " ++ @tagName(cond)),
1813 }
1814 try as.skip(1, .{.space});
1815 }
1816
1817 try as.skip(1, .{.space});
1818 const tok = try as.it.next();
1819 switch (tok.id) {
1820 .new_line, .eof => {},
1821 else => return error.InvalidOperand,
1822 }
1823 }
1824
1825 fn registerFromString(bytes: []const u8) ?Register {
1826 const ti = @typeInfo(Register).Enum;
1827 inline for (ti.fields) |field| {
1828 if (std.mem.eql(u8, bytes, field.name)) {
1829 return @field(Register, field.name);
1830 }
1831 }
1832 return null;
1833 }
1834
1835 fn parseMemory(as: *Assembler) ParseError!Memory {
1836 const ptr_size: ?Memory.PtrSize = blk: {
1837 const pos = as.it.pos;
1838 const ptr_size = as.parsePtrSize() catch |err| switch (err) {
1839 error.UnexpectedToken => {
1840 as.it.seekTo(pos);
1841 break :blk null;
1842 },
1843 else => return err,
1844 };
1845 break :blk ptr_size;
1846 };
1847
1848 try as.skip(1, .{.space});
1849
1850 // Supported rules and orderings.
1851 const rules = .{
1852 .{ .open_br, .base, .close_br }, // [ base ]
1853 .{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ]
1854 .{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ]
1855 .{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ]
1856 .{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ]
1857 .{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ]
1858 .{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ]
1859 .{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ]
1860 .{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ]
1861 .{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ]
1862 .{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ]
1863 .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ]
1864 .{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ]
1865 .{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ]
1866 .{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ]
1867 .{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ]
1868 .{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ]
1869 .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ]
1870 .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ]
1871 .{ .base, .colon, .disp }, // seg:disp
1872 };
1873
1874 const pos = as.it.pos;
1875 inline for (rules) |rule| {
1876 if (as.parseMemoryRule(rule)) |res| {
1877 if (res.rip) {
1878 if (res.base != null or res.scale_index != null or res.offset != null)
1879 return error.InvalidMemoryOperand;
1880 return Memory.rip(ptr_size orelse .qword, res.disp orelse 0);
1881 }
1882 if (res.base) |base| {
1883 if (res.rip)
1884 return error.InvalidMemoryOperand;
1885 if (res.offset) |offset| {
1886 if (res.scale_index != null or res.disp != null)
1887 return error.InvalidMemoryOperand;
1888 return Memory.moffs(base, offset);
1889 }
1890 return Memory.sib(ptr_size orelse .qword, .{
1891 .base = base,
1892 .scale_index = res.scale_index,
1893 .disp = res.disp orelse 0,
1894 });
1895 }
1896 return error.InvalidMemoryOperand;
1897 } else |_| {
1898 as.it.seekTo(pos);
1899 }
1900 }
1901
1902 return error.InvalidOperand;
1903 }
1904
1905 const MemoryParseResult = struct {
1906 rip: bool = false,
1907 base: ?Register = null,
1908 scale_index: ?Memory.ScaleIndex = null,
1909 disp: ?i32 = null,
1910 offset: ?u64 = null,
1911 };
1912
1913 fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
1914 var res: MemoryParseResult = .{};
1915 inline for (rule, 0..) |cond, i| {
1916 if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
1917 @compileError("unsupported condition type in the rule: " ++ @typeName(@TypeOf(cond)));
1918 }
1919 switch (cond) {
1920 .open_br, .close_br, .plus, .minus, .star, .colon => {
1921 _ = try as.expect(cond);
1922 },
1923 .base => {
1924 const tok = try as.expect(.string);
1925 res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
1926 },
1927 .rip => {
1928 const tok = try as.expect(.string);
1929 if (!std.mem.eql(u8, as.source(tok), "rip")) return error.InvalidMemoryOperand;
1930 res.rip = true;
1931 },
1932 .index => {
1933 const tok = try as.expect(.string);
1934 const index = registerFromString(as.source(tok)) orelse
1935 return error.InvalidMemoryOperand;
1936 if (res.scale_index) |*si| {
1937 si.index = index;
1938 } else {
1939 res.scale_index = .{ .scale = 1, .index = index };
1940 }
1941 },
1942 .scale => {
1943 const tok = try as.expect(.numeral);
1944 const scale = try std.fmt.parseInt(u2, as.source(tok), 0);
1945 if (res.scale_index) |*si| {
1946 si.scale = scale;
1947 } else {
1948 res.scale_index = .{ .scale = scale, .index = undefined };
1949 }
1950 },
1951 .disp => {
1952 const tok = try as.expect(.numeral);
1953 const is_neg = blk: {
1954 if (i > 0) {
1955 if (rule[i - 1] == .minus) break :blk true;
1956 }
1957 break :blk false;
1958 };
1959 if (std.fmt.parseInt(i32, as.source(tok), 0)) |disp| {
1960 res.disp = if (is_neg) -1 * disp else disp;
1961 } else |err| switch (err) {
1962 error.Overflow => {
1963 if (is_neg) return err;
1964 if (res.base) |base| {
1965 if (base.class() != .segment) return err;
1966 }
1967 const offset = try std.fmt.parseInt(u64, as.source(tok), 0);
1968 res.offset = offset;
1969 },
1970 else => return err,
1971 }
1972 },
1973 else => @compileError("unhandled operand output type: " ++ @tagName(cond)),
1974 }
1975 try as.skip(1, .{.space});
1976 }
1977 return res;
1978 }
1979
1980 fn parsePtrSize(as: *Assembler) ParseError!Memory.PtrSize {
1981 const size = try as.expect(.string);
1982 try as.skip(1, .{.space});
1983 const ptr = try as.expect(.string);
1984
1985 const size_raw = as.source(size);
1986 const ptr_raw = as.source(ptr);
1987 const len = size_raw.len + ptr_raw.len + 1;
1988 var buf: ["qword ptr".len]u8 = undefined;
1989 if (len > buf.len) return error.InvalidPtrSize;
1990
1991 for (size_raw, 0..) |c, i| {
1992 buf[i] = std.ascii.toLower(c);
1993 }
1994 buf[size_raw.len] = ' ';
1995 for (ptr_raw, 0..) |c, i| {
1996 buf[size_raw.len + i + 1] = std.ascii.toLower(c);
1997 }
1998
1999 const slice = buf[0..len];
2000 if (std.mem.eql(u8, slice, "qword ptr")) return .qword;
2001 if (std.mem.eql(u8, slice, "dword ptr")) return .dword;
2002 if (std.mem.eql(u8, slice, "word ptr")) return .word;
2003 if (std.mem.eql(u8, slice, "byte ptr")) return .byte;
2004 if (std.mem.eql(u8, slice, "tbyte ptr")) return .tbyte;
2005 return error.InvalidPtrSize;
2006 }
2007};
2008
2009test "assemble" {
2010 const input =
2011 \\int3
2012 \\mov rax, rbx
2013 \\mov qword ptr [rbp], rax
2014 \\mov qword ptr [rbp - 16], rax
2015 \\mov qword ptr [16 + rbp], rax
2016 \\mov rax, 0x10
2017 \\mov byte ptr [rbp - 0x10], 0x10
2018 \\mov word ptr [rbp + r12], r11w
2019 \\mov word ptr [rbp + r12 * 2], r11w
2020 \\mov word ptr [rbp + r12 * 2 - 16], r11w
2021 \\mov dword ptr [rip - 16], r12d
2022 \\mov rax, fs:0x0
2023 \\mov rax, gs:0x1000000000000000
2024 \\movzx r12, al
2025 \\imul r12, qword ptr [rbp - 16], 6
2026 \\jmp 0x0
2027 \\jc 0x0
2028 \\jb 0x0
2029 \\sal rax, 1
2030 \\sal rax, 63
2031 \\shl rax, 63
2032 \\sar rax, 63
2033 \\shr rax, 63
2034 \\test byte ptr [rbp - 16], r12b
2035 \\sal r12, cl
2036 \\mul qword ptr [rip - 16]
2037 \\div r12
2038 \\idiv byte ptr [rbp - 16]
2039 \\cwde
2040 \\cbw
2041 \\cdqe
2042 \\test byte ptr [rbp], ah
2043 \\test byte ptr [r12], spl
2044 \\cdq
2045 \\cwd
2046 \\cqo
2047 \\test bl, 0x1
2048 \\mov rbx,0x8000000000000000
2049 \\movss xmm0, dword ptr [rbp]
2050 \\movss xmm0, xmm1
2051 \\movss dword ptr [rbp - 16 + rax * 2], xmm7
2052 \\movss dword ptr [rbp - 16 + rax * 2], xmm8
2053 \\movss xmm15, xmm9
2054 \\movsd xmm8, qword ptr [rbp - 16]
2055 \\movsd qword ptr [rbp - 8], xmm0
2056 \\movq xmm8, qword ptr [rbp - 16]
2057 \\movq qword ptr [rbp - 16], xmm8
2058 \\ucomisd xmm0, qword ptr [rbp - 16]
2059 \\fisttp qword ptr [rbp - 16]
2060 \\fisttp word ptr [rip + 32]
2061 \\fisttp dword ptr [rax]
2062 \\fld tbyte ptr [rbp]
2063 \\fld dword ptr [rbp]
2064 \\xor bl, 0xff
2065 \\ud2
2066 \\add rsp, -1
2067 \\add rsp, 0xff
2068 \\mov sil, byte ptr [rax + rcx * 1]
2069 \\
2070 ;
2071
2072 // zig fmt: off
2073 const expected = &[_]u8{
2074 0xCC,
2075 0x48, 0x89, 0xD8,
2076 0x48, 0x89, 0x45, 0x00,
2077 0x48, 0x89, 0x45, 0xF0,
2078 0x48, 0x89, 0x45, 0x10,
2079 0x48, 0xC7, 0xC0, 0x10, 0x00, 0x00, 0x00,
2080 0xC6, 0x45, 0xF0, 0x10,
2081 0x66, 0x46, 0x89, 0x5C, 0x25, 0x00,
2082 0x66, 0x46, 0x89, 0x5C, 0x65, 0x00,
2083 0x66, 0x46, 0x89, 0x5C, 0x65, 0xF0,
2084 0x44, 0x89, 0x25, 0xF0, 0xFF, 0xFF, 0xFF,
2085 0x64, 0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00,
2086 0x65, 0x48, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
2087 0x4C, 0x0F, 0xB6, 0xE0,
2088 0x4C, 0x6B, 0x65, 0xF0, 0x06,
2089 0xE9, 0x00, 0x00, 0x00, 0x00,
2090 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00,
2091 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00,
2092 0x48, 0xD1, 0xE0,
2093 0x48, 0xC1, 0xE0, 0x3F,
2094 0x48, 0xC1, 0xE0, 0x3F,
2095 0x48, 0xC1, 0xF8, 0x3F,
2096 0x48, 0xC1, 0xE8, 0x3F,
2097 0x44, 0x84, 0x65, 0xF0,
2098 0x49, 0xD3, 0xE4,
2099 0x48, 0xF7, 0x25, 0xF0, 0xFF, 0xFF, 0xFF,
2100 0x49, 0xF7, 0xF4,
2101 0xF6, 0x7D, 0xF0,
2102 0x98,
2103 0x66, 0x98,
2104 0x48, 0x98,
2105 0x84, 0x65, 0x00,
2106 0x41, 0x84, 0x24, 0x24,
2107 0x99,
2108 0x66, 0x99,
2109 0x48, 0x99,
2110 0xF6, 0xC3, 0x01,
2111 0x48, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2112 0xF3, 0x0F, 0x10, 0x45, 0x00,
2113 0xF3, 0x0F, 0x10, 0xC1,
2114 0xF3, 0x0F, 0x11, 0x7C, 0x45, 0xF0,
2115 0xF3, 0x44, 0x0F, 0x11, 0x44, 0x45, 0xF0,
2116 0xF3, 0x45, 0x0F, 0x10, 0xF9,
2117 0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0,
2118 0xF2, 0x0F, 0x11, 0x45, 0xF8,
2119 0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0,
2120 0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0,
2121 0x66, 0x0F, 0x2E, 0x45, 0xF0,
2122 0xDD, 0x4D, 0xF0,
2123 0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00,
2124 0xDB, 0x08,
2125 0xDB, 0x6D, 0x00,
2126 0xD9, 0x45, 0x00,
2127 0x80, 0xF3, 0xFF,
2128 0x0F, 0x0B,
2129 0x48, 0x83, 0xC4, 0xFF,
2130 0x48, 0x81, 0xC4, 0xFF, 0x00, 0x00, 0x00,
2131 0x40, 0x8A, 0x34, 0x08,
2132 };
2133 // zig fmt: on
2134
2135 var as = Assembler.init(input);
2136 var output = std.ArrayList(u8).init(testing.allocator);
2137 defer output.deinit();
2138 try as.assemble(output.writer());
2139 try expectEqualHexStrings(expected, output.items, input);
2140}
2141
2142test "assemble - Jcc" {
2143 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2144 .{ .ja, 0x87 },
2145 .{ .jae, 0x83 },
2146 .{ .jb, 0x82 },
2147 .{ .jbe, 0x86 },
2148 .{ .jc, 0x82 },
2149 .{ .je, 0x84 },
2150 .{ .jg, 0x8f },
2151 .{ .jge, 0x8d },
2152 .{ .jl, 0x8c },
2153 .{ .jle, 0x8e },
2154 .{ .jna, 0x86 },
2155 .{ .jnae, 0x82 },
2156 .{ .jnb, 0x83 },
2157 .{ .jnbe, 0x87 },
2158 .{ .jnc, 0x83 },
2159 .{ .jne, 0x85 },
2160 .{ .jng, 0x8e },
2161 .{ .jnge, 0x8c },
2162 .{ .jnl, 0x8d },
2163 .{ .jnle, 0x8f },
2164 .{ .jno, 0x81 },
2165 .{ .jnp, 0x8b },
2166 .{ .jns, 0x89 },
2167 .{ .jnz, 0x85 },
2168 .{ .jo, 0x80 },
2169 .{ .jp, 0x8a },
2170 .{ .jpe, 0x8a },
2171 .{ .jpo, 0x8b },
2172 .{ .js, 0x88 },
2173 .{ .jz, 0x84 },
2174 };
2175
2176 inline for (&mnemonics) |mnemonic| {
2177 const input = @tagName(mnemonic[0]) ++ " 0x0";
2178 const expected = [_]u8{ 0x0f, mnemonic[1], 0x0, 0x0, 0x0, 0x0 };
2179 var as = Assembler.init(input);
2180 var output = std.ArrayList(u8).init(testing.allocator);
2181 defer output.deinit();
2182 try as.assemble(output.writer());
2183 try expectEqualHexStrings(&expected, output.items, input);
2184 }
2185}
2186
2187test "assemble - SETcc" {
2188 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2189 .{ .seta, 0x97 },
2190 .{ .setae, 0x93 },
2191 .{ .setb, 0x92 },
2192 .{ .setbe, 0x96 },
2193 .{ .setc, 0x92 },
2194 .{ .sete, 0x94 },
2195 .{ .setg, 0x9f },
2196 .{ .setge, 0x9d },
2197 .{ .setl, 0x9c },
2198 .{ .setle, 0x9e },
2199 .{ .setna, 0x96 },
2200 .{ .setnae, 0x92 },
2201 .{ .setnb, 0x93 },
2202 .{ .setnbe, 0x97 },
2203 .{ .setnc, 0x93 },
2204 .{ .setne, 0x95 },
2205 .{ .setng, 0x9e },
2206 .{ .setnge, 0x9c },
2207 .{ .setnl, 0x9d },
2208 .{ .setnle, 0x9f },
2209 .{ .setno, 0x91 },
2210 .{ .setnp, 0x9b },
2211 .{ .setns, 0x99 },
2212 .{ .setnz, 0x95 },
2213 .{ .seto, 0x90 },
2214 .{ .setp, 0x9a },
2215 .{ .setpe, 0x9a },
2216 .{ .setpo, 0x9b },
2217 .{ .sets, 0x98 },
2218 .{ .setz, 0x94 },
2219 };
2220
2221 inline for (&mnemonics) |mnemonic| {
2222 const input = @tagName(mnemonic[0]) ++ " al";
2223 const expected = [_]u8{ 0x0f, mnemonic[1], 0xC0 };
2224 var as = Assembler.init(input);
2225 var output = std.ArrayList(u8).init(testing.allocator);
2226 defer output.deinit();
2227 try as.assemble(output.writer());
2228 try expectEqualHexStrings(&expected, output.items, input);
2229 }
2230}
2231
2232test "assemble - CMOVcc" {
2233 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2234 .{ .cmova, 0x47 },
2235 .{ .cmovae, 0x43 },
2236 .{ .cmovb, 0x42 },
2237 .{ .cmovbe, 0x46 },
2238 .{ .cmovc, 0x42 },
2239 .{ .cmove, 0x44 },
2240 .{ .cmovg, 0x4f },
2241 .{ .cmovge, 0x4d },
2242 .{ .cmovl, 0x4c },
2243 .{ .cmovle, 0x4e },
2244 .{ .cmovna, 0x46 },
2245 .{ .cmovnae, 0x42 },
2246 .{ .cmovnb, 0x43 },
2247 .{ .cmovnbe, 0x47 },
2248 .{ .cmovnc, 0x43 },
2249 .{ .cmovne, 0x45 },
2250 .{ .cmovng, 0x4e },
2251 .{ .cmovnge, 0x4c },
2252 .{ .cmovnl, 0x4d },
2253 .{ .cmovnle, 0x4f },
2254 .{ .cmovno, 0x41 },
2255 .{ .cmovnp, 0x4b },
2256 .{ .cmovns, 0x49 },
2257 .{ .cmovnz, 0x45 },
2258 .{ .cmovo, 0x40 },
2259 .{ .cmovp, 0x4a },
2260 .{ .cmovpe, 0x4a },
2261 .{ .cmovpo, 0x4b },
2262 .{ .cmovs, 0x48 },
2263 .{ .cmovz, 0x44 },
2264 };
2265
2266 inline for (&mnemonics) |mnemonic| {
2267 const input = @tagName(mnemonic[0]) ++ " rax, rbx";
2268 const expected = [_]u8{ 0x48, 0x0f, mnemonic[1], 0xC3 };
2269 var as = Assembler.init(input);
2270 var output = std.ArrayList(u8).init(testing.allocator);
2271 defer output.deinit();
2272 try as.assemble(output.writer());
2273 try expectEqualHexStrings(&expected, output.items, input);
2274 }
2275}
src/arch/x86_64/encodings.zig created+621
...@@ -0,0 +1,621 @@
1const Encoding = @import("Encoding.zig");
2const Mnemonic = Encoding.Mnemonic;
3const OpEn = Encoding.OpEn;
4const Op = Encoding.Op;
5const Mode = Encoding.Mode;
6
7const opcode_len = u2;
8const modrm_ext = u3;
9
10const Entry = struct { Mnemonic, OpEn, Op, Op, Op, Op, opcode_len, u8, u8, u8, modrm_ext, Mode };
11
12// TODO move this into a .zon file when Zig is capable of importing .zon files
13// zig fmt: off
14pub const table = &[_]Entry{
15 // General-purpose
16 .{ .adc, .zi, .al, .imm8, .none, .none, 1, 0x14, 0x00, 0x00, 0, .none },
17 .{ .adc, .zi, .ax, .imm16, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none },
18 .{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none },
19 .{ .adc, .zi, .rax, .imm32s, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long },
20 .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none },
21 .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .rex },
22 .{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none },
23 .{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none },
24 .{ .adc, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long },
25 .{ .adc, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none },
26 .{ .adc, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none },
27 .{ .adc, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long },
28 .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none },
29 .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .rex },
30 .{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none },
31 .{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none },
32 .{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long },
33 .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none },
34 .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .rex },
35 .{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none },
36 .{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none },
37 .{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long },
38
39 .{ .add, .zi, .al, .imm8, .none, .none, 1, 0x04, 0x00, 0x00, 0, .none },
40 .{ .add, .zi, .ax, .imm16, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none },
41 .{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none },
42 .{ .add, .zi, .rax, .imm32s, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long },
43 .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none },
44 .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .rex },
45 .{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none },
46 .{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none },
47 .{ .add, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long },
48 .{ .add, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none },
49 .{ .add, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none },
50 .{ .add, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long },
51 .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none },
52 .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .rex },
53 .{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none },
54 .{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none },
55 .{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long },
56 .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none },
57 .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .rex },
58 .{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none },
59 .{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none },
60 .{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long },
61
62 .{ .@"and", .zi, .al, .imm8, .none, .none, 1, 0x24, 0x00, 0x00, 0, .none },
63 .{ .@"and", .zi, .ax, .imm16, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none },
64 .{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none },
65 .{ .@"and", .zi, .rax, .imm32s, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long },
66 .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none },
67 .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .rex },
68 .{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none },
69 .{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none },
70 .{ .@"and", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long },
71 .{ .@"and", .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none },
72 .{ .@"and", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none },
73 .{ .@"and", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long },
74 .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none },
75 .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .rex },
76 .{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none },
77 .{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none },
78 .{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long },
79 .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none },
80 .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .rex },
81 .{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none },
82 .{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none },
83 .{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long },
84
85 // This is M encoding according to Intel, but D makes more sense here.
86 .{ .call, .d, .rel32, .none, .none, .none, 1, 0xe8, 0x00, 0x00, 0, .none },
87 .{ .call, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 2, .none },
88
89 .{ .cbw, .np, .o16, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none },
90 .{ .cwde, .np, .o32, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none },
91 .{ .cdqe, .np, .o64, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .long },
92
93 .{ .cwd, .np, .o16, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none },
94 .{ .cdq, .np, .o32, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none },
95 .{ .cqo, .np, .o64, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .long },
96
97 .{ .cmova, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
98 .{ .cmova, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
99 .{ .cmova, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long },
100 .{ .cmovae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
101 .{ .cmovae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
102 .{ .cmovae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long },
103 .{ .cmovb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
104 .{ .cmovb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
105 .{ .cmovb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long },
106 .{ .cmovbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
107 .{ .cmovbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
108 .{ .cmovbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long },
109 .{ .cmovc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
110 .{ .cmovc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
111 .{ .cmovc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long },
112 .{ .cmove, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
113 .{ .cmove, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
114 .{ .cmove, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long },
115 .{ .cmovg, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
116 .{ .cmovg, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
117 .{ .cmovg, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long },
118 .{ .cmovge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
119 .{ .cmovge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
120 .{ .cmovge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long },
121 .{ .cmovl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
122 .{ .cmovl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
123 .{ .cmovl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long },
124 .{ .cmovle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
125 .{ .cmovle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
126 .{ .cmovle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long },
127 .{ .cmovna, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
128 .{ .cmovna, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
129 .{ .cmovna, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long },
130 .{ .cmovnae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
131 .{ .cmovnae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
132 .{ .cmovnae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long },
133 .{ .cmovnb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
134 .{ .cmovnb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
135 .{ .cmovnb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long },
136 .{ .cmovnbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
137 .{ .cmovnbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
138 .{ .cmovnbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long },
139 .{ .cmovnc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
140 .{ .cmovnc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
141 .{ .cmovnc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long },
142 .{ .cmovne, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
143 .{ .cmovne, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
144 .{ .cmovne, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long },
145 .{ .cmovng, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
146 .{ .cmovng, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
147 .{ .cmovng, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long },
148 .{ .cmovnge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
149 .{ .cmovnge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
150 .{ .cmovnge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long },
151 .{ .cmovnl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
152 .{ .cmovnl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
153 .{ .cmovnl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long },
154 .{ .cmovnle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
155 .{ .cmovnle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
156 .{ .cmovnle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long },
157 .{ .cmovno, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none },
158 .{ .cmovno, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none },
159 .{ .cmovno, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .long },
160 .{ .cmovnp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
161 .{ .cmovnp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
162 .{ .cmovnp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long },
163 .{ .cmovns, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none },
164 .{ .cmovns, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none },
165 .{ .cmovns, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .long },
166 .{ .cmovnz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
167 .{ .cmovnz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
168 .{ .cmovnz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long },
169 .{ .cmovo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none },
170 .{ .cmovo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none },
171 .{ .cmovo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .long },
172 .{ .cmovp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
173 .{ .cmovp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
174 .{ .cmovp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long },
175 .{ .cmovpe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
176 .{ .cmovpe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
177 .{ .cmovpe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long },
178 .{ .cmovpo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
179 .{ .cmovpo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
180 .{ .cmovpo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long },
181 .{ .cmovs, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none },
182 .{ .cmovs, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none },
183 .{ .cmovs, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .long },
184 .{ .cmovz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
185 .{ .cmovz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
186 .{ .cmovz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long },
187
188 .{ .cmp, .zi, .al, .imm8, .none, .none, 1, 0x3c, 0x00, 0x00, 0, .none },
189 .{ .cmp, .zi, .ax, .imm16, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none },
190 .{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none },
191 .{ .cmp, .zi, .rax, .imm32s, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long },
192 .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none },
193 .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .rex },
194 .{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none },
195 .{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none },
196 .{ .cmp, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long },
197 .{ .cmp, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none },
198 .{ .cmp, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none },
199 .{ .cmp, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long },
200 .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none },
201 .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .rex },
202 .{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none },
203 .{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none },
204 .{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long },
205 .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none },
206 .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .rex },
207 .{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none },
208 .{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none },
209 .{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long },
210
211 .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .none },
212 .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .rex },
213 .{ .div, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none },
214 .{ .div, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none },
215 .{ .div, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .long },
216
217 .{ .fisttp, .m, .m16, .none, .none, .none, 1, 0xdf, 0x00, 0x00, 1, .fpu },
218 .{ .fisttp, .m, .m32, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 1, .fpu },
219 .{ .fisttp, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 1, .fpu },
220
221 .{ .fld, .m, .m32, .none, .none, .none, 1, 0xd9, 0x00, 0x00, 0, .fpu },
222 .{ .fld, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 0, .fpu },
223 .{ .fld, .m, .m80, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 5, .fpu },
224
225 .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .none },
226 .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .rex },
227 .{ .idiv, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none },
228 .{ .idiv, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none },
229 .{ .idiv, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .long },
230
231 .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none },
232 .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .rex },
233 .{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none },
234 .{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none },
235 .{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long },
236 .{ .imul, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none },
237 .{ .imul, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none },
238 .{ .imul, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .long },
239 .{ .imul, .rmi, .r16, .rm16, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .none },
240 .{ .imul, .rmi, .r32, .rm32, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .none },
241 .{ .imul, .rmi, .r64, .rm64, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .long },
242 .{ .imul, .rmi, .r16, .rm16, .imm16, .none, 1, 0x69, 0x00, 0x00, 0, .none },
243 .{ .imul, .rmi, .r32, .rm32, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .none },
244 .{ .imul, .rmi, .r64, .rm64, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .long },
245
246 .{ .int3, .np, .none, .none, .none, .none, 1, 0xcc, 0x00, 0x00, 0, .none },
247
248 .{ .ja, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none },
249 .{ .jae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none },
250 .{ .jb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none },
251 .{ .jbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none },
252 .{ .jc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none },
253 .{ .jrcxz, .d, .rel32, .none, .none, .none, 1, 0xe3, 0x00, 0x00, 0, .none },
254 .{ .je, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none },
255 .{ .jg, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none },
256 .{ .jge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none },
257 .{ .jl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none },
258 .{ .jle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none },
259 .{ .jna, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none },
260 .{ .jnae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none },
261 .{ .jnb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none },
262 .{ .jnbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none },
263 .{ .jnc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none },
264 .{ .jne, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none },
265 .{ .jng, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none },
266 .{ .jnge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none },
267 .{ .jnl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none },
268 .{ .jnle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none },
269 .{ .jno, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x81, 0x00, 0, .none },
270 .{ .jnp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none },
271 .{ .jns, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x89, 0x00, 0, .none },
272 .{ .jnz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none },
273 .{ .jo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x80, 0x00, 0, .none },
274 .{ .jp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none },
275 .{ .jpe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none },
276 .{ .jpo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none },
277 .{ .js, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x88, 0x00, 0, .none },
278 .{ .jz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none },
279
280 .{ .jmp, .d, .rel32, .none, .none, .none, 1, 0xe9, 0x00, 0x00, 0, .none },
281 .{ .jmp, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 4, .none },
282
283 .{ .lea, .rm, .r16, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none },
284 .{ .lea, .rm, .r32, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none },
285 .{ .lea, .rm, .r64, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .long },
286
287 .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none },
288 .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .rex },
289 .{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none },
290 .{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none },
291 .{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long },
292 .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none },
293 .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .rex },
294 .{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none },
295 .{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none },
296 .{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long },
297 .{ .mov, .mr, .rm16, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .none },
298 .{ .mov, .mr, .rm64, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .long },
299 .{ .mov, .rm, .sreg, .rm16, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .none },
300 .{ .mov, .rm, .sreg, .rm64, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .long },
301 .{ .mov, .fd, .al, .moffs, .none, .none, 1, 0xa0, 0x00, 0x00, 0, .none },
302 .{ .mov, .fd, .ax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none },
303 .{ .mov, .fd, .eax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none },
304 .{ .mov, .fd, .rax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .long },
305 .{ .mov, .td, .moffs, .al, .none, .none, 1, 0xa2, 0x00, 0x00, 0, .none },
306 .{ .mov, .td, .moffs, .ax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none },
307 .{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none },
308 .{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long },
309 .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none },
310 .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .rex },
311 .{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none },
312 .{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none },
313 .{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long },
314 .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none },
315 .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .rex },
316 .{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none },
317 .{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none },
318 .{ .mov, .mi, .rm64, .imm32s, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long },
319
320 .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none },
321 .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .rex },
322 .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none },
323 .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .rex },
324 .{ .movsx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .long },
325 .{ .movsx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .none },
326 .{ .movsx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .long },
327
328 // This instruction is discouraged.
329 .{ .movsxd, .rm, .r32, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .none },
330 .{ .movsxd, .rm, .r64, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .long },
331
332 .{ .movzx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none },
333 .{ .movzx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none },
334 .{ .movzx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .long },
335 .{ .movzx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .none },
336 .{ .movzx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .long },
337
338 .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .none },
339 .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .rex },
340 .{ .mul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none },
341 .{ .mul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none },
342 .{ .mul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .long },
343
344 .{ .nop, .np, .none, .none, .none, .none, 1, 0x90, 0x00, 0x00, 0, .none },
345
346 .{ .@"or", .zi, .al, .imm8, .none, .none, 1, 0x0c, 0x00, 0x00, 0, .none },
347 .{ .@"or", .zi, .ax, .imm16, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none },
348 .{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none },
349 .{ .@"or", .zi, .rax, .imm32s, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long },
350 .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none },
351 .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .rex },
352 .{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none },
353 .{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none },
354 .{ .@"or", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long },
355 .{ .@"or", .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none },
356 .{ .@"or", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none },
357 .{ .@"or", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long },
358 .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none },
359 .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .rex },
360 .{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none },
361 .{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none },
362 .{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long },
363 .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none },
364 .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .rex },
365 .{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none },
366 .{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none },
367 .{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long },
368
369 .{ .pop, .o, .r16, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none },
370 .{ .pop, .o, .r64, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none },
371 .{ .pop, .m, .rm16, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none },
372 .{ .pop, .m, .rm64, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none },
373
374 .{ .push, .o, .r16, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none },
375 .{ .push, .o, .r64, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none },
376 .{ .push, .m, .rm16, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none },
377 .{ .push, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none },
378 .{ .push, .i, .imm8, .none, .none, .none, 1, 0x6a, 0x00, 0x00, 0, .none },
379 .{ .push, .i, .imm16, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none },
380 .{ .push, .i, .imm32, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none },
381
382 .{ .ret, .np, .none, .none, .none, .none, 1, 0xc3, 0x00, 0x00, 0, .none },
383
384 .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none },
385 .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .rex },
386 .{ .sal, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
387 .{ .sal, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
388 .{ .sal, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long },
389 .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none },
390 .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .rex },
391 .{ .sal, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
392 .{ .sal, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
393 .{ .sal, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long },
394 .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none },
395 .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .rex },
396 .{ .sal, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
397 .{ .sal, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
398 .{ .sal, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long },
399
400 .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .none },
401 .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .rex },
402 .{ .sar, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none },
403 .{ .sar, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none },
404 .{ .sar, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .long },
405 .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .none },
406 .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .rex },
407 .{ .sar, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none },
408 .{ .sar, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none },
409 .{ .sar, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .long },
410 .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .none },
411 .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .rex },
412 .{ .sar, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none },
413 .{ .sar, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none },
414 .{ .sar, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .long },
415
416 .{ .sbb, .zi, .al, .imm8, .none, .none, 1, 0x1c, 0x00, 0x00, 0, .none },
417 .{ .sbb, .zi, .ax, .imm16, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none },
418 .{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none },
419 .{ .sbb, .zi, .rax, .imm32s, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long },
420 .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none },
421 .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .rex },
422 .{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none },
423 .{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none },
424 .{ .sbb, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long },
425 .{ .sbb, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none },
426 .{ .sbb, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none },
427 .{ .sbb, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long },
428 .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none },
429 .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .rex },
430 .{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none },
431 .{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none },
432 .{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long },
433 .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none },
434 .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .rex },
435 .{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none },
436 .{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none },
437 .{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long },
438
439 .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none },
440 .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .rex },
441 .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none },
442 .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex },
443 .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none },
444 .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex },
445 .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none },
446 .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .rex },
447 .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none },
448 .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex },
449 .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none },
450 .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .rex },
451 .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none },
452 .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .rex },
453 .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none },
454 .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .rex },
455 .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none },
456 .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .rex },
457 .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none },
458 .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .rex },
459 .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none },
460 .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .rex },
461 .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none },
462 .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex },
463 .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none },
464 .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex },
465 .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none },
466 .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .rex },
467 .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none },
468 .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex },
469 .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none },
470 .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .rex },
471 .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none },
472 .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .rex },
473 .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none },
474 .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .rex },
475 .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none },
476 .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .rex },
477 .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none },
478 .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .rex },
479 .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .none },
480 .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .rex },
481 .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none },
482 .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .rex },
483 .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .none },
484 .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .rex },
485 .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none },
486 .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .rex },
487 .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .none },
488 .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .rex },
489 .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none },
490 .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .rex },
491 .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none },
492 .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .rex },
493 .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none },
494 .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .rex },
495 .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .none },
496 .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .rex },
497 .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none },
498 .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .rex },
499
500 .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none },
501 .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .rex },
502 .{ .shl, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
503 .{ .shl, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
504 .{ .shl, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long },
505 .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none },
506 .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .rex },
507 .{ .shl, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
508 .{ .shl, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
509 .{ .shl, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long },
510 .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none },
511 .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .rex },
512 .{ .shl, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
513 .{ .shl, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
514 .{ .shl, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long },
515
516 .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .none },
517 .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .rex },
518 .{ .shr, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none },
519 .{ .shr, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none },
520 .{ .shr, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .long },
521 .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .none },
522 .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .rex },
523 .{ .shr, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none },
524 .{ .shr, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none },
525 .{ .shr, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .long },
526 .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .none },
527 .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .rex },
528 .{ .shr, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none },
529 .{ .shr, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none },
530 .{ .shr, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .long },
531
532 .{ .sub, .zi, .al, .imm8, .none, .none, 1, 0x2c, 0x00, 0x00, 0, .none },
533 .{ .sub, .zi, .ax, .imm16, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none },
534 .{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none },
535 .{ .sub, .zi, .rax, .imm32s, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long },
536 .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none },
537 .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .rex },
538 .{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none },
539 .{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none },
540 .{ .sub, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long },
541 .{ .sub, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none },
542 .{ .sub, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none },
543 .{ .sub, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long },
544 .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none },
545 .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .rex },
546 .{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none },
547 .{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none },
548 .{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long },
549 .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none },
550 .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .rex },
551 .{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none },
552 .{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none },
553 .{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long },
554
555 .{ .syscall, .np, .none, .none, .none, .none, 2, 0x0f, 0x05, 0x00, 0, .none },
556
557 .{ .@"test", .zi, .al, .imm8, .none, .none, 1, 0xa8, 0x00, 0x00, 0, .none },
558 .{ .@"test", .zi, .ax, .imm16, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none },
559 .{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none },
560 .{ .@"test", .zi, .rax, .imm32s, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long },
561 .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none },
562 .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .rex },
563 .{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none },
564 .{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none },
565 .{ .@"test", .mi, .rm64, .imm32s, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long },
566 .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none },
567 .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .rex },
568 .{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none },
569 .{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none },
570 .{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long },
571
572 .{ .ud2, .np, .none, .none, .none, .none, 2, 0x0f, 0x0b, 0x00, 0, .none },
573
574 .{ .xor, .zi, .al, .imm8, .none, .none, 1, 0x34, 0x00, 0x00, 0, .none },
575 .{ .xor, .zi, .ax, .imm16, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none },
576 .{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none },
577 .{ .xor, .zi, .rax, .imm32s, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long },
578 .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none },
579 .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .rex },
580 .{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none },
581 .{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none },
582 .{ .xor, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long },
583 .{ .xor, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none },
584 .{ .xor, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none },
585 .{ .xor, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long },
586 .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none },
587 .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .rex },
588 .{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none },
589 .{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none },
590 .{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long },
591 .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none },
592 .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .rex },
593 .{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none },
594 .{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none },
595 .{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long },
596
597 // SSE
598 .{ .addss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x58, 0, .sse },
599
600 .{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, 3, 0xf3, 0x0f, 0xc2, 0, .sse },
601
602 .{ .movss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x10, 0, .sse },
603 .{ .movss, .mr, .xmm_m32, .xmm, .none, .none, 3, 0xf3, 0x0f, 0x11, 0, .sse },
604
605 .{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, 2, 0x0f, 0x2e, 0x00, 0, .sse },
606
607 // SSE2
608 .{ .addsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x58, 0, .sse2 },
609
610 .{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, 3, 0xf2, 0x0f, 0xc2, 0, .sse2 },
611
612 .{ .movq, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf3, 0x0f, 0x7e, 0, .sse2 },
613 .{ .movq, .mr, .xmm_m64, .xmm, .none, .none, 3, 0x66, 0x0f, 0xd6, 0, .sse2 },
614
615 .{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x10, 0, .sse2 },
616 .{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, 3, 0xf2, 0x0f, 0x11, 0, .sse2 },
617
618 .{ .ucomisd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0x66, 0x0f, 0x2e, 0, .sse2 },
619};
620// zig fmt: on
621
src/codegen.zig+287-19
...@@ -29,11 +29,10 @@ pub const Result = union(enum) {...@@ -29,11 +29,10 @@ pub const Result = union(enum) {
29 fail: *ErrorMsg,29 fail: *ErrorMsg,
30};30};
3131
32pub const GenerateSymbolError = error{32pub const CodeGenError = error{
33 OutOfMemory,33 OutOfMemory,
34 Overflow,34 Overflow,
35 /// A Decl that this symbol depends on had a semantic analysis failure.35 CodegenFail,
36 AnalysisFail,
37};36};
3837
39pub const DebugInfoOutput = union(enum) {38pub const DebugInfoOutput = union(enum) {
...@@ -63,19 +62,6 @@ pub const DebugInfoOutput = union(enum) {...@@ -63,19 +62,6 @@ pub const DebugInfoOutput = union(enum) {
63 none,62 none,
64};63};
6564
66/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
67/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
68/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
69/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
70pub const LinkerLoad = struct {
71 type: enum {
72 got,
73 direct,
74 import,
75 },
76 sym_index: u32,
77};
78
79pub fn generateFunction(65pub fn generateFunction(
80 bin_file: *link.File,66 bin_file: *link.File,
81 src_loc: Module.SrcLoc,67 src_loc: Module.SrcLoc,
...@@ -84,7 +70,7 @@ pub fn generateFunction(...@@ -84,7 +70,7 @@ pub fn generateFunction(
84 liveness: Liveness,70 liveness: Liveness,
85 code: *std.ArrayList(u8),71 code: *std.ArrayList(u8),
86 debug_output: DebugInfoOutput,72 debug_output: DebugInfoOutput,
87) GenerateSymbolError!Result {73) CodeGenError!Result {
88 switch (bin_file.options.target.cpu.arch) {74 switch (bin_file.options.target.cpu.arch) {
89 .arm,75 .arm,
90 .armeb,76 .armeb,
...@@ -120,7 +106,7 @@ pub fn generateSymbol(...@@ -120,7 +106,7 @@ pub fn generateSymbol(
120 code: *std.ArrayList(u8),106 code: *std.ArrayList(u8),
121 debug_output: DebugInfoOutput,107 debug_output: DebugInfoOutput,
122 reloc_info: RelocInfo,108 reloc_info: RelocInfo,
123) GenerateSymbolError!Result {109) CodeGenError!Result {
124 const tracy = trace(@src());110 const tracy = trace(@src());
125 defer tracy.end();111 defer tracy.end();
126112
...@@ -823,7 +809,7 @@ fn lowerDeclRef(...@@ -823,7 +809,7 @@ fn lowerDeclRef(
823 code: *std.ArrayList(u8),809 code: *std.ArrayList(u8),
824 debug_output: DebugInfoOutput,810 debug_output: DebugInfoOutput,
825 reloc_info: RelocInfo,811 reloc_info: RelocInfo,
826) GenerateSymbolError!Result {812) CodeGenError!Result {
827 const target = bin_file.options.target;813 const target = bin_file.options.target;
828 const module = bin_file.options.module.?;814 const module = bin_file.options.module.?;
829 if (typed_value.ty.isSlice()) {815 if (typed_value.ty.isSlice()) {
...@@ -880,6 +866,288 @@ fn lowerDeclRef(...@@ -880,6 +866,288 @@ fn lowerDeclRef(
880 return Result.ok;866 return Result.ok;
881}867}
882868
869/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
870/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
871/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
872/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
873pub const LinkerLoad = struct {
874 type: enum {
875 got,
876 direct,
877 import,
878 },
879 sym_index: u32,
880};
881
882pub const GenResult = union(enum) {
883 mcv: MCValue,
884 fail: *ErrorMsg,
885
886 const MCValue = union(enum) {
887 none,
888 undef,
889 /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets
890 /// such as ARM, the immediate will never exceed 32-bits.
891 immediate: u64,
892 linker_load: LinkerLoad,
893 /// Direct by-address reference to memory location.
894 memory: u64,
895 };
896
897 fn mcv(val: MCValue) GenResult {
898 return .{ .mcv = val };
899 }
900
901 fn fail(
902 gpa: Allocator,
903 src_loc: Module.SrcLoc,
904 comptime format: []const u8,
905 args: anytype,
906 ) Allocator.Error!GenResult {
907 const msg = try ErrorMsg.create(gpa, src_loc, format, args);
908 return .{ .fail = msg };
909 }
910};
911
912fn genDeclRef(
913 bin_file: *link.File,
914 src_loc: Module.SrcLoc,
915 tv: TypedValue,
916 decl_index: Module.Decl.Index,
917) CodeGenError!GenResult {
918 log.debug("genDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
919
920 const target = bin_file.options.target;
921 const ptr_bits = target.cpu.arch.ptrBitWidth();
922 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
923
924 const module = bin_file.options.module.?;
925 const decl = module.declPtr(decl_index);
926
927 if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
928 const imm: u64 = switch (ptr_bytes) {
929 1 => 0xaa,
930 2 => 0xaaaa,
931 4 => 0xaaaaaaaa,
932 8 => 0xaaaaaaaaaaaaaaaa,
933 else => unreachable,
934 };
935 return GenResult.mcv(.{ .immediate = imm });
936 }
937
938 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
939 if (tv.ty.zigTypeTag() == .Pointer) blk: {
940 if (tv.ty.castPtrToFn()) |_| break :blk;
941 if (!tv.ty.elemType2().hasRuntimeBits()) {
942 return GenResult.mcv(.none);
943 }
944 }
945
946 module.markDeclAlive(decl);
947
948 if (bin_file.cast(link.File.Elf)) |elf_file| {
949 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
950 const atom = elf_file.getAtom(atom_index);
951 return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });
952 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
953 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
954 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
955 return GenResult.mcv(.{ .linker_load = .{
956 .type = .got,
957 .sym_index = sym_index,
958 } });
959 } else if (bin_file.cast(link.File.Coff)) |coff_file| {
960 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
961 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
962 return GenResult.mcv(.{ .linker_load = .{
963 .type = .got,
964 .sym_index = sym_index,
965 } });
966 } else if (bin_file.cast(link.File.Plan9)) |p9| {
967 const decl_block_index = try p9.seeDecl(decl_index);
968 const decl_block = p9.getDeclBlock(decl_block_index);
969 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
970 return GenResult.mcv(.{ .memory = got_addr });
971 } else {
972 return GenResult.fail(bin_file.allocator, src_loc, "TODO genDeclRef for target {}", .{target});
973 }
974}
975
976fn genUnnamedConst(
977 bin_file: *link.File,
978 src_loc: Module.SrcLoc,
979 tv: TypedValue,
980 owner_decl_index: Module.Decl.Index,
981) CodeGenError!GenResult {
982 log.debug("genUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
983
984 const target = bin_file.options.target;
985 const local_sym_index = bin_file.lowerUnnamedConst(tv, owner_decl_index) catch |err| {
986 return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});
987 };
988 if (bin_file.cast(link.File.Elf)) |elf_file| {
989 return GenResult.mcv(.{ .memory = elf_file.getSymbol(local_sym_index).st_value });
990 } else if (bin_file.cast(link.File.MachO)) |_| {
991 return GenResult.mcv(.{ .linker_load = .{
992 .type = .direct,
993 .sym_index = local_sym_index,
994 } });
995 } else if (bin_file.cast(link.File.Coff)) |_| {
996 return GenResult.mcv(.{ .linker_load = .{
997 .type = .direct,
998 .sym_index = local_sym_index,
999 } });
1000 } else if (bin_file.cast(link.File.Plan9)) |p9| {
1001 const ptr_bits = target.cpu.arch.ptrBitWidth();
1002 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1003 const got_index = local_sym_index; // the plan9 backend returns the got_index
1004 const got_addr = p9.bases.data + got_index * ptr_bytes;
1005 return GenResult.mcv(.{ .memory = got_addr });
1006 } else {
1007 return GenResult.fail(bin_file.allocator, src_loc, "TODO genUnnamedConst for target {}", .{target});
1008 }
1009}
1010
1011pub fn genTypedValue(
1012 bin_file: *link.File,
1013 src_loc: Module.SrcLoc,
1014 arg_tv: TypedValue,
1015 owner_decl_index: Module.Decl.Index,
1016) CodeGenError!GenResult {
1017 var typed_value = arg_tv;
1018 if (typed_value.val.castTag(.runtime_value)) |rt| {
1019 typed_value.val = rt.data;
1020 }
1021
1022 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
1023
1024 if (typed_value.val.isUndef())
1025 return GenResult.mcv(.undef);
1026
1027 const target = bin_file.options.target;
1028 const ptr_bits = target.cpu.arch.ptrBitWidth();
1029
1030 if (typed_value.val.castTag(.decl_ref)) |payload| {
1031 return genDeclRef(bin_file, src_loc, typed_value, payload.data);
1032 }
1033 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
1034 return genDeclRef(bin_file, src_loc, typed_value, payload.data.decl_index);
1035 }
1036
1037 switch (typed_value.ty.zigTypeTag()) {
1038 .Void => return GenResult.mcv(.none),
1039 .Pointer => switch (typed_value.ty.ptrSize()) {
1040 .Slice => {},
1041 else => {
1042 switch (typed_value.val.tag()) {
1043 .int_u64 => {
1044 return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) });
1045 },
1046 else => {},
1047 }
1048 },
1049 },
1050 .Int => {
1051 const info = typed_value.ty.intInfo(target);
1052 if (info.bits <= ptr_bits) {
1053 const unsigned = switch (info.signedness) {
1054 .signed => @bitCast(u64, typed_value.val.toSignedInt(target)),
1055 .unsigned => typed_value.val.toUnsignedInt(target),
1056 };
1057 return GenResult.mcv(.{ .immediate = unsigned });
1058 }
1059 },
1060 .Bool => {
1061 return GenResult.mcv(.{ .immediate = @boolToInt(typed_value.val.toBool()) });
1062 },
1063 .Optional => {
1064 if (typed_value.ty.isPtrLikeOptional()) {
1065 if (typed_value.val.isNull())
1066 return GenResult.mcv(.{ .immediate = 0 });
1067
1068 var buf: Type.Payload.ElemType = undefined;
1069 return genTypedValue(bin_file, src_loc, .{
1070 .ty = typed_value.ty.optionalChild(&buf),
1071 .val = typed_value.val,
1072 }, owner_decl_index);
1073 } else if (typed_value.ty.abiSize(target) == 1) {
1074 return GenResult.mcv(.{ .immediate = @boolToInt(!typed_value.val.isNull()) });
1075 }
1076 },
1077 .Enum => {
1078 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
1079 switch (typed_value.ty.tag()) {
1080 .enum_simple => {
1081 return GenResult.mcv(.{ .immediate = field_index.data });
1082 },
1083 .enum_full, .enum_nonexhaustive => {
1084 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
1085 if (enum_full.values.count() != 0) {
1086 const tag_val = enum_full.values.keys()[field_index.data];
1087 return genTypedValue(bin_file, src_loc, .{
1088 .ty = enum_full.tag_ty,
1089 .val = tag_val,
1090 }, owner_decl_index);
1091 } else {
1092 return GenResult.mcv(.{ .immediate = field_index.data });
1093 }
1094 },
1095 else => unreachable,
1096 }
1097 } else {
1098 var int_tag_buffer: Type.Payload.Bits = undefined;
1099 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
1100 return genTypedValue(bin_file, src_loc, .{
1101 .ty = int_tag_ty,
1102 .val = typed_value.val,
1103 }, owner_decl_index);
1104 }
1105 },
1106 .ErrorSet => {
1107 switch (typed_value.val.tag()) {
1108 .@"error" => {
1109 const err_name = typed_value.val.castTag(.@"error").?.data.name;
1110 const module = bin_file.options.module.?;
1111 const global_error_set = module.global_error_set;
1112 const error_index = global_error_set.get(err_name).?;
1113 return GenResult.mcv(.{ .immediate = error_index });
1114 },
1115 else => {
1116 // In this case we are rendering an error union which has a 0 bits payload.
1117 return GenResult.mcv(.{ .immediate = 0 });
1118 },
1119 }
1120 },
1121 .ErrorUnion => {
1122 const error_type = typed_value.ty.errorUnionSet();
1123 const payload_type = typed_value.ty.errorUnionPayload();
1124 const is_pl = typed_value.val.errorUnionIsPayload();
1125
1126 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
1127 // We use the error type directly as the type.
1128 const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
1129 return genTypedValue(bin_file, src_loc, .{
1130 .ty = error_type,
1131 .val = err_val,
1132 }, owner_decl_index);
1133 }
1134 },
1135
1136 .ComptimeInt => unreachable,
1137 .ComptimeFloat => unreachable,
1138 .Type => unreachable,
1139 .EnumLiteral => unreachable,
1140 .NoReturn => unreachable,
1141 .Undefined => unreachable,
1142 .Null => unreachable,
1143 .Opaque => unreachable,
1144
1145 else => {},
1146 }
1147
1148 return genUnnamedConst(bin_file, src_loc, typed_value, owner_decl_index);
1149}
1150
883pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {1151pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
884 const payload_align = payload_ty.abiAlignment(target);1152 const payload_align = payload_ty.abiAlignment(target);
885 const error_align = Type.anyerror.abiAlignment(target);1153 const error_align = Type.anyerror.abiAlignment(target);
src/codegen/c.zig+912-515
...@@ -17,12 +17,6 @@ const LazySrcLoc = Module.LazySrcLoc;...@@ -17,12 +17,6 @@ const LazySrcLoc = Module.LazySrcLoc;
17const Air = @import("../Air.zig");17const Air = @import("../Air.zig");
18const Liveness = @import("../Liveness.zig");18const Liveness = @import("../Liveness.zig");
1919
20const target_util = @import("../target.zig");
21const libcFloatPrefix = target_util.libcFloatPrefix;
22const libcFloatSuffix = target_util.libcFloatSuffix;
23const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev;
24const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
25
26const BigIntLimb = std.math.big.Limb;20const BigIntLimb = std.math.big.Limb;
27const BigInt = std.math.big.int;21const BigInt = std.math.big.int;
2822
...@@ -112,11 +106,7 @@ const ValueRenderLocation = enum {...@@ -112,11 +106,7 @@ const ValueRenderLocation = enum {
112 }106 }
113};107};
114108
115const BuiltinInfo = enum {109const BuiltinInfo = enum { none, bits };
116 None,
117 Range,
118 Bits,
119};
120110
121const reserved_idents = std.ComptimeStringMap(void, .{111const reserved_idents = std.ComptimeStringMap(void, .{
122 // C language112 // C language
...@@ -440,16 +430,24 @@ pub const Function = struct {...@@ -440,16 +430,24 @@ pub const Function = struct {
440 return f.object.dg.typeToCType(ty, kind);430 return f.object.dg.typeToCType(ty, kind);
441 }431 }
442432
433 fn byteSize(f: *Function, cty: CType) u64 {
434 return f.object.dg.byteSize(cty);
435 }
436
443 fn renderType(f: *Function, w: anytype, t: Type) !void {437 fn renderType(f: *Function, w: anytype, t: Type) !void {
444 return f.object.dg.renderType(w, t);438 return f.object.dg.renderType(w, t);
445 }439 }
446440
447 fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void {441 fn renderCType(f: *Function, w: anytype, t: CType.Index) !void {
448 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src } }, src_ty, location);442 return f.object.dg.renderCType(w, t);
443 }
444
445 fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorizer, src_ty: Type, location: ValueRenderLocation) !void {
446 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);
449 }447 }
450448
451 fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) {449 fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) {
452 return f.object.dg.fmtIntLiteral(ty, val);450 return f.object.dg.fmtIntLiteral(ty, val, .Other);
453 }451 }
454452
455 fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 {453 fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 {
...@@ -574,9 +572,9 @@ pub const DeclGen = struct {...@@ -574,9 +572,9 @@ pub const DeclGen = struct {
574 const len_val = Value.initPayload(&len_pl.base);572 const len_val = Value.initPayload(&len_pl.base);
575573
576 if (location == .StaticInitializer) {574 if (location == .StaticInitializer) {
577 return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)});575 return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
578 } else {576 } else {
579 return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)});577 return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
580 }578 }
581 }579 }
582580
...@@ -606,7 +604,7 @@ pub const DeclGen = struct {...@@ -606,7 +604,7 @@ pub const DeclGen = struct {
606 try writer.writeByte(')');604 try writer.writeByte(')');
607 }605 }
608 switch (ptr_val.tag()) {606 switch (ptr_val.tag()) {
609 .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val)}),607 .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}),
610 .decl_ref_mut, .decl_ref, .variable => {608 .decl_ref_mut, .decl_ref, .variable => {
611 const decl_index = switch (ptr_val.tag()) {609 const decl_index = switch (ptr_val.tag()) {
612 .decl_ref => ptr_val.castTag(.decl_ref).?.data,610 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
...@@ -670,7 +668,9 @@ pub const DeclGen = struct {...@@ -670,7 +668,9 @@ pub const DeclGen = struct {
670 container_ptr_ty,668 container_ptr_ty,
671 location,669 location,
672 );670 );
673 try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)});671 try writer.print(" + {})", .{
672 try dg.fmtIntLiteral(Type.usize, byte_offset_val, .Other),
673 });
674 },674 },
675 .end => {675 .end => {
676 try writer.writeAll("((");676 try writer.writeAll("((");
...@@ -680,7 +680,9 @@ pub const DeclGen = struct {...@@ -680,7 +680,9 @@ pub const DeclGen = struct {
680 container_ptr_ty,680 container_ptr_ty,
681 location,681 location,
682 );682 );
683 try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)});683 try writer.print(") + {})", .{
684 try dg.fmtIntLiteral(Type.usize, Value.one, .Other),
685 });
684 },686 },
685 }687 }
686 },688 },
...@@ -746,7 +748,7 @@ pub const DeclGen = struct {...@@ -746,7 +748,7 @@ pub const DeclGen = struct {
746 return writer.writeAll("false");748 return writer.writeAll("false");
747 }749 }
748 },750 },
749 .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteralLoc(ty, val, location)}),751 .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}),
750 .Float => {752 .Float => {
751 const bits = ty.floatBits(target);753 const bits = ty.floatBits(target);
752 var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits };754 var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits };
...@@ -780,11 +782,11 @@ pub const DeclGen = struct {...@@ -780,11 +782,11 @@ pub const DeclGen = struct {
780 var buf: Type.SlicePtrFieldTypeBuffer = undefined;782 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
781 const ptr_ty = ty.slicePtrFieldType(&buf);783 const ptr_ty = ty.slicePtrFieldType(&buf);
782 try dg.renderType(writer, ptr_ty);784 try dg.renderType(writer, ptr_ty);
783 return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)});785 return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
784 } else {786 } else {
785 try writer.writeAll("((");787 try writer.writeAll("((");
786 try dg.renderType(writer, ty);788 try dg.renderType(writer, ty);
787 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)});789 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
788 },790 },
789 .Optional => {791 .Optional => {
790 var opt_buf: Type.Payload.ElemType = undefined;792 var opt_buf: Type.Payload.ElemType = undefined;
...@@ -831,7 +833,7 @@ pub const DeclGen = struct {...@@ -831,7 +833,7 @@ pub const DeclGen = struct {
831833
832 return writer.writeByte('}');834 return writer.writeByte('}');
833 },835 },
834 .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}),836 .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef, .Other)}),
835 },837 },
836 .Union => {838 .Union => {
837 if (!location.isInitializer()) {839 if (!location.isInitializer()) {
...@@ -854,7 +856,7 @@ pub const DeclGen = struct {...@@ -854,7 +856,7 @@ pub const DeclGen = struct {
854 if (!field.ty.hasRuntimeBits()) continue;856 if (!field.ty.hasRuntimeBits()) continue;
855 try dg.renderValue(writer, field.ty, val, initializer_type);857 try dg.renderValue(writer, field.ty, val, initializer_type);
856 break;858 break;
857 } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)});859 } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef, .Other)});
858 if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}');860 if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}');
859 return writer.writeByte('}');861 return writer.writeByte('}');
860 },862 },
...@@ -868,7 +870,7 @@ pub const DeclGen = struct {...@@ -868,7 +870,7 @@ pub const DeclGen = struct {
868 try writer.writeAll("{ .payload = ");870 try writer.writeAll("{ .payload = ");
869 try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type);871 try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type);
870 return writer.print(", .error = {x} }}", .{872 return writer.print(", .error = {x} }}", .{
871 try dg.fmtIntLiteral(ty.errorUnionSet(), val),873 try dg.fmtIntLiteral(ty.errorUnionSet(), val, .Other),
872 });874 });
873 },875 },
874 .Array, .Vector => {876 .Array, .Vector => {
...@@ -927,7 +929,7 @@ pub const DeclGen = struct {...@@ -927,7 +929,7 @@ pub const DeclGen = struct {
927 .decl_ref_mut,929 .decl_ref_mut,
928 .decl_ref,930 .decl_ref,
929 => try dg.renderParentPtr(writer, val, ty, location),931 => try dg.renderParentPtr(writer, val, ty, location),
930 else => try writer.print("{}", .{try dg.fmtIntLiteralLoc(ty, val, location)}),932 else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}),
931 },933 },
932 .Float => {934 .Float => {
933 const bits = ty.floatBits(target);935 const bits = ty.floatBits(target);
...@@ -999,8 +1001,9 @@ pub const DeclGen = struct {...@@ -999,8 +1001,9 @@ pub const DeclGen = struct {
999 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});1001 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});
1000 }1002 }
10011003
1002 try writer.writeAll("zig_make_special_");1004 try writer.writeAll("zig_");
1003 if (location == .StaticInitializer) try writer.writeAll("constant_");1005 try writer.writeAll(if (location == .StaticInitializer) "init" else "make");
1006 try writer.writeAll("_special_");
1004 try dg.renderTypeForBuiltinFnName(writer, ty);1007 try dg.renderTypeForBuiltinFnName(writer, ty);
1005 try writer.writeByte('(');1008 try writer.writeByte('(');
1006 if (std.math.signbit(f128_val)) try writer.writeByte('-');1009 if (std.math.signbit(f128_val)) try writer.writeByte('-');
...@@ -1020,7 +1023,7 @@ pub const DeclGen = struct {...@@ -1020,7 +1023,7 @@ pub const DeclGen = struct {
1020 try writer.writeAll(", ");1023 try writer.writeAll(", ");
1021 empty = false;1024 empty = false;
1022 }1025 }
1023 try writer.print("{x}", .{try dg.fmtIntLiteralLoc(int_ty, int_val, location)});1026 try writer.print("{x}", .{try dg.fmtIntLiteral(int_ty, int_val, location)});
1024 if (!empty) try writer.writeByte(')');1027 if (!empty) try writer.writeByte(')');
1025 return;1028 return;
1026 },1029 },
...@@ -1069,7 +1072,7 @@ pub const DeclGen = struct {...@@ -1069,7 +1072,7 @@ pub const DeclGen = struct {
1069 .int_u64, .one => {1072 .int_u64, .one => {
1070 try writer.writeAll("((");1073 try writer.writeAll("((");
1071 try dg.renderType(writer, ty);1074 try dg.renderType(writer, ty);
1072 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)});1075 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
1073 },1076 },
1074 .field_ptr,1077 .field_ptr,
1075 .elem_ptr,1078 .elem_ptr,
...@@ -1561,6 +1564,10 @@ pub const DeclGen = struct {...@@ -1561,6 +1564,10 @@ pub const DeclGen = struct {
1561 return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind);1564 return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind);
1562 }1565 }
15631566
1567 fn byteSize(dg: *DeclGen, cty: CType) u64 {
1568 return cty.byteSize(dg.ctypes.set, dg.module.getTarget());
1569 }
1570
1564 /// Renders a type as a single identifier, generating intermediate typedefs1571 /// Renders a type as a single identifier, generating intermediate typedefs
1565 /// if necessary.1572 /// if necessary.
1566 ///1573 ///
...@@ -1573,9 +1580,12 @@ pub const DeclGen = struct {...@@ -1573,9 +1580,12 @@ pub const DeclGen = struct {
1573 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" |1580 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" |
1574 ///1581 ///
1575 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {1582 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
1583 try dg.renderCType(w, try dg.typeToIndex(t, .complete));
1584 }
1585
1586 fn renderCType(dg: *DeclGen, w: anytype, idx: CType.Index) error{ OutOfMemory, AnalysisFail }!void {
1576 const store = &dg.ctypes.set;1587 const store = &dg.ctypes.set;
1577 const module = dg.module;1588 const module = dg.module;
1578 const idx = try dg.typeToIndex(t, .complete);
1579 _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{});1589 _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{});
1580 try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{});1590 try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{});
1581 }1591 }
...@@ -1584,6 +1594,7 @@ pub const DeclGen = struct {...@@ -1584,6 +1594,7 @@ pub const DeclGen = struct {
1584 c_value: struct {1594 c_value: struct {
1585 f: *Function,1595 f: *Function,
1586 value: CValue,1596 value: CValue,
1597 v: Vectorizer,
1587 },1598 },
1588 value: struct {1599 value: struct {
1589 value: Value,1600 value: Value,
...@@ -1593,6 +1604,7 @@ pub const DeclGen = struct {...@@ -1593,6 +1604,7 @@ pub const DeclGen = struct {
1593 switch (self.*) {1604 switch (self.*) {
1594 .c_value => |v| {1605 .c_value => |v| {
1595 try v.f.writeCValue(w, v.value, location);1606 try v.f.writeCValue(w, v.value, location);
1607 try v.v.elem(v.f, w);
1596 },1608 },
1597 .value => |v| {1609 .value => |v| {
1598 try dg.renderValue(w, value_ty, v.value, location);1610 try dg.renderValue(w, value_ty, v.value, location);
...@@ -1829,111 +1841,93 @@ pub const DeclGen = struct {...@@ -1829,111 +1841,93 @@ pub const DeclGen = struct {
1829 dg.module.markDeclAlive(decl);1841 dg.module.markDeclAlive(decl);
18301842
1831 if (dg.module.decl_exports.get(decl_index)) |exports| {1843 if (dg.module.decl_exports.get(decl_index)) |exports| {
1832 return writer.writeAll(exports.items[export_index].options.name);1844 try writer.writeAll(exports.items[export_index].options.name);
1833 } else if (decl.isExtern()) {1845 } else if (decl.isExtern()) {
1834 return writer.writeAll(mem.sliceTo(decl.name, 0));1846 try writer.writeAll(mem.sliceTo(decl.name, 0));
1835 } else if (dg.module.test_functions.get(decl_index)) |_| {
1836 const gpa = dg.gpa;
1837 const name = try decl.getFullyQualifiedName(dg.module);
1838 defer gpa.free(name);
1839 return writer.print("{}_{d}", .{ fmtIdent(name), @enumToInt(decl_index) });
1840 } else {1847 } else {
1841 const gpa = dg.gpa;1848 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
1842 const name = try decl.getFullyQualifiedName(dg.module);1849 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
1843 defer gpa.free(name);1850 var name: [100]u8 = undefined;
18441851 var name_stream = std.io.fixedBufferStream(&name);
1845 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), expand1852 decl.renderFullyQualifiedName(dg.module, name_stream.writer()) catch |err| switch (err) {
1846 // to 3x the length of its input1853 error.NoSpaceLeft => {},
1847 if (name.len > 1365) {1854 };
1848 var hash = ident_hasher_init;1855 try writer.print("{}__{d}", .{
1849 hash.update(name);1856 fmtIdent(name_stream.getWritten()),
1850 const ident_hash = hash.finalInt();1857 @enumToInt(decl_index),
1851 try writer.writeAll("zig_D_");1858 });
1852 return std.fmt.formatIntValue(ident_hash, "x", .{}, writer);
1853 } else {
1854 return writer.print("{}", .{fmtIdent(name)});
1855 }
1856 }1859 }
1857 }1860 }
18581861
1859 fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void {1862 fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void {
1860 const target = dg.module.getTarget();1863 try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete));
1861 if (ty.isAbiInt()) {1864 }
1862 const int_info = ty.intInfo(target);1865
1863 const c_bits = toCIntBits(int_info.bits) orelse1866 fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void {
1864 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});1867 switch (cty.tag()) {
1865 try writer.print("{c}{d}", .{ signAbbrev(int_info.signedness), c_bits });1868 else => try writer.print("{c}{d}", .{
1866 } else if (ty.isRuntimeFloat()) {1869 if (cty.isBool())
1867 try ty.print(writer, dg.module);1870 signAbbrev(.unsigned)
1868 } else if (ty.isPtrAtRuntime()) {1871 else if (cty.isInteger())
1869 try writer.print("p{d}", .{ty.bitSize(target)});1872 signAbbrev(cty.signedness() orelse .unsigned)
1870 } else if (ty.zigTypeTag() == .Bool) {1873 else if (cty.isFloat())
1871 try writer.print("u8", .{});1874 @as(u8, 'f')
1872 } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{1875 else if (cty.isPointer())
1873 ty.fmt(dg.module),1876 @as(u8, 'p')
1874 });1877 else
1878 return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{
1879 cty.tag(),
1880 }),
1881 if (cty.isFloat()) cty.floatActiveBits(dg.module.getTarget()) else dg.byteSize(cty) * 8,
1882 }),
1883 .array => try writer.writeAll("big"),
1884 }
1875 }1885 }
18761886
1877 fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void {1887 fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void {
1878 const target = dg.module.getTarget();1888 const cty = try dg.typeToCType(ty, .complete);
1879 switch (info) {1889 const is_big = cty.tag() == .array;
1880 .None => {},
1881 .Range => {
1882 var arena = std.heap.ArenaAllocator.init(dg.gpa);
1883 defer arena.deinit();
1884
1885 const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
1886 var stack align(@alignOf(ExpectedContents)) =
1887 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());
1888
1889 const int_info = ty.intInfo(target);
1890 if (int_info.signedness == .signed) {
1891 const min_val = try ty.minInt(stack.get(), target);
1892 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val)});
1893 }
18941890
1895 const max_val = try ty.maxInt(stack.get(), target);1891 switch (info) {
1896 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)});1892 .none => if (!is_big) return,
1897 },1893 .bits => {},
1898 .Bits => {
1899 var bits_pl = Value.Payload.U64{
1900 .base = .{ .tag = .int_u64 },
1901 .data = ty.bitSize(target),
1902 };
1903 const bits_val = Value.initPayload(&bits_pl.base);
1904 try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val)});
1905 },
1906 }1894 }
1895
1896 const target = dg.module.getTarget();
1897 const int_info = if (ty.isAbiInt()) ty.intInfo(target) else std.builtin.Type.Int{
1898 .signedness = .unsigned,
1899 .bits = @intCast(u16, ty.bitSize(target)),
1900 };
1901
1902 if (is_big) try writer.print(", {}", .{int_info.signedness == .signed});
1903
1904 var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits };
1905 try writer.print(", {}", .{try dg.fmtIntLiteral(
1906 if (is_big) Type.u16 else Type.u8,
1907 Value.initPayload(&bits_pl.base),
1908 .FunctionArgument,
1909 )});
1907 }1910 }
19081911
1909 fn fmtIntLiteral(1912 fn fmtIntLiteral(
1910 dg: *DeclGen,1913 dg: *DeclGen,
1911 ty: Type,1914 ty: Type,
1912 val: Value,1915 val: Value,
1916 loc: ValueRenderLocation,
1913 ) !std.fmt.Formatter(formatIntLiteral) {1917 ) !std.fmt.Formatter(formatIntLiteral) {
1914 const int_info = ty.intInfo(dg.module.getTarget());1918 const kind: CType.Kind = switch (loc) {
1915 const c_bits = toCIntBits(int_info.bits);1919 .FunctionArgument => .parameter,
1916 if (c_bits == null or c_bits.? > 128)1920 .Initializer, .Other => .complete,
1917 return dg.fail("TODO implement integer constants larger than 128 bits", .{});1921 .StaticInitializer => .global,
1922 };
1918 return std.fmt.Formatter(formatIntLiteral){ .data = .{1923 return std.fmt.Formatter(formatIntLiteral){ .data = .{
1919 .ty = ty,1924 .dg = dg,
1925 .int_info = ty.intInfo(dg.module.getTarget()),
1926 .kind = kind,
1927 .cty = try dg.typeToCType(ty, kind),
1920 .val = val,1928 .val = val,
1921 .mod = dg.module,
1922 } };1929 } };
1923 }1930 }
1924
1925 fn fmtIntLiteralLoc(
1926 dg: *DeclGen,
1927 ty: Type,
1928 val: Value,
1929 location: ValueRenderLocation, // TODO: Instead add this as optional arg to fmtIntLiteral
1930 ) !std.fmt.Formatter(formatIntLiteral) {
1931 const int_info = ty.intInfo(dg.module.getTarget());
1932 const c_bits = toCIntBits(int_info.bits);
1933 if (c_bits == null or c_bits.? > 128)
1934 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
1935 return std.fmt.Formatter(formatIntLiteral){ .data = .{ .ty = ty, .val = val, .mod = dg.module, .location = location } };
1936 }
1937};1931};
19381932
1939const CTypeFix = enum { prefix, suffix };1933const CTypeFix = enum { prefix, suffix };
...@@ -2450,7 +2444,7 @@ pub fn genErrDecls(o: *Object) !void {...@@ -2450,7 +2444,7 @@ pub fn genErrDecls(o: *Object) !void {
2450 const len_val = Value.initPayload(&len_pl.base);2444 const len_val = Value.initPayload(&len_pl.base);
24512445
2452 try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{2446 try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{
2453 fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val),2447 fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
2454 });2448 });
2455 }2449 }
2456 try writer.writeAll("};\n");2450 try writer.writeAll("};\n");
...@@ -2501,7 +2495,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {...@@ -2501,7 +2495,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
2501 var int_pl: Value.Payload.U64 = undefined;2495 var int_pl: Value.Payload.U64 = undefined;
2502 const int_val = tag_val.enumToInt(enum_ty, &int_pl);2496 const int_val = tag_val.enumToInt(enum_ty, &int_pl);
25032497
2504 var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len };2498 var name_ty_pl = Type.Payload.Len{
2499 .base = .{ .tag = .array_u8_sentinel_0 },
2500 .data = name.len,
2501 };
2505 const name_ty = Type.initPayload(&name_ty_pl.base);2502 const name_ty = Type.initPayload(&name_ty_pl.base);
25062503
2507 var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name };2504 var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name };
...@@ -2510,14 +2507,16 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {...@@ -2510,14 +2507,16 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
2510 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };2507 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
2511 const len_val = Value.initPayload(&len_pl.base);2508 const len_val = Value.initPayload(&len_pl.base);
25122509
2513 try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)});2510 try w.print(" case {}: {{\n static ", .{
2511 try o.dg.fmtIntLiteral(enum_ty, int_val, .Other),
2512 });
2514 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete);2513 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete);
2515 try w.writeAll(" = ");2514 try w.writeAll(" = ");
2516 try o.dg.renderValue(w, name_ty, name_val, .Initializer);2515 try o.dg.renderValue(w, name_ty, name_val, .Initializer);
2517 try w.writeAll(";\n return (");2516 try w.writeAll(";\n return (");
2518 try o.dg.renderType(w, name_slice_ty);2517 try o.dg.renderType(w, name_slice_ty);
2519 try w.print("){{{}, {}}};\n", .{2518 try w.print("){{{}, {}}};\n", .{
2520 fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val),2519 fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
2521 });2520 });
25222521
2523 try w.writeAll(" }\n");2522 try w.writeAll(" }\n");
...@@ -2535,7 +2534,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {...@@ -2535,7 +2534,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
25352534
2536 const fwd_decl_writer = o.dg.fwd_decl.writer();2535 const fwd_decl_writer = o.dg.fwd_decl.writer();
2537 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});2536 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});
2538 try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name });2537 try o.dg.renderFunctionSignature(
2538 fwd_decl_writer,
2539 fn_decl_index,
2540 .forward,
2541 .{ .string = fn_name },
2542 );
2539 try fwd_decl_writer.writeAll(";\n");2543 try fwd_decl_writer.writeAll(";\n");
25402544
2541 try w.print("static zig_{s} ", .{@tagName(key)});2545 try w.print("static zig_{s} ", .{@tagName(key)});
...@@ -2741,6 +2745,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -2741,6 +2745,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
2741 .const_ty => unreachable, // excluded from function bodies2745 .const_ty => unreachable, // excluded from function bodies
2742 .arg => try airArg(f, inst),2746 .arg => try airArg(f, inst),
27432747
2748 .trap => try airTrap(f.object.writer()),
2744 .breakpoint => try airBreakpoint(f.object.writer()),2749 .breakpoint => try airBreakpoint(f.object.writer()),
2745 .ret_addr => try airRetAddr(f, inst),2750 .ret_addr => try airRetAddr(f, inst),
2746 .frame_addr => try airFrameAddress(f, inst),2751 .frame_addr => try airFrameAddress(f, inst),
...@@ -2752,35 +2757,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -2752,35 +2757,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
27522757
2753 // TODO use a different strategy for add, sub, mul, div2758 // TODO use a different strategy for add, sub, mul, div
2754 // that communicates to the optimizer that wrapping is UB.2759 // that communicates to the optimizer that wrapping is UB.
2755 .add => try airBinOp(f, inst, "+", "add", .None),2760 .add => try airBinOp(f, inst, "+", "add", .none),
2756 .sub => try airBinOp(f, inst, "-", "sub", .None),2761 .sub => try airBinOp(f, inst, "-", "sub", .none),
2757 .mul => try airBinOp(f, inst, "*", "mul", .None),2762 .mul => try airBinOp(f, inst, "*", "mul", .none),
27582763
2759 .neg => try airFloatNeg(f, inst),2764 .neg => try airFloatNeg(f, inst),
2760 .div_float => try airBinBuiltinCall(f, inst, "div", .None),2765 .div_float => try airBinBuiltinCall(f, inst, "div", .none),
27612766
2762 .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None),2767 .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none),
2763 .rem => blk: {2768 .rem => blk: {
2764 const bin_op = f.air.instructions.items(.data)[inst].bin_op;2769 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
2765 const lhs_ty = f.air.typeOf(bin_op.lhs);2770 const lhs_scalar_ty = f.air.typeOf(bin_op.lhs).scalarType();
2766 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),2771 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
2767 // so we only check one.2772 // so we only check one.
2768 break :blk if (lhs_ty.isInt())2773 break :blk if (lhs_scalar_ty.isInt())
2769 try airBinOp(f, inst, "%", "rem", .None)2774 try airBinOp(f, inst, "%", "rem", .none)
2770 else2775 else
2771 try airBinFloatOp(f, inst, "fmod");2776 try airBinFloatOp(f, inst, "fmod");
2772 },2777 },
2773 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None),2778 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
2774 .mod => try airBinBuiltinCall(f, inst, "mod", .None),2779 .mod => try airBinBuiltinCall(f, inst, "mod", .none),
27752780
2776 .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits),2781 .addwrap => try airBinBuiltinCall(f, inst, "addw", .bits),
2777 .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits),2782 .subwrap => try airBinBuiltinCall(f, inst, "subw", .bits),
2778 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits),2783 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .bits),
27792784
2780 .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits),2785 .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits),
2781 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits),2786 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits),
2782 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits),2787 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits),
2783 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits),2788 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits),
27842789
2785 .sqrt => try airUnFloatOp(f, inst, "sqrt"),2790 .sqrt => try airUnFloatOp(f, inst, "sqrt"),
2786 .sin => try airUnFloatOp(f, inst, "sin"),2791 .sin => try airUnFloatOp(f, inst, "sin"),
...@@ -2799,34 +2804,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -2799,34 +2804,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
27992804
2800 .mul_add => try airMulAdd(f, inst),2805 .mul_add => try airMulAdd(f, inst),
28012806
2802 .add_with_overflow => try airOverflow(f, inst, "add", .Bits),2807 .add_with_overflow => try airOverflow(f, inst, "add", .bits),
2803 .sub_with_overflow => try airOverflow(f, inst, "sub", .Bits),2808 .sub_with_overflow => try airOverflow(f, inst, "sub", .bits),
2804 .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits),2809 .mul_with_overflow => try airOverflow(f, inst, "mul", .bits),
2805 .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits),2810 .shl_with_overflow => try airOverflow(f, inst, "shl", .bits),
28062811
2807 .min => try airMinMax(f, inst, '<', "fmin"),2812 .min => try airMinMax(f, inst, '<', "fmin"),
2808 .max => try airMinMax(f, inst, '>', "fmax"),2813 .max => try airMinMax(f, inst, '>', "fmax"),
28092814
2810 .slice => try airSlice(f, inst),2815 .slice => try airSlice(f, inst),
28112816
2812 .cmp_gt => try airCmpOp(f, inst, ">", "gt"),2817 .cmp_gt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gt),
2813 .cmp_gte => try airCmpOp(f, inst, ">=", "ge"),2818 .cmp_gte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gte),
2814 .cmp_lt => try airCmpOp(f, inst, "<", "lt"),2819 .cmp_lt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lt),
2815 .cmp_lte => try airCmpOp(f, inst, "<=", "le"),2820 .cmp_lte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lte),
28162821
2817 .cmp_eq => try airEquality(f, inst, "((", "==", "eq"),2822 .cmp_eq => try airEquality(f, inst, .eq),
2818 .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"),2823 .cmp_neq => try airEquality(f, inst, .neq),
28192824
2820 .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}),2825 .cmp_vector => blk: {
2826 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2827 const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data;
2828 break :blk try airCmpOp(f, inst, extra, extra.compareOperator());
2829 },
2821 .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst),2830 .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst),
28222831
2823 // bool_and and bool_or are non-short-circuit operations2832 // bool_and and bool_or are non-short-circuit operations
2824 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None),2833 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .none),
2825 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None),2834 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .none),
2826 .xor => try airBinOp(f, inst, "^", "xor", .None),2835 .xor => try airBinOp(f, inst, "^", "xor", .none),
2827 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None),2836 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .none),
2828 .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits),2837 .shl, => try airBinBuiltinCall(f, inst, "shlw", .bits),
2829 .shl_exact => try airBinOp(f, inst, "<<", "shl", .None),2838 .shl_exact => try airBinOp(f, inst, "<<", "shl", .none),
2830 .not => try airNot (f, inst),2839 .not => try airNot (f, inst),
28312840
2832 .optional_payload => try airOptionalPayload(f, inst),2841 .optional_payload => try airOptionalPayload(f, inst),
...@@ -2871,11 +2880,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -2871,11 +2880,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
2871 .memcpy => try airMemcpy(f, inst),2880 .memcpy => try airMemcpy(f, inst),
2872 .set_union_tag => try airSetUnionTag(f, inst),2881 .set_union_tag => try airSetUnionTag(f, inst),
2873 .get_union_tag => try airGetUnionTag(f, inst),2882 .get_union_tag => try airGetUnionTag(f, inst),
2874 .clz => try airUnBuiltinCall(f, inst, "clz", .Bits),2883 .clz => try airUnBuiltinCall(f, inst, "clz", .bits),
2875 .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits),2884 .ctz => try airUnBuiltinCall(f, inst, "ctz", .bits),
2876 .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits),2885 .popcount => try airUnBuiltinCall(f, inst, "popcount", .bits),
2877 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits),2886 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .bits),
2878 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits),2887 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .bits),
2879 .tag_name => try airTagName(f, inst),2888 .tag_name => try airTagName(f, inst),
2880 .error_name => try airErrorName(f, inst),2889 .error_name => try airErrorName(f, inst),
2881 .splat => try airSplat(f, inst),2890 .splat => try airSplat(f, inst),
...@@ -3266,7 +3275,10 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3266,7 +3275,10 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
32663275
3267fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {3276fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3268 const ty_op = f.air.instructions.items(.data)[inst].ty_op;3277 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3269 const ptr_info = f.air.typeOf(ty_op.operand).ptrInfo().data;3278
3279 const ptr_ty = f.air.typeOf(ty_op.operand);
3280 const ptr_scalar_ty = ptr_ty.scalarType();
3281 const ptr_info = ptr_scalar_ty.ptrInfo().data;
3270 const src_ty = ptr_info.pointee_type;3282 const src_ty = ptr_info.pointee_type;
32713283
3272 if (!src_ty.hasRuntimeBitsIgnoreComptime() or3284 if (!src_ty.hasRuntimeBitsIgnoreComptime() or
...@@ -3284,20 +3296,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3284,20 +3296,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3284 const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target);3296 const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target);
3285 const is_array = lowersToArray(src_ty, target);3297 const is_array = lowersToArray(src_ty, target);
3286 const need_memcpy = !is_aligned or is_array;3298 const need_memcpy = !is_aligned or is_array;
3287 const writer = f.object.writer();
32883299
3300 const writer = f.object.writer();
3289 const local = try f.allocLocal(inst, src_ty);3301 const local = try f.allocLocal(inst, src_ty);
3302 const v = try Vectorizer.start(f, inst, writer, ptr_ty);
32903303
3291 if (need_memcpy) {3304 if (need_memcpy) {
3292 try writer.writeAll("memcpy(");3305 try writer.writeAll("memcpy(");
3293 if (!is_array) try writer.writeByte('&');3306 if (!is_array) try writer.writeByte('&');
3294 try f.writeCValue(writer, local, .FunctionArgument);3307 try f.writeCValue(writer, local, .Other);
3308 try v.elem(f, writer);
3295 try writer.writeAll(", (const char *)");3309 try writer.writeAll(", (const char *)");
3296 try f.writeCValue(writer, operand, .Other);3310 try f.writeCValue(writer, operand, .Other);
3311 try v.elem(f, writer);
3297 try writer.writeAll(", sizeof(");3312 try writer.writeAll(", sizeof(");
3298 try f.renderType(writer, src_ty);3313 try f.renderType(writer, src_ty);
3299 try writer.writeAll("))");3314 try writer.writeAll("))");
3300 } else if (ptr_info.host_size != 0) {3315 } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) {
3301 var host_pl = Type.Payload.Bits{3316 var host_pl = Type.Payload.Bits{
3302 .base = .{ .tag = .int_unsigned },3317 .base = .{ .tag = .int_unsigned },
3303 .data = ptr_info.host_size * 8,3318 .data = ptr_info.host_size * 8,
...@@ -3323,6 +3338,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3323,6 +3338,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3323 const field_ty = Type.initPayload(&field_pl.base);3338 const field_ty = Type.initPayload(&field_pl.base);
33243339
3325 try f.writeCValue(writer, local, .Other);3340 try f.writeCValue(writer, local, .Other);
3341 try v.elem(f, writer);
3326 try writer.writeAll(" = (");3342 try writer.writeAll(" = (");
3327 try f.renderType(writer, src_ty);3343 try f.renderType(writer, src_ty);
3328 try writer.writeAll(")zig_wrap_");3344 try writer.writeAll(")zig_wrap_");
...@@ -3341,16 +3357,21 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3341,16 +3357,21 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3341 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);3357 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
3342 try writer.writeByte('(');3358 try writer.writeByte('(');
3343 try f.writeCValueDeref(writer, operand);3359 try f.writeCValueDeref(writer, operand);
3360 try v.elem(f, writer);
3344 try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});3361 try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});
3345 if (cant_cast) try writer.writeByte(')');3362 if (cant_cast) try writer.writeByte(')');
3346 try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits);3363 try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits);
3347 try writer.writeByte(')');3364 try writer.writeByte(')');
3348 } else {3365 } else {
3349 try f.writeCValue(writer, local, .Other);3366 try f.writeCValue(writer, local, .Other);
3367 try v.elem(f, writer);
3350 try writer.writeAll(" = ");3368 try writer.writeAll(" = ");
3351 try f.writeCValueDeref(writer, operand);3369 try f.writeCValueDeref(writer, operand);
3370 try v.elem(f, writer);
3352 }3371 }
3353 try writer.writeAll(";\n");3372 try writer.writeAll(";\n");
3373 try v.end(f, inst, writer);
3374
3354 return local;3375 return local;
3355}3376}
33563377
...@@ -3416,15 +3437,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3416,15 +3437,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
34163437
3417 const operand = try f.resolveInst(ty_op.operand);3438 const operand = try f.resolveInst(ty_op.operand);
3418 try reap(f, inst, &.{ty_op.operand});3439 try reap(f, inst, &.{ty_op.operand});
3419 const writer = f.object.writer();3440
3420 const inst_ty = f.air.typeOfIndex(inst);3441 const inst_ty = f.air.typeOfIndex(inst);
3421 const local = try f.allocLocal(inst, inst_ty);3442 const inst_scalar_ty = inst_ty.scalarType();
3422 const operand_ty = f.air.typeOf(ty_op.operand);3443 const operand_ty = f.air.typeOf(ty_op.operand);
3444 const scalar_ty = operand_ty.scalarType();
34233445
3446 const writer = f.object.writer();
3447 const local = try f.allocLocal(inst, inst_ty);
3448 const v = try Vectorizer.start(f, inst, writer, operand_ty);
3424 try f.writeCValue(writer, local, .Other);3449 try f.writeCValue(writer, local, .Other);
3450 try v.elem(f, writer);
3425 try writer.writeAll(" = ");3451 try writer.writeAll(" = ");
3426 try f.renderIntCast(writer, inst_ty, operand, operand_ty, .Other);3452 try f.renderIntCast(writer, inst_scalar_ty, operand, v, scalar_ty, .Other);
3427 try writer.writeAll(";\n");3453 try writer.writeAll(";\n");
3454 try v.end(f, inst, writer);
3455
3428 return local;3456 return local;
3429}3457}
34303458
...@@ -3438,34 +3466,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3438,34 +3466,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3438 const operand = try f.resolveInst(ty_op.operand);3466 const operand = try f.resolveInst(ty_op.operand);
3439 try reap(f, inst, &.{ty_op.operand});3467 try reap(f, inst, &.{ty_op.operand});
3440 const inst_ty = f.air.typeOfIndex(inst);3468 const inst_ty = f.air.typeOfIndex(inst);
3441 const writer = f.object.writer();3469 const inst_scalar_ty = inst_ty.scalarType();
3442 const local = try f.allocLocal(inst, inst_ty);
3443 const target = f.object.dg.module.getTarget();3470 const target = f.object.dg.module.getTarget();
3444 const dest_int_info = inst_ty.intInfo(target);3471 const dest_int_info = inst_scalar_ty.intInfo(target);
3445 const dest_bits = dest_int_info.bits;3472 const dest_bits = dest_int_info.bits;
3446 const dest_c_bits = toCIntBits(dest_int_info.bits) orelse3473 const dest_c_bits = toCIntBits(dest_int_info.bits) orelse
3447 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});3474 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
3448 const operand_ty = f.air.typeOf(ty_op.operand);3475 const operand_ty = f.air.typeOf(ty_op.operand);
3449 const operand_int_info = operand_ty.intInfo(target);3476 const scalar_ty = operand_ty.scalarType();
3477 const scalar_int_info = scalar_ty.intInfo(target);
3478
3479 const writer = f.object.writer();
3480 const local = try f.allocLocal(inst, inst_ty);
3481 const v = try Vectorizer.start(f, inst, writer, operand_ty);
34503482
3451 try f.writeCValue(writer, local, .Other);3483 try f.writeCValue(writer, local, .Other);
3484 try v.elem(f, writer);
3452 try writer.writeAll(" = ");3485 try writer.writeAll(" = ");
34533486
3454 if (dest_c_bits < 64) {3487 if (dest_c_bits < 64) {
3455 try writer.writeByte('(');3488 try writer.writeByte('(');
3456 try f.renderType(writer, inst_ty);3489 try f.renderType(writer, inst_scalar_ty);
3457 try writer.writeByte(')');3490 try writer.writeByte(')');
3458 }3491 }
34593492
3460 const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64;3493 const needs_lo = scalar_int_info.bits > 64 and dest_bits <= 64;
3461 if (needs_lo) {3494 if (needs_lo) {
3462 try writer.writeAll("zig_lo_");3495 try writer.writeAll("zig_lo_");
3463 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);3496 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
3464 try writer.writeByte('(');3497 try writer.writeByte('(');
3465 }3498 }
34663499
3467 if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) {3500 if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) {
3468 try f.writeCValue(writer, operand, .Other);3501 try f.writeCValue(writer, operand, .Other);
3502 try v.elem(f, writer);
3469 } else switch (dest_int_info.signedness) {3503 } else switch (dest_int_info.signedness) {
3470 .unsigned => {3504 .unsigned => {
3471 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa);3505 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa);
...@@ -3475,15 +3509,16 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3475,15 +3509,16 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3475 var stack align(@alignOf(ExpectedContents)) =3509 var stack align(@alignOf(ExpectedContents)) =
3476 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());3510 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());
34773511
3478 const mask_val = try inst_ty.maxInt(stack.get(), target);3512 const mask_val = try inst_scalar_ty.maxInt(stack.get(), target);
3479 try writer.writeAll("zig_and_");3513 try writer.writeAll("zig_and_");
3480 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);3514 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
3481 try writer.writeByte('(');3515 try writer.writeByte('(');
3482 try f.writeCValue(writer, operand, .FunctionArgument);3516 try f.writeCValue(writer, operand, .FunctionArgument);
3483 try writer.print(", {x})", .{try f.fmtIntLiteral(operand_ty, mask_val)});3517 try v.elem(f, writer);
3518 try writer.print(", {x})", .{try f.fmtIntLiteral(scalar_ty, mask_val)});
3484 },3519 },
3485 .signed => {3520 .signed => {
3486 const c_bits = toCIntBits(operand_int_info.bits) orelse3521 const c_bits = toCIntBits(scalar_int_info.bits) orelse
3487 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});3522 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
3488 var shift_pl = Value.Payload.U64{3523 var shift_pl = Value.Payload.U64{
3489 .base = .{ .tag = .int_u64 },3524 .base = .{ .tag = .int_u64 },
...@@ -3492,7 +3527,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3492,7 +3527,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3492 const shift_val = Value.initPayload(&shift_pl.base);3527 const shift_val = Value.initPayload(&shift_pl.base);
34933528
3494 try writer.writeAll("zig_shr_");3529 try writer.writeAll("zig_shr_");
3495 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);3530 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
3496 if (c_bits == 128) {3531 if (c_bits == 128) {
3497 try writer.print("(zig_bitcast_i{d}(", .{c_bits});3532 try writer.print("(zig_bitcast_i{d}(", .{c_bits});
3498 } else {3533 } else {
...@@ -3505,6 +3540,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3505,6 +3540,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3505 try writer.print("(uint{d}_t)", .{c_bits});3540 try writer.print("(uint{d}_t)", .{c_bits});
3506 }3541 }
3507 try f.writeCValue(writer, operand, .FunctionArgument);3542 try f.writeCValue(writer, operand, .FunctionArgument);
3543 try v.elem(f, writer);
3508 if (c_bits == 128) try writer.writeByte(')');3544 if (c_bits == 128) try writer.writeByte(')');
3509 try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)});3545 try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)});
3510 if (c_bits == 128) try writer.writeByte(')');3546 if (c_bits == 128) try writer.writeByte(')');
...@@ -3514,6 +3550,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3514,6 +3550,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
35143550
3515 if (needs_lo) try writer.writeByte(')');3551 if (needs_lo) try writer.writeByte(')');
3516 try writer.writeAll(";\n");3552 try writer.writeAll(";\n");
3553 try v.end(f, inst, writer);
3554
3517 return local;3555 return local;
3518}3556}
35193557
...@@ -3550,7 +3588,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {...@@ -3550,7 +3588,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {
3550fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {3588fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3551 // *a = b;3589 // *a = b;
3552 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3590 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3553 const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data;3591
3592 const ptr_ty = f.air.typeOf(bin_op.lhs);
3593 const ptr_scalar_ty = ptr_ty.scalarType();
3594 const ptr_info = ptr_scalar_ty.ptrInfo().data;
3554 if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) {3595 if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) {
3555 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3596 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3556 return .none;3597 return .none;
...@@ -3573,11 +3614,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3573,11 +3614,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3573 ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target);3614 ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target);
3574 const is_array = lowersToArray(ptr_info.pointee_type, target);3615 const is_array = lowersToArray(ptr_info.pointee_type, target);
3575 const need_memcpy = !is_aligned or is_array;3616 const need_memcpy = !is_aligned or is_array;
3576 const writer = f.object.writer();
35773617
3578 const src_val = try f.resolveInst(bin_op.rhs);3618 const src_val = try f.resolveInst(bin_op.rhs);
3579 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3619 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
35803620
3621 const writer = f.object.writer();
3622 const v = try Vectorizer.start(f, inst, writer, ptr_ty);
3623
3581 if (need_memcpy) {3624 if (need_memcpy) {
3582 // For this memcpy to safely work we need the rhs to have the same3625 // For this memcpy to safely work we need the rhs to have the same
3583 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).3626 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
...@@ -3598,16 +3641,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3598,16 +3641,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
35983641
3599 try writer.writeAll("memcpy((char *)");3642 try writer.writeAll("memcpy((char *)");
3600 try f.writeCValue(writer, ptr_val, .FunctionArgument);3643 try f.writeCValue(writer, ptr_val, .FunctionArgument);
3644 try v.elem(f, writer);
3601 try writer.writeAll(", ");3645 try writer.writeAll(", ");
3602 if (!is_array) try writer.writeByte('&');3646 if (!is_array) try writer.writeByte('&');
3603 try f.writeCValue(writer, array_src, .FunctionArgument);3647 try f.writeCValue(writer, array_src, .FunctionArgument);
3648 try v.elem(f, writer);
3604 try writer.writeAll(", sizeof(");3649 try writer.writeAll(", sizeof(");
3605 try f.renderType(writer, src_ty);3650 try f.renderType(writer, src_ty);
3606 try writer.writeAll("))");3651 try writer.writeAll("))");
3607 if (src_val == .constant) {3652 if (src_val == .constant) {
3608 try freeLocal(f, inst, array_src.new_local, 0);3653 try freeLocal(f, inst, array_src.new_local, 0);
3609 }3654 }
3610 } else if (ptr_info.host_size != 0) {3655 } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) {
3611 const host_bits = ptr_info.host_size * 8;3656 const host_bits = ptr_info.host_size * 8;
3612 var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits };3657 var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits };
3613 const host_ty = Type.initPayload(&host_pl.base);3658 const host_ty = Type.initPayload(&host_pl.base);
...@@ -3644,12 +3689,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3644,12 +3689,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3644 const mask_val = Value.initPayload(&mask_pl.base);3689 const mask_val = Value.initPayload(&mask_pl.base);
36453690
3646 try f.writeCValueDeref(writer, ptr_val);3691 try f.writeCValueDeref(writer, ptr_val);
3692 try v.elem(f, writer);
3647 try writer.writeAll(" = zig_or_");3693 try writer.writeAll(" = zig_or_");
3648 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);3694 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
3649 try writer.writeAll("(zig_and_");3695 try writer.writeAll("(zig_and_");
3650 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);3696 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
3651 try writer.writeByte('(');3697 try writer.writeByte('(');
3652 try f.writeCValueDeref(writer, ptr_val);3698 try f.writeCValueDeref(writer, ptr_val);
3699 try v.elem(f, writer);
3653 try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)});3700 try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)});
3654 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);3701 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
3655 try writer.writeByte('(');3702 try writer.writeByte('(');
...@@ -3671,14 +3718,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3671,14 +3718,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3671 try writer.writeByte(')');3718 try writer.writeByte(')');
3672 }3719 }
3673 try f.writeCValue(writer, src_val, .Other);3720 try f.writeCValue(writer, src_val, .Other);
3721 try v.elem(f, writer);
3674 if (cant_cast) try writer.writeByte(')');3722 if (cant_cast) try writer.writeByte(')');
3675 try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});3723 try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});
3676 } else {3724 } else {
3677 try f.writeCValueDeref(writer, ptr_val);3725 try f.writeCValueDeref(writer, ptr_val);
3726 try v.elem(f, writer);
3678 try writer.writeAll(" = ");3727 try writer.writeAll(" = ");
3679 try f.writeCValue(writer, src_val, .Other);3728 try f.writeCValue(writer, src_val, .Other);
3729 try v.elem(f, writer);
3680 }3730 }
3681 try writer.writeAll(";\n");3731 try writer.writeAll(";\n");
3732 try v.end(f, inst, writer);
3733
3682 return .none;3734 return .none;
3683}3735}
36843736
...@@ -3696,51 +3748,39 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:...@@ -3696,51 +3748,39 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
3696 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3748 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36973749
3698 const inst_ty = f.air.typeOfIndex(inst);3750 const inst_ty = f.air.typeOfIndex(inst);
3699 const vector_ty = f.air.typeOf(bin_op.lhs);3751 const operand_ty = f.air.typeOf(bin_op.lhs);
3700 const scalar_ty = vector_ty.scalarType();3752 const scalar_ty = operand_ty.scalarType();
3701 const w = f.object.writer();
37023753
3754 const w = f.object.writer();
3703 const local = try f.allocLocal(inst, inst_ty);3755 const local = try f.allocLocal(inst, inst_ty);
37043756 const v = try Vectorizer.start(f, inst, w, operand_ty);
3705 switch (vector_ty.zigTypeTag()) {3757 try f.writeCValueMember(w, local, .{ .field = 1 });
3706 .Vector => {3758 try v.elem(f, w);
3707 try w.writeAll("zig_v");3759 try w.writeAll(" = zig_");
3708 try w.writeAll(operation);3760 try w.writeAll(operation);
3709 try w.writeAll("o_");3761 try w.writeAll("o_");
3710 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);3762 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3711 try w.writeAll("(");3763 try w.writeAll("(&");
3712 try f.writeCValueMember(w, local, .{ .field = 1 });3764 try f.writeCValueMember(w, local, .{ .field = 0 });
3713 try w.writeAll(", ");3765 try v.elem(f, w);
3714 try f.writeCValueMember(w, local, .{ .field = 0 });3766 try w.writeAll(", ");
3715 try w.print(", {d}, ", .{vector_ty.vectorLen()});
3716 },
3717 else => {
3718 try f.writeCValueMember(w, local, .{ .field = 1 });
3719 try w.writeAll(" = zig_");
3720 try w.writeAll(operation);
3721 try w.writeAll("o_");
3722 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3723 try w.writeAll("(&");
3724 try f.writeCValueMember(w, local, .{ .field = 0 });
3725 try w.writeAll(", ");
3726 },
3727 }
3728
3729 try f.writeCValue(w, lhs, .FunctionArgument);3767 try f.writeCValue(w, lhs, .FunctionArgument);
3768 try v.elem(f, w);
3730 try w.writeAll(", ");3769 try w.writeAll(", ");
3731 try f.writeCValue(w, rhs, .FunctionArgument);3770 try f.writeCValue(w, rhs, .FunctionArgument);
3771 try v.elem(f, w);
3732 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);3772 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
3733 try w.writeAll(");\n");3773 try w.writeAll(");\n");
3774 try v.end(f, inst, w);
37343775
3735 return local;3776 return local;
3736}3777}
37373778
3738fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {3779fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
3739 const inst_ty = f.air.typeOfIndex(inst);
3740 if (inst_ty.tag() != .bool)
3741 return try airUnBuiltinCall(f, inst, "not", .Bits);
3742
3743 const ty_op = f.air.instructions.items(.data)[inst].ty_op;3780 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3781 const operand_ty = f.air.typeOf(ty_op.operand);
3782 const scalar_ty = operand_ty.scalarType();
3783 if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits);
37443784
3745 if (f.liveness.isUnused(inst)) {3785 if (f.liveness.isUnused(inst)) {
3746 try reap(f, inst, &.{ty_op.operand});3786 try reap(f, inst, &.{ty_op.operand});
...@@ -3750,14 +3790,20 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3750,14 +3790,20 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
3750 const op = try f.resolveInst(ty_op.operand);3790 const op = try f.resolveInst(ty_op.operand);
3751 try reap(f, inst, &.{ty_op.operand});3791 try reap(f, inst, &.{ty_op.operand});
37523792
3793 const inst_ty = f.air.typeOfIndex(inst);
3794
3753 const writer = f.object.writer();3795 const writer = f.object.writer();
3754 const local = try f.allocLocal(inst, inst_ty);3796 const local = try f.allocLocal(inst, inst_ty);
37553797 const v = try Vectorizer.start(f, inst, writer, operand_ty);
3756 try f.writeCValue(writer, local, .Other);3798 try f.writeCValue(writer, local, .Other);
3799 try v.elem(f, writer);
3757 try writer.writeAll(" = ");3800 try writer.writeAll(" = ");
3758 try writer.writeByte('!');3801 try writer.writeByte('!');
3759 try f.writeCValue(writer, op, .Other);3802 try f.writeCValue(writer, op, .Other);
3803 try v.elem(f, writer);
3760 try writer.writeAll(";\n");3804 try writer.writeAll(";\n");
3805 try v.end(f, inst, writer);
3806
3761 return local;3807 return local;
3762}3808}
37633809
...@@ -3770,63 +3816,89 @@ fn airBinOp(...@@ -3770,63 +3816,89 @@ fn airBinOp(
3770) !CValue {3816) !CValue {
3771 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3817 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3772 const operand_ty = f.air.typeOf(bin_op.lhs);3818 const operand_ty = f.air.typeOf(bin_op.lhs);
3819 const scalar_ty = operand_ty.scalarType();
3773 const target = f.object.dg.module.getTarget();3820 const target = f.object.dg.module.getTarget();
3774 if ((operand_ty.isInt() and operand_ty.bitSize(target) > 64) or operand_ty.isRuntimeFloat())3821 if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat())
3775 return try airBinBuiltinCall(f, inst, operation, info);3822 return try airBinBuiltinCall(f, inst, operation, info);
37763823
3824 if (f.liveness.isUnused(inst)) {
3825 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3826 return .none;
3827 }
3828
3777 const lhs = try f.resolveInst(bin_op.lhs);3829 const lhs = try f.resolveInst(bin_op.lhs);
3778 const rhs = try f.resolveInst(bin_op.rhs);3830 const rhs = try f.resolveInst(bin_op.rhs);
3779
3780 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3831 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37813832
3782 if (f.liveness.isUnused(inst)) return .none;
3783
3784 const inst_ty = f.air.typeOfIndex(inst);3833 const inst_ty = f.air.typeOfIndex(inst);
37853834
3786 const writer = f.object.writer();3835 const writer = f.object.writer();
3787 const local = try f.allocLocal(inst, inst_ty);3836 const local = try f.allocLocal(inst, inst_ty);
3837 const v = try Vectorizer.start(f, inst, writer, operand_ty);
3788 try f.writeCValue(writer, local, .Other);3838 try f.writeCValue(writer, local, .Other);
3839 try v.elem(f, writer);
3789 try writer.writeAll(" = ");3840 try writer.writeAll(" = ");
3790 try f.writeCValue(writer, lhs, .Other);3841 try f.writeCValue(writer, lhs, .Other);
3842 try v.elem(f, writer);
3791 try writer.writeByte(' ');3843 try writer.writeByte(' ');
3792 try writer.writeAll(operator);3844 try writer.writeAll(operator);
3793 try writer.writeByte(' ');3845 try writer.writeByte(' ');
3794 try f.writeCValue(writer, rhs, .Other);3846 try f.writeCValue(writer, rhs, .Other);
3847 try v.elem(f, writer);
3795 try writer.writeAll(";\n");3848 try writer.writeAll(";\n");
3849 try v.end(f, inst, writer);
37963850
3797 return local;3851 return local;
3798}3852}
37993853
3800fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8) !CValue {3854fn airCmpOp(
3801 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3855 f: *Function,
38023856 inst: Air.Inst.Index,
3857 data: anytype,
3858 operator: std.math.CompareOperator,
3859) !CValue {
3803 if (f.liveness.isUnused(inst)) {3860 if (f.liveness.isUnused(inst)) {
3804 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3861 try reap(f, inst, &.{ data.lhs, data.rhs });
3805 return .none;3862 return .none;
3806 }3863 }
38073864
3808 const operand_ty = f.air.typeOf(bin_op.lhs);3865 const operand_ty = f.air.typeOf(data.lhs);
3866 const scalar_ty = operand_ty.scalarType();
3867
3809 const target = f.object.dg.module.getTarget();3868 const target = f.object.dg.module.getTarget();
3810 if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)3869 const scalar_bits = scalar_ty.bitSize(target);
3811 return try cmpBuiltinCall(f, inst, operator, "cmp");3870 if (scalar_ty.isInt() and scalar_bits > 64)
3812 if (operand_ty.isRuntimeFloat())3871 return airCmpBuiltinCall(
3813 return try cmpBuiltinCall(f, inst, operator, operation);3872 f,
3873 inst,
3874 data,
3875 operator,
3876 .cmp,
3877 if (scalar_bits > 128) .bits else .none,
3878 );
3879 if (scalar_ty.isRuntimeFloat())
3880 return airCmpBuiltinCall(f, inst, data, operator, .operator, .none);
38143881
3815 const inst_ty = f.air.typeOfIndex(inst);3882 const inst_ty = f.air.typeOfIndex(inst);
3816 const lhs = try f.resolveInst(bin_op.lhs);3883 const lhs = try f.resolveInst(data.lhs);
3817 const rhs = try f.resolveInst(bin_op.rhs);3884 const rhs = try f.resolveInst(data.rhs);
3818 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3885 try reap(f, inst, &.{ data.lhs, data.rhs });
38193886
3820 const writer = f.object.writer();3887 const writer = f.object.writer();
3821 const local = try f.allocLocal(inst, inst_ty);3888 const local = try f.allocLocal(inst, inst_ty);
3889 const v = try Vectorizer.start(f, inst, writer, operand_ty);
3822 try f.writeCValue(writer, local, .Other);3890 try f.writeCValue(writer, local, .Other);
3891 try v.elem(f, writer);
3823 try writer.writeAll(" = ");3892 try writer.writeAll(" = ");
3824 try f.writeCValue(writer, lhs, .Other);3893 try f.writeCValue(writer, lhs, .Other);
3894 try v.elem(f, writer);
3825 try writer.writeByte(' ');3895 try writer.writeByte(' ');
3826 try writer.writeAll(operator);3896 try writer.writeAll(compareOperatorC(operator));
3827 try writer.writeByte(' ');3897 try writer.writeByte(' ');
3828 try f.writeCValue(writer, rhs, .Other);3898 try f.writeCValue(writer, rhs, .Other);
3899 try v.elem(f, writer);
3829 try writer.writeAll(";\n");3900 try writer.writeAll(";\n");
3901 try v.end(f, inst, writer);
38303902
3831 return local;3903 return local;
3832}3904}
...@@ -3834,9 +3906,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation:...@@ -3834,9 +3906,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation:
3834fn airEquality(3906fn airEquality(
3835 f: *Function,3907 f: *Function,
3836 inst: Air.Inst.Index,3908 inst: Air.Inst.Index,
3837 negate_prefix: []const u8,3909 operator: std.math.CompareOperator,
3838 operator: []const u8,
3839 operation: []const u8,
3840) !CValue {3910) !CValue {
3841 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3911 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
38423912
...@@ -3847,10 +3917,18 @@ fn airEquality(...@@ -3847,10 +3917,18 @@ fn airEquality(
38473917
3848 const operand_ty = f.air.typeOf(bin_op.lhs);3918 const operand_ty = f.air.typeOf(bin_op.lhs);
3849 const target = f.object.dg.module.getTarget();3919 const target = f.object.dg.module.getTarget();
3850 if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)3920 const operand_bits = operand_ty.bitSize(target);
3851 return try cmpBuiltinCall(f, inst, operator, "cmp");3921 if (operand_ty.isInt() and operand_bits > 64)
3922 return airCmpBuiltinCall(
3923 f,
3924 inst,
3925 bin_op,
3926 operator,
3927 .cmp,
3928 if (operand_bits > 128) .bits else .none,
3929 );
3852 if (operand_ty.isRuntimeFloat())3930 if (operand_ty.isRuntimeFloat())
3853 return try cmpBuiltinCall(f, inst, operator, operation);3931 return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none);
38543932
3855 const lhs = try f.resolveInst(bin_op.lhs);3933 const lhs = try f.resolveInst(bin_op.lhs);
3856 const rhs = try f.resolveInst(bin_op.rhs);3934 const rhs = try f.resolveInst(bin_op.rhs);
...@@ -3866,7 +3944,12 @@ fn airEquality(...@@ -3866,7 +3944,12 @@ fn airEquality(
3866 // (A && B) || (C && (A == B))3944 // (A && B) || (C && (A == B))
3867 // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload3945 // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload
38683946
3869 try writer.writeAll(negate_prefix);3947 switch (operator) {
3948 .eq => {},
3949 .neq => try writer.writeByte('!'),
3950 else => unreachable,
3951 }
3952 try writer.writeAll("((");
3870 try f.writeCValue(writer, lhs, .Other);3953 try f.writeCValue(writer, lhs, .Other);
3871 try writer.writeAll(".is_null && ");3954 try writer.writeAll(".is_null && ");
3872 try f.writeCValue(writer, rhs, .Other);3955 try f.writeCValue(writer, rhs, .Other);
...@@ -3885,7 +3968,7 @@ fn airEquality(...@@ -3885,7 +3968,7 @@ fn airEquality(
38853968
3886 try f.writeCValue(writer, lhs, .Other);3969 try f.writeCValue(writer, lhs, .Other);
3887 try writer.writeByte(' ');3970 try writer.writeByte(' ');
3888 try writer.writeAll(operator);3971 try writer.writeAll(compareOperatorC(operator));
3889 try writer.writeByte(' ');3972 try writer.writeByte(' ');
3890 try f.writeCValue(writer, rhs, .Other);3973 try f.writeCValue(writer, rhs, .Other);
3891 try writer.writeAll(";\n");3974 try writer.writeAll(";\n");
...@@ -3927,11 +4010,14 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {...@@ -3927,11 +4010,14 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
3927 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });4010 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
39284011
3929 const inst_ty = f.air.typeOfIndex(inst);4012 const inst_ty = f.air.typeOfIndex(inst);
3930 const elem_ty = inst_ty.elemType2();4013 const inst_scalar_ty = inst_ty.scalarType();
4014 const elem_ty = inst_scalar_ty.elemType2();
39314015
3932 const local = try f.allocLocal(inst, inst_ty);4016 const local = try f.allocLocal(inst, inst_ty);
3933 const writer = f.object.writer();4017 const writer = f.object.writer();
4018 const v = try Vectorizer.start(f, inst, writer, inst_ty);
3934 try f.writeCValue(writer, local, .Other);4019 try f.writeCValue(writer, local, .Other);
4020 try v.elem(f, writer);
3935 try writer.writeAll(" = ");4021 try writer.writeAll(" = ");
39364022
3937 if (elem_ty.hasRuntimeBitsIgnoreComptime()) {4023 if (elem_ty.hasRuntimeBitsIgnoreComptime()) {
...@@ -3939,19 +4025,26 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {...@@ -3939,19 +4025,26 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
3939 // results in a NULL pointer, or if LHS is NULL. The operation is only UB4025 // results in a NULL pointer, or if LHS is NULL. The operation is only UB
3940 // if the result is NULL and then dereferenced.4026 // if the result is NULL and then dereferenced.
3941 try writer.writeByte('(');4027 try writer.writeByte('(');
3942 try f.renderType(writer, inst_ty);4028 try f.renderType(writer, inst_scalar_ty);
3943 try writer.writeAll(")(((uintptr_t)");4029 try writer.writeAll(")(((uintptr_t)");
3944 try f.writeCValue(writer, lhs, .Other);4030 try f.writeCValue(writer, lhs, .Other);
4031 try v.elem(f, writer);
3945 try writer.writeAll(") ");4032 try writer.writeAll(") ");
3946 try writer.writeByte(operator);4033 try writer.writeByte(operator);
3947 try writer.writeAll(" (");4034 try writer.writeAll(" (");
3948 try f.writeCValue(writer, rhs, .Other);4035 try f.writeCValue(writer, rhs, .Other);
4036 try v.elem(f, writer);
3949 try writer.writeAll("*sizeof(");4037 try writer.writeAll("*sizeof(");
3950 try f.renderType(writer, elem_ty);4038 try f.renderType(writer, elem_ty);
3951 try writer.writeAll(")))");4039 try writer.writeAll(")))");
3952 } else try f.writeCValue(writer, lhs, .Initializer);4040 } else {
4041 try f.writeCValue(writer, lhs, .Other);
4042 try v.elem(f, writer);
4043 }
39534044
3954 try writer.writeAll(";\n");4045 try writer.writeAll(";\n");
4046 try v.end(f, inst, writer);
4047
3955 return local;4048 return local;
3956}4049}
39574050
...@@ -3964,10 +4057,12 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons...@@ -3964,10 +4057,12 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
3964 }4057 }
39654058
3966 const inst_ty = f.air.typeOfIndex(inst);4059 const inst_ty = f.air.typeOfIndex(inst);
4060 const inst_scalar_ty = inst_ty.scalarType();
4061
3967 const target = f.object.dg.module.getTarget();4062 const target = f.object.dg.module.getTarget();
3968 if (inst_ty.isInt() and inst_ty.bitSize(target) > 64)4063 if (inst_scalar_ty.isInt() and inst_scalar_ty.bitSize(target) > 64)
3969 return try airBinBuiltinCall(f, inst, operation[1..], .None);4064 return try airBinBuiltinCall(f, inst, operation[1..], .none);
3970 if (inst_ty.isRuntimeFloat())4065 if (inst_scalar_ty.isRuntimeFloat())
3971 return try airBinFloatOp(f, inst, operation);4066 return try airBinFloatOp(f, inst, operation);
39724067
3973 const lhs = try f.resolveInst(bin_op.lhs);4068 const lhs = try f.resolveInst(bin_op.lhs);
...@@ -3976,19 +4071,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons...@@ -3976,19 +4071,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
39764071
3977 const writer = f.object.writer();4072 const writer = f.object.writer();
3978 const local = try f.allocLocal(inst, inst_ty);4073 const local = try f.allocLocal(inst, inst_ty);
4074 const v = try Vectorizer.start(f, inst, writer, inst_ty);
3979 try f.writeCValue(writer, local, .Other);4075 try f.writeCValue(writer, local, .Other);
4076 try v.elem(f, writer);
3980 // (lhs <> rhs) ? lhs : rhs4077 // (lhs <> rhs) ? lhs : rhs
3981 try writer.writeAll(" = (");4078 try writer.writeAll(" = (");
3982 try f.writeCValue(writer, lhs, .Other);4079 try f.writeCValue(writer, lhs, .Other);
4080 try v.elem(f, writer);
3983 try writer.writeByte(' ');4081 try writer.writeByte(' ');
3984 try writer.writeByte(operator);4082 try writer.writeByte(operator);
3985 try writer.writeByte(' ');4083 try writer.writeByte(' ');
3986 try f.writeCValue(writer, rhs, .Other);4084 try f.writeCValue(writer, rhs, .Other);
4085 try v.elem(f, writer);
3987 try writer.writeAll(") ? ");4086 try writer.writeAll(") ? ");
3988 try f.writeCValue(writer, lhs, .Other);4087 try f.writeCValue(writer, lhs, .Other);
4088 try v.elem(f, writer);
3989 try writer.writeAll(" : ");4089 try writer.writeAll(" : ");
3990 try f.writeCValue(writer, rhs, .Other);4090 try f.writeCValue(writer, rhs, .Other);
4091 try v.elem(f, writer);
3991 try writer.writeAll(";\n");4092 try writer.writeAll(";\n");
4093 try v.end(f, inst, writer);
39924094
3993 return local;4095 return local;
3994}4096}
...@@ -4412,12 +4514,56 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4412,12 +4514,56 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
44124514
4413 // Ensure padding bits have the expected value.4515 // Ensure padding bits have the expected value.
4414 if (dest_ty.isAbiInt()) {4516 if (dest_ty.isAbiInt()) {
4517 const dest_cty = try f.typeToCType(dest_ty, .complete);
4518 const dest_info = dest_ty.intInfo(target);
4519 var info_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) {
4520 .unsigned => .int_unsigned,
4521 .signed => .int_signed,
4522 } }, .data = dest_info.bits };
4523 var wrap_cty: ?CType = null;
4524 var need_bitcasts = false;
4525
4415 try f.writeCValue(writer, local, .Other);4526 try f.writeCValue(writer, local, .Other);
4416 try writer.writeAll(" = zig_wrap_");4527 if (dest_cty.castTag(.array)) |pl| {
4417 try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty);4528 try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) {
4529 .Little => pl.data.len - 1,
4530 .Big => 0,
4531 }});
4532 const elem_cty = f.indexToCType(pl.data.elem_type);
4533 wrap_cty = elem_cty.toSignedness(dest_info.signedness);
4534 need_bitcasts = wrap_cty.?.tag() == .zig_i128;
4535 info_ty_pl.data -= 1;
4536 info_ty_pl.data %= @intCast(u16, f.byteSize(elem_cty) * 8);
4537 info_ty_pl.data += 1;
4538 }
4539 try writer.writeAll(" = ");
4540 if (need_bitcasts) {
4541 try writer.writeAll("zig_bitcast_");
4542 try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?.toUnsigned());
4543 try writer.writeByte('(');
4544 }
4545 try writer.writeAll("zig_wrap_");
4546 const info_ty = Type.initPayload(&info_ty_pl.base);
4547 if (wrap_cty) |cty|
4548 try f.object.dg.renderCTypeForBuiltinFnName(writer, cty)
4549 else
4550 try f.object.dg.renderTypeForBuiltinFnName(writer, info_ty);
4418 try writer.writeByte('(');4551 try writer.writeByte('(');
4552 if (need_bitcasts) {
4553 try writer.writeAll("zig_bitcast_");
4554 try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?);
4555 try writer.writeByte('(');
4556 }
4419 try f.writeCValue(writer, local, .Other);4557 try f.writeCValue(writer, local, .Other);
4420 try f.object.dg.renderBuiltinInfo(writer, dest_ty, .Bits);4558 if (dest_cty.castTag(.array)) |pl| {
4559 try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) {
4560 .Little => pl.data.len - 1,
4561 .Big => 0,
4562 }});
4563 }
4564 if (need_bitcasts) try writer.writeByte(')');
4565 try f.object.dg.renderBuiltinInfo(writer, info_ty, .bits);
4566 if (need_bitcasts) try writer.writeByte(')');
4421 try writer.writeAll(");\n");4567 try writer.writeAll(");\n");
4422 }4568 }
44234569
...@@ -4428,6 +4574,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4428,6 +4574,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
4428 return local;4574 return local;
4429}4575}
44304576
4577fn airTrap(writer: anytype) !CValue {
4578 try writer.writeAll("zig_trap();\n");
4579 return .none;
4580}
4581
4431fn airBreakpoint(writer: anytype) !CValue {4582fn airBreakpoint(writer: anytype) !CValue {
4432 try writer.writeAll("zig_breakpoint();\n");4583 try writer.writeAll("zig_breakpoint();\n");
4433 return .none;4584 return .none;
...@@ -5427,7 +5578,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5427,7 +5578,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5427 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);5578 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
5428 try writer.writeByte(')');5579 try writer.writeByte(')');
5429 if (cant_cast) try writer.writeByte(')');5580 if (cant_cast) try writer.writeByte(')');
5430 try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .Bits);5581 try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits);
5431 try writer.writeAll(");\n");5582 try writer.writeAll(");\n");
5432 if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local;5583 if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local;
54335584
...@@ -5860,7 +6011,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5860,7 +6011,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
5860 try f.writeCValue(writer, operand, .FunctionArgument);6011 try f.writeCValue(writer, operand, .FunctionArgument);
5861 try writer.writeByte(')');6012 try writer.writeByte(')');
5862 if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) {6013 if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) {
5863 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits);6014 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);
5864 try writer.writeByte(')');6015 try writer.writeByte(')');
5865 }6016 }
5866 try writer.writeAll(";\n");6017 try writer.writeAll(";\n");
...@@ -5906,19 +6057,35 @@ fn airUnBuiltinCall(...@@ -5906,19 +6057,35 @@ fn airUnBuiltinCall(
5906 const operand = try f.resolveInst(ty_op.operand);6057 const operand = try f.resolveInst(ty_op.operand);
5907 try reap(f, inst, &.{ty_op.operand});6058 try reap(f, inst, &.{ty_op.operand});
5908 const inst_ty = f.air.typeOfIndex(inst);6059 const inst_ty = f.air.typeOfIndex(inst);
6060 const inst_scalar_ty = inst_ty.scalarType();
5909 const operand_ty = f.air.typeOf(ty_op.operand);6061 const operand_ty = f.air.typeOf(ty_op.operand);
6062 const scalar_ty = operand_ty.scalarType();
6063
6064 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete);
6065 const ref_ret = inst_scalar_cty.tag() == .array;
59106066
5911 const writer = f.object.writer();6067 const writer = f.object.writer();
5912 const local = try f.allocLocal(inst, inst_ty);6068 const local = try f.allocLocal(inst, inst_ty);
5913 try f.writeCValue(writer, local, .Other);6069 const v = try Vectorizer.start(f, inst, writer, operand_ty);
5914 try writer.writeAll(" = zig_");6070 if (!ref_ret) {
5915 try writer.writeAll(operation);6071 try f.writeCValue(writer, local, .Other);
5916 try writer.writeByte('_');6072 try v.elem(f, writer);
5917 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);6073 try writer.writeAll(" = ");
6074 }
6075 try writer.print("zig_{s}_", .{operation});
6076 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
5918 try writer.writeByte('(');6077 try writer.writeByte('(');
6078 if (ref_ret) {
6079 try f.writeCValue(writer, local, .FunctionArgument);
6080 try v.elem(f, writer);
6081 try writer.writeAll(", ");
6082 }
5919 try f.writeCValue(writer, operand, .FunctionArgument);6083 try f.writeCValue(writer, operand, .FunctionArgument);
5920 try f.object.dg.renderBuiltinInfo(writer, operand_ty, info);6084 try v.elem(f, writer);
6085 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info);
5921 try writer.writeAll(");\n");6086 try writer.writeAll(");\n");
6087 try v.end(f, inst, writer);
6088
5922 return local;6089 return local;
5923}6090}
59246091
...@@ -5935,55 +6102,108 @@ fn airBinBuiltinCall(...@@ -5935,55 +6102,108 @@ fn airBinBuiltinCall(
5935 return .none;6102 return .none;
5936 }6103 }
59376104
6105 const operand_ty = f.air.typeOf(bin_op.lhs);
6106 const operand_cty = try f.typeToCType(operand_ty, .complete);
6107 const is_big = operand_cty.tag() == .array;
6108
5938 const lhs = try f.resolveInst(bin_op.lhs);6109 const lhs = try f.resolveInst(bin_op.lhs);
5939 const rhs = try f.resolveInst(bin_op.rhs);6110 const rhs = try f.resolveInst(bin_op.rhs);
5940 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });6111 if (!is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
59416112
5942 const inst_ty = f.air.typeOfIndex(inst);6113 const inst_ty = f.air.typeOfIndex(inst);
5943 const operand_ty = f.air.typeOf(bin_op.lhs);6114 const inst_scalar_ty = inst_ty.scalarType();
6115 const scalar_ty = operand_ty.scalarType();
6116
6117 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete);
6118 const ref_ret = inst_scalar_cty.tag() == .array;
59446119
5945 const writer = f.object.writer();6120 const writer = f.object.writer();
5946 const local = try f.allocLocal(inst, inst_ty);6121 const local = try f.allocLocal(inst, inst_ty);
5947 try f.writeCValue(writer, local, .Other);6122 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
5948 try writer.writeAll(" = zig_");6123 const v = try Vectorizer.start(f, inst, writer, operand_ty);
5949 try writer.writeAll(operation);6124 if (!ref_ret) {
5950 try writer.writeByte('_');6125 try f.writeCValue(writer, local, .Other);
5951 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);6126 try v.elem(f, writer);
6127 try writer.writeAll(" = ");
6128 }
6129 try writer.print("zig_{s}_", .{operation});
6130 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
5952 try writer.writeByte('(');6131 try writer.writeByte('(');
6132 if (ref_ret) {
6133 try f.writeCValue(writer, local, .FunctionArgument);
6134 try v.elem(f, writer);
6135 try writer.writeAll(", ");
6136 }
5953 try f.writeCValue(writer, lhs, .FunctionArgument);6137 try f.writeCValue(writer, lhs, .FunctionArgument);
6138 try v.elem(f, writer);
5954 try writer.writeAll(", ");6139 try writer.writeAll(", ");
5955 try f.writeCValue(writer, rhs, .FunctionArgument);6140 try f.writeCValue(writer, rhs, .FunctionArgument);
5956 try f.object.dg.renderBuiltinInfo(writer, operand_ty, info);6141 try v.elem(f, writer);
6142 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info);
5957 try writer.writeAll(");\n");6143 try writer.writeAll(");\n");
6144 try v.end(f, inst, writer);
6145
5958 return local;6146 return local;
5959}6147}
59606148
5961fn cmpBuiltinCall(6149fn airCmpBuiltinCall(
5962 f: *Function,6150 f: *Function,
5963 inst: Air.Inst.Index,6151 inst: Air.Inst.Index,
5964 operator: []const u8,6152 data: anytype,
5965 operation: []const u8,6153 operator: std.math.CompareOperator,
6154 operation: enum { cmp, operator },
6155 info: BuiltinInfo,
5966) !CValue {6156) !CValue {
6157 if (f.liveness.isUnused(inst)) {
6158 try reap(f, inst, &.{ data.lhs, data.rhs });
6159 return .none;
6160 }
6161
6162 const lhs = try f.resolveInst(data.lhs);
6163 const rhs = try f.resolveInst(data.rhs);
6164 try reap(f, inst, &.{ data.lhs, data.rhs });
6165
5967 const inst_ty = f.air.typeOfIndex(inst);6166 const inst_ty = f.air.typeOfIndex(inst);
5968 const bin_op = f.air.instructions.items(.data)[inst].bin_op;6167 const inst_scalar_ty = inst_ty.scalarType();
5969 const operand_ty = f.air.typeOf(bin_op.lhs);6168 const operand_ty = f.air.typeOf(data.lhs);
6169 const scalar_ty = operand_ty.scalarType();
59706170
5971 const lhs = try f.resolveInst(bin_op.lhs);6171 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete);
5972 const rhs = try f.resolveInst(bin_op.rhs);6172 const ref_ret = inst_scalar_cty.tag() == .array;
5973 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
59746173
5975 const writer = f.object.writer();6174 const writer = f.object.writer();
5976 const local = try f.allocLocal(inst, inst_ty);6175 const local = try f.allocLocal(inst, inst_ty);
5977 try f.writeCValue(writer, local, .Other);6176 const v = try Vectorizer.start(f, inst, writer, operand_ty);
5978 try writer.writeAll(" = zig_");6177 if (!ref_ret) {
5979 try writer.writeAll(operation);6178 try f.writeCValue(writer, local, .Other);
5980 try writer.writeByte('_');6179 try v.elem(f, writer);
5981 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);6180 try writer.writeAll(" = ");
6181 }
6182 try writer.print("zig_{s}_", .{switch (operation) {
6183 else => @tagName(operation),
6184 .operator => compareOperatorAbbrev(operator),
6185 }});
6186 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
5982 try writer.writeByte('(');6187 try writer.writeByte('(');
6188 if (ref_ret) {
6189 try f.writeCValue(writer, local, .FunctionArgument);
6190 try v.elem(f, writer);
6191 try writer.writeAll(", ");
6192 }
5983 try f.writeCValue(writer, lhs, .FunctionArgument);6193 try f.writeCValue(writer, lhs, .FunctionArgument);
6194 try v.elem(f, writer);
5984 try writer.writeAll(", ");6195 try writer.writeAll(", ");
5985 try f.writeCValue(writer, rhs, .FunctionArgument);6196 try f.writeCValue(writer, rhs, .FunctionArgument);
5986 try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) });6197 try v.elem(f, writer);
6198 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info);
6199 try writer.writeByte(')');
6200 if (!ref_ret) try writer.print(" {s} {}", .{
6201 compareOperatorC(operator),
6202 try f.fmtIntLiteral(Type.initTag(.i32), Value.zero),
6203 });
6204 try writer.writeAll(";\n");
6205 try v.end(f, inst, writer);
6206
5987 return local;6207 return local;
5988}6208}
59896209
...@@ -6328,33 +6548,120 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6328,33 +6548,120 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
63286548
6329fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {6549fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
6330 const ty_op = f.air.instructions.items(.data)[inst].ty_op;6550 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
6551
6331 if (f.liveness.isUnused(inst)) {6552 if (f.liveness.isUnused(inst)) {
6332 try reap(f, inst, &.{ty_op.operand});6553 try reap(f, inst, &.{ty_op.operand});
6333 return .none;6554 return .none;
6334 }6555 }
63356556
6336 const inst_ty = f.air.typeOfIndex(inst);
6337 const operand = try f.resolveInst(ty_op.operand);6557 const operand = try f.resolveInst(ty_op.operand);
6338 try reap(f, inst, &.{ty_op.operand});6558 try reap(f, inst, &.{ty_op.operand});
6559
6560 const inst_ty = f.air.typeOfIndex(inst);
6561 const inst_scalar_ty = inst_ty.scalarType();
6562 const inst_scalar_cty = try f.typeToIndex(inst_scalar_ty, .complete);
6563 const need_memcpy = f.indexToCType(inst_scalar_cty).tag() == .array;
6564
6339 const writer = f.object.writer();6565 const writer = f.object.writer();
6340 const local = try f.allocLocal(inst, inst_ty);6566 const local = try f.allocLocal(inst, inst_ty);
6567 const v = try Vectorizer.start(f, inst, writer, inst_ty);
6568 if (need_memcpy) try writer.writeAll("memcpy(&");
6341 try f.writeCValue(writer, local, .Other);6569 try f.writeCValue(writer, local, .Other);
6342 try writer.writeAll(" = ");6570 try v.elem(f, writer);
6571 try writer.writeAll(if (need_memcpy) ", &" else " = ");
6572 try f.writeCValue(writer, operand, .Other);
6573 if (need_memcpy) {
6574 try writer.writeAll(", sizeof(");
6575 try f.renderCType(writer, inst_scalar_cty);
6576 try writer.writeAll("))");
6577 }
6578 try writer.writeAll(";\n");
6579 try v.end(f, inst, writer);
63436580
6344 _ = operand;6581 return local;
6345 return f.fail("TODO: C backend: implement airSplat", .{});
6346}6582}
63476583
6348fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {6584fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
6349 if (f.liveness.isUnused(inst)) return .none;6585 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
6586 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
6587
6588 if (f.liveness.isUnused(inst)) {
6589 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6590 return .none;
6591 }
6592
6593 const pred = try f.resolveInst(pl_op.operand);
6594 const lhs = try f.resolveInst(extra.lhs);
6595 const rhs = try f.resolveInst(extra.rhs);
6596 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6597
6598 const inst_ty = f.air.typeOfIndex(inst);
6599
6600 const writer = f.object.writer();
6601 const local = try f.allocLocal(inst, inst_ty);
6602 const v = try Vectorizer.start(f, inst, writer, inst_ty);
6603 try f.writeCValue(writer, local, .Other);
6604 try v.elem(f, writer);
6605 try writer.writeAll(" = ");
6606 try f.writeCValue(writer, pred, .Other);
6607 try v.elem(f, writer);
6608 try writer.writeAll(" ? ");
6609 try f.writeCValue(writer, lhs, .Other);
6610 try v.elem(f, writer);
6611 try writer.writeAll(" : ");
6612 try f.writeCValue(writer, rhs, .Other);
6613 try v.elem(f, writer);
6614 try writer.writeAll(";\n");
6615 try v.end(f, inst, writer);
63506616
6351 return f.fail("TODO: C backend: implement airSelect", .{});6617 return local;
6352}6618}
63536619
6354fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {6620fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
6355 if (f.liveness.isUnused(inst)) return .none;6621 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
6622 const extra = f.air.extraData(Air.Shuffle, ty_pl.payload).data;
63566623
6357 return f.fail("TODO: C backend: implement airShuffle", .{});6624 if (f.liveness.isUnused(inst)) {
6625 try reap(f, inst, &.{ extra.a, extra.b });
6626 return .none;
6627 }
6628
6629 const mask = f.air.values[extra.mask];
6630 const lhs = try f.resolveInst(extra.a);
6631 const rhs = try f.resolveInst(extra.b);
6632
6633 const module = f.object.dg.module;
6634 const target = module.getTarget();
6635 const inst_ty = f.air.typeOfIndex(inst);
6636
6637 const writer = f.object.writer();
6638 const local = try f.allocLocal(inst, inst_ty);
6639 try reap(f, inst, &.{ extra.a, extra.b }); // local cannot alias operands
6640 for (0..extra.mask_len) |index| {
6641 var dst_pl = Value.Payload.U64{
6642 .base = .{ .tag = .int_u64 },
6643 .data = @intCast(u64, index),
6644 };
6645
6646 try f.writeCValue(writer, local, .Other);
6647 try writer.writeByte('[');
6648 try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&dst_pl.base), .Other);
6649 try writer.writeAll("] = ");
6650
6651 var buf: Value.ElemValueBuffer = undefined;
6652 const mask_elem = mask.elemValueBuffer(module, index, &buf).toSignedInt(target);
6653 var src_pl = Value.Payload.U64{
6654 .base = .{ .tag = .int_u64 },
6655 .data = @intCast(u64, mask_elem ^ mask_elem >> 63),
6656 };
6657
6658 try f.writeCValue(writer, if (mask_elem >= 0) lhs else rhs, .Other);
6659 try writer.writeByte('[');
6660 try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&src_pl.base), .Other);
6661 try writer.writeAll("];\n");
6662 }
6663
6664 return local;
6358}6665}
63596666
6360fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {6667fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
...@@ -6370,65 +6677,45 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6370,65 +6677,45 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
6370 const operand = try f.resolveInst(reduce.operand);6677 const operand = try f.resolveInst(reduce.operand);
6371 try reap(f, inst, &.{reduce.operand});6678 try reap(f, inst, &.{reduce.operand});
6372 const operand_ty = f.air.typeOf(reduce.operand);6679 const operand_ty = f.air.typeOf(reduce.operand);
6373 const vector_len = operand_ty.vectorLen();
6374 const writer = f.object.writer();6680 const writer = f.object.writer();
63756681
6376 const Op = union(enum) {6682 const use_operator = scalar_ty.bitSize(target) <= 64;
6377 call_fn: []const u8,6683 const op: union(enum) {
6684 const Func = struct { operation: []const u8, info: BuiltinInfo = .none };
6685 float_op: Func,
6686 builtin: Func,
6378 infix: []const u8,6687 infix: []const u8,
6379 ternary: []const u8,6688 ternary: []const u8,
6380 };6689 } = switch (reduce.operation) {
6381 var fn_name_buf: [64]u8 = undefined;6690 .And => if (use_operator) .{ .infix = " &= " } else .{ .builtin = .{ .operation = "and" } },
6382 const op: Op = switch (reduce.operation) {6691 .Or => if (use_operator) .{ .infix = " |= " } else .{ .builtin = .{ .operation = "or" } },
6383 .And => .{ .infix = " &= " },6692 .Xor => if (use_operator) .{ .infix = " ^= " } else .{ .builtin = .{ .operation = "xor" } },
6384 .Or => .{ .infix = " |= " },
6385 .Xor => .{ .infix = " ^= " },
6386 .Min => switch (scalar_ty.zigTypeTag()) {6693 .Min => switch (scalar_ty.zigTypeTag()) {
6387 .Int => Op{ .ternary = " < " },6694 .Int => if (use_operator) .{ .ternary = " < " } else .{
6388 .Float => op: {6695 .builtin = .{ .operation = "min" },
6389 const float_bits = scalar_ty.floatBits(target);
6390 break :op Op{
6391 .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "{s}fmin{s}", .{
6392 libcFloatPrefix(float_bits), libcFloatSuffix(float_bits),
6393 }) catch unreachable,
6394 };
6395 },6696 },
6697 .Float => .{ .float_op = .{ .operation = "fmin" } },
6396 else => unreachable,6698 else => unreachable,
6397 },6699 },
6398 .Max => switch (scalar_ty.zigTypeTag()) {6700 .Max => switch (scalar_ty.zigTypeTag()) {
6399 .Int => Op{ .ternary = " > " },6701 .Int => if (use_operator) .{ .ternary = " > " } else .{
6400 .Float => op: {6702 .builtin = .{ .operation = "max" },
6401 const float_bits = scalar_ty.floatBits(target);
6402 break :op Op{
6403 .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "{s}fmax{s}", .{
6404 libcFloatPrefix(float_bits), libcFloatSuffix(float_bits),
6405 }) catch unreachable,
6406 };
6407 },6703 },
6704 .Float => .{ .float_op = .{ .operation = "fmax" } },
6408 else => unreachable,6705 else => unreachable,
6409 },6706 },
6410 .Add => switch (scalar_ty.zigTypeTag()) {6707 .Add => switch (scalar_ty.zigTypeTag()) {
6411 .Int => Op{ .infix = " += " },6708 .Int => if (use_operator) .{ .infix = " += " } else .{
6412 .Float => op: {6709 .builtin = .{ .operation = "addw", .info = .bits },
6413 const float_bits = scalar_ty.floatBits(target);
6414 break :op Op{
6415 .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "__add{s}f3", .{
6416 compilerRtFloatAbbrev(float_bits),
6417 }) catch unreachable,
6418 };
6419 },6710 },
6711 .Float => .{ .builtin = .{ .operation = "add" } },
6420 else => unreachable,6712 else => unreachable,
6421 },6713 },
6422 .Mul => switch (scalar_ty.zigTypeTag()) {6714 .Mul => switch (scalar_ty.zigTypeTag()) {
6423 .Int => Op{ .infix = " *= " },6715 .Int => if (use_operator) .{ .infix = " *= " } else .{
6424 .Float => op: {6716 .builtin = .{ .operation = "mulw", .info = .bits },
6425 const float_bits = scalar_ty.floatBits(target);
6426 break :op Op{
6427 .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "__mul{s}f3", .{
6428 compilerRtFloatAbbrev(float_bits),
6429 }) catch unreachable,
6430 };
6431 },6717 },
6718 .Float => .{ .builtin = .{ .operation = "mul" } },
6432 else => unreachable,6719 else => unreachable,
6433 },6720 },
6434 };6721 };
...@@ -6444,75 +6731,96 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6444,75 +6731,96 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
6444 // }6731 // }
6445 // break :reduce accum;6732 // break :reduce accum;
6446 // }6733 // }
6447 const it = try f.allocLocal(inst, Type.usize);
6448 try f.writeCValue(writer, it, .Other);
6449 try writer.writeAll(" = 0;\n");
64506734
6451 const accum = try f.allocLocal(inst, scalar_ty);6735 const accum = try f.allocLocal(inst, scalar_ty);
6452 try f.writeCValue(writer, accum, .Other);6736 try f.writeCValue(writer, accum, .Other);
6453 try writer.writeAll(" = ");6737 try writer.writeAll(" = ");
64546738
6455 const init_val = switch (reduce.operation) {6739 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa);
6456 .And, .Or, .Xor, .Add => "0",6740 defer arena.deinit();
6741
6742 const ExpectedContents = union {
6743 u: Value.Payload.U64,
6744 i: Value.Payload.I64,
6745 f16: Value.Payload.Float_16,
6746 f32: Value.Payload.Float_32,
6747 f64: Value.Payload.Float_64,
6748 f80: Value.Payload.Float_80,
6749 f128: Value.Payload.Float_128,
6750 };
6751 var stack align(@alignOf(ExpectedContents)) =
6752 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());
6753
6754 try f.object.dg.renderValue(writer, scalar_ty, switch (reduce.operation) {
6755 .Or, .Xor, .Add => Value.zero,
6756 .And => switch (scalar_ty.zigTypeTag()) {
6757 .Bool => Value.one,
6758 else => switch (scalar_ty.intInfo(target).signedness) {
6759 .unsigned => try scalar_ty.maxInt(stack.get(), target),
6760 .signed => Value.negative_one,
6761 },
6762 },
6457 .Min => switch (scalar_ty.zigTypeTag()) {6763 .Min => switch (scalar_ty.zigTypeTag()) {
6458 .Int => "TODO_intmax",6764 .Bool => Value.one,
6459 .Float => "TODO_nan",6765 .Int => try scalar_ty.maxInt(stack.get(), target),
6766 .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target),
6460 else => unreachable,6767 else => unreachable,
6461 },6768 },
6462 .Max => switch (scalar_ty.zigTypeTag()) {6769 .Max => switch (scalar_ty.zigTypeTag()) {
6463 .Int => "TODO_intmin",6770 .Bool => Value.zero,
6464 .Float => "TODO_nan",6771 .Int => try scalar_ty.minInt(stack.get(), target),
6772 .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target),
6465 else => unreachable,6773 else => unreachable,
6466 },6774 },
6467 .Mul => "1",6775 .Mul => Value.one,
6468 };6776 }, .Initializer);
6469 try writer.writeAll(init_val);6777 try writer.writeAll(";\n");
6470 try writer.writeAll(";");
6471 try f.object.indent_writer.insertNewline();
6472 try writer.writeAll("for (;");
6473 try f.writeCValue(writer, it, .Other);
6474 try writer.print("<{d};++", .{vector_len});
6475 try f.writeCValue(writer, it, .Other);
6476 try writer.writeAll(") ");
6477 try f.writeCValue(writer, accum, .Other);
64786778
6779 const v = try Vectorizer.start(f, inst, writer, operand_ty);
6780 try f.writeCValue(writer, accum, .Other);
6479 switch (op) {6781 switch (op) {
6480 .call_fn => |fn_name| {6782 .float_op => |func| {
6481 try writer.print(" = {s}(", .{fn_name});6783 try writer.writeAll(" = zig_libc_name_");
6784 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
6785 try writer.print("({s})(", .{func.operation});
6786 try f.writeCValue(writer, accum, .FunctionArgument);
6787 try writer.writeAll(", ");
6788 try f.writeCValue(writer, operand, .Other);
6789 try v.elem(f, writer);
6790 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info);
6791 try writer.writeByte(')');
6792 },
6793 .builtin => |func| {
6794 try writer.print(" = zig_{s}_", .{func.operation});
6795 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
6796 try writer.writeByte('(');
6482 try f.writeCValue(writer, accum, .FunctionArgument);6797 try f.writeCValue(writer, accum, .FunctionArgument);
6483 try writer.writeAll(", ");6798 try writer.writeAll(", ");
6484 try f.writeCValue(writer, operand, .Other);6799 try f.writeCValue(writer, operand, .Other);
6485 try writer.writeAll("[");6800 try v.elem(f, writer);
6486 try f.writeCValue(writer, it, .Other);6801 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info);
6487 try writer.writeAll("])");6802 try writer.writeByte(')');
6488 },6803 },
6489 .infix => |ass| {6804 .infix => |ass| {
6490 try writer.writeAll(ass);6805 try writer.writeAll(ass);
6491 try f.writeCValue(writer, operand, .Other);6806 try f.writeCValue(writer, operand, .Other);
6492 try writer.writeAll("[");6807 try v.elem(f, writer);
6493 try f.writeCValue(writer, it, .Other);
6494 try writer.writeAll("]");
6495 },6808 },
6496 .ternary => |cmp| {6809 .ternary => |cmp| {
6497 try writer.writeAll(" = ");6810 try writer.writeAll(" = ");
6498 try f.writeCValue(writer, accum, .Other);6811 try f.writeCValue(writer, accum, .Other);
6499 try writer.writeAll(cmp);6812 try writer.writeAll(cmp);
6500 try f.writeCValue(writer, operand, .Other);6813 try f.writeCValue(writer, operand, .Other);
6501 try writer.writeAll("[");6814 try v.elem(f, writer);
6502 try f.writeCValue(writer, it, .Other);6815 try writer.writeAll(" ? ");
6503 try writer.writeAll("] ? ");
6504 try f.writeCValue(writer, accum, .Other);6816 try f.writeCValue(writer, accum, .Other);
6505 try writer.writeAll(" : ");6817 try writer.writeAll(" : ");
6506 try f.writeCValue(writer, operand, .Other);6818 try f.writeCValue(writer, operand, .Other);
6507 try writer.writeAll("[");6819 try v.elem(f, writer);
6508 try f.writeCValue(writer, it, .Other);
6509 try writer.writeAll("]");
6510 },6820 },
6511 }6821 }
6512
6513 try writer.writeAll(";\n");6822 try writer.writeAll(";\n");
65146823 try v.end(f, inst, writer);
6515 try freeLocal(f, inst, it.new_local, 0);
65166824
6517 return accum;6825 return accum;
6518}6826}
...@@ -6646,7 +6954,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6646,7 +6954,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
6646 try writer.writeByte('(');6954 try writer.writeByte('(');
66476955
6648 if (inst_ty.isAbiInt() and (field_ty.isAbiInt() or field_ty.isPtrAtRuntime())) {6956 if (inst_ty.isAbiInt() and (field_ty.isAbiInt() or field_ty.isPtrAtRuntime())) {
6649 try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument);6957 try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument);
6650 } else {6958 } else {
6651 try writer.writeByte('(');6959 try writer.writeByte('(');
6652 try f.renderType(writer, inst_ty);6960 try f.renderType(writer, inst_ty);
...@@ -6664,7 +6972,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6664,7 +6972,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
66646972
6665 try writer.writeAll(", ");6973 try writer.writeAll(", ");
6666 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);6974 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
6667 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits);6975 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);
6668 try writer.writeByte(')');6976 try writer.writeByte(')');
6669 if (!empty) try writer.writeByte(')');6977 if (!empty) try writer.writeByte(')');
66706978
...@@ -6788,7 +7096,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6788,7 +7096,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
6788}7096}
67897097
6790fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {7098fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
6791 const inst_ty = f.air.typeOfIndex(inst);
6792 const un_op = f.air.instructions.items(.data)[inst].un_op;7099 const un_op = f.air.instructions.items(.data)[inst].un_op;
6793 if (f.liveness.isUnused(inst)) {7100 if (f.liveness.isUnused(inst)) {
6794 try reap(f, inst, &.{un_op});7101 try reap(f, inst, &.{un_op});
...@@ -6797,16 +7104,23 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6797,16 +7104,23 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
67977104
6798 const operand = try f.resolveInst(un_op);7105 const operand = try f.resolveInst(un_op);
6799 try reap(f, inst, &.{un_op});7106 try reap(f, inst, &.{un_op});
7107
6800 const operand_ty = f.air.typeOf(un_op);7108 const operand_ty = f.air.typeOf(un_op);
7109 const scalar_ty = operand_ty.scalarType();
68017110
6802 const writer = f.object.writer();7111 const writer = f.object.writer();
6803 const local = try f.allocLocal(inst, inst_ty);7112 const local = try f.allocLocal(inst, operand_ty);
7113 const v = try Vectorizer.start(f, inst, writer, operand_ty);
6804 try f.writeCValue(writer, local, .Other);7114 try f.writeCValue(writer, local, .Other);
7115 try v.elem(f, writer);
6805 try writer.writeAll(" = zig_neg_");7116 try writer.writeAll(" = zig_neg_");
6806 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);7117 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
6807 try writer.writeByte('(');7118 try writer.writeByte('(');
6808 try f.writeCValue(writer, operand, .FunctionArgument);7119 try f.writeCValue(writer, operand, .FunctionArgument);
7120 try v.elem(f, writer);
6809 try writer.writeAll(");\n");7121 try writer.writeAll(");\n");
7122 try v.end(f, inst, writer);
7123
6810 return local;7124 return local;
6811}7125}
68127126
...@@ -6816,19 +7130,28 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal...@@ -6816,19 +7130,28 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal
6816 try reap(f, inst, &.{un_op});7130 try reap(f, inst, &.{un_op});
6817 return .none;7131 return .none;
6818 }7132 }
7133
6819 const operand = try f.resolveInst(un_op);7134 const operand = try f.resolveInst(un_op);
6820 try reap(f, inst, &.{un_op});7135 try reap(f, inst, &.{un_op});
6821 const writer = f.object.writer();7136
6822 const inst_ty = f.air.typeOfIndex(inst);7137 const inst_ty = f.air.typeOfIndex(inst);
7138 const inst_scalar_ty = inst_ty.scalarType();
7139
7140 const writer = f.object.writer();
6823 const local = try f.allocLocal(inst, inst_ty);7141 const local = try f.allocLocal(inst, inst_ty);
7142 const v = try Vectorizer.start(f, inst, writer, inst_ty);
6824 try f.writeCValue(writer, local, .Other);7143 try f.writeCValue(writer, local, .Other);
7144 try v.elem(f, writer);
6825 try writer.writeAll(" = zig_libc_name_");7145 try writer.writeAll(" = zig_libc_name_");
6826 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);7146 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
6827 try writer.writeByte('(');7147 try writer.writeByte('(');
6828 try writer.writeAll(operation);7148 try writer.writeAll(operation);
6829 try writer.writeAll(")(");7149 try writer.writeAll(")(");
6830 try f.writeCValue(writer, operand, .FunctionArgument);7150 try f.writeCValue(writer, operand, .FunctionArgument);
7151 try v.elem(f, writer);
6831 try writer.writeAll(");\n");7152 try writer.writeAll(");\n");
7153 try v.end(f, inst, writer);
7154
6832 return local;7155 return local;
6833}7156}
68347157
...@@ -6838,23 +7161,32 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa...@@ -6838,23 +7161,32 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa
6838 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });7161 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6839 return .none;7162 return .none;
6840 }7163 }
7164
6841 const lhs = try f.resolveInst(bin_op.lhs);7165 const lhs = try f.resolveInst(bin_op.lhs);
6842 const rhs = try f.resolveInst(bin_op.rhs);7166 const rhs = try f.resolveInst(bin_op.rhs);
6843 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });7167 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
68447168
6845 const writer = f.object.writer();
6846 const inst_ty = f.air.typeOfIndex(inst);7169 const inst_ty = f.air.typeOfIndex(inst);
7170 const inst_scalar_ty = inst_ty.scalarType();
7171
7172 const writer = f.object.writer();
6847 const local = try f.allocLocal(inst, inst_ty);7173 const local = try f.allocLocal(inst, inst_ty);
7174 const v = try Vectorizer.start(f, inst, writer, inst_ty);
6848 try f.writeCValue(writer, local, .Other);7175 try f.writeCValue(writer, local, .Other);
7176 try v.elem(f, writer);
6849 try writer.writeAll(" = zig_libc_name_");7177 try writer.writeAll(" = zig_libc_name_");
6850 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);7178 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
6851 try writer.writeByte('(');7179 try writer.writeByte('(');
6852 try writer.writeAll(operation);7180 try writer.writeAll(operation);
6853 try writer.writeAll(")(");7181 try writer.writeAll(")(");
6854 try f.writeCValue(writer, lhs, .FunctionArgument);7182 try f.writeCValue(writer, lhs, .FunctionArgument);
7183 try v.elem(f, writer);
6855 try writer.writeAll(", ");7184 try writer.writeAll(", ");
6856 try f.writeCValue(writer, rhs, .FunctionArgument);7185 try f.writeCValue(writer, rhs, .FunctionArgument);
7186 try v.elem(f, writer);
6857 try writer.writeAll(");\n");7187 try writer.writeAll(");\n");
7188 try v.end(f, inst, writer);
7189
6858 return local;7190 return local;
6859}7191}
68607192
...@@ -6865,23 +7197,34 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6865,23 +7197,34 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
6865 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });7197 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
6866 return .none;7198 return .none;
6867 }7199 }
6868 const inst_ty = f.air.typeOfIndex(inst);7200
6869 const mulend1 = try f.resolveInst(bin_op.lhs);7201 const mulend1 = try f.resolveInst(bin_op.lhs);
6870 const mulend2 = try f.resolveInst(bin_op.rhs);7202 const mulend2 = try f.resolveInst(bin_op.rhs);
6871 const addend = try f.resolveInst(pl_op.operand);7203 const addend = try f.resolveInst(pl_op.operand);
6872 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });7204 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
7205
7206 const inst_ty = f.air.typeOfIndex(inst);
7207 const inst_scalar_ty = inst_ty.scalarType();
7208
6873 const writer = f.object.writer();7209 const writer = f.object.writer();
6874 const local = try f.allocLocal(inst, inst_ty);7210 const local = try f.allocLocal(inst, inst_ty);
7211 const v = try Vectorizer.start(f, inst, writer, inst_ty);
6875 try f.writeCValue(writer, local, .Other);7212 try f.writeCValue(writer, local, .Other);
7213 try v.elem(f, writer);
6876 try writer.writeAll(" = zig_libc_name_");7214 try writer.writeAll(" = zig_libc_name_");
6877 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);7215 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
6878 try writer.writeAll("(fma)(");7216 try writer.writeAll("(fma)(");
6879 try f.writeCValue(writer, mulend1, .FunctionArgument);7217 try f.writeCValue(writer, mulend1, .FunctionArgument);
7218 try v.elem(f, writer);
6880 try writer.writeAll(", ");7219 try writer.writeAll(", ");
6881 try f.writeCValue(writer, mulend2, .FunctionArgument);7220 try f.writeCValue(writer, mulend2, .FunctionArgument);
7221 try v.elem(f, writer);
6882 try writer.writeAll(", ");7222 try writer.writeAll(", ");
6883 try f.writeCValue(writer, addend, .FunctionArgument);7223 try f.writeCValue(writer, addend, .FunctionArgument);
7224 try v.elem(f, writer);
6884 try writer.writeAll(");\n");7225 try writer.writeAll(");\n");
7226 try v.end(f, inst, writer);
7227
6885 return local;7228 return local;
6886}7229}
68877230
...@@ -7083,6 +7426,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 {...@@ -7083,6 +7426,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 {
7083 } else unreachable;7426 } else unreachable;
7084}7427}
70857428
7429fn compareOperatorAbbrev(operator: std.math.CompareOperator) []const u8 {
7430 return switch (operator) {
7431 .lt => "lt",
7432 .lte => "le",
7433 .eq => "eq",
7434 .gte => "ge",
7435 .gt => "gt",
7436 .neq => "ne",
7437 };
7438}
7439
7440fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
7441 return switch (operator) {
7442 .lt => "<",
7443 .lte => "<=",
7444 .eq => "==",
7445 .gte => ">=",
7446 .gt => ">",
7447 .neq => "!=",
7448 };
7449}
7450
7086fn StringLiteral(comptime WriterType: type) type {7451fn StringLiteral(comptime WriterType: type) type {
7087 // MSVC has a length limit of 16380 per string literal (before concatenation)7452 // MSVC has a length limit of 16380 per string literal (before concatenation)
7088 const max_char_len = 4;7453 const max_char_len = 4;
...@@ -7171,30 +7536,33 @@ fn undefPattern(comptime IntType: type) IntType {...@@ -7171,30 +7536,33 @@ fn undefPattern(comptime IntType: type) IntType {
7171 return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3));7536 return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3));
7172}7537}
71737538
7174const FormatIntLiteralContext = struct { ty: Type, val: Value, mod: *Module, location: ?ValueRenderLocation = null };7539const FormatIntLiteralContext = struct {
7540 dg: *DeclGen,
7541 int_info: std.builtin.Type.Int,
7542 kind: CType.Kind,
7543 cty: CType,
7544 val: Value,
7545};
7175fn formatIntLiteral(7546fn formatIntLiteral(
7176 data: FormatIntLiteralContext,7547 data: FormatIntLiteralContext,
7177 comptime fmt: []const u8,7548 comptime fmt: []const u8,
7178 options: std.fmt.FormatOptions,7549 options: std.fmt.FormatOptions,
7179 writer: anytype,7550 writer: anytype,
7180) @TypeOf(writer).Error!void {7551) @TypeOf(writer).Error!void {
7181 const target = data.mod.getTarget();7552 const target = data.dg.module.getTarget();
7182 const int_info = data.ty.intInfo(target);
71837553
7184 const ExpectedContents = struct {7554 const ExpectedContents = struct {
7185 const base = 10;7555 const base = 10;
7186 const limbs_count_128 = BigInt.calcTwosCompLimbCount(128);7556 const bits = 128;
7187 const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base);7557 const limbs_count = BigInt.calcTwosCompLimbCount(bits);
7188 const worst_case_int = BigInt.Const{
7189 .limbs = &([1]BigIntLimb{std.math.maxInt(BigIntLimb)} ** expected_needed_limbs_count),
7190 .positive = false,
7191 };
71927558
7193 undef_limbs: [limbs_count_128]BigIntLimb,7559 undef_limbs: [limbs_count]BigIntLimb,
7194 wrap_limbs: [limbs_count_128]BigIntLimb,7560 wrap_limbs: [limbs_count]BigIntLimb,
7561 to_string_buf: [bits]u8,
7562 to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,
7195 };7563 };
7196 var stack align(@alignOf(ExpectedContents)) =7564 var stack align(@alignOf(ExpectedContents)) =
7197 std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa);7565 std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);
7198 const allocator = stack.get();7566 const allocator = stack.get();
71997567
7200 var undef_limbs: []BigIntLimb = &.{};7568 var undef_limbs: []BigIntLimb = &.{};
...@@ -7202,7 +7570,7 @@ fn formatIntLiteral(...@@ -7202,7 +7570,7 @@ fn formatIntLiteral(
72027570
7203 var int_buf: Value.BigIntSpace = undefined;7571 var int_buf: Value.BigIntSpace = undefined;
7204 const int = if (data.val.isUndefDeep()) blk: {7572 const int = if (data.val.isUndefDeep()) blk: {
7205 undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(int_info.bits));7573 undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits));
7206 std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));7574 std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));
72077575
7208 var undef_int = BigInt.Mutable{7576 var undef_int = BigInt.Mutable{
...@@ -7210,164 +7578,193 @@ fn formatIntLiteral(...@@ -7210,164 +7578,193 @@ fn formatIntLiteral(
7210 .len = undef_limbs.len,7578 .len = undef_limbs.len,
7211 .positive = true,7579 .positive = true,
7212 };7580 };
7213 undef_int.truncate(undef_int.toConst(), int_info.signedness, int_info.bits);7581 undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits);
7214 break :blk undef_int.toConst();7582 break :blk undef_int.toConst();
7215 } else data.val.toBigInt(&int_buf, target);7583 } else data.val.toBigInt(&int_buf, target);
7216 assert(int.fitsInTwosComp(int_info.signedness, int_info.bits));7584 assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits));
72177585
7218 const c_bits = toCIntBits(int_info.bits) orelse unreachable;7586 const c_bits = @intCast(usize, data.cty.byteSize(data.dg.ctypes.set, target) * 8);
7219 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;7587 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;
7220 const one = BigInt.Mutable.init(&one_limbs, 1).toConst();7588 const one = BigInt.Mutable.init(&one_limbs, 1).toConst();
72217589
7222 const wrap_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits));7590 var wrap = BigInt.Mutable{
7223 defer allocator.free(wrap_limbs);7591 .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)),
7224 var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined };7592 .len = undefined,
7225 if (wrap.addWrap(int, one, int_info.signedness, c_bits) or7593 .positive = undefined,
7226 int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits))7594 };
7227 {7595 defer allocator.free(wrap.limbs);
7228 const abbrev = switch (data.ty.tag()) {7596
7229 .c_short, .c_ushort => "SHRT",7597 const c_limb_info: struct {
7230 .c_int, .c_uint => "INT",7598 cty: CType,
7231 .c_long, .c_ulong => "LONG",7599 count: usize,
7232 .c_longlong, .c_ulonglong => "LLONG",7600 endian: std.builtin.Endian,
7233 .isize, .usize => "INTPTR",7601 homogeneous: bool,
7234 else => return writer.print("zig_{s}Int_{c}{d}", .{7602 } = switch (data.cty.tag()) {
7235 if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits,7603 else => .{
7236 }),7604 .cty = CType.initTag(.void),
7237 };7605 .count = 1,
7238 if (int_info.signedness == .unsigned) try writer.writeByte('U');7606 .endian = .Little,
7239 return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" });7607 .homogeneous = true,
7240 }
7241
7242 var use_twos_comp = false;
7243 if (!int.positive) {
7244 if (c_bits > 64) {
7245 // TODO: Can this be done for decimal literals as well?
7246 if (fmt.len == 1 and fmt[0] != 'd') {
7247 use_twos_comp = true;
7248 } else {
7249 // TODO: Use fmtIntLiteral for 0?
7250 try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits });
7251 }
7252 } else {
7253 try writer.writeByte('-');
7254 }
7255 }
7256
7257 switch (data.ty.tag()) {
7258 .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {},
7259 else => {
7260 if (int_info.bits <= 64) {
7261 try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) {
7262 .signed => "",
7263 .unsigned => "U",
7264 }, c_bits });
7265 } else if (data.location != null and data.location.? == .StaticInitializer) {
7266 // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers
7267 try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
7268 } else {
7269 try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
7270 }
7271 },7608 },
7272 }7609 .zig_u128, .zig_i128 => .{
7610 .cty = CType.initTag(.uint64_t),
7611 .count = 2,
7612 .endian = .Big,
7613 .homogeneous = false,
7614 },
7615 .array => info: {
7616 const array_data = data.cty.castTag(.array).?.data;
7617 break :info .{
7618 .cty = data.dg.indexToCType(array_data.elem_type),
7619 .count = @intCast(usize, array_data.len),
7620 .endian = target.cpu.arch.endian(),
7621 .homogeneous = true,
7622 };
7623 },
7624 };
7625 if (c_limb_info.count == 1) {
7626 if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
7627 data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
7628 return writer.print("{s}_{s}", .{
7629 data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{
7630 if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
7631 }),
7632 if (int.positive) "MAX" else "MIN",
7633 });
7634
7635 if (!int.positive) try writer.writeByte('-');
7636 try data.cty.renderLiteralPrefix(writer, data.kind);
72737637
7274 const limbs_count_64 = @divExact(64, @bitSizeOf(BigIntLimb));7638 const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) {
7275 if (c_bits <= 64) {7639 0 => .{ .base = 10 },
7276 var base: u8 = undefined;
7277 var case: std.fmt.Case = undefined;
7278 switch (fmt.len) {
7279 0 => base = 10,
7280 1 => switch (fmt[0]) {7640 1 => switch (fmt[0]) {
7281 'b' => {7641 'b' => style: {
7282 base = 2;
7283 try writer.writeAll("0b");7642 try writer.writeAll("0b");
7643 break :style .{ .base = 2 };
7284 },7644 },
7285 'o' => {7645 'o' => style: {
7286 base = 8;
7287 try writer.writeByte('0');7646 try writer.writeByte('0');
7647 break :style .{ .base = 8 };
7288 },7648 },
7289 'd' => base = 10,7649 'd' => .{ .base = 10 },
7290 'x' => {7650 'x', 'X' => |base| style: {
7291 base = 16;
7292 case = .lower;
7293 try writer.writeAll("0x");
7294 },
7295 'X' => {
7296 base = 16;
7297 case = .upper;
7298 try writer.writeAll("0x");7651 try writer.writeAll("0x");
7652 break :style .{ .base = 16, .case = switch (base) {
7653 'x' => .lower,
7654 'X' => .upper,
7655 else => unreachable,
7656 } };
7299 },7657 },
7300 else => @compileError("Invalid fmt: " ++ fmt),7658 else => @compileError("Invalid fmt: " ++ fmt),
7301 },7659 },
7302 else => @compileError("Invalid fmt: " ++ fmt),7660 else => @compileError("Invalid fmt: " ++ fmt),
7303 }7661 };
73047662
7305 var str: [64]u8 = undefined;7663 const string = try int.abs().toStringAlloc(allocator, style.base, style.case);
7306 var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]BigIntLimb = undefined;7664 defer allocator.free(string);
7307 try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]);7665 try writer.writeAll(string);
7308 } else {7666 } else {
7309 assert(c_bits == 128);7667 try data.cty.renderLiteralPrefix(writer, data.kind);
7310 const split = std.math.min(int.limbs.len, limbs_count_64);7668 wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits);
7311 var twos_comp_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined;7669 std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
73127670 wrap.len = wrap.limbs.len;
7313 // Adding a negation in the C code before the doesn't work in all cases:7671 const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
7314 // - struct versions would require an extra zig_sub_ call to negate, which wouldn't work in constant expressions7672
7315 // - negating the f80 int representation (i128) doesn't make sense7673 var c_limb_int_info = std.builtin.Type.Int{
7316 // Instead we write out the literal as a negative number in twos complement7674 .signedness = undefined,
7317 var limbs = int.limbs;7675 .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)),
73187676 };
7319 if (use_twos_comp) {7677 var c_limb_cty: CType = undefined;
7320 var twos_comp = BigInt.Mutable{7678
7321 .limbs = &twos_comp_limbs,7679 var limb_offset: usize = 0;
7322 .positive = undefined,7680 const most_significant_limb_i = wrap.len - limbs_per_c_limb;
7681 while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) {
7682 const limb_i = switch (c_limb_info.endian) {
7683 .Little => limb_offset,
7684 .Big => most_significant_limb_i - limb_offset,
7685 };
7686 var c_limb_mut = BigInt.Mutable{
7687 .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb],
7323 .len = undefined,7688 .len = undefined,
7689 .positive = true,
7690 };
7691 c_limb_mut.normalize(limbs_per_c_limb);
7692
7693 if (limb_i == most_significant_limb_i and
7694 !c_limb_info.homogeneous and data.int_info.signedness == .signed)
7695 {
7696 // most significant limb is actually signed
7697 c_limb_int_info.signedness = .signed;
7698 c_limb_cty = c_limb_info.cty.toSigned();
7699
7700 c_limb_mut.positive = wrap.positive;
7701 c_limb_mut.truncate(
7702 c_limb_mut.toConst(),
7703 .signed,
7704 data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb),
7705 );
7706 } else {
7707 c_limb_int_info.signedness = .unsigned;
7708 c_limb_cty = c_limb_info.cty;
7709 }
7710 var c_limb_val_pl = Value.Payload.BigInt{
7711 .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative },
7712 .data = c_limb_mut.limbs[0..c_limb_mut.len],
7324 };7713 };
73257714
7326 twos_comp.convertToTwosComplement(int, .signed, int_info.bits);7715 if (limb_offset > 0) try writer.writeAll(", ");
7327 limbs = twos_comp.limbs;7716 try formatIntLiteral(.{
7717 .dg = data.dg,
7718 .int_info = c_limb_int_info,
7719 .kind = data.kind,
7720 .cty = c_limb_cty,
7721 .val = Value.initPayload(&c_limb_val_pl.base),
7722 }, fmt, options, writer);
7328 }7723 }
7724 }
7725 try data.cty.renderLiteralSuffix(writer);
7726}
73297727
7330 var upper_pl = Value.Payload.BigInt{7728const Vectorizer = struct {
7331 .base = .{ .tag = .int_big_positive },7729 index: CValue = .none,
7332 .data = limbs[split..],
7333 };
7334 const upper_val = Value.initPayload(&upper_pl.base);
7335 try formatIntLiteral(.{
7336 .ty = switch (int_info.signedness) {
7337 .unsigned => Type.u64,
7338 .signed => if (use_twos_comp) Type.u64 else Type.i64,
7339 },
7340 .val = upper_val,
7341 .mod = data.mod,
7342 }, fmt, options, writer);
73437730
7344 try writer.writeAll(", ");7731 pub fn start(f: *Function, inst: Air.Inst.Index, writer: anytype, ty: Type) !Vectorizer {
7732 return if (ty.zigTypeTag() == .Vector) index: {
7733 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = ty.vectorLen() };
73457734
7346 var lower_pl = Value.Payload.BigInt{7735 const local = try f.allocLocal(inst, Type.usize);
7347 .base = .{ .tag = .int_big_positive },
7348 .data = limbs[0..split],
7349 };
7350 const lower_val = Value.initPayload(&lower_pl.base);
7351 try formatIntLiteral(.{
7352 .ty = Type.u64,
7353 .val = lower_val,
7354 .mod = data.mod,
7355 }, fmt, options, writer);
73567736
7357 if (!int.positive and c_bits > 64 and !use_twos_comp) try writer.writeByte(')');7737 try writer.writeAll("for (");
7358 return writer.writeByte(')');7738 try f.writeCValue(writer, local, .Other);
7739 try writer.print(" = {d}; ", .{try f.fmtIntLiteral(Type.usize, Value.zero)});
7740 try f.writeCValue(writer, local, .Other);
7741 try writer.print(" < {d}; ", .{
7742 try f.fmtIntLiteral(Type.usize, Value.initPayload(&len_pl.base)),
7743 });
7744 try f.writeCValue(writer, local, .Other);
7745 try writer.print(" += {d}) {{\n", .{try f.fmtIntLiteral(Type.usize, Value.one)});
7746 f.object.indent_writer.pushIndent();
7747
7748 break :index .{ .index = local };
7749 } else .{};
7359 }7750 }
73607751
7361 switch (data.ty.tag()) {7752 pub fn elem(self: Vectorizer, f: *Function, writer: anytype) !void {
7362 .c_short, .c_ushort, .c_int => {},7753 if (self.index != .none) {
7363 .c_uint => try writer.writeAll("u"),7754 try writer.writeByte('[');
7364 .c_long => try writer.writeAll("l"),7755 try f.writeCValue(writer, self.index, .Other);
7365 .c_ulong => try writer.writeAll("ul"),7756 try writer.writeByte(']');
7366 .c_longlong => try writer.writeAll("ll"),7757 }
7367 .c_ulonglong => try writer.writeAll("ull"),
7368 else => try writer.writeByte(')'),
7369 }7758 }
7370}7759
7760 pub fn end(self: Vectorizer, f: *Function, inst: Air.Inst.Index, writer: anytype) !void {
7761 if (self.index != .none) {
7762 f.object.indent_writer.popIndent();
7763 try writer.writeAll("}\n");
7764 try freeLocal(f, inst, self.index.new_local, 0);
7765 }
7766 }
7767};
73717768
7372fn isByRef(ty: Type) bool {7769fn isByRef(ty: Type) bool {
7373 _ = ty;7770 _ = ty;
src/codegen/c/type.zig+449-32
...@@ -496,6 +496,427 @@ pub const CType = extern union {...@@ -496,6 +496,427 @@ pub const CType = extern union {
496 }496 }
497 };497 };
498498
499 pub fn isBool(self: CType) bool {
500 return switch (self.tag()) {
501 ._Bool,
502 .bool,
503 => true,
504 else => false,
505 };
506 }
507
508 pub fn isInteger(self: CType) bool {
509 return switch (self.tag()) {
510 .char,
511 .@"signed char",
512 .short,
513 .int,
514 .long,
515 .@"long long",
516 .@"unsigned char",
517 .@"unsigned short",
518 .@"unsigned int",
519 .@"unsigned long",
520 .@"unsigned long long",
521 .size_t,
522 .ptrdiff_t,
523 .uint8_t,
524 .int8_t,
525 .uint16_t,
526 .int16_t,
527 .uint32_t,
528 .int32_t,
529 .uint64_t,
530 .int64_t,
531 .uintptr_t,
532 .intptr_t,
533 .zig_u128,
534 .zig_i128,
535 => true,
536 else => false,
537 };
538 }
539
540 pub fn signedness(self: CType) ?std.builtin.Signedness {
541 return switch (self.tag()) {
542 .char => null, // unknown signedness
543 .@"signed char",
544 .short,
545 .int,
546 .long,
547 .@"long long",
548 .ptrdiff_t,
549 .int8_t,
550 .int16_t,
551 .int32_t,
552 .int64_t,
553 .intptr_t,
554 .zig_i128,
555 => .signed,
556 .@"unsigned char",
557 .@"unsigned short",
558 .@"unsigned int",
559 .@"unsigned long",
560 .@"unsigned long long",
561 .size_t,
562 .uint8_t,
563 .uint16_t,
564 .uint32_t,
565 .uint64_t,
566 .uintptr_t,
567 .zig_u128,
568 => .unsigned,
569 else => unreachable,
570 };
571 }
572
573 pub fn isFloat(self: CType) bool {
574 return switch (self.tag()) {
575 .float,
576 .double,
577 .@"long double",
578 .zig_f16,
579 .zig_f32,
580 .zig_f64,
581 .zig_f80,
582 .zig_f128,
583 .zig_c_longdouble,
584 => true,
585 else => false,
586 };
587 }
588
589 pub fn isPointer(self: CType) bool {
590 return switch (self.tag()) {
591 .pointer,
592 .pointer_const,
593 .pointer_volatile,
594 .pointer_const_volatile,
595 => true,
596 else => false,
597 };
598 }
599
600 pub fn isFunction(self: CType) bool {
601 return switch (self.tag()) {
602 .function,
603 .varargs_function,
604 => true,
605 else => false,
606 };
607 }
608
609 pub fn toSigned(self: CType) CType {
610 return CType.initTag(switch (self.tag()) {
611 .char, .@"signed char", .@"unsigned char" => .@"signed char",
612 .short, .@"unsigned short" => .short,
613 .int, .@"unsigned int" => .int,
614 .long, .@"unsigned long" => .long,
615 .@"long long", .@"unsigned long long" => .@"long long",
616 .size_t, .ptrdiff_t => .ptrdiff_t,
617 .uint8_t, .int8_t => .int8_t,
618 .uint16_t, .int16_t => .int16_t,
619 .uint32_t, .int32_t => .int32_t,
620 .uint64_t, .int64_t => .int64_t,
621 .uintptr_t, .intptr_t => .intptr_t,
622 .zig_u128, .zig_i128 => .zig_i128,
623 .float,
624 .double,
625 .@"long double",
626 .zig_f16,
627 .zig_f32,
628 .zig_f80,
629 .zig_f128,
630 .zig_c_longdouble,
631 => |t| t,
632 else => unreachable,
633 });
634 }
635
636 pub fn toUnsigned(self: CType) CType {
637 return CType.initTag(switch (self.tag()) {
638 .char, .@"signed char", .@"unsigned char" => .@"unsigned char",
639 .short, .@"unsigned short" => .@"unsigned short",
640 .int, .@"unsigned int" => .@"unsigned int",
641 .long, .@"unsigned long" => .@"unsigned long",
642 .@"long long", .@"unsigned long long" => .@"unsigned long long",
643 .size_t, .ptrdiff_t => .size_t,
644 .uint8_t, .int8_t => .uint8_t,
645 .uint16_t, .int16_t => .uint16_t,
646 .uint32_t, .int32_t => .uint32_t,
647 .uint64_t, .int64_t => .uint64_t,
648 .uintptr_t, .intptr_t => .uintptr_t,
649 .zig_u128, .zig_i128 => .zig_u128,
650 else => unreachable,
651 });
652 }
653
654 pub fn toSignedness(self: CType, s: std.builtin.Signedness) CType {
655 return switch (s) {
656 .unsigned => self.toUnsigned(),
657 .signed => self.toSigned(),
658 };
659 }
660
661 pub fn getStandardDefineAbbrev(self: CType) ?[]const u8 {
662 return switch (self.tag()) {
663 .char => "CHAR",
664 .@"signed char" => "SCHAR",
665 .short => "SHRT",
666 .int => "INT",
667 .long => "LONG",
668 .@"long long" => "LLONG",
669 .@"unsigned char" => "UCHAR",
670 .@"unsigned short" => "USHRT",
671 .@"unsigned int" => "UINT",
672 .@"unsigned long" => "ULONG",
673 .@"unsigned long long" => "ULLONG",
674 .float => "FLT",
675 .double => "DBL",
676 .@"long double" => "LDBL",
677 .size_t => "SIZE",
678 .ptrdiff_t => "PTRDIFF",
679 .uint8_t => "UINT8",
680 .int8_t => "INT8",
681 .uint16_t => "UINT16",
682 .int16_t => "INT16",
683 .uint32_t => "UINT32",
684 .int32_t => "INT32",
685 .uint64_t => "UINT64",
686 .int64_t => "INT64",
687 .uintptr_t => "UINTPTR",
688 .intptr_t => "INTPTR",
689 else => null,
690 };
691 }
692
693 pub fn renderLiteralPrefix(self: CType, writer: anytype, kind: Kind) @TypeOf(writer).Error!void {
694 switch (self.tag()) {
695 .void => unreachable,
696 ._Bool,
697 .char,
698 .@"signed char",
699 .short,
700 .@"unsigned short",
701 .bool,
702 .size_t,
703 .ptrdiff_t,
704 .uintptr_t,
705 .intptr_t,
706 => |t| switch (kind) {
707 else => try writer.print("({s})", .{@tagName(t)}),
708 .global => {},
709 },
710 .int,
711 .long,
712 .@"long long",
713 .@"unsigned char",
714 .@"unsigned int",
715 .@"unsigned long",
716 .@"unsigned long long",
717 .float,
718 .double,
719 .@"long double",
720 => {},
721 .uint8_t,
722 .int8_t,
723 .uint16_t,
724 .int16_t,
725 .uint32_t,
726 .int32_t,
727 .uint64_t,
728 .int64_t,
729 => try writer.print("{s}_C(", .{self.getStandardDefineAbbrev().?}),
730 .zig_u128,
731 .zig_i128,
732 .zig_f16,
733 .zig_f32,
734 .zig_f64,
735 .zig_f80,
736 .zig_f128,
737 .zig_c_longdouble,
738 => |t| try writer.print("zig_{s}_{s}(", .{
739 switch (kind) {
740 else => "make",
741 .global => "init",
742 },
743 @tagName(t)["zig_".len..],
744 }),
745 .pointer,
746 .pointer_const,
747 .pointer_volatile,
748 .pointer_const_volatile,
749 => unreachable,
750 .array,
751 .vector,
752 => try writer.writeByte('{'),
753 .fwd_anon_struct,
754 .fwd_anon_union,
755 .fwd_struct,
756 .fwd_union,
757 .unnamed_struct,
758 .unnamed_union,
759 .packed_unnamed_struct,
760 .packed_unnamed_union,
761 .anon_struct,
762 .anon_union,
763 .@"struct",
764 .@"union",
765 .packed_struct,
766 .packed_union,
767 .function,
768 .varargs_function,
769 => unreachable,
770 }
771 }
772
773 pub fn renderLiteralSuffix(self: CType, writer: anytype) @TypeOf(writer).Error!void {
774 switch (self.tag()) {
775 .void => unreachable,
776 ._Bool => {},
777 .char,
778 .@"signed char",
779 .short,
780 .int,
781 => {},
782 .long => try writer.writeByte('l'),
783 .@"long long" => try writer.writeAll("ll"),
784 .@"unsigned char",
785 .@"unsigned short",
786 .@"unsigned int",
787 => try writer.writeByte('u'),
788 .@"unsigned long",
789 .size_t,
790 .uintptr_t,
791 => try writer.writeAll("ul"),
792 .@"unsigned long long" => try writer.writeAll("ull"),
793 .float => try writer.writeByte('f'),
794 .double => {},
795 .@"long double" => try writer.writeByte('l'),
796 .bool,
797 .ptrdiff_t,
798 .intptr_t,
799 => {},
800 .uint8_t,
801 .int8_t,
802 .uint16_t,
803 .int16_t,
804 .uint32_t,
805 .int32_t,
806 .uint64_t,
807 .int64_t,
808 .zig_u128,
809 .zig_i128,
810 .zig_f16,
811 .zig_f32,
812 .zig_f64,
813 .zig_f80,
814 .zig_f128,
815 .zig_c_longdouble,
816 => try writer.writeByte(')'),
817 .pointer,
818 .pointer_const,
819 .pointer_volatile,
820 .pointer_const_volatile,
821 => unreachable,
822 .array,
823 .vector,
824 => try writer.writeByte('}'),
825 .fwd_anon_struct,
826 .fwd_anon_union,
827 .fwd_struct,
828 .fwd_union,
829 .unnamed_struct,
830 .unnamed_union,
831 .packed_unnamed_struct,
832 .packed_unnamed_union,
833 .anon_struct,
834 .anon_union,
835 .@"struct",
836 .@"union",
837 .packed_struct,
838 .packed_union,
839 .function,
840 .varargs_function,
841 => unreachable,
842 }
843 }
844
845 pub fn floatActiveBits(self: CType, target: Target) u16 {
846 return switch (self.tag()) {
847 .float => target.c_type_bit_size(.float),
848 .double => target.c_type_bit_size(.double),
849 .@"long double", .zig_c_longdouble => target.c_type_bit_size(.longdouble),
850 .zig_f16 => 16,
851 .zig_f32 => 32,
852 .zig_f64 => 64,
853 .zig_f80 => 80,
854 .zig_f128 => 128,
855 else => unreachable,
856 };
857 }
858
859 pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 {
860 return switch (self.tag()) {
861 .void => 0,
862 .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1,
863 .short => target.c_type_byte_size(.short),
864 .int => target.c_type_byte_size(.int),
865 .long => target.c_type_byte_size(.long),
866 .@"long long" => target.c_type_byte_size(.longlong),
867 .@"unsigned short" => target.c_type_byte_size(.ushort),
868 .@"unsigned int" => target.c_type_byte_size(.uint),
869 .@"unsigned long" => target.c_type_byte_size(.ulong),
870 .@"unsigned long long" => target.c_type_byte_size(.ulonglong),
871 .float => target.c_type_byte_size(.float),
872 .double => target.c_type_byte_size(.double),
873 .@"long double" => target.c_type_byte_size(.longdouble),
874 .size_t,
875 .ptrdiff_t,
876 .uintptr_t,
877 .intptr_t,
878 .pointer,
879 .pointer_const,
880 .pointer_volatile,
881 .pointer_const_volatile,
882 => @divExact(target.cpu.arch.ptrBitWidth(), 8),
883 .uint16_t, .int16_t, .zig_f16 => 2,
884 .uint32_t, .int32_t, .zig_f32 => 4,
885 .uint64_t, .int64_t, .zig_f64 => 8,
886 .zig_u128, .zig_i128, .zig_f128 => 16,
887 .zig_f80 => if (target.c_type_bit_size(.longdouble) == 80)
888 target.c_type_byte_size(.longdouble)
889 else
890 16,
891 .zig_c_longdouble => target.c_type_byte_size(.longdouble),
892
893 .array,
894 .vector,
895 => {
896 const data = self.cast(Payload.Sequence).?.data;
897 return data.len * store.indexToCType(data.elem_type).byteSize(store, target);
898 },
899
900 .fwd_anon_struct,
901 .fwd_anon_union,
902 .fwd_struct,
903 .fwd_union,
904 .unnamed_struct,
905 .unnamed_union,
906 .packed_unnamed_struct,
907 .packed_unnamed_union,
908 .anon_struct,
909 .anon_union,
910 .@"struct",
911 .@"union",
912 .packed_struct,
913 .packed_union,
914 .function,
915 .varargs_function,
916 => unreachable,
917 };
918 }
919
499 pub fn isPacked(self: CType) bool {920 pub fn isPacked(self: CType) bool {
500 return switch (self.tag()) {921 return switch (self.tag()) {
501 else => false,922 else => false,
...@@ -787,26 +1208,26 @@ pub const CType = extern union {...@@ -787,26 +1208,26 @@ pub const CType = extern union {
787 };1208 };
788 }1209 }
7891210
790 fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag {1211 fn tagFromIntInfo(int_info: std.builtin.Type.Int) Tag {
791 return switch (bits) {1212 return switch (int_info.bits) {
792 0 => .void,1213 0 => .void,
793 1...8 => switch (signedness) {1214 1...8 => switch (int_info.signedness) {
794 .unsigned => .uint8_t,1215 .unsigned => .uint8_t,
795 .signed => .int8_t,1216 .signed => .int8_t,
796 },1217 },
797 9...16 => switch (signedness) {1218 9...16 => switch (int_info.signedness) {
798 .unsigned => .uint16_t,1219 .unsigned => .uint16_t,
799 .signed => .int16_t,1220 .signed => .int16_t,
800 },1221 },
801 17...32 => switch (signedness) {1222 17...32 => switch (int_info.signedness) {
802 .unsigned => .uint32_t,1223 .unsigned => .uint32_t,
803 .signed => .int32_t,1224 .signed => .int32_t,
804 },1225 },
805 33...64 => switch (signedness) {1226 33...64 => switch (int_info.signedness) {
806 .unsigned => .uint64_t,1227 .unsigned => .uint64_t,
807 .signed => .int64_t,1228 .signed => .int64_t,
808 },1229 },
809 65...128 => switch (signedness) {1230 65...128 => switch (int_info.signedness) {
810 .unsigned => .zig_u128,1231 .unsigned => .zig_u128,
811 .signed => .zig_i128,1232 .signed => .zig_i128,
812 },1233 },
...@@ -945,31 +1366,27 @@ pub const CType = extern union {...@@ -945,31 +1366,27 @@ pub const CType = extern union {
945 .c_ulong => self.init(.@"unsigned long"),1366 .c_ulong => self.init(.@"unsigned long"),
946 .c_longlong => self.init(.@"long long"),1367 .c_longlong => self.init(.@"long long"),
947 .c_ulonglong => self.init(.@"unsigned long long"),1368 .c_ulonglong => self.init(.@"unsigned long long"),
948 else => {1369 else => switch (tagFromIntInfo(ty.intInfo(target))) {
949 const info = ty.intInfo(target);1370 .void => unreachable,
950 const t = tagFromIntInfo(info.signedness, info.bits);1371 else => |t| self.init(t),
951 switch (t) {1372 .array => switch (kind) {
952 .void => unreachable,1373 .forward, .complete, .global => {
953 else => self.init(t),1374 const abi_size = ty.abiSize(target);
954 .array => switch (kind) {1375 const abi_align = ty.abiAlignment(target);
955 .forward, .complete, .global => {1376 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
956 const abi_size = ty.abiSize(target);1377 .len = @divExact(abi_size, abi_align),
957 const abi_align = ty.abiAlignment(target);1378 .elem_type = tagFromIntInfo(.{
958 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{1379 .signedness = .unsigned,
959 .len = @divExact(abi_size, abi_align),1380 .bits = @intCast(u16, abi_align * 8),
960 .elem_type = tagFromIntInfo(1381 }).toIndex(),
961 .unsigned,1382 } } };
962 @intCast(u16, abi_align * 8),1383 self.value = .{ .cty = initPayload(&self.storage.seq) };
963 ).toIndex(),
964 } } };
965 self.value = .{ .cty = initPayload(&self.storage.seq) };
966 },
967 .forward_parameter,
968 .parameter,
969 => try self.initArrayParameter(ty, kind, lookup),
970 .payload => unreachable,
971 },1384 },
972 }1385 .forward_parameter,
1386 .parameter,
1387 => try self.initArrayParameter(ty, kind, lookup),
1388 .payload => unreachable,
1389 },
973 },1390 },
974 } else switch (ty.zigTypeTag()) {1391 } else switch (ty.zigTypeTag()) {
975 .Frame => unreachable,1392 .Frame => unreachable,
...@@ -1048,7 +1465,7 @@ pub const CType = extern union {...@@ -1048,7 +1465,7 @@ pub const CType = extern union {
1048 .base = .{ .tag = .int_unsigned },1465 .base = .{ .tag = .int_unsigned },
1049 .data = info.host_size * 8,1466 .data = info.host_size * 8,
1050 };1467 };
1051 const pointee_ty = if (info.host_size > 0)1468 const pointee_ty = if (info.host_size > 0 and info.vector_index == .none)
1052 Type.initPayload(&host_int_pl.base)1469 Type.initPayload(&host_int_pl.base)
1053 else1470 else
1054 info.pointee_type;1471 info.pointee_type;
src/codegen/llvm.zig+10-1
...@@ -1778,7 +1778,7 @@ pub const Object = struct {...@@ -1778,7 +1778,7 @@ pub const Object = struct {
1778 if (ty.optionalReprIsPayload()) {1778 if (ty.optionalReprIsPayload()) {
1779 const ptr_di_ty = try o.lowerDebugType(child_ty, resolve);1779 const ptr_di_ty = try o.lowerDebugType(child_ty, resolve);
1780 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.1780 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
1781 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module });1781 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module });
1782 return ptr_di_ty;1782 return ptr_di_ty;
1783 }1783 }
17841784
...@@ -4595,6 +4595,7 @@ pub const FuncGen = struct {...@@ -4595,6 +4595,7 @@ pub const FuncGen = struct {
4595 .block => try self.airBlock(inst),4595 .block => try self.airBlock(inst),
4596 .br => try self.airBr(inst),4596 .br => try self.airBr(inst),
4597 .switch_br => try self.airSwitchBr(inst),4597 .switch_br => try self.airSwitchBr(inst),
4598 .trap => try self.airTrap(inst),
4598 .breakpoint => try self.airBreakpoint(inst),4599 .breakpoint => try self.airBreakpoint(inst),
4599 .ret_addr => try self.airRetAddr(inst),4600 .ret_addr => try self.airRetAddr(inst),
4600 .frame_addr => try self.airFrameAddress(inst),4601 .frame_addr => try self.airFrameAddress(inst),
...@@ -8261,6 +8262,14 @@ pub const FuncGen = struct {...@@ -8261,6 +8262,14 @@ pub const FuncGen = struct {
8261 return fg.load(ptr, ptr_ty);8262 return fg.load(ptr, ptr_ty);
8262 }8263 }
82638264
8265 fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8266 _ = inst;
8267 const llvm_fn = self.getIntrinsic("llvm.trap", &.{});
8268 _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .Cold, .Auto, "");
8269 _ = self.builder.buildUnreachable();
8270 return null;
8271 }
8272
8264 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8273 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8265 _ = inst;8274 _ = inst;
8266 const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});8275 const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
src/glibc.zig+11-8
...@@ -161,7 +161,7 @@ pub const CRTFile = enum {...@@ -161,7 +161,7 @@ pub const CRTFile = enum {
161 libc_nonshared_a,161 libc_nonshared_a,
162};162};
163163
164pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {164pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
165 if (!build_options.have_llvm) {165 if (!build_options.have_llvm) {
166 return error.ZigCompilerNotBuiltWithLLVMExtensions;166 return error.ZigCompilerNotBuiltWithLLVMExtensions;
167 }167 }
...@@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
196 "-DASSEMBLER",196 "-DASSEMBLER",
197 "-Wa,--noexecstack",197 "-Wa,--noexecstack",
198 });198 });
199 return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{199 return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{
200 .{200 .{
201 .src_path = try start_asm_path(comp, arena, "crti.S"),201 .src_path = try start_asm_path(comp, arena, "crti.S"),
202 .cache_exempt_flags = args.items,202 .cache_exempt_flags = args.items,
...@@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
215 "-DASSEMBLER",215 "-DASSEMBLER",
216 "-Wa,--noexecstack",216 "-Wa,--noexecstack",
217 });217 });
218 return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{218 return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{
219 .{219 .{
220 .src_path = try start_asm_path(comp, arena, "crtn.S"),220 .src_path = try start_asm_path(comp, arena, "crtn.S"),
221 .cache_exempt_flags = args.items,221 .cache_exempt_flags = args.items,
...@@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
265 .cache_exempt_flags = args.items,265 .cache_exempt_flags = args.items,
266 };266 };
267 };267 };
268 return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_o, abi_note_o });268 return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &.{
269 start_o, abi_note_o,
270 });
269 },271 },
270 .libc_nonshared_a => {272 .libc_nonshared_a => {
271 const s = path.sep_str;273 const s = path.sep_str;
...@@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
366 files_index += 1;368 files_index += 1;
367 }369 }
368 const files = files_buf[0..files_index];370 const files = files_buf[0..files_index];
369 return comp.build_crt_file("c_nonshared", .Lib, files);371 return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", prog_node, files);
370 },372 },
371 }373 }
372}374}
...@@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct {...@@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct {
639641
640const all_map_basename = "all.map";642const all_map_basename = "all.map";
641643
642pub fn buildSharedObjects(comp: *Compilation) !void {644pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !void {
643 const tracy = trace(@src());645 const tracy = trace(@src());
644 defer tracy.end();646 defer tracy.end();
645647
...@@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void {...@@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
1023 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;1025 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
1024 try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items);1026 try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items);
10251027
1026 try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib);1028 try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node);
1027 }1029 }
10281030
1029 man.writeManifest() catch |err| {1031 man.writeManifest() catch |err| {
...@@ -1046,6 +1048,7 @@ fn buildSharedLib(...@@ -1046,6 +1048,7 @@ fn buildSharedLib(
1046 bin_directory: Compilation.Directory,1048 bin_directory: Compilation.Directory,
1047 asm_file_basename: []const u8,1049 asm_file_basename: []const u8,
1048 lib: Lib,1050 lib: Lib,
1051 prog_node: *std.Progress.Node,
1049) !void {1052) !void {
1050 const tracy = trace(@src());1053 const tracy = trace(@src());
1051 defer tracy.end();1054 defer tracy.end();
...@@ -1105,7 +1108,7 @@ fn buildSharedLib(...@@ -1105,7 +1108,7 @@ fn buildSharedLib(
1105 });1108 });
1106 defer sub_compilation.destroy();1109 defer sub_compilation.destroy();
11071110
1108 try sub_compilation.updateSubCompilation();1111 try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node);
1109}1112}
11101113
1111// Return true if glibc has crti/crtn sources for that architecture.1114// Return true if glibc has crti/crtn sources for that architecture.
src/libcxx.zig+4-4
...@@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{...@@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{
96 "src/verbose_abort.cpp",96 "src/verbose_abort.cpp",
97};97};
9898
99pub fn buildLibCXX(comp: *Compilation) !void {99pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void {
100 if (!build_options.have_llvm) {100 if (!build_options.have_llvm) {
101 return error.ZigCompilerNotBuiltWithLLVMExtensions;101 return error.ZigCompilerNotBuiltWithLLVMExtensions;
102 }102 }
...@@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
258 });258 });
259 defer sub_compilation.destroy();259 defer sub_compilation.destroy();
260260
261 try sub_compilation.updateSubCompilation();261 try comp.updateSubCompilation(sub_compilation, .libcxx, prog_node);
262262
263 assert(comp.libcxx_static_lib == null);263 assert(comp.libcxx_static_lib == null);
264 comp.libcxx_static_lib = Compilation.CRTFile{264 comp.libcxx_static_lib = Compilation.CRTFile{
...@@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
269 };269 };
270}270}
271271
272pub fn buildLibCXXABI(comp: *Compilation) !void {272pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void {
273 if (!build_options.have_llvm) {273 if (!build_options.have_llvm) {
274 return error.ZigCompilerNotBuiltWithLLVMExtensions;274 return error.ZigCompilerNotBuiltWithLLVMExtensions;
275 }275 }
...@@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {...@@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
418 });418 });
419 defer sub_compilation.destroy();419 defer sub_compilation.destroy();
420420
421 try sub_compilation.updateSubCompilation();421 try comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node);
422422
423 assert(comp.libcxxabi_static_lib == null);423 assert(comp.libcxxabi_static_lib == null);
424 comp.libcxxabi_static_lib = Compilation.CRTFile{424 comp.libcxxabi_static_lib = Compilation.CRTFile{
src/libtsan.zig+2-2
...@@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig");...@@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig");
5const build_options = @import("build_options");5const build_options = @import("build_options");
6const trace = @import("tracy.zig").trace;6const trace = @import("tracy.zig").trace;
77
8pub fn buildTsan(comp: *Compilation) !void {8pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void {
9 if (!build_options.have_llvm) {9 if (!build_options.have_llvm) {
10 return error.ZigCompilerNotBuiltWithLLVMExtensions;10 return error.ZigCompilerNotBuiltWithLLVMExtensions;
11 }11 }
...@@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void {
235 });235 });
236 defer sub_compilation.destroy();236 defer sub_compilation.destroy();
237237
238 try sub_compilation.updateSubCompilation();238 try comp.updateSubCompilation(sub_compilation, .libtsan, prog_node);
239239
240 assert(comp.tsan_static_lib == null);240 assert(comp.tsan_static_lib == null);
241 comp.tsan_static_lib = Compilation.CRTFile{241 comp.tsan_static_lib = Compilation.CRTFile{
src/libunwind.zig+2-2
...@@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig");...@@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig");
7const build_options = @import("build_options");7const build_options = @import("build_options");
8const trace = @import("tracy.zig").trace;8const trace = @import("tracy.zig").trace;
99
10pub fn buildStaticLib(comp: *Compilation) !void {10pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void {
11 if (!build_options.have_llvm) {11 if (!build_options.have_llvm) {
12 return error.ZigCompilerNotBuiltWithLLVMExtensions;12 return error.ZigCompilerNotBuiltWithLLVMExtensions;
13 }13 }
...@@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {...@@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
130 });130 });
131 defer sub_compilation.destroy();131 defer sub_compilation.destroy();
132132
133 try sub_compilation.updateSubCompilation();133 try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node);
134134
135 assert(comp.libunwind_static_lib == null);135 assert(comp.libunwind_static_lib == null);
136136
src/link.zig+42-3
...@@ -264,6 +264,8 @@ pub const File = struct {...@@ -264,6 +264,8 @@ pub const File = struct {
264 /// of this linking operation.264 /// of this linking operation.
265 lock: ?Cache.Lock = null,265 lock: ?Cache.Lock = null,
266266
267 child_pid: ?std.ChildProcess.Id = null,
268
267 /// Attempts incremental linking, if the file already exists. If269 /// Attempts incremental linking, if the file already exists. If
268 /// incremental linking fails, falls back to truncating the file and270 /// incremental linking fails, falls back to truncating the file and
269 /// rewriting it. A malicious file is detected as incremental link failure271 /// rewriting it. A malicious file is detected as incremental link failure
...@@ -376,6 +378,26 @@ pub const File = struct {...@@ -376,6 +378,26 @@ pub const File = struct {
376 if (build_options.only_c) unreachable;378 if (build_options.only_c) unreachable;
377 if (base.file != null) return;379 if (base.file != null) return;
378 const emit = base.options.emit orelse return;380 const emit = base.options.emit orelse return;
381 if (base.child_pid) |pid| {
382 // If we try to open the output file in write mode while it is running,
383 // it will return ETXTBSY. So instead, we copy the file, atomically rename it
384 // over top of the exe path, and then proceed normally. This changes the inode,
385 // avoiding the error.
386 const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{
387 emit.sub_path, std.crypto.random.int(u32),
388 });
389 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
390 try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
391 switch (builtin.os.tag) {
392 .linux => {
393 switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) {
394 .SUCCESS => {},
395 else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}),
396 }
397 },
398 else => return error.HotSwapUnavailableOnHostOperatingSystem,
399 }
400 }
379 base.file = try emit.directory.handle.createFile(emit.sub_path, .{401 base.file = try emit.directory.handle.createFile(emit.sub_path, .{
380 .truncate = false,402 .truncate = false,
381 .read = true,403 .read = true,
...@@ -424,6 +446,18 @@ pub const File = struct {...@@ -424,6 +446,18 @@ pub const File = struct {
424 }446 }
425 f.close();447 f.close();
426 base.file = null;448 base.file = null;
449
450 if (base.child_pid) |pid| {
451 switch (builtin.os.tag) {
452 .linux => {
453 switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) {
454 .SUCCESS => {},
455 else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}),
456 }
457 },
458 else => return error.HotSwapUnavailableOnHostOperatingSystem,
459 }
460 }
427 },461 },
428 .c, .spirv, .nvptx => {},462 .c, .spirv, .nvptx => {},
429 }463 }
...@@ -460,6 +494,9 @@ pub const File = struct {...@@ -460,6 +494,9 @@ pub const File = struct {
460 CurrentWorkingDirectoryUnlinked,494 CurrentWorkingDirectoryUnlinked,
461 LockViolation,495 LockViolation,
462 NetNameDeleted,496 NetNameDeleted,
497 DeviceBusy,
498 InvalidArgument,
499 HotSwapUnavailableOnHostOperatingSystem,
463 };500 };
464501
465 /// Called from within the CodeGen to lower a local variable instantion as an unnamed502 /// Called from within the CodeGen to lower a local variable instantion as an unnamed
...@@ -1051,9 +1088,11 @@ pub const File = struct {...@@ -1051,9 +1088,11 @@ pub const File = struct {
1051 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)});1088 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)});
1052 };1089 };
10531090
1054 man.writeManifest() catch |err| {1091 if (man.have_exclusive_lock) {
1055 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});1092 man.writeManifest() catch |err| {
1056 };1093 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});
1094 };
1095 }
10571096
1058 base.lock = man.toOwnedLock();1097 base.lock = man.toOwnedLock();
1059 }1098 }
src/link/C.zig+14-8
...@@ -221,14 +221,19 @@ pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void...@@ -221,14 +221,19 @@ pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void
221 return self.flushModule(comp, prog_node);221 return self.flushModule(comp, prog_node);
222}222}
223223
224fn abiDefine(comp: *Compilation) ?[]const u8 {224fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
225 return switch (comp.getTarget().abi) {225 var defines = std.ArrayList(u8).init(self.base.allocator);
226 .msvc => "#define ZIG_TARGET_ABI_MSVC\n",226 errdefer defines.deinit();
227 else => null,227 const writer = defines.writer();
228 };228 switch (target.abi) {
229 .msvc => try writer.writeAll("#define ZIG_TARGET_ABI_MSVC\n"),
230 else => {},
231 }
232 try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{target.maxIntAlignment()});
233 return defines;
229}234}
230235
231pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {236pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !void {
232 const tracy = trace(@src());237 const tracy = trace(@src());
233 defer tracy.end();238 defer tracy.end();
234239
...@@ -245,12 +250,13 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -245,12 +250,13 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)
245 var f: Flush = .{};250 var f: Flush = .{};
246 defer f.deinit(gpa);251 defer f.deinit(gpa);
247252
248 const abi_define = abiDefine(comp);253 const abi_defines = try self.abiDefines(module.getTarget());
254 defer abi_defines.deinit();
249255
250 // Covers defines, zig.h, ctypes, asm, lazy fwd.256 // Covers defines, zig.h, ctypes, asm, lazy fwd.
251 try f.all_buffers.ensureUnusedCapacity(gpa, 5);257 try f.all_buffers.ensureUnusedCapacity(gpa, 5);
252258
253 if (abi_define) |buf| f.appendBufAssumeCapacity(buf);259 f.appendBufAssumeCapacity(abi_defines.items);
254 f.appendBufAssumeCapacity(zig_h);260 f.appendBufAssumeCapacity(zig_h);
255261
256 const ctypes_index = f.all_buffers.items.len;262 const ctypes_index = f.all_buffers.items.len;
src/link/Coff.zig+1-1
...@@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
1060 decl.analysis = .codegen_failure;1060 decl.analysis = .codegen_failure;
1061 try mod.failed_decls.put(mod.gpa, decl_index, em);1061 try mod.failed_decls.put(mod.gpa, decl_index, em);
1062 log.err("{s}", .{em.msg});1062 log.err("{s}", .{em.msg});
1063 return error.AnalysisFail;1063 return error.CodegenFail;
1064 },1064 },
1065 };1065 };
10661066
src/link/Elf.zig+60-8
...@@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
467 .p_paddr = entry_addr,467 .p_paddr = entry_addr,
468 .p_memsz = file_size,468 .p_memsz = file_size,
469 .p_align = p_align,469 .p_align = p_align,
470 .p_flags = elf.PF_X | elf.PF_R,470 .p_flags = elf.PF_X | elf.PF_R | elf.PF_W,
471 });471 });
472 self.entry_addr = null;472 self.entry_addr = null;
473 self.phdr_table_dirty = true;473 self.phdr_table_dirty = true;
...@@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
493 .p_paddr = got_addr,493 .p_paddr = got_addr,
494 .p_memsz = file_size,494 .p_memsz = file_size,
495 .p_align = p_align,495 .p_align = p_align,
496 .p_flags = elf.PF_R,496 .p_flags = elf.PF_R | elf.PF_W,
497 });497 });
498 self.phdr_table_dirty = true;498 self.phdr_table_dirty = true;
499 }499 }
...@@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
516 .p_paddr = rodata_addr,516 .p_paddr = rodata_addr,
517 .p_memsz = file_size,517 .p_memsz = file_size,
518 .p_align = p_align,518 .p_align = p_align,
519 .p_flags = elf.PF_R,519 .p_flags = elf.PF_R | elf.PF_W,
520 });520 });
521 self.phdr_table_dirty = true;521 self.phdr_table_dirty = true;
522 }522 }
...@@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {...@@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
2097 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.2097 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2098 const local_sym_index = atom.getSymbolIndex().?;2098 const local_sym_index = atom.getSymbolIndex().?;
20992099
2100 log.debug("adding %{d} to local symbols free list", .{local_sym_index});
2100 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};2101 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
2101 self.local_symbols.items[local_sym_index].st_info = 0;2102 self.local_symbols.items[local_sym_index] = .{
2102 self.local_symbols.items[local_sym_index].st_shndx = 0;2103 .st_name = 0,
2104 .st_info = 0,
2105 .st_other = 0,
2106 .st_shndx = 0,
2107 .st_value = 0,
2108 .st_size = 0,
2109 };
2103 _ = self.atom_by_index_table.remove(local_sym_index);2110 _ = self.atom_by_index_table.remove(local_sym_index);
2104 self.getAtomPtr(atom_index).local_sym_index = 0;2111 self.getAtomPtr(atom_index).local_sym_index = 0;
21052112
...@@ -2159,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme...@@ -2159,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme
2159 // First we look for an appropriately sized free list node.2166 // First we look for an appropriately sized free list node.
2160 // The list is unordered. We'll just take the first thing that works.2167 // The list is unordered. We'll just take the first thing that works.
2161 const vaddr = blk: {2168 const vaddr = blk: {
2162 var i: usize = 0;2169 var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len;
2163 while (i < free_list.items.len) {2170 while (i < free_list.items.len) {
2164 const big_atom_index = free_list.items[i];2171 const big_atom_index = free_list.items[i];
2165 const big_atom = self.getAtom(big_atom_index);2172 const big_atom = self.getAtom(big_atom_index);
...@@ -2390,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2390,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2390 const atom = self.getAtom(atom_index);2397 const atom = self.getAtom(atom_index);
23912398
2392 const shdr_index = decl_metadata.shdr;2399 const shdr_index = decl_metadata.shdr;
2393 if (atom.getSymbol(self).st_size != 0) {2400 if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) {
2394 const local_sym = atom.getSymbolPtr(self);2401 const local_sym = atom.getSymbolPtr(self);
2395 local_sym.st_name = try self.shstrtab.insert(gpa, decl_name);2402 local_sym.st_name = try self.shstrtab.insert(gpa, decl_name);
2396 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;2403 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
...@@ -2444,6 +2451,28 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2444,6 +2451,28 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2444 const phdr_index = self.sections.items(.phdr_index)[shdr_index];2451 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
2445 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;2452 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
2446 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;2453 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
2454
2455 if (self.base.child_pid) |pid| {
2456 switch (builtin.os.tag) {
2457 .linux => {
2458 var code_vec: [1]std.os.iovec_const = .{.{
2459 .iov_base = code.ptr,
2460 .iov_len = code.len,
2461 }};
2462 var remote_vec: [1]std.os.iovec_const = .{.{
2463 .iov_base = @intToPtr([*]u8, @intCast(usize, local_sym.st_value)),
2464 .iov_len = code.len,
2465 }};
2466 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
2467 switch (std.os.errno(rc)) {
2468 .SUCCESS => assert(rc == code.len),
2469 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
2470 }
2471 },
2472 else => return error.HotSwapUnavailableOnHostOperatingSystem,
2473 }
2474 }
2475
2447 try self.base.file.?.pwriteAll(code, file_offset);2476 try self.base.file.?.pwriteAll(code, file_offset);
24482477
2449 return local_sym;2478 return local_sym;
...@@ -2618,7 +2647,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2618,7 +2647,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2618 decl.analysis = .codegen_failure;2647 decl.analysis = .codegen_failure;
2619 try mod.failed_decls.put(mod.gpa, decl_index, em);2648 try mod.failed_decls.put(mod.gpa, decl_index, em);
2620 log.err("{s}", .{em.msg});2649 log.err("{s}", .{em.msg});
2621 return error.AnalysisFail;2650 return error.CodegenFail;
2622 },2651 },
2623 };2652 };
26242653
...@@ -2813,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {...@@ -2813,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2813 const endian = self.base.options.target.cpu.arch.endian();2842 const endian = self.base.options.target.cpu.arch.endian();
2814 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];2843 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
2815 const off = shdr.sh_offset + @as(u64, entry_size) * index;2844 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2845 const phdr = &self.program_headers.items[self.phdr_got_index.?];
2846 const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;
2816 switch (entry_size) {2847 switch (entry_size) {
2817 2 => {2848 2 => {
2818 var buf: [2]u8 = undefined;2849 var buf: [2]u8 = undefined;
...@@ -2828,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {...@@ -2828,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2828 var buf: [8]u8 = undefined;2859 var buf: [8]u8 = undefined;
2829 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);2860 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
2830 try self.base.file.?.pwriteAll(&buf, off);2861 try self.base.file.?.pwriteAll(&buf, off);
2862
2863 if (self.base.child_pid) |pid| {
2864 switch (builtin.os.tag) {
2865 .linux => {
2866 var local_vec: [1]std.os.iovec_const = .{.{
2867 .iov_base = &buf,
2868 .iov_len = buf.len,
2869 }};
2870 var remote_vec: [1]std.os.iovec_const = .{.{
2871 .iov_base = @intToPtr([*]u8, @intCast(usize, vaddr)),
2872 .iov_len = buf.len,
2873 }};
2874 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
2875 switch (std.os.errno(rc)) {
2876 .SUCCESS => assert(rc == buf.len),
2877 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
2878 }
2879 },
2880 else => return error.HotSwapUnavailableOnHostOperatingSystem,
2881 }
2882 }
2831 },2883 },
2832 else => unreachable,2884 else => unreachable,
2833 }2885 }
src/link/MachO.zig+1-1
...@@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
2089 decl.analysis = .codegen_failure;2089 decl.analysis = .codegen_failure;
2090 try module.failed_decls.put(module.gpa, decl_index, em);2090 try module.failed_decls.put(module.gpa, decl_index, em);
2091 log.err("{s}", .{em.msg});2091 log.err("{s}", .{em.msg});
2092 return error.AnalysisFail;2092 return error.CodegenFail;
2093 },2093 },
2094 };2094 };
20952095
src/link/MachO/CodeSignature.zig+2-2
...@@ -7,12 +7,12 @@ const log = std.log.scoped(.link);...@@ -7,12 +7,12 @@ const log = std.log.scoped(.link);
7const macho = std.macho;7const macho = std.macho;
8const mem = std.mem;8const mem = std.mem;
9const testing = std.testing;9const testing = std.testing;
10const ThreadPool = std.Thread.Pool;
11const WaitGroup = std.Thread.WaitGroup;
1012
11const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
12const Compilation = @import("../../Compilation.zig");14const Compilation = @import("../../Compilation.zig");
13const Sha256 = std.crypto.hash.sha2.Sha256;15const Sha256 = std.crypto.hash.sha2.Sha256;
14const ThreadPool = @import("../../ThreadPool.zig");
15const WaitGroup = @import("../../WaitGroup.zig");
1616
17const hash_size = Sha256.digest_length;17const hash_size = Sha256.digest_length;
1818
src/link/Plan9.zig+1-1
...@@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I...@@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
377 decl.analysis = .codegen_failure;377 decl.analysis = .codegen_failure;
378 try mod.failed_decls.put(mod.gpa, decl_index, em);378 try mod.failed_decls.put(mod.gpa, decl_index, em);
379 log.err("{s}", .{em.msg});379 log.err("{s}", .{em.msg});
380 return error.AnalysisFail;380 return error.CodegenFail;
381 },381 },
382 };382 };
383 // duped_code is freed when the unnamed const is freed383 // duped_code is freed when the unnamed const is freed
src/link/Wasm.zig+79-46
...@@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol...@@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
468 .flags = 0,468 .flags = 0,
469 .tag = tag,469 .tag = tag,
470 .index = undefined,470 .index = undefined,
471 .virtual_address = undefined,
471 });472 });
472 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});473 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});
473 try wasm.globals.put(wasm.base.allocator, name_offset, loc);474 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
...@@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
886 const loc = try wasm.createSyntheticSymbol("__heap_base", .data);887 const loc = try wasm.createSyntheticSymbol("__heap_base", .data);
887 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);888 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
888 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.889 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
889
890 // TODO: Can we use `createAtom` here while also re-using the symbol
891 // from `createSyntheticSymbol`.
892 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
893 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
894 atom.* = Atom.empty;
895 atom.sym_index = loc.index;
896 atom.alignment = 1;
897
898 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
899 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
900 }890 }
901891
902 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {892 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
903 const loc = try wasm.createSyntheticSymbol("__heap_end", .data);893 const loc = try wasm.createSyntheticSymbol("__heap_end", .data);
904 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
905 _ = wasm.resolved_symbols.swapRemove(loc);895 _ = wasm.resolved_symbols.swapRemove(loc);
906
907 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
908 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
909 atom.* = Atom.empty;
910 atom.sym_index = loc.index;
911 atom.alignment = 1;
912
913 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
914 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
915 }896 }
916}897}
917898
...@@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {...@@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {
1011 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),992 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1012 .tag = undefined, // will be set after updateDecl993 .tag = undefined, // will be set after updateDecl
1013 .index = undefined, // will be set after updateDecl994 .index = undefined, // will be set after updateDecl
995 .virtual_address = undefined, // will be set during atom allocation
1014 };996 };
1015 if (wasm.symbols_free_list.popOrNull()) |index| {997 if (wasm.symbols_free_list.popOrNull()) |index| {
1016 wasm.symbols.items[index] = symbol;998 wasm.symbols.items[index] = symbol;
...@@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
1246 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),1228 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1247 .tag = .data,1229 .tag = .data,
1248 .index = undefined,1230 .index = undefined,
1231 .virtual_address = undefined,
1249 };1232 };
1250 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});1233 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
12511234
...@@ -1265,7 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -1265,7 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
1265 .fail => |em| {1248 .fail => |em| {
1266 decl.analysis = .codegen_failure;1249 decl.analysis = .codegen_failure;
1267 try mod.failed_decls.put(mod.gpa, decl_index, em);1250 try mod.failed_decls.put(mod.gpa, decl_index, em);
1268 return error.AnalysisFail;1251 return error.CodegenFail;
1269 },1252 },
1270 };1253 };
1271 };1254 };
...@@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {...@@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {
1292 .flags = 0,1275 .flags = 0,
1293 .index = undefined, // index to type will be set after merging function symbols1276 .index = undefined, // index to type will be set after merging function symbols
1294 .tag = .function,1277 .tag = .function,
1278 .virtual_address = undefined,
1295 };1279 };
1296 symbol.setGlobal(true);1280 symbol.setGlobal(true);
1297 symbol.setUndefined(true);1281 symbol.setUndefined(true);
...@@ -1610,7 +1594,6 @@ const Kind = union(enum) {...@@ -1610,7 +1594,6 @@ const Kind = union(enum) {
1610 read_only,1594 read_only,
1611 uninitialized,1595 uninitialized,
1612 initialized,1596 initialized,
1613 synthetic,
1614 },1597 },
1615 function: void,1598 function: void,
16161599
...@@ -1621,7 +1604,6 @@ const Kind = union(enum) {...@@ -1621,7 +1604,6 @@ const Kind = union(enum) {
1621 .read_only => return ".rodata.",1604 .read_only => return ".rodata.",
1622 .uninitialized => return ".bss.",1605 .uninitialized => return ".bss.",
1623 .initialized => return ".data.",1606 .initialized => return ".data.",
1624 .synthetic => return ".synthetic",
1625 }1607 }
1626 }1608 }
1627};1609};
...@@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void {
1788 }1770 }
1789}1771}
17901772
1773/// For each data symbol, sets the virtual address.
1774fn allocateVirtualAddresses(wasm: *Wasm) void {
1775 for (wasm.resolved_symbols.keys()) |loc| {
1776 const symbol = loc.getSymbol(wasm);
1777 if (symbol.tag != .data) {
1778 continue; // only data symbols have virtual addresses
1779 }
1780 const atom_index = wasm.symbol_atom.get(loc) orelse {
1781 // synthetic symbol that does not contain an atom
1782 continue;
1783 };
1784
1785 const atom = wasm.getAtom(atom_index);
1786 const merge_segment = wasm.base.options.output_mode != .Obj;
1787 const segment_info = if (atom.file) |object_index| blk: {
1788 break :blk wasm.objects.items[object_index].segment_info;
1789 } else wasm.segment_info.values();
1790 const segment_name = segment_info[symbol.index].outputName(merge_segment);
1791 const segment_index = wasm.data_segments.get(segment_name).?;
1792 const segment = wasm.segments.items[segment_index];
1793 symbol.virtual_address = atom.offset + segment.offset;
1794 }
1795}
1796
1791fn sortDataSegments(wasm: *Wasm) !void {1797fn sortDataSegments(wasm: *Wasm) !void {
1792 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};1798 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
1793 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());1799 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());
...@@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void {...@@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void {
1805 if (mem.startsWith(u8, name, ".rodata")) return 0;1811 if (mem.startsWith(u8, name, ".rodata")) return 0;
1806 if (mem.startsWith(u8, name, ".data")) return 1;1812 if (mem.startsWith(u8, name, ".data")) return 1;
1807 if (mem.startsWith(u8, name, ".text")) return 2;1813 if (mem.startsWith(u8, name, ".text")) return 2;
1808 if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end
1809 return 3;1814 return 3;
1810 }1815 }
1811 };1816 };
...@@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void {
2137 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);2142 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
2138 };2143 };
2139 const exp: types.Export = if (symbol.tag == .data) exp: {2144 const exp: types.Export = if (symbol.tag == .data) exp: {
2140 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2141 const atom = wasm.getAtom(atom_index);
2142 const va = atom.getVA(wasm, symbol);
2143 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);2145 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
2144 try wasm.wasm_globals.append(wasm.base.allocator, .{2146 try wasm.wasm_globals.append(wasm.base.allocator, .{
2145 .global_type = .{ .valtype = .i32, .mutable = false },2147 .global_type = .{ .valtype = .i32, .mutable = false },
2146 .init = .{ .i32_const = @intCast(i32, va) },2148 .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) },
2147 });2149 });
2148 break :exp .{2150 break :exp .{
2149 .name = export_name,2151 .name = export_name,
...@@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void {
2220 var offset: u32 = @intCast(u32, memory_ptr);2222 var offset: u32 = @intCast(u32, memory_ptr);
2221 var data_seg_it = wasm.data_segments.iterator();2223 var data_seg_it = wasm.data_segments.iterator();
2222 while (data_seg_it.next()) |entry| {2224 while (data_seg_it.next()) |entry| {
2223 if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) {
2224 // do not update synthetic segments as they are not part of the output
2225 continue;
2226 }
2227 const segment = &wasm.segments.items[entry.value_ptr.*];2225 const segment = &wasm.segments.items[entry.value_ptr.*];
2228 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);2226 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
2229 memory_ptr += segment.size;2227 memory_ptr += segment.size;
...@@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void {
2240 // One of the linked object files has a reference to the __heap_base symbol.2238 // One of the linked object files has a reference to the __heap_base symbol.
2241 // We must set its virtual address so it can be used in relocations.2239 // We must set its virtual address so it can be used in relocations.
2242 if (wasm.findGlobalSymbol("__heap_base")) |loc| {2240 if (wasm.findGlobalSymbol("__heap_base")) |loc| {
2243 const segment_index = wasm.data_segments.get(".synthetic").?;2241 const symbol = loc.getSymbol(wasm);
2244 const segment = &wasm.segments.items[segment_index];2242 symbol.virtual_address = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
2245 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.
2246 const atom_index = wasm.symbol_atom.get(loc).?;
2247 const atom = wasm.getAtomPtr(atom_index);
2248 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
2249 }2243 }
22502244
2251 // Setup the max amount of pages2245 // Setup the max amount of pages
...@@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void {
2274 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});2268 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
22752269
2276 if (wasm.findGlobalSymbol("__heap_end")) |loc| {2270 if (wasm.findGlobalSymbol("__heap_end")) |loc| {
2277 const segment_index = wasm.data_segments.get(".synthetic").?;2271 const symbol = loc.getSymbol(wasm);
2278 const segment = &wasm.segments.items[segment_index];2272 symbol.virtual_address = @intCast(u32, memory_ptr);
2279 segment.offset = 0;
2280 const atom_index = wasm.symbol_atom.get(loc).?;
2281 const atom = wasm.getAtomPtr(atom_index);
2282 atom.offset = @intCast(u32, memory_ptr);
2283 }2273 }
22842274
2285 if (wasm.base.options.max_memory) |max_memory| {2275 if (wasm.base.options.max_memory) |max_memory| {
...@@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2417 .tag = .data,2407 .tag = .data,
2418 .flags = 0,2408 .flags = 0,
2419 .index = 0,2409 .index = 0,
2410 .virtual_address = undefined,
2420 };2411 };
2421 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2412 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24222413
...@@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
2449 .tag = .data,2440 .tag = .data,
2450 .flags = 0,2441 .flags = 0,
2451 .index = 0,2442 .index = 0,
2443 .virtual_address = undefined,
2452 };2444 };
2453 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2445 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24542446
...@@ -2635,6 +2627,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -2635,6 +2627,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
2635 try man.addOptionalFile(compiler_rt_path);2627 try man.addOptionalFile(compiler_rt_path);
2636 man.hash.addOptionalBytes(options.entry);2628 man.hash.addOptionalBytes(options.entry);
2637 man.hash.addOptional(options.stack_size_override);2629 man.hash.addOptional(options.stack_size_override);
2630 man.hash.add(wasm.base.options.build_id);
2638 man.hash.add(options.import_memory);2631 man.hash.add(options.import_memory);
2639 man.hash.add(options.import_table);2632 man.hash.add(options.import_table);
2640 man.hash.add(options.export_table);2633 man.hash.add(options.export_table);
...@@ -2748,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -2748,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
27482741
2749 try wasm.allocateAtoms();2742 try wasm.allocateAtoms();
2750 try wasm.setupMemory();2743 try wasm.setupMemory();
2744 wasm.allocateVirtualAddresses();
2751 wasm.mapFunctionTable();2745 wasm.mapFunctionTable();
2752 try wasm.mergeSections();2746 try wasm.mergeSections();
2753 try wasm.mergeTypes();2747 try wasm.mergeTypes();
...@@ -2866,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2866,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28662860
2867 try wasm.allocateAtoms();2861 try wasm.allocateAtoms();
2868 try wasm.setupMemory();2862 try wasm.setupMemory();
2863 wasm.allocateVirtualAddresses();
2869 wasm.mapFunctionTable();2864 wasm.mapFunctionTable();
2870 try wasm.mergeSections();2865 try wasm.mergeSections();
2871 try wasm.mergeTypes();2866 try wasm.mergeTypes();
...@@ -3225,6 +3220,12 @@ fn writeToFile(...@@ -3225,6 +3220,12 @@ fn writeToFile(
3225 }3220 }
32263221
3227 if (!wasm.base.options.strip) {3222 if (!wasm.base.options.strip) {
3223 // The build id must be computed on the main sections only,
3224 // so we have to do it now, before the debug sections.
3225 if (wasm.base.options.build_id) {
3226 try emitBuildIdSection(&binary_bytes);
3227 }
3228
3228 // if (wasm.dwarf) |*dwarf| {3229 // if (wasm.dwarf) |*dwarf| {
3229 // const mod = wasm.base.options.module.?;3230 // const mod = wasm.base.options.module.?;
3230 // try dwarf.writeDbgAbbrev();3231 // try dwarf.writeDbgAbbrev();
...@@ -3363,6 +3364,33 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {...@@ -3363,6 +3364,33 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
3363 );3364 );
3364}3365}
33653366
3367fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8)) !void {
3368 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3369
3370 const writer = binary_bytes.writer();
3371 const build_id = "build_id";
3372 try leb.writeULEB128(writer, @intCast(u32, build_id.len));
3373 try writer.writeAll(build_id);
3374
3375 var id: [16]u8 = undefined;
3376 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
3377 var uuid: [36]u8 = undefined;
3378 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
3379 std.fmt.fmtSliceHexLower(id[0..4]), std.fmt.fmtSliceHexLower(id[4..6]), std.fmt.fmtSliceHexLower(id[6..8]),
3380 std.fmt.fmtSliceHexLower(id[8..10]), std.fmt.fmtSliceHexLower(id[10..]),
3381 });
3382
3383 try leb.writeULEB128(writer, @as(u32, 1));
3384 try leb.writeULEB128(writer, @as(u32, uuid.len));
3385 try writer.writeAll(&uuid);
3386
3387 try writeCustomSectionHeader(
3388 binary_bytes.items,
3389 header_offset,
3390 @intCast(u32, binary_bytes.items.len - header_offset - 6),
3391 );
3392}
3393
3366fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {3394fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {
3367 const header_offset = try reserveCustomSectionHeader(binary_bytes);3395 const header_offset = try reserveCustomSectionHeader(binary_bytes);
33683396
...@@ -3426,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem...@@ -3426,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
3426 // bss section is not emitted when this condition holds true, so we also3454 // bss section is not emitted when this condition holds true, so we also
3427 // do not output a name for it.3455 // do not output a name for it.
3428 if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;3456 if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;
3429 // Synthetic segments are not emitted
3430 if (std.mem.eql(u8, key, ".synthetic")) continue;
3431 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });3457 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
3432 data_segment_index += 1;3458 data_segment_index += 1;
3433 }3459 }
...@@ -3594,6 +3620,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -3594,6 +3620,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
3594 try man.addOptionalFile(compiler_rt_path);3620 try man.addOptionalFile(compiler_rt_path);
3595 man.hash.addOptionalBytes(wasm.base.options.entry);3621 man.hash.addOptionalBytes(wasm.base.options.entry);
3596 man.hash.addOptional(wasm.base.options.stack_size_override);3622 man.hash.addOptional(wasm.base.options.stack_size_override);
3623 man.hash.add(wasm.base.options.build_id);
3597 man.hash.add(wasm.base.options.import_memory);3624 man.hash.add(wasm.base.options.import_memory);
3598 man.hash.add(wasm.base.options.import_table);3625 man.hash.add(wasm.base.options.import_table);
3599 man.hash.add(wasm.base.options.export_table);3626 man.hash.add(wasm.base.options.export_table);
...@@ -3760,6 +3787,12 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -3760,6 +3787,12 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
3760 if (wasm.base.options.import_symbols) {3787 if (wasm.base.options.import_symbols) {
3761 try argv.append("--allow-undefined");3788 try argv.append("--allow-undefined");
3762 }3789 }
3790
3791 // XXX - TODO: add when wasm-ld supports --build-id.
3792 // if (wasm.base.options.build_id) {
3793 // try argv.append("--build-id=tree");
3794 // }
3795
3763 try argv.appendSlice(&.{ "-o", full_out_path });3796 try argv.appendSlice(&.{ "-o", full_out_path });
37643797
3765 if (target.cpu.arch == .wasm64) {3798 if (target.cpu.arch == .wasm64) {
src/link/Wasm/Atom.zig+1-23
...@@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 {...@@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 {
89 return atom.sym_index;89 return atom.sym_index;
90}90}
9191
92/// Returns the virtual address of the `Atom`. This is the address starting
93/// from the first entry within a section.
94pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 {
95 if (symbol.tag == .function) return atom.offset;
96 std.debug.assert(symbol.tag == .data);
97 const merge_segment = wasm.base.options.output_mode != .Obj;
98 const segment_info = if (atom.file) |object_index| blk: {
99 break :blk wasm.objects.items[object_index].segment_info;
100 } else wasm.segment_info.values();
101 const segment_name = segment_info[symbol.index].outputName(merge_segment);
102 const segment_index = wasm.data_segments.get(segment_name).?;
103 const segment = wasm.segments.items[segment_index];
104 return segment.offset + atom.offset;
105}
106
107/// Resolves the relocations within the atom, writing the new value92/// Resolves the relocations within the atom, writing the new value
108/// at the calculated offset.93/// at the calculated offset.
109pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {94pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
...@@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
186 if (symbol.isUndefined()) {171 if (symbol.isUndefined()) {
187 return 0;172 return 0;
188 }173 }
189 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {174 const va = @intCast(i64, symbol.virtual_address);
190 // this can only occur during incremental-compilation when a relocation
191 // still points to a freed decl. It is fine to emit the value 0 here
192 // as no actual code will point towards it.
193 return 0;
194 };
195 const target_atom = wasm_bin.getAtom(target_atom_index);
196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
197 return @intCast(u32, va + relocation.addend);175 return @intCast(u32, va + relocation.addend);
198 },176 },
199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,177 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
src/link/Wasm/Object.zig+2
...@@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {...@@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
270 .name = table_import.name,270 .name = table_import.name,
271 .tag = .table,271 .tag = .table,
272 .index = 0,272 .index = 0,
273 .virtual_address = undefined,
273 };274 };
274 table_symbol.setFlag(.WASM_SYM_UNDEFINED);275 table_symbol.setFlag(.WASM_SYM_UNDEFINED);
275 table_symbol.setFlag(.WASM_SYM_NO_STRIP);276 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
...@@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type {
758 .tag = tag,759 .tag = tag,
759 .name = undefined,760 .name = undefined,
760 .index = undefined,761 .index = undefined,
762 .virtual_address = undefined,
761 };763 };
762764
763 switch (tag) {765 switch (tag) {
src/link/Wasm/Symbol.zig+3
...@@ -20,6 +20,9 @@ name: u32,...@@ -20,6 +20,9 @@ name: u32,
20index: u32,20index: u32,
21/// Represents the kind of the symbol, such as a function or global.21/// Represents the kind of the symbol, such as a function or global.
22tag: Tag,22tag: Tag,
23/// Contains the virtual address of the symbol, relative to the start of its section.
24/// This differs from the offset of an `Atom` which is relative to the start of a segment.
25virtual_address: u32,
2326
24pub const Tag = enum {27pub const Tag = enum {
25 function,28 function,
src/main.zig+501-342
...@@ -9,6 +9,8 @@ const Allocator = mem.Allocator;...@@ -9,6 +9,8 @@ const Allocator = mem.Allocator;
9const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
10const Ast = std.zig.Ast;10const Ast = std.zig.Ast;
11const warn = std.log.warn;11const warn = std.log.warn;
12const ThreadPool = std.Thread.Pool;
13const cleanExit = std.process.cleanExit;
1214
13const tracy = @import("tracy.zig");15const tracy = @import("tracy.zig");
14const Compilation = @import("Compilation.zig");16const Compilation = @import("Compilation.zig");
...@@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig");...@@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig");
22const clang = @import("clang.zig");24const clang = @import("clang.zig");
23const Cache = std.Build.Cache;25const Cache = std.Build.Cache;
24const target_util = @import("target.zig");26const target_util = @import("target.zig");
25const ThreadPool = @import("ThreadPool.zig");
26const crash_report = @import("crash_report.zig");27const crash_report = @import("crash_report.zig");
28const Module = @import("Module.zig");
29const AstGen = @import("AstGen.zig");
30const Server = std.zig.Server;
2731
28pub const std_options = struct {32pub const std_options = struct {
29 pub const wasiCwd = wasi_cwd;33 pub const wasiCwd = wasi_cwd;
...@@ -361,7 +365,6 @@ const usage_build_generic =...@@ -361,7 +365,6 @@ const usage_build_generic =
361 \\365 \\
362 \\General Options:366 \\General Options:
363 \\ -h, --help Print this help and exit367 \\ -h, --help Print this help and exit
364 \\ --watch Enable compiler REPL
365 \\ --color [auto|off|on] Enable or disable colored error messages368 \\ --color [auto|off|on] Enable or disable colored error messages
366 \\ -femit-bin[=path] (default) Output machine code369 \\ -femit-bin[=path] (default) Output machine code
367 \\ -fno-emit-bin Do not output machine code370 \\ -fno-emit-bin Do not output machine code
...@@ -666,6 +669,16 @@ const ArgMode = union(enum) {...@@ -666,6 +669,16 @@ const ArgMode = union(enum) {
666 run,669 run,
667};670};
668671
672/// Avoid dragging networking into zig2.c because it adds dependencies on some
673/// linker symbols that are annoying to satisfy while bootstrapping.
674const Ip4Address = if (build_options.omit_pkg_fetching_code) void else std.net.Ip4Address;
675
676const Listen = union(enum) {
677 none,
678 ip4: Ip4Address,
679 stdio,
680};
681
669fn buildOutputType(682fn buildOutputType(
670 gpa: Allocator,683 gpa: Allocator,
671 arena: Allocator,684 arena: Allocator,
...@@ -686,7 +699,7 @@ fn buildOutputType(...@@ -686,7 +699,7 @@ fn buildOutputType(
686 var formatted_panics: ?bool = null;699 var formatted_panics: ?bool = null;
687 var function_sections = false;700 var function_sections = false;
688 var no_builtin = false;701 var no_builtin = false;
689 var watch = false;702 var listen: Listen = .none;
690 var debug_compile_errors = false;703 var debug_compile_errors = false;
691 var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK");704 var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK");
692 var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC");705 var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC");
...@@ -1144,6 +1157,23 @@ fn buildOutputType(...@@ -1144,6 +1157,23 @@ fn buildOutputType(
1144 } else {1157 } else {
1145 try log_scopes.append(gpa, args_iter.nextOrFatal());1158 try log_scopes.append(gpa, args_iter.nextOrFatal());
1146 }1159 }
1160 } else if (mem.eql(u8, arg, "--listen")) {
1161 const next_arg = args_iter.nextOrFatal();
1162 if (mem.eql(u8, next_arg, "-")) {
1163 listen = .stdio;
1164 } else {
1165 if (build_options.omit_pkg_fetching_code) unreachable;
1166 // example: --listen 127.0.0.1:9000
1167 var it = std.mem.split(u8, next_arg, ":");
1168 const host = it.next().?;
1169 const port_text = it.next() orelse "14735";
1170 const port = std.fmt.parseInt(u16, port_text, 10) catch |err|
1171 fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) });
1172 listen = .{ .ip4 = std.net.Ip4Address.parse(host, port) catch |err|
1173 fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) };
1174 }
1175 } else if (mem.eql(u8, arg, "--listen=-")) {
1176 listen = .stdio;
1147 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {1177 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
1148 if (!build_options.enable_link_snapshots) {1178 if (!build_options.enable_link_snapshots) {
1149 std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});1179 std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
...@@ -1172,8 +1202,6 @@ fn buildOutputType(...@@ -1172,8 +1202,6 @@ fn buildOutputType(
1172 test_evented_io = true;1202 test_evented_io = true;
1173 } else if (mem.eql(u8, arg, "--test-no-exec")) {1203 } else if (mem.eql(u8, arg, "--test-no-exec")) {
1174 test_no_exec = true;1204 test_no_exec = true;
1175 } else if (mem.eql(u8, arg, "--watch")) {
1176 watch = true;
1177 } else if (mem.eql(u8, arg, "-ftime-report")) {1205 } else if (mem.eql(u8, arg, "-ftime-report")) {
1178 time_report = true;1206 time_report = true;
1179 } else if (mem.eql(u8, arg, "-fstack-report")) {1207 } else if (mem.eql(u8, arg, "-fstack-report")) {
...@@ -2999,7 +3027,7 @@ fn buildOutputType(...@@ -2999,7 +3027,7 @@ fn buildOutputType(
2999 defer zig_lib_directory.handle.close();3027 defer zig_lib_directory.handle.close();
30003028
3001 var thread_pool: ThreadPool = undefined;3029 var thread_pool: ThreadPool = undefined;
3002 try thread_pool.init(gpa);3030 try thread_pool.init(.{ .allocator = gpa });
3003 defer thread_pool.deinit();3031 defer thread_pool.deinit();
30043032
3005 var libc_installation: ?LibCInstallation = null;3033 var libc_installation: ?LibCInstallation = null;
...@@ -3259,8 +3287,52 @@ fn buildOutputType(...@@ -3259,8 +3287,52 @@ fn buildOutputType(
3259 if (show_builtin) {3287 if (show_builtin) {
3260 return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));3288 return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
3261 }3289 }
3290 switch (listen) {
3291 .none => {},
3292 .stdio => {
3293 if (build_options.only_c) unreachable;
3294 try serve(
3295 comp,
3296 std.io.getStdIn(),
3297 std.io.getStdOut(),
3298 test_exec_args.items,
3299 self_exe_path,
3300 arg_mode,
3301 all_args,
3302 runtime_args_start,
3303 );
3304 return cleanExit();
3305 },
3306 .ip4 => |ip4_addr| {
3307 if (build_options.omit_pkg_fetching_code) unreachable;
3308
3309 var server = std.net.StreamServer.init(.{
3310 .reuse_address = true,
3311 });
3312 defer server.deinit();
3313
3314 try server.listen(.{ .in = ip4_addr });
3315
3316 while (true) {
3317 const conn = try server.accept();
3318 defer conn.stream.close();
3319
3320 try serve(
3321 comp,
3322 .{ .handle = conn.stream.handle },
3323 .{ .handle = conn.stream.handle },
3324 test_exec_args.items,
3325 self_exe_path,
3326 arg_mode,
3327 all_args,
3328 runtime_args_start,
3329 );
3330 }
3331 },
3332 }
3333
3262 if (arg_mode == .translate_c) {3334 if (arg_mode == .translate_c) {
3263 return cmdTranslateC(comp, arena, have_enable_cache);3335 return cmdTranslateC(comp, arena, null);
3264 }3336 }
32653337
3266 const hook: AfterUpdateHook = blk: {3338 const hook: AfterUpdateHook = blk: {
...@@ -3276,7 +3348,7 @@ fn buildOutputType(...@@ -3276,7 +3348,7 @@ fn buildOutputType(
3276 };3348 };
32773349
3278 updateModule(gpa, comp, hook) catch |err| switch (err) {3350 updateModule(gpa, comp, hook) catch |err| switch (err) {
3279 error.SemanticAnalyzeFail => if (!watch) process.exit(1),3351 error.SemanticAnalyzeFail => if (listen == .none) process.exit(1),
3280 else => |e| return e,3352 else => |e| return e,
3281 };3353 };
3282 if (build_options.only_c) return cleanExit();3354 if (build_options.only_c) return cleanExit();
...@@ -3332,7 +3404,6 @@ fn buildOutputType(...@@ -3332,7 +3404,6 @@ fn buildOutputType(
3332 self_exe_path.?,3404 self_exe_path.?,
3333 arg_mode,3405 arg_mode,
3334 target_info,3406 target_info,
3335 watch,
3336 &comp_destroyed,3407 &comp_destroyed,
3337 all_args,3408 all_args,
3338 runtime_args_start,3409 runtime_args_start,
...@@ -3340,109 +3411,215 @@ fn buildOutputType(...@@ -3340,109 +3411,215 @@ fn buildOutputType(
3340 );3411 );
3341 }3412 }
33423413
3343 const stdin = std.io.getStdIn().reader();3414 // Skip resource deallocation in release builds; let the OS do it.
3344 const stderr = std.io.getStdErr().writer();3415 return cleanExit();
3345 var repl_buf: [1024]u8 = undefined;3416}
3417
3418fn serve(
3419 comp: *Compilation,
3420 in: fs.File,
3421 out: fs.File,
3422 test_exec_args: []const ?[]const u8,
3423 self_exe_path: ?[]const u8,
3424 arg_mode: ArgMode,
3425 all_args: []const []const u8,
3426 runtime_args_start: ?usize,
3427) !void {
3428 const gpa = comp.gpa;
33463429
3347 const ReplCmd = enum {3430 var server = try Server.init(.{
3348 update,3431 .gpa = gpa,
3349 help,3432 .in = in,
3350 run,3433 .out = out,
3351 update_and_run,3434 .zig_version = build_options.version,
3435 });
3436 defer server.deinit();
3437
3438 var child_pid: ?std.ChildProcess.Id = null;
3439
3440 var progress: std.Progress = .{
3441 .terminal = null,
3442 .root = .{
3443 .context = undefined,
3444 .parent = null,
3445 .name = "",
3446 .unprotected_estimated_total_items = 0,
3447 .unprotected_completed_items = 0,
3448 },
3449 .columns_written = 0,
3450 .prev_refresh_timestamp = 0,
3451 .timer = null,
3452 .done = false,
3352 };3453 };
3454 const main_progress_node = &progress.root;
3455 main_progress_node.context = &progress;
33533456
3354 var last_cmd: ReplCmd = .help;3457 while (true) {
3458 const hdr = try server.receiveMessage();
33553459
3356 while (watch) {3460 switch (hdr.tag) {
3357 try stderr.print("(zig) ", .{});3461 .exit => {
3358 try comp.makeBinFileExecutable();3462 return cleanExit();
3359 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {3463 },
3360 try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)});3464 .update => {
3361 continue;3465 assert(main_progress_node.recently_updated_child == null);
3362 }) |line| {3466 tracy.frameMark();
3363 const actual_line = mem.trimRight(u8, line, "\r\n ");3467
3364 const cmd: ReplCmd = blk: {3468 if (arg_mode == .translate_c) {
3365 if (mem.eql(u8, actual_line, "update")) {3469 var arena_instance = std.heap.ArenaAllocator.init(gpa);
3366 break :blk .update;3470 defer arena_instance.deinit();
3367 } else if (mem.eql(u8, actual_line, "exit")) {3471 const arena = arena_instance.allocator();
3368 break;3472 var output: TranslateCOutput = undefined;
3369 } else if (mem.eql(u8, actual_line, "help")) {3473 try cmdTranslateC(comp, arena, &output);
3370 break :blk .help;3474 try server.serveEmitBinPath(output.path, .{
3371 } else if (mem.eql(u8, actual_line, "run")) {3475 .flags = .{ .cache_hit = output.cache_hit },
3372 break :blk .run;3476 });
3373 } else if (mem.eql(u8, actual_line, "update-and-run")) {
3374 break :blk .update_and_run;
3375 } else if (actual_line.len == 0) {
3376 break :blk last_cmd;
3377 } else {
3378 try stderr.print("unknown command: {s}\n", .{actual_line});
3379 continue;3477 continue;
3380 }3478 }
3381 };3479
3382 last_cmd = cmd;3480 if (comp.bin_file.options.output_mode == .Exe) {
3383 switch (cmd) {3481 try comp.makeBinFileWritable();
3384 .update => {3482 }
3385 tracy.frameMark();3483
3386 if (output_mode == .Exe) {3484 {
3387 try comp.makeBinFileWritable();3485 var reset: std.Thread.ResetEvent = .{};
3486
3487 var progress_thread = try std.Thread.spawn(.{}, progressThread, .{
3488 &progress, &server, &reset,
3489 });
3490 defer {
3491 reset.set();
3492 progress_thread.join();
3388 }3493 }
3389 updateModule(gpa, comp, hook) catch |err| switch (err) {3494
3390 error.SemanticAnalyzeFail => continue,3495 try comp.update(main_progress_node);
3391 else => |e| return e,3496 }
3392 };3497
3393 },3498 try comp.makeBinFileExecutable();
3394 .help => {3499 try serveUpdateResults(&server, comp);
3395 try stderr.writeAll(repl_help);3500 },
3396 },3501 .run => {
3397 .run => {3502 if (child_pid != null) {
3398 tracy.frameMark();3503 @panic("TODO block until the child exits");
3399 try runOrTest(3504 }
3400 comp,3505 @panic("TODO call runOrTest");
3401 gpa,3506 //try runOrTest(
3402 arena,3507 // comp,
3403 test_exec_args.items,3508 // gpa,
3404 self_exe_path.?,3509 // arena,
3405 arg_mode,3510 // test_exec_args,
3406 target_info,3511 // self_exe_path.?,
3407 watch,3512 // arg_mode,
3408 &comp_destroyed,3513 // target_info,
3409 all_args,3514 // true,
3410 runtime_args_start,3515 // &comp_destroyed,
3411 link_libc,3516 // all_args,
3412 );3517 // runtime_args_start,
3413 },3518 // link_libc,
3414 .update_and_run => {3519 //);
3415 tracy.frameMark();3520 },
3416 if (output_mode == .Exe) {3521 .hot_update => {
3522 tracy.frameMark();
3523 assert(main_progress_node.recently_updated_child == null);
3524 if (child_pid) |pid| {
3525 try comp.hotCodeSwap(main_progress_node, pid);
3526 try serveUpdateResults(&server, comp);
3527 } else {
3528 if (comp.bin_file.options.output_mode == .Exe) {
3417 try comp.makeBinFileWritable();3529 try comp.makeBinFileWritable();
3418 }3530 }
3419 updateModule(gpa, comp, hook) catch |err| switch (err) {3531 try comp.update(main_progress_node);
3420 error.SemanticAnalyzeFail => continue,
3421 else => |e| return e,
3422 };
3423 try comp.makeBinFileExecutable();3532 try comp.makeBinFileExecutable();
3424 try runOrTest(3533 try serveUpdateResults(&server, comp);
3534
3535 child_pid = try runOrTestHotSwap(
3425 comp,3536 comp,
3426 gpa,3537 gpa,
3427 arena,3538 test_exec_args,
3428 test_exec_args.items,
3429 self_exe_path.?,3539 self_exe_path.?,
3430 arg_mode,3540 arg_mode,
3431 target_info,
3432 watch,
3433 &comp_destroyed,
3434 all_args,3541 all_args,
3435 runtime_args_start,3542 runtime_args_start,
3436 link_libc,
3437 );3543 );
3438 },3544 }
3545 },
3546 else => {
3547 fatal("unrecognized message from client: 0x{x}", .{@enumToInt(hdr.tag)});
3548 },
3549 }
3550 }
3551}
3552
3553fn progressThread(progress: *std.Progress, server: *const Server, reset: *std.Thread.ResetEvent) void {
3554 while (true) {
3555 if (reset.timedWait(500 * std.time.ns_per_ms)) |_| {
3556 // The Compilation update has completed.
3557 return;
3558 } else |err| switch (err) {
3559 error.Timeout => {},
3560 }
3561
3562 var buf: std.BoundedArray(u8, 160) = .{};
3563
3564 {
3565 progress.update_mutex.lock();
3566 defer progress.update_mutex.unlock();
3567
3568 var need_ellipse = false;
3569 var maybe_node: ?*std.Progress.Node = &progress.root;
3570 while (maybe_node) |node| {
3571 if (need_ellipse) {
3572 buf.appendSlice("... ") catch {};
3573 }
3574 need_ellipse = false;
3575 const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic);
3576 const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic);
3577 const current_item = completed_items + 1;
3578 if (node.name.len != 0 or eti > 0) {
3579 if (node.name.len != 0) {
3580 buf.appendSlice(node.name) catch {};
3581 need_ellipse = true;
3582 }
3583 if (eti > 0) {
3584 if (need_ellipse) buf.appendSlice(" ") catch {};
3585 buf.writer().print("[{d}/{d}] ", .{ current_item, eti }) catch {};
3586 need_ellipse = false;
3587 } else if (completed_items != 0) {
3588 if (need_ellipse) buf.appendSlice(" ") catch {};
3589 buf.writer().print("[{d}] ", .{current_item}) catch {};
3590 need_ellipse = false;
3591 }
3592 }
3593 maybe_node = @atomicLoad(?*std.Progress.Node, &node.recently_updated_child, .Acquire);
3439 }3594 }
3440 } else {
3441 break;
3442 }3595 }
3596
3597 const progress_string = buf.slice();
3598
3599 server.serveMessage(.{
3600 .tag = .progress,
3601 .bytes_len = @intCast(u32, progress_string.len),
3602 }, &.{
3603 progress_string,
3604 }) catch |err| {
3605 fatal("unable to write to client: {s}", .{@errorName(err)});
3606 };
3607 }
3608}
3609
3610fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
3611 const gpa = comp.gpa;
3612 var error_bundle = try comp.getAllErrorsAlloc();
3613 defer error_bundle.deinit(gpa);
3614 if (error_bundle.errorMessageCount() > 0) {
3615 try s.serveErrorBundle(error_bundle);
3616 } else if (comp.bin_file.options.emit) |emit| {
3617 const full_path = try emit.directory.join(gpa, &.{emit.sub_path});
3618 defer gpa.free(full_path);
3619 try s.serveEmitBinPath(full_path, .{
3620 .flags = .{ .cache_hit = comp.last_update_was_cache_hit },
3621 });
3443 }3622 }
3444 // Skip resource deallocation in release builds; let the OS do it.
3445 return cleanExit();
3446}3623}
34473624
3448const ModuleDepIterator = struct {3625const ModuleDepIterator = struct {
...@@ -3530,7 +3707,6 @@ fn runOrTest(...@@ -3530,7 +3707,6 @@ fn runOrTest(
3530 self_exe_path: []const u8,3707 self_exe_path: []const u8,
3531 arg_mode: ArgMode,3708 arg_mode: ArgMode,
3532 target_info: std.zig.system.NativeTargetInfo,3709 target_info: std.zig.system.NativeTargetInfo,
3533 watch: bool,
3534 comp_destroyed: *bool,3710 comp_destroyed: *bool,
3535 all_args: []const []const u8,3711 all_args: []const []const u8,
3536 runtime_args_start: ?usize,3712 runtime_args_start: ?usize,
...@@ -3561,7 +3737,7 @@ fn runOrTest(...@@ -3561,7 +3737,7 @@ fn runOrTest(
35613737
3562 // We do not execve for tests because if the test fails we want to print3738 // We do not execve for tests because if the test fails we want to print
3563 // the error message and invocation below.3739 // the error message and invocation below.
3564 if (std.process.can_execv and arg_mode == .run and !watch) {3740 if (std.process.can_execv and arg_mode == .run) {
3565 // execv releases the locks; no need to destroy the Compilation here.3741 // execv releases the locks; no need to destroy the Compilation here.
3566 const err = std.process.execve(gpa, argv.items, &env_map);3742 const err = std.process.execve(gpa, argv.items, &env_map);
3567 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);3743 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);
...@@ -3574,12 +3750,10 @@ fn runOrTest(...@@ -3574,12 +3750,10 @@ fn runOrTest(
3574 child.stdout_behavior = .Inherit;3750 child.stdout_behavior = .Inherit;
3575 child.stderr_behavior = .Inherit;3751 child.stderr_behavior = .Inherit;
35763752
3577 if (!watch) {3753 // Here we release all the locks associated with the Compilation so
3578 // Here we release all the locks associated with the Compilation so3754 // that whatever this child process wants to do won't deadlock.
3579 // that whatever this child process wants to do won't deadlock.3755 comp.destroy();
3580 comp.destroy();3756 comp_destroyed.* = true;
3581 comp_destroyed.* = true;
3582 }
35833757
3584 const term = child.spawnAndWait() catch |err| {3758 const term = child.spawnAndWait() catch |err| {
3585 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);3759 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);
...@@ -3591,19 +3765,13 @@ fn runOrTest(...@@ -3591,19 +3765,13 @@ fn runOrTest(
3591 switch (term) {3765 switch (term) {
3592 .Exited => |code| {3766 .Exited => |code| {
3593 if (code == 0) {3767 if (code == 0) {
3594 if (!watch) return cleanExit();3768 return cleanExit();
3595 } else if (watch) {
3596 warn("process exited with code {d}", .{code});
3597 } else {3769 } else {
3598 process.exit(code);3770 process.exit(code);
3599 }3771 }
3600 },3772 },
3601 else => {3773 else => {
3602 if (watch) {3774 process.exit(1);
3603 warn("process aborted abnormally", .{});
3604 } else {
3605 process.exit(1);
3606 }
3607 },3775 },
3608 }3776 }
3609 },3777 },
...@@ -3611,7 +3779,7 @@ fn runOrTest(...@@ -3611,7 +3779,7 @@ fn runOrTest(
3611 switch (term) {3779 switch (term) {
3612 .Exited => |code| {3780 .Exited => |code| {
3613 if (code == 0) {3781 if (code == 0) {
3614 if (!watch) return cleanExit();3782 return cleanExit();
3615 } else {3783 } else {
3616 const cmd = try std.mem.join(arena, " ", argv.items);3784 const cmd = try std.mem.join(arena, " ", argv.items);
3617 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });3785 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
...@@ -3631,6 +3799,62 @@ fn runOrTest(...@@ -3631,6 +3799,62 @@ fn runOrTest(
3631 }3799 }
3632}3800}
36333801
3802fn runOrTestHotSwap(
3803 comp: *Compilation,
3804 gpa: Allocator,
3805 test_exec_args: []const ?[]const u8,
3806 self_exe_path: []const u8,
3807 arg_mode: ArgMode,
3808 all_args: []const []const u8,
3809 runtime_args_start: ?usize,
3810) !std.ChildProcess.Id {
3811 const exe_emit = comp.bin_file.options.emit.?;
3812 // A naive `directory.join` here will indeed get the correct path to the binary,
3813 // however, in the case of cwd, we actually want `./foo` so that the path can be executed.
3814 const exe_path = try fs.path.join(gpa, &[_][]const u8{
3815 exe_emit.directory.path orelse ".", exe_emit.sub_path,
3816 });
3817 defer gpa.free(exe_path);
3818
3819 var argv = std.ArrayList([]const u8).init(gpa);
3820 defer argv.deinit();
3821
3822 if (test_exec_args.len == 0) {
3823 // when testing pass the zig_exe_path to argv
3824 if (arg_mode == .zig_test)
3825 try argv.appendSlice(&[_][]const u8{
3826 exe_path, self_exe_path,
3827 })
3828 // when running just pass the current exe
3829 else
3830 try argv.appendSlice(&[_][]const u8{
3831 exe_path,
3832 });
3833 } else {
3834 for (test_exec_args) |arg| {
3835 if (arg) |a| {
3836 try argv.append(a);
3837 } else {
3838 try argv.appendSlice(&[_][]const u8{
3839 exe_path, self_exe_path,
3840 });
3841 }
3842 }
3843 }
3844 if (runtime_args_start) |i| {
3845 try argv.appendSlice(all_args[i..]);
3846 }
3847 var child = std.ChildProcess.init(argv.items, gpa);
3848
3849 child.stdin_behavior = .Inherit;
3850 child.stdout_behavior = .Inherit;
3851 child.stderr_behavior = .Inherit;
3852
3853 try child.spawn();
3854
3855 return child.id;
3856}
3857
3634const AfterUpdateHook = union(enum) {3858const AfterUpdateHook = union(enum) {
3635 none,3859 none,
3636 print_emit_bin_dir_path,3860 print_emit_bin_dir_path,
...@@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) {...@@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) {
3638};3862};
36393863
3640fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void {3864fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void {
3641 try comp.update();3865 {
3866 // If the terminal is dumb, we dont want to show the user all the output.
3867 var progress: std.Progress = .{ .dont_print_on_dumb = true };
3868 const main_progress_node = progress.start("", 0);
3869 defer main_progress_node.end();
3870 switch (comp.color) {
3871 .off => {
3872 progress.terminal = null;
3873 },
3874 .on => {
3875 progress.terminal = std.io.getStdErr();
3876 progress.supports_ansi_escape_codes = true;
3877 },
3878 .auto => {},
3879 }
3880
3881 try comp.update(main_progress_node);
3882 }
36423883
3643 var errors = try comp.getAllErrorsAlloc();3884 var errors = try comp.getAllErrorsAlloc();
3644 defer errors.deinit(comp.gpa);3885 defer errors.deinit(comp.gpa);
36453886
3646 if (errors.list.len != 0) {3887 if (errors.errorMessageCount() > 0) {
3647 const ttyconf: std.debug.TTY.Config = switch (comp.color) {3888 errors.renderToStdErr(renderOptions(comp.color));
3648 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
3649 .on => .escape_codes,
3650 .off => .no_color,
3651 };
3652 for (errors.list) |full_err_msg| {
3653 full_err_msg.renderToStdErr(ttyconf);
3654 }
3655 const log_text = comp.getCompileLogOutput();
3656 if (log_text.len != 0) {
3657 std.debug.print("\nCompile Log Output:\n{s}", .{log_text});
3658 }
3659 return error.SemanticAnalyzeFail;3889 return error.SemanticAnalyzeFail;
3660 } else switch (hook) {3890 } else switch (hook) {
3661 .none => {},3891 .none => {},
...@@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void...@@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void
3697 }3927 }
3698}3928}
36993929
3700fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {3930const TranslateCOutput = struct {
3931 path: []const u8,
3932 cache_hit: bool,
3933};
3934
3935fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*TranslateCOutput) !void {
3701 if (!build_options.have_llvm)3936 if (!build_options.have_llvm)
3702 fatal("cannot translate-c: compiler built without LLVM extensions", .{});3937 fatal("cannot translate-c: compiler built without LLVM extensions", .{});
37033938
...@@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void...@@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
37083943
3709 var man: Cache.Manifest = comp.obtainCObjectCacheManifest();3944 var man: Cache.Manifest = comp.obtainCObjectCacheManifest();
3710 man.want_shared_lock = false;3945 man.want_shared_lock = false;
3711 defer if (enable_cache) man.deinit();3946 defer man.deinit();
37123947
3713 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects3948 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
3714 Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {3949 Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
3715 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });3950 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
3716 };3951 };
37173952
3953 if (fancy_output) |p| p.cache_hit = true;
3718 const digest = if (try man.hit()) man.final() else digest: {3954 const digest = if (try man.hit()) man.final() else digest: {
3955 if (fancy_output) |p| p.cache_hit = false;
3719 var argv = std.ArrayList([]const u8).init(arena);3956 var argv = std.ArrayList([]const u8).init(arena);
3720 try argv.append(""); // argv[0] is program name, actual args start at [1]3957 try argv.append(""); // argv[0] is program name, actual args start at [1]
37213958
...@@ -3766,6 +4003,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void...@@ -3766,6 +4003,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
3766 error.OutOfMemory => return error.OutOfMemory,4003 error.OutOfMemory => return error.OutOfMemory,
3767 error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),4004 error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
3768 error.SemanticAnalyzeFail => {4005 error.SemanticAnalyzeFail => {
4006 // TODO convert these to zig errors
3769 for (clang_errors) |clang_err| {4007 for (clang_errors) |clang_err| {
3770 std.debug.print("{s}:{d}:{d}: {s}\n", .{4008 std.debug.print("{s}:{d}:{d}: {s}\n", .{
3771 if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",4009 if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
...@@ -3810,12 +4048,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void...@@ -3810,12 +4048,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
3810 break :digest digest;4048 break :digest digest;
3811 };4049 };
38124050
3813 if (enable_cache) {4051 if (fancy_output) |p| {
3814 const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{4052 const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
3815 "o", &digest, translated_zig_basename,4053 "o", &digest, translated_zig_basename,
3816 });4054 });
3817 try io.getStdOut().writer().print("{s}\n", .{full_zig_path});4055 p.path = full_zig_path;
3818 return cleanExit();
3819 } else {4056 } else {
3820 const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename });4057 const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename });
3821 const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| {4058 const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| {
...@@ -4009,6 +4246,8 @@ pub const usage_build =...@@ -4009,6 +4246,8 @@ pub const usage_build =
4009 \\Options:4246 \\Options:
4010 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error4247 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
4011 \\ -fno-reference-trace Disable reference trace4248 \\ -fno-reference-trace Disable reference trace
4249 \\ -fsummary Print the build summary, even on success
4250 \\ -fno-summary Omit the build summary, even on failure
4012 \\ --build-file [file] Override path to build.zig4251 \\ --build-file [file] Override path to build.zig
4013 \\ --cache-dir [path] Override path to local Zig cache directory4252 \\ --cache-dir [path] Override path to local Zig cache directory
4014 \\ --global-cache-dir [path] Override path to global Zig cache directory4253 \\ --global-cache-dir [path] Override path to global Zig cache directory
...@@ -4021,7 +4260,6 @@ pub const usage_build =...@@ -4021,7 +4260,6 @@ pub const usage_build =
40214260
4022pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {4261pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4023 var color: Color = .auto;4262 var color: Color = .auto;
4024 var prominent_compile_errors: bool = false;
40254263
4026 // We want to release all the locks before executing the child process, so we make a nice4264 // We want to release all the locks before executing the child process, so we make a nice
4027 // big block here to ensure the cleanup gets run when we extract out our argv.4265 // big block here to ensure the cleanup gets run when we extract out our argv.
...@@ -4082,8 +4320,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4082,8 +4320,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4082 i += 1;4320 i += 1;
4083 override_global_cache_dir = args[i];4321 override_global_cache_dir = args[i];
4084 continue;4322 continue;
4085 } else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
4086 prominent_compile_errors = true;
4087 } else if (mem.eql(u8, arg, "-freference-trace")) {4323 } else if (mem.eql(u8, arg, "-freference-trace")) {
4088 try child_argv.append(arg);4324 try child_argv.append(arg);
4089 reference_trace = 256;4325 reference_trace = 256;
...@@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4201 .basename = exe_basename,4437 .basename = exe_basename,
4202 };4438 };
4203 var thread_pool: ThreadPool = undefined;4439 var thread_pool: ThreadPool = undefined;
4204 try thread_pool.init(gpa);4440 try thread_pool.init(.{ .allocator = gpa });
4205 defer thread_pool.deinit();4441 defer thread_pool.deinit();
42064442
4207 var cleanup_build_runner_dir: ?fs.Dir = null;4443 var cleanup_build_runner_dir: ?fs.Dir = null;
...@@ -4228,6 +4464,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4228,6 +4464,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4228 .root_src_path = "build_runner.zig",4464 .root_src_path = "build_runner.zig",
4229 };4465 };
42304466
4467 var build_pkg: Package = .{
4468 .root_src_directory = build_directory,
4469 .root_src_path = build_zig_basename,
4470 };
4231 if (!build_options.omit_pkg_fetching_code) {4471 if (!build_options.omit_pkg_fetching_code) {
4232 var http_client: std.http.Client = .{ .allocator = gpa };4472 var http_client: std.http.Client = .{ .allocator = gpa };
4233 defer http_client.deinit();4473 defer http_client.deinit();
...@@ -4247,9 +4487,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4247,9 +4487,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4247 var all_modules: Package.AllModules = .{};4487 var all_modules: Package.AllModules = .{};
4248 defer all_modules.deinit(gpa);4488 defer all_modules.deinit(gpa);
42494489
4490 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
4491 try wip_errors.init(gpa);
4492 defer wip_errors.deinit();
4493
4250 // Here we borrow main package's table and will replace it with a fresh4494 // Here we borrow main package's table and will replace it with a fresh
4251 // one after this process completes.4495 // one after this process completes.
4252 main_pkg.fetchAndAddDependencies(4496 const fetch_result = build_pkg.fetchAndAddDependencies(
4497 &main_pkg,
4253 arena,4498 arena,
4254 &thread_pool,4499 &thread_pool,
4255 &http_client,4500 &http_client,
...@@ -4259,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4259,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4259 &dependencies_source,4504 &dependencies_source,
4260 &build_roots_source,4505 &build_roots_source,
4261 "",4506 "",
4262 color,4507 &wip_errors,
4263 &all_modules,4508 &all_modules,
4264 ) catch |err| switch (err) {4509 );
4265 error.PackageFetchFailed => process.exit(1),4510 if (wip_errors.root_list.items.len > 0) {
4266 else => |e| return e,4511 var errors = try wip_errors.toOwnedBundle("");
4267 };4512 defer errors.deinit(gpa);
4513 errors.renderToStdErr(renderOptions(color));
4514 process.exit(1);
4515 }
4516 try fetch_result;
42684517
4269 try dependencies_source.appendSlice("};\npub const build_root = struct {\n");4518 try dependencies_source.appendSlice("};\npub const build_root = struct {\n");
4270 try dependencies_source.appendSlice(build_roots_source.items);4519 try dependencies_source.appendSlice(build_roots_source.items);
...@@ -4280,11 +4529,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4280,11 +4529,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4280 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);4529 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);
4281 try main_pkg.add(gpa, "@dependencies", deps_pkg);4530 try main_pkg.add(gpa, "@dependencies", deps_pkg);
4282 }4531 }
4283
4284 var build_pkg: Package = .{
4285 .root_src_directory = build_directory,
4286 .root_src_path = build_zig_basename,
4287 };
4288 try main_pkg.add(gpa, "@build", &build_pkg);4532 try main_pkg.add(gpa, "@build", &build_pkg);
42894533
4290 const comp = Compilation.create(gpa, .{4534 const comp = Compilation.create(gpa, .{
...@@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4312 defer comp.destroy();4556 defer comp.destroy();
43134557
4314 updateModule(gpa, comp, .none) catch |err| switch (err) {4558 updateModule(gpa, comp, .none) catch |err| switch (err) {
4315 error.SemanticAnalyzeFail => process.exit(1),4559 error.SemanticAnalyzeFail => process.exit(2),
4316 else => |e| return e,4560 else => |e| return e,
4317 };4561 };
4318 try comp.makeBinFileExecutable();4562 try comp.makeBinFileExecutable();
...@@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4336 switch (term) {4580 switch (term) {
4337 .Exited => |code| {4581 .Exited => |code| {
4338 if (code == 0) return cleanExit();4582 if (code == 0) return cleanExit();
4583 // Indicates that the build runner has reported compile errors
4584 // and this parent process does not need to report any further
4585 // diagnostics.
4586 if (code == 2) process.exit(2);
43394587
4340 if (prominent_compile_errors) {4588 const cmd = try std.mem.join(arena, " ", child_argv);
4341 fatal("the build command failed with exit code {d}", .{code});4589 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
4342 } else {
4343 const cmd = try std.mem.join(arena, " ", child_argv);
4344 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
4345 }
4346 },4590 },
4347 else => {4591 else => {
4348 const cmd = try std.mem.join(arena, " ", child_argv);4592 const cmd = try std.mem.join(arena, " ", child_argv);
...@@ -4356,7 +4600,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4356,7 +4600,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4356}4600}
43574601
4358fn readSourceFileToEndAlloc(4602fn readSourceFileToEndAlloc(
4359 allocator: mem.Allocator,4603 allocator: Allocator,
4360 input: *const fs.File,4604 input: *const fs.File,
4361 size_hint: ?usize,4605 size_hint: ?usize,
4362) ![:0]u8 {4606) ![:0]u8 {
...@@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4500 };4744 };
4501 defer tree.deinit(gpa);4745 defer tree.deinit(gpa);
45024746
4503 try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color);
4504 var has_ast_error = false;
4505 if (check_ast_flag) {4747 if (check_ast_flag) {
4506 const Module = @import("Module.zig");
4507 const AstGen = @import("AstGen.zig");
4508
4509 var file: Module.File = .{4748 var file: Module.File = .{
4510 .status = .never_loaded,4749 .status = .never_loaded,
4511 .source_loaded = true,4750 .source_loaded = true,
...@@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4528 defer file.zir.deinit(gpa);4767 defer file.zir.deinit(gpa);
45294768
4530 if (file.zir.hasCompileErrors()) {4769 if (file.zir.hasCompileErrors()) {
4531 var arena_instance = std.heap.ArenaAllocator.init(gpa);4770 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
4532 defer arena_instance.deinit();4771 try wip_errors.init(gpa);
4533 var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa);4772 defer wip_errors.deinit();
4534 defer errors.deinit();4773 try Compilation.addZirErrorMessages(&wip_errors, &file);
45354774 var error_bundle = try wip_errors.toOwnedBundle("");
4536 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);4775 defer error_bundle.deinit(gpa);
4537 const ttyconf: std.debug.TTY.Config = switch (color) {4776 error_bundle.renderToStdErr(renderOptions(color));
4538 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),4777 process.exit(2);
4539 .on => .escape_codes,
4540 .off => .no_color,
4541 };
4542 for (errors.items) |full_err_msg| {
4543 full_err_msg.renderToStdErr(ttyconf);
4544 }
4545 has_ast_error = true;
4546 }4778 }
4547 }4779 } else if (tree.errors.len != 0) {
4548 if (tree.errors.len != 0 or has_ast_error) {4780 try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
4549 process.exit(1);4781 process.exit(2);
4550 }4782 }
4551 const formatted = try tree.render(gpa);4783 const formatted = try tree.render(gpa);
4552 defer gpa.free(formatted);4784 defer gpa.free(formatted);
...@@ -4622,6 +4854,7 @@ const FmtError = error{...@@ -4622,6 +4854,7 @@ const FmtError = error{
4622 ConnectionResetByPeer,4854 ConnectionResetByPeer,
4623 LockViolation,4855 LockViolation,
4624 NetNameDeleted,4856 NetNameDeleted,
4857 InvalidArgument,
4625} || fs.File.OpenError;4858} || fs.File.OpenError;
46264859
4627fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {4860fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
...@@ -4687,12 +4920,13 @@ fn fmtPathFile(...@@ -4687,12 +4920,13 @@ fn fmtPathFile(
4687 if (stat.kind == .Directory)4920 if (stat.kind == .Directory)
4688 return error.IsDir;4921 return error.IsDir;
46894922
4923 const gpa = fmt.gpa;
4690 const source_code = try readSourceFileToEndAlloc(4924 const source_code = try readSourceFileToEndAlloc(
4691 fmt.gpa,4925 gpa,
4692 &source_file,4926 &source_file,
4693 std.math.cast(usize, stat.size) orelse return error.FileTooBig,4927 std.math.cast(usize, stat.size) orelse return error.FileTooBig,
4694 );4928 );
4695 defer fmt.gpa.free(source_code);4929 defer gpa.free(source_code);
46964930
4697 source_file.close();4931 source_file.close();
4698 file_closed = true;4932 file_closed = true;
...@@ -4700,19 +4934,16 @@ fn fmtPathFile(...@@ -4700,19 +4934,16 @@ fn fmtPathFile(
4700 // Add to set after no longer possible to get error.IsDir.4934 // Add to set after no longer possible to get error.IsDir.
4701 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;4935 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
47024936
4703 var tree = try Ast.parse(fmt.gpa, source_code, .zig);4937 var tree = try Ast.parse(gpa, source_code, .zig);
4704 defer tree.deinit(fmt.gpa);4938 defer tree.deinit(gpa);
47054939
4706 try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree, file_path, fmt.color);
4707 if (tree.errors.len != 0) {4940 if (tree.errors.len != 0) {
4941 try printAstErrorsToStderr(gpa, tree, file_path, fmt.color);
4708 fmt.any_error = true;4942 fmt.any_error = true;
4709 return;4943 return;
4710 }4944 }
47114945
4712 if (fmt.check_ast) {4946 if (fmt.check_ast) {
4713 const Module = @import("Module.zig");
4714 const AstGen = @import("AstGen.zig");
4715
4716 var file: Module.File = .{4947 var file: Module.File = .{
4717 .status = .never_loaded,4948 .status = .never_loaded,
4718 .source_loaded = true,4949 .source_loaded = true,
...@@ -4731,31 +4962,24 @@ fn fmtPathFile(...@@ -4731,31 +4962,24 @@ fn fmtPathFile(
4731 .root_decl = .none,4962 .root_decl = .none,
4732 };4963 };
47334964
4734 file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path);4965 file.pkg = try Package.create(gpa, null, file.sub_file_path);
4735 defer file.pkg.destroy(fmt.gpa);4966 defer file.pkg.destroy(gpa);
47364967
4737 if (stat.size > max_src_size)4968 if (stat.size > max_src_size)
4738 return error.FileTooBig;4969 return error.FileTooBig;
47394970
4740 file.zir = try AstGen.generate(fmt.gpa, file.tree);4971 file.zir = try AstGen.generate(gpa, file.tree);
4741 file.zir_loaded = true;4972 file.zir_loaded = true;
4742 defer file.zir.deinit(fmt.gpa);4973 defer file.zir.deinit(gpa);
47434974
4744 if (file.zir.hasCompileErrors()) {4975 if (file.zir.hasCompileErrors()) {
4745 var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa);4976 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
4746 defer arena_instance.deinit();4977 try wip_errors.init(gpa);
4747 var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa);4978 defer wip_errors.deinit();
4748 defer errors.deinit();4979 try Compilation.addZirErrorMessages(&wip_errors, &file);
47494980 var error_bundle = try wip_errors.toOwnedBundle("");
4750 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);4981 defer error_bundle.deinit(gpa);
4751 const ttyconf: std.debug.TTY.Config = switch (fmt.color) {4982 error_bundle.renderToStdErr(renderOptions(fmt.color));
4752 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
4753 .on => .escape_codes,
4754 .off => .no_color,
4755 };
4756 for (errors.items) |full_err_msg| {
4757 full_err_msg.renderToStdErr(ttyconf);
4758 }
4759 fmt.any_error = true;4983 fmt.any_error = true;
4760 }4984 }
4761 }4985 }
...@@ -4783,100 +5007,50 @@ fn fmtPathFile(...@@ -4783,100 +5007,50 @@ fn fmtPathFile(
4783 }5007 }
4784}5008}
47855009
4786pub fn printErrsMsgToStdErr(5010fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
4787 gpa: mem.Allocator,5011 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
4788 arena: mem.Allocator,5012 try wip_errors.init(gpa);
5013 defer wip_errors.deinit();
5014
5015 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);
5016
5017 var error_bundle = try wip_errors.toOwnedBundle("");
5018 defer error_bundle.deinit(gpa);
5019 error_bundle.renderToStdErr(renderOptions(color));
5020}
5021
5022pub fn putAstErrorsIntoBundle(
5023 gpa: Allocator,
4789 tree: Ast,5024 tree: Ast,
4790 path: []const u8,5025 path: []const u8,
4791 color: Color,5026 wip_errors: *std.zig.ErrorBundle.Wip,
4792) !void {5027) !void {
4793 const parse_errors: []const Ast.Error = tree.errors;5028 var file: Module.File = .{
4794 var i: usize = 0;5029 .status = .never_loaded,
4795 while (i < parse_errors.len) : (i += 1) {5030 .source_loaded = true,
4796 const parse_error = parse_errors[i];5031 .zir_loaded = false,
4797 const lok_token = parse_error.token;5032 .sub_file_path = path,
4798 const token_tags = tree.tokens.items(.tag);5033 .source = tree.source,
4799 const start_loc = tree.tokenLocation(0, lok_token);5034 .stat = .{
4800 const source_line = tree.source[start_loc.line_start..start_loc.line_end];5035 .size = 0,
48015036 .inode = 0,
4802 var text_buf = std.ArrayList(u8).init(gpa);5037 .mtime = 0,
4803 defer text_buf.deinit();5038 },
4804 const writer = text_buf.writer();5039 .tree = tree,
4805 try tree.renderError(parse_error, writer);5040 .tree_loaded = true,
4806 const text = try arena.dupe(u8, text_buf.items);5041 .zir = undefined,
48075042 .pkg = undefined,
4808 var notes_buffer: [2]Compilation.AllErrors.Message = undefined;5043 .root_decl = .none,
4809 var notes_len: usize = 0;5044 };
4810
4811 if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) {
4812 const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len);
4813 const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off;
4814 notes_buffer[notes_len] = .{
4815 .src = .{
4816 .src_path = path,
4817 .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{
4818 std.zig.fmtEscapes(tree.source[byte_offset..][0..1]),
4819 }),
4820 .span = .{ .start = byte_offset, .end = byte_offset + 1, .main = byte_offset },
4821 .line = @intCast(u32, start_loc.line),
4822 .column = @intCast(u32, start_loc.column) + bad_off,
4823 .source_line = source_line,
4824 },
4825 };
4826 notes_len += 1;
4827 }
4828
4829 for (parse_errors[i + 1 ..]) |note| {
4830 if (!note.is_note) break;
4831
4832 text_buf.items.len = 0;
4833 try tree.renderError(note, writer);
4834 const note_loc = tree.tokenLocation(0, note.token);
4835 const byte_offset = @intCast(u32, note_loc.line_start);
4836 notes_buffer[notes_len] = .{
4837 .src = .{
4838 .src_path = path,
4839 .msg = try arena.dupe(u8, text_buf.items),
4840 .span = .{
4841 .start = byte_offset,
4842 .end = byte_offset + @intCast(u32, tree.tokenSlice(note.token).len),
4843 .main = byte_offset,
4844 },
4845 .line = @intCast(u32, note_loc.line),
4846 .column = @intCast(u32, note_loc.column),
4847 .source_line = tree.source[note_loc.line_start..note_loc.line_end],
4848 },
4849 };
4850 i += 1;
4851 notes_len += 1;
4852 }
48535045
4854 const extra_offset = tree.errorOffset(parse_error);5046 file.pkg = try Package.create(gpa, null, path);
4855 const byte_offset = @intCast(u32, start_loc.line_start) + extra_offset;5047 defer file.pkg.destroy(gpa);
4856 const message: Compilation.AllErrors.Message = .{
4857 .src = .{
4858 .src_path = path,
4859 .msg = text,
4860 .span = .{
4861 .start = byte_offset,
4862 .end = byte_offset + @intCast(u32, tree.tokenSlice(lok_token).len),
4863 .main = byte_offset,
4864 },
4865 .line = @intCast(u32, start_loc.line),
4866 .column = @intCast(u32, start_loc.column) + extra_offset,
4867 .source_line = source_line,
4868 .notes = notes_buffer[0..notes_len],
4869 },
4870 };
48715048
4872 const ttyconf: std.debug.TTY.Config = switch (color) {5049 file.zir = try AstGen.generate(gpa, file.tree);
4873 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),5050 file.zir_loaded = true;
4874 .on => .escape_codes,5051 defer file.zir.deinit(gpa);
4875 .off => .no_color,
4876 };
48775052
4878 message.renderToStdErr(ttyconf);5053 try Compilation.addZirErrorMessages(wip_errors, &file);
4879 }
4880}5054}
48815055
4882pub const info_zen =5056pub const info_zen =
...@@ -5324,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat...@@ -5324,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat
5324 return std.zig.system.NativeTargetInfo.detect(cross_target);5498 return std.zig.system.NativeTargetInfo.detect(cross_target);
5325}5499}
53265500
5327/// Indicate that we are now terminating with a successful exit code.
5328/// In debug builds, this is a no-op, so that the calling code's
5329/// cleanup mechanisms are tested and so that external tools that
5330/// check for resource leaks can be accurate. In release builds, this
5331/// calls exit(0), and does not return.
5332pub fn cleanExit() void {
5333 if (builtin.mode == .Debug) {
5334 return;
5335 } else {
5336 process.exit(0);
5337 }
5338}
5339
5340const usage_ast_check =5501const usage_ast_check =
5341 \\Usage: zig ast-check [file]5502 \\Usage: zig ast-check [file]
5342 \\5503 \\
...@@ -5359,8 +5520,6 @@ pub fn cmdAstCheck(...@@ -5359,8 +5520,6 @@ pub fn cmdAstCheck(
5359 arena: Allocator,5520 arena: Allocator,
5360 args: []const []const u8,5521 args: []const []const u8,
5361) !void {5522) !void {
5362 const Module = @import("Module.zig");
5363 const AstGen = @import("AstGen.zig");
5364 const Zir = @import("Zir.zig");5523 const Zir = @import("Zir.zig");
53655524
5366 var color: Color = .auto;5525 var color: Color = .auto;
...@@ -5450,26 +5609,18 @@ pub fn cmdAstCheck(...@@ -5450,26 +5609,18 @@ pub fn cmdAstCheck(
5450 file.tree_loaded = true;5609 file.tree_loaded = true;
5451 defer file.tree.deinit(gpa);5610 defer file.tree.deinit(gpa);
54525611
5453 try printErrsMsgToStdErr(gpa, arena, file.tree, file.sub_file_path, color);
5454 if (file.tree.errors.len != 0) {
5455 process.exit(1);
5456 }
5457
5458 file.zir = try AstGen.generate(gpa, file.tree);5612 file.zir = try AstGen.generate(gpa, file.tree);
5459 file.zir_loaded = true;5613 file.zir_loaded = true;
5460 defer file.zir.deinit(gpa);5614 defer file.zir.deinit(gpa);
54615615
5462 if (file.zir.hasCompileErrors()) {5616 if (file.zir.hasCompileErrors()) {
5463 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);5617 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5464 try Compilation.AllErrors.addZir(arena, &errors, &file);5618 try wip_errors.init(gpa);
5465 const ttyconf: std.debug.TTY.Config = switch (color) {5619 defer wip_errors.deinit();
5466 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),5620 try Compilation.addZirErrorMessages(&wip_errors, &file);
5467 .on => .escape_codes,5621 var error_bundle = try wip_errors.toOwnedBundle("");
5468 .off => .no_color,5622 defer error_bundle.deinit(gpa);
5469 };5623 error_bundle.renderToStdErr(renderOptions(color));
5470 for (errors.items) |full_err_msg| {
5471 full_err_msg.renderToStdErr(ttyconf);
5472 }
5473 process.exit(1);5624 process.exit(1);
5474 }5625 }
54755626
...@@ -5527,8 +5678,7 @@ pub fn cmdChangelist(...@@ -5527,8 +5678,7 @@ pub fn cmdChangelist(
5527 arena: Allocator,5678 arena: Allocator,
5528 args: []const []const u8,5679 args: []const []const u8,
5529) !void {5680) !void {
5530 const Module = @import("Module.zig");5681 const color: Color = .auto;
5531 const AstGen = @import("AstGen.zig");
5532 const Zir = @import("Zir.zig");5682 const Zir = @import("Zir.zig");
55335683
5534 const old_source_file = args[0];5684 const old_source_file = args[0];
...@@ -5576,22 +5726,18 @@ pub fn cmdChangelist(...@@ -5576,22 +5726,18 @@ pub fn cmdChangelist(
5576 file.tree_loaded = true;5726 file.tree_loaded = true;
5577 defer file.tree.deinit(gpa);5727 defer file.tree.deinit(gpa);
55785728
5579 try printErrsMsgToStdErr(gpa, arena, file.tree, old_source_file, .auto);
5580 if (file.tree.errors.len != 0) {
5581 process.exit(1);
5582 }
5583
5584 file.zir = try AstGen.generate(gpa, file.tree);5729 file.zir = try AstGen.generate(gpa, file.tree);
5585 file.zir_loaded = true;5730 file.zir_loaded = true;
5586 defer file.zir.deinit(gpa);5731 defer file.zir.deinit(gpa);
55875732
5588 if (file.zir.hasCompileErrors()) {5733 if (file.zir.hasCompileErrors()) {
5589 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);5734 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5590 try Compilation.AllErrors.addZir(arena, &errors, &file);5735 try wip_errors.init(gpa);
5591 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());5736 defer wip_errors.deinit();
5592 for (errors.items) |full_err_msg| {5737 try Compilation.addZirErrorMessages(&wip_errors, &file);
5593 full_err_msg.renderToStdErr(ttyconf);5738 var error_bundle = try wip_errors.toOwnedBundle("");
5594 }5739 defer error_bundle.deinit(gpa);
5740 error_bundle.renderToStdErr(renderOptions(color));
5595 process.exit(1);5741 process.exit(1);
5596 }5742 }
55975743
...@@ -5613,11 +5759,6 @@ pub fn cmdChangelist(...@@ -5613,11 +5759,6 @@ pub fn cmdChangelist(
5613 var new_tree = try Ast.parse(gpa, new_source, .zig);5759 var new_tree = try Ast.parse(gpa, new_source, .zig);
5614 defer new_tree.deinit(gpa);5760 defer new_tree.deinit(gpa);
56155761
5616 try printErrsMsgToStdErr(gpa, arena, new_tree, new_source_file, .auto);
5617 if (new_tree.errors.len != 0) {
5618 process.exit(1);
5619 }
5620
5621 var old_zir = file.zir;5762 var old_zir = file.zir;
5622 defer old_zir.deinit(gpa);5763 defer old_zir.deinit(gpa);
5623 file.zir_loaded = false;5764 file.zir_loaded = false;
...@@ -5625,12 +5766,13 @@ pub fn cmdChangelist(...@@ -5625,12 +5766,13 @@ pub fn cmdChangelist(
5625 file.zir_loaded = true;5766 file.zir_loaded = true;
56265767
5627 if (file.zir.hasCompileErrors()) {5768 if (file.zir.hasCompileErrors()) {
5628 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);5769 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5629 try Compilation.AllErrors.addZir(arena, &errors, &file);5770 try wip_errors.init(gpa);
5630 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());5771 defer wip_errors.deinit();
5631 for (errors.items) |full_err_msg| {5772 try Compilation.addZirErrorMessages(&wip_errors, &file);
5632 full_err_msg.renderToStdErr(ttyconf);5773 var error_bundle = try wip_errors.toOwnedBundle("");
5633 }5774 defer error_bundle.deinit(gpa);
5775 error_bundle.renderToStdErr(renderOptions(color));
5634 process.exit(1);5776 process.exit(1);
5635 }5777 }
56365778
...@@ -5891,3 +6033,20 @@ const ClangSearchSanitizer = struct {...@@ -5891,3 +6033,20 @@ const ClangSearchSanitizer = struct {
5891 iframework: bool = false,6033 iframework: bool = false,
5892 };6034 };
5893};6035};
6036
6037fn get_tty_conf(color: Color) std.debug.TTY.Config {
6038 return switch (color) {
6039 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
6040 .on => .escape_codes,
6041 .off => .no_color,
6042 };
6043}
6044
6045fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
6046 const ttyconf = get_tty_conf(color);
6047 return .{
6048 .ttyconf = ttyconf,
6049 .include_source_line = ttyconf != .no_color,
6050 .include_reference_trace = ttyconf != .no_color,
6051 };
6052}
src/mingw.zig+7-7
...@@ -19,7 +19,7 @@ pub const CRTFile = enum {...@@ -19,7 +19,7 @@ pub const CRTFile = enum {
19 uuid_lib,19 uuid_lib,
20};20};
2121
22pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {22pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
23 if (!build_options.have_llvm) {23 if (!build_options.have_llvm) {
24 return error.ZigCompilerNotBuiltWithLLVMExtensions;24 return error.ZigCompilerNotBuiltWithLLVMExtensions;
25 }25 }
...@@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
41 //"-D_UNICODE",41 //"-D_UNICODE",
42 //"-DWPRFLAG=1",42 //"-DWPRFLAG=1",
43 });43 });
44 return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{44 return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &.{
45 .{45 .{
46 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{46 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
47 "libc", "mingw", "crt", "crtexe.c",47 "libc", "mingw", "crt", "crtexe.c",
...@@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
60 "-U__CRTDLL__",60 "-U__CRTDLL__",
61 "-D__MSVCRT__",61 "-D__MSVCRT__",
62 });62 });
63 return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{63 return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &.{
64 .{64 .{
65 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{65 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
66 "libc", "mingw", "crt", "crtdll.c",66 "libc", "mingw", "crt", "crtdll.c",
...@@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
100 .extra_flags = args.items,100 .extra_flags = args.items,
101 };101 };
102 }102 }
103 return comp.build_crt_file("mingw32", .Lib, &c_source_files);103 return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, &c_source_files);
104 },104 },
105105
106 .msvcrt_os_lib => {106 .msvcrt_os_lib => {
...@@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
148 };148 };
149 }149 }
150 }150 }
151 return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items);151 return comp.build_crt_file("msvcrt-os", .Lib, .@"mingw-w64 msvcrt-os.lib", prog_node, c_source_files.items);
152 },152 },
153153
154 .mingwex_lib => {154 .mingwex_lib => {
...@@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
211 } else {211 } else {
212 @panic("unsupported arch");212 @panic("unsupported arch");
213 }213 }
214 return comp.build_crt_file("mingwex", .Lib, c_source_files.items);214 return comp.build_crt_file("mingwex", .Lib, .@"mingw-w64 mingwex.lib", prog_node, c_source_files.items);
215 },215 },
216216
217 .uuid_lib => {217 .uuid_lib => {
...@@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
244 .extra_flags = extra_flags,244 .extra_flags = extra_flags,
245 };245 };
246 }246 }
247 return comp.build_crt_file("uuid", .Lib, &c_source_files);247 return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", prog_node, &c_source_files);
248 },248 },
249 }249 }
250}250}
src/musl.zig+8-8
...@@ -17,7 +17,7 @@ pub const CRTFile = enum {...@@ -17,7 +17,7 @@ pub const CRTFile = enum {
17 libc_so,17 libc_so,
18};18};
1919
20pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {20pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
21 if (!build_options.have_llvm) {21 if (!build_options.have_llvm) {
22 return error.ZigCompilerNotBuiltWithLLVMExtensions;22 return error.ZigCompilerNotBuiltWithLLVMExtensions;
23 }23 }
...@@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
33 try args.appendSlice(&[_][]const u8{33 try args.appendSlice(&[_][]const u8{
34 "-Qunused-arguments",34 "-Qunused-arguments",
35 });35 });
36 return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{36 return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{
37 .{37 .{
38 .src_path = try start_asm_path(comp, arena, "crti.s"),38 .src_path = try start_asm_path(comp, arena, "crti.s"),
39 .extra_flags = args.items,39 .extra_flags = args.items,
...@@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
46 try args.appendSlice(&[_][]const u8{46 try args.appendSlice(&[_][]const u8{
47 "-Qunused-arguments",47 "-Qunused-arguments",
48 });48 });
49 return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{49 return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{
50 .{50 .{
51 .src_path = try start_asm_path(comp, arena, "crtn.s"),51 .src_path = try start_asm_path(comp, arena, "crtn.s"),
52 .extra_flags = args.items,52 .extra_flags = args.items,
...@@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
60 "-fno-stack-protector",60 "-fno-stack-protector",
61 "-DCRT",61 "-DCRT",
62 });62 });
63 return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{63 return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{
64 .{64 .{
65 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{65 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
66 "libc", "musl", "crt", "crt1.c",66 "libc", "musl", "crt", "crt1.c",
...@@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
77 "-fno-stack-protector",77 "-fno-stack-protector",
78 "-DCRT",78 "-DCRT",
79 });79 });
80 return comp.build_crt_file("rcrt1", .Obj, &[1]Compilation.CSourceFile{80 return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{
81 .{81 .{
82 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{82 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
83 "libc", "musl", "crt", "rcrt1.c",83 "libc", "musl", "crt", "rcrt1.c",
...@@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
94 "-fno-stack-protector",94 "-fno-stack-protector",
95 "-DCRT",95 "-DCRT",
96 });96 });
97 return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{97 return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{
98 .{98 .{
99 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{99 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
100 "libc", "musl", "crt", "Scrt1.c",100 "libc", "musl", "crt", "Scrt1.c",
...@@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
187 .extra_flags = args.items,187 .extra_flags = args.items,
188 };188 };
189 }189 }
190 return comp.build_crt_file("c", .Lib, c_source_files.items);190 return comp.build_crt_file("c", .Lib, .@"musl libc.a", prog_node, c_source_files.items);
191 },191 },
192 .libc_so => {192 .libc_so => {
193 const target = comp.getTarget();193 const target = comp.getTarget();
...@@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
241 });241 });
242 defer sub_compilation.destroy();242 defer sub_compilation.destroy();
243243
244 try sub_compilation.updateSubCompilation();244 try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node);
245245
246 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);246 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
247247
src/objcopy.zig+45-5
...@@ -4,22 +4,25 @@ const fs = std.fs;...@@ -4,22 +4,25 @@ const fs = std.fs;
4const elf = std.elf;4const elf = std.elf;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const File = std.fs.File;6const File = std.fs.File;
7const assert = std.debug.assert;
8
7const main = @import("main.zig");9const main = @import("main.zig");
8const fatal = main.fatal;10const fatal = main.fatal;
9const cleanExit = main.cleanExit;11const Server = std.zig.Server;
12const build_options = @import("build_options");
1013
11pub fn cmdObjCopy(14pub fn cmdObjCopy(
12 gpa: Allocator,15 gpa: Allocator,
13 arena: Allocator,16 arena: Allocator,
14 args: []const []const u8,17 args: []const []const u8,
15) !void {18) !void {
16 _ = gpa;
17 var i: usize = 0;19 var i: usize = 0;
18 var opt_out_fmt: ?std.Target.ObjectFormat = null;20 var opt_out_fmt: ?std.Target.ObjectFormat = null;
19 var opt_input: ?[]const u8 = null;21 var opt_input: ?[]const u8 = null;
20 var opt_output: ?[]const u8 = null;22 var opt_output: ?[]const u8 = null;
21 var only_section: ?[]const u8 = null;23 var only_section: ?[]const u8 = null;
22 var pad_to: ?u64 = null;24 var pad_to: ?u64 = null;
25 var listen = false;
23 while (i < args.len) : (i += 1) {26 while (i < args.len) : (i += 1) {
24 const arg = args[i];27 const arg = args[i];
25 if (!mem.startsWith(u8, arg, "-")) {28 if (!mem.startsWith(u8, arg, "-")) {
...@@ -54,6 +57,8 @@ pub fn cmdObjCopy(...@@ -54,6 +57,8 @@ pub fn cmdObjCopy(
54 i += 1;57 i += 1;
55 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});58 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
56 only_section = args[i];59 only_section = args[i];
60 } else if (mem.eql(u8, arg, "--listen=-")) {
61 listen = true;
57 } else if (mem.startsWith(u8, arg, "--only-section=")) {62 } else if (mem.startsWith(u8, arg, "--only-section=")) {
58 only_section = arg["--output-target=".len..];63 only_section = arg["--output-target=".len..];
59 } else if (mem.eql(u8, arg, "--pad-to")) {64 } else if (mem.eql(u8, arg, "--pad-to")) {
...@@ -102,10 +107,45 @@ pub fn cmdObjCopy(...@@ -102,10 +107,45 @@ pub fn cmdObjCopy(
102 .only_section = only_section,107 .only_section = only_section,
103 .pad_to = pad_to,108 .pad_to = pad_to,
104 });109 });
105 return cleanExit();
106 },110 },
107 else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}),111 else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}),
108 }112 }
113
114 if (listen) {
115 var server = try Server.init(.{
116 .gpa = gpa,
117 .in = std.io.getStdIn(),
118 .out = std.io.getStdOut(),
119 .zig_version = build_options.version,
120 });
121 defer server.deinit();
122
123 var seen_update = false;
124 while (true) {
125 const hdr = try server.receiveMessage();
126 switch (hdr.tag) {
127 .exit => {
128 return std.process.cleanExit();
129 },
130 .update => {
131 if (seen_update) {
132 std.debug.print("zig objcopy only supports 1 update for now\n", .{});
133 std.process.exit(1);
134 }
135 seen_update = true;
136
137 try server.serveEmitBinPath(output, .{
138 .flags = .{ .cache_hit = false },
139 });
140 },
141 else => {
142 std.debug.print("unsupported message: {s}", .{@tagName(hdr.tag)});
143 std.process.exit(1);
144 },
145 }
146 }
147 }
148 return std.process.cleanExit();
109}149}
110150
111const usage =151const usage =
...@@ -417,7 +457,7 @@ const HexWriter = struct {...@@ -417,7 +457,7 @@ const HexWriter = struct {
417 }457 }
418458
419 fn Address(address: u32) Record {459 fn Address(address: u32) Record {
420 std.debug.assert(address > 0xFFFF);460 assert(address > 0xFFFF);
421 const segment = @intCast(u16, address / 0x10000);461 const segment = @intCast(u16, address / 0x10000);
422 if (address > 0xFFFFF) {462 if (address > 0xFFFFF) {
423 return Record{463 return Record{
...@@ -460,7 +500,7 @@ const HexWriter = struct {...@@ -460,7 +500,7 @@ const HexWriter = struct {
460 const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len;500 const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len;
461 var outbuf: [BUFSIZE]u8 = undefined;501 var outbuf: [BUFSIZE]u8 = undefined;
462 const payload_bytes = self.getPayloadBytes();502 const payload_bytes = self.getPayloadBytes();
463 std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN);503 assert(payload_bytes.len <= MAX_PAYLOAD_LEN);
464504
465 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{505 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{
466 @intCast(u8, payload_bytes.len),506 @intCast(u8, payload_bytes.len),
src/print_air.zig+1
...@@ -194,6 +194,7 @@ const Writer = struct {...@@ -194,6 +194,7 @@ const Writer = struct {
194 .c_va_end,194 .c_va_end,
195 => try w.writeUnOp(s, inst),195 => try w.writeUnOp(s, inst),
196196
197 .trap,
197 .breakpoint,198 .breakpoint,
198 .unreach,199 .unreach,
199 .ret_addr,200 .ret_addr,
src/print_zir.zig+2-1
...@@ -196,7 +196,6 @@ const Writer = struct {...@@ -196,7 +196,6 @@ const Writer = struct {
196 .error_name,196 .error_name,
197 .panic,197 .panic,
198 .panic_comptime,198 .panic_comptime,
199 .set_cold,
200 .set_runtime_safety,199 .set_runtime_safety,
201 .sqrt,200 .sqrt,
202 .sin,201 .sin,
...@@ -411,6 +410,7 @@ const Writer = struct {...@@ -411,6 +410,7 @@ const Writer = struct {
411 .alloc_inferred_comptime_mut,410 .alloc_inferred_comptime_mut,
412 .ret_ptr,411 .ret_ptr,
413 .ret_type,412 .ret_type,
413 .trap,
414 => try self.writeNode(stream, inst),414 => try self.writeNode(stream, inst),
415415
416 .error_value,416 .error_value,
...@@ -503,6 +503,7 @@ const Writer = struct {...@@ -503,6 +503,7 @@ const Writer = struct {
503 .fence,503 .fence,
504 .set_float_mode,504 .set_float_mode,
505 .set_align_stack,505 .set_align_stack,
506 .set_cold,
506 .wasm_memory_size,507 .wasm_memory_size,
507 .error_to_int,508 .error_to_int,
508 .int_to_error,509 .int_to_error,
src/register_manager.zig+3
...@@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{...@@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{
19 /// Can happen when spilling an instruction in codegen runs out of19 /// Can happen when spilling an instruction in codegen runs out of
20 /// memory, so we propagate that error20 /// memory, so we propagate that error
21 OutOfMemory,21 OutOfMemory,
22 /// Can happen when spilling an instruction in codegen triggers integer
23 /// overflow, so we propagate that error
24 Overflow,
22 /// Can happen when spilling an instruction triggers a codegen25 /// Can happen when spilling an instruction triggers a codegen
23 /// error, so we propagate that error26 /// error, so we propagate that error
24 CodegenFail,27 CodegenFail,
src/test.zig deleted-1984
...@@ -1,1984 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Allocator = std.mem.Allocator;
4const CrossTarget = std.zig.CrossTarget;
5const print = std.debug.print;
6const assert = std.debug.assert;
7
8const link = @import("link.zig");
9const Compilation = @import("Compilation.zig");
10const Package = @import("Package.zig");
11const introspect = @import("introspect.zig");
12const build_options = @import("build_options");
13const ThreadPool = @import("ThreadPool.zig");
14const WaitGroup = @import("WaitGroup.zig");
15const zig_h = link.File.C.zig_h;
16
17const enable_qemu: bool = build_options.enable_qemu;
18const enable_wine: bool = build_options.enable_wine;
19const enable_wasmtime: bool = build_options.enable_wasmtime;
20const enable_darling: bool = build_options.enable_darling;
21const enable_rosetta: bool = build_options.enable_rosetta;
22const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir;
23const skip_stage1 = true;
24
25const hr = "=" ** 80;
26
27test {
28 const use_gpa = build_options.force_gpa or !builtin.link_libc;
29 const gpa = gpa: {
30 if (use_gpa) {
31 break :gpa std.testing.allocator;
32 }
33 // We would prefer to use raw libc allocator here, but cannot
34 // use it if it won't support the alignment we need.
35 if (@alignOf(std.c.max_align_t) < @alignOf(i128)) {
36 break :gpa std.heap.c_allocator;
37 }
38 break :gpa std.heap.raw_c_allocator;
39 };
40
41 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
42 defer arena_allocator.deinit();
43 const arena = arena_allocator.allocator();
44
45 var ctx = TestContext.init(gpa, arena);
46 defer ctx.deinit();
47
48 {
49 const dir_path = try std.fs.path.join(arena, &.{
50 std.fs.path.dirname(@src().file).?, "..", "test", "cases",
51 });
52
53 var dir = try std.fs.cwd().openIterableDir(dir_path, .{});
54 defer dir.close();
55
56 ctx.addTestCasesFromDir(dir);
57 }
58
59 try @import("../test/cases.zig").addCases(&ctx);
60
61 try ctx.run();
62}
63
64const ErrorMsg = union(enum) {
65 src: struct {
66 src_path: []const u8,
67 msg: []const u8,
68 // maxint means match anything
69 // this is a workaround for stage1 compiler bug I ran into when making it ?u32
70 line: u32,
71 // maxint means match anything
72 // this is a workaround for stage1 compiler bug I ran into when making it ?u32
73 column: u32,
74 kind: Kind,
75 count: u32,
76 },
77 plain: struct {
78 msg: []const u8,
79 kind: Kind,
80 count: u32,
81 },
82
83 const Kind = enum {
84 @"error",
85 note,
86 };
87
88 fn init(other: Compilation.AllErrors.Message, kind: Kind) ErrorMsg {
89 switch (other) {
90 .src => |src| return .{
91 .src = .{
92 .src_path = src.src_path,
93 .msg = src.msg,
94 .line = @intCast(u32, src.line),
95 .column = @intCast(u32, src.column),
96 .kind = kind,
97 .count = src.count,
98 },
99 },
100 .plain => |plain| return .{
101 .plain = .{
102 .msg = plain.msg,
103 .kind = kind,
104 .count = plain.count,
105 },
106 },
107 }
108 }
109
110 pub fn format(
111 self: ErrorMsg,
112 comptime fmt: []const u8,
113 options: std.fmt.FormatOptions,
114 writer: anytype,
115 ) !void {
116 _ = fmt;
117 _ = options;
118 switch (self) {
119 .src => |src| {
120 if (!std.mem.eql(u8, src.src_path, "?") or
121 src.line != std.math.maxInt(u32) or
122 src.column != std.math.maxInt(u32))
123 {
124 try writer.print("{s}:", .{src.src_path});
125 if (src.line != std.math.maxInt(u32)) {
126 try writer.print("{d}:", .{src.line + 1});
127 } else {
128 try writer.writeAll("?:");
129 }
130 if (src.column != std.math.maxInt(u32)) {
131 try writer.print("{d}: ", .{src.column + 1});
132 } else {
133 try writer.writeAll("?: ");
134 }
135 }
136 try writer.print("{s}: {s}", .{ @tagName(src.kind), src.msg });
137 if (src.count != 1) {
138 try writer.print(" ({d} times)", .{src.count});
139 }
140 },
141 .plain => |plain| {
142 try writer.print("{s}: {s}", .{ @tagName(plain.kind), plain.msg });
143 if (plain.count != 1) {
144 try writer.print(" ({d} times)", .{plain.count});
145 }
146 },
147 }
148 }
149};
150
151/// Default config values for known test manifest key-value pairings.
152/// Currently handled defaults are:
153/// * backend
154/// * target
155/// * output_mode
156/// * is_test
157const TestManifestConfigDefaults = struct {
158 /// Asserts if the key doesn't exist - yep, it's an oversight alright.
159 fn get(@"type": TestManifest.Type, key: []const u8) []const u8 {
160 if (std.mem.eql(u8, key, "backend")) {
161 return "stage2";
162 } else if (std.mem.eql(u8, key, "target")) {
163 comptime {
164 var defaults: []const u8 = "";
165 // TODO should we only return "mainstream" targets by default here?
166 // TODO we should also specify ABIs explicitly as the backends are
167 // getting more and more complete
168 // Linux
169 inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| {
170 defaults = defaults ++ arch ++ "-linux" ++ ",";
171 }
172 // macOS
173 inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| {
174 defaults = defaults ++ arch ++ "-macos" ++ ",";
175 }
176 // Windows
177 defaults = defaults ++ "x86_64-windows" ++ ",";
178 // Wasm
179 defaults = defaults ++ "wasm32-wasi";
180 return defaults;
181 }
182 } else if (std.mem.eql(u8, key, "output_mode")) {
183 return switch (@"type") {
184 .@"error" => "Obj",
185 .run => "Exe",
186 .cli => @panic("TODO test harness for CLI tests"),
187 };
188 } else if (std.mem.eql(u8, key, "is_test")) {
189 return "0";
190 } else unreachable;
191 }
192};
193
194/// Manifest syntax example:
195/// (see https://github.com/ziglang/zig/issues/11288)
196///
197/// error
198/// backend=stage1,stage2
199/// output_mode=exe
200///
201/// :3:19: error: foo
202///
203/// run
204/// target=x86_64-linux,aarch64-macos
205///
206/// I am expected stdout! Hello!
207///
208/// cli
209///
210/// build test
211const TestManifest = struct {
212 type: Type,
213 config_map: std.StringHashMap([]const u8),
214 trailing_bytes: []const u8 = "",
215
216 const Type = enum {
217 @"error",
218 run,
219 cli,
220 };
221
222 const TrailingIterator = struct {
223 inner: std.mem.TokenIterator(u8),
224
225 fn next(self: *TrailingIterator) ?[]const u8 {
226 const next_inner = self.inner.next() orelse return null;
227 return std.mem.trim(u8, next_inner[2..], " \t");
228 }
229 };
230
231 fn ConfigValueIterator(comptime T: type) type {
232 return struct {
233 inner: std.mem.SplitIterator(u8),
234
235 fn next(self: *@This()) !?T {
236 const next_raw = self.inner.next() orelse return null;
237 const parseFn = getDefaultParser(T);
238 return try parseFn(next_raw);
239 }
240 };
241 }
242
243 fn parse(arena: Allocator, bytes: []const u8) !TestManifest {
244 // The manifest is the last contiguous block of comments in the file
245 // We scan for the beginning by searching backward for the first non-empty line that does not start with "//"
246 var start: ?usize = null;
247 var end: usize = bytes.len;
248 if (bytes.len > 0) {
249 var cursor: usize = bytes.len - 1;
250 while (true) {
251 // Move to beginning of line
252 while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1;
253
254 if (std.mem.startsWith(u8, bytes[cursor..], "//")) {
255 start = cursor; // Contiguous comment line, include in manifest
256 } else {
257 if (start != null) break; // Encountered non-comment line, end of manifest
258
259 // We ignore all-whitespace lines following the comment block, but anything else
260 // means that there is no manifest present.
261 if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) {
262 end = cursor;
263 } else break; // If it's not whitespace, there is no manifest
264 }
265
266 // Move to previous line
267 if (cursor != 0) cursor -= 1 else break;
268 }
269 }
270
271 const actual_start = start orelse return error.MissingTestManifest;
272 const manifest_bytes = bytes[actual_start..end];
273
274 var it = std.mem.tokenize(u8, manifest_bytes, "\r\n");
275
276 // First line is the test type
277 const tt: Type = blk: {
278 const line = it.next() orelse return error.MissingTestCaseType;
279 const raw = std.mem.trim(u8, line[2..], " \t");
280 if (std.mem.eql(u8, raw, "error")) {
281 break :blk .@"error";
282 } else if (std.mem.eql(u8, raw, "run")) {
283 break :blk .run;
284 } else if (std.mem.eql(u8, raw, "cli")) {
285 break :blk .cli;
286 } else {
287 std.log.warn("unknown test case type requested: {s}", .{raw});
288 return error.UnknownTestCaseType;
289 }
290 };
291
292 var manifest: TestManifest = .{
293 .type = tt,
294 .config_map = std.StringHashMap([]const u8).init(arena),
295 };
296
297 // Any subsequent line until a blank comment line is key=value(s) pair
298 while (it.next()) |line| {
299 const trimmed = std.mem.trim(u8, line[2..], " \t");
300 if (trimmed.len == 0) break;
301
302 // Parse key=value(s)
303 var kv_it = std.mem.split(u8, trimmed, "=");
304 const key = kv_it.first();
305 try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig);
306 }
307
308 // Finally, trailing is expected output
309 manifest.trailing_bytes = manifest_bytes[it.index..];
310
311 return manifest;
312 }
313
314 fn getConfigForKey(
315 self: TestManifest,
316 key: []const u8,
317 comptime T: type,
318 ) ConfigValueIterator(T) {
319 const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key);
320 return ConfigValueIterator(T){
321 .inner = std.mem.split(u8, bytes, ","),
322 };
323 }
324
325 fn getConfigForKeyAlloc(
326 self: TestManifest,
327 allocator: Allocator,
328 key: []const u8,
329 comptime T: type,
330 ) ![]const T {
331 var out = std.ArrayList(T).init(allocator);
332 defer out.deinit();
333 var it = self.getConfigForKey(key, T);
334 while (try it.next()) |item| {
335 try out.append(item);
336 }
337 return try out.toOwnedSlice();
338 }
339
340 fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T {
341 var it = self.getConfigForKey(key, T);
342 const res = (try it.next()) orelse unreachable;
343 assert((try it.next()) == null);
344 return res;
345 }
346
347 fn trailing(self: TestManifest) TrailingIterator {
348 return .{
349 .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"),
350 };
351 }
352
353 fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 {
354 var out = std.ArrayList([]const u8).init(allocator);
355 defer out.deinit();
356 var it = self.trailing();
357 while (it.next()) |line| {
358 try out.append(line);
359 }
360 return try out.toOwnedSlice();
361 }
362
363 fn ParseFn(comptime T: type) type {
364 return fn ([]const u8) anyerror!T;
365 }
366
367 fn getDefaultParser(comptime T: type) ParseFn(T) {
368 if (T == CrossTarget) return struct {
369 fn parse(str: []const u8) anyerror!T {
370 var opts = CrossTarget.ParseOptions{
371 .arch_os_abi = str,
372 };
373 return try CrossTarget.parse(opts);
374 }
375 }.parse;
376
377 switch (@typeInfo(T)) {
378 .Int => return struct {
379 fn parse(str: []const u8) anyerror!T {
380 return try std.fmt.parseInt(T, str, 0);
381 }
382 }.parse,
383 .Bool => return struct {
384 fn parse(str: []const u8) anyerror!T {
385 const as_int = try std.fmt.parseInt(u1, str, 0);
386 return as_int > 0;
387 }
388 }.parse,
389 .Enum => return struct {
390 fn parse(str: []const u8) anyerror!T {
391 return std.meta.stringToEnum(T, str) orelse {
392 std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str });
393 return error.UnknownEnumVariant;
394 };
395 }
396 }.parse,
397 .Struct => @compileError("no default parser for " ++ @typeName(T)),
398 else => @compileError("no default parser for " ++ @typeName(T)),
399 }
400 }
401};
402
403const TestStrategy = enum {
404 /// Execute tests as independent compilations, unless they are explicitly
405 /// incremental ("foo.0.zig", "foo.1.zig", etc.)
406 independent,
407 /// Execute all tests as incremental updates to a single compilation. Explicitly
408 /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order
409 incremental,
410};
411
412/// Iterates a set of filenames extracting batches that are either incremental
413/// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.).
414/// Assumes filenames are sorted.
415const TestIterator = struct {
416 start: usize = 0,
417 end: usize = 0,
418 filenames: []const []const u8,
419 /// reset on each call to `next`
420 index: usize = 0,
421
422 const Error = error{InvalidIncrementalTestIndex};
423
424 fn next(it: *TestIterator) Error!?[]const []const u8 {
425 try it.nextInner();
426 if (it.start == it.end) return null;
427 return it.filenames[it.start..it.end];
428 }
429
430 fn nextInner(it: *TestIterator) Error!void {
431 it.start = it.end;
432 if (it.end == it.filenames.len) return;
433 if (it.end + 1 == it.filenames.len) {
434 it.end += 1;
435 return;
436 }
437
438 const remaining = it.filenames[it.end..];
439 it.index = 0;
440 while (it.index < remaining.len - 1) : (it.index += 1) {
441 // First, check if this file is part of an incremental update sequence
442 // Split filename into "<base_name>.<index>.<file_ext>"
443 const prev_parts = getTestFileNameParts(remaining[it.index]);
444 const new_parts = getTestFileNameParts(remaining[it.index + 1]);
445
446 // If base_name and file_ext match, these files are in the same test sequence
447 // and the new one should be the incremented version of the previous test
448 if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and
449 std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext))
450 {
451 // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1
452 if (prev_parts.test_index == null)
453 return error.InvalidIncrementalTestIndex;
454 if (new_parts.test_index == null)
455 return error.InvalidIncrementalTestIndex;
456 if (new_parts.test_index.? != prev_parts.test_index.? + 1)
457 return error.InvalidIncrementalTestIndex;
458 } else {
459 // This is not the same test sequence, so the new file must be the first file
460 // in a new sequence ("*.0.zig") or an independent test file ("*.zig")
461 if (new_parts.test_index != null and new_parts.test_index.? != 0)
462 return error.InvalidIncrementalTestIndex;
463
464 it.end += it.index + 1;
465 break;
466 }
467 } else {
468 it.end += remaining.len;
469 }
470 }
471
472 /// In the event of an `error.InvalidIncrementalTestIndex`, this function can
473 /// be used to find the current filename that was being processed.
474 /// Asserts the iterator hasn't reached the end.
475 fn currentFilename(it: TestIterator) []const u8 {
476 assert(it.end != it.filenames.len);
477 const remaining = it.filenames[it.end..];
478 return remaining[it.index + 1];
479 }
480};
481
482/// For a filename in the format "<filename>.X.<ext>" or "<filename>.<ext>", returns
483/// "<filename>", "<ext>" and X parsed as a decimal number. If X is not present, or
484/// cannot be parsed as a decimal number, it is treated as part of <filename>
485fn getTestFileNameParts(name: []const u8) struct {
486 base_name: []const u8,
487 file_ext: []const u8,
488 test_index: ?usize,
489} {
490 const file_ext = std.fs.path.extension(name);
491 const trimmed = name[0 .. name.len - file_ext.len]; // Trim off ".<ext>"
492 const maybe_index = std.fs.path.extension(trimmed); // Extract ".X"
493
494 // Attempt to parse index
495 const index: ?usize = if (maybe_index.len > 0)
496 std.fmt.parseInt(usize, maybe_index[1..], 10) catch null
497 else
498 null;
499
500 // Adjust "<filename>" extent based on parsing success
501 const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0;
502 return .{
503 .base_name = name[0..base_name_end],
504 .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext,
505 .test_index = index,
506 };
507}
508
509/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig",
510/// "foo.1.zig", etc.) are contiguous and appear in numerical order.
511fn sortTestFilenames(filenames: [][]const u8) void {
512 const Context = struct {
513 pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool {
514 const a_parts = getTestFileNameParts(a);
515 const b_parts = getTestFileNameParts(b);
516
517 // Sort "<base_name>.X.<file_ext>" based on "<base_name>" and "<file_ext>" first
518 return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) {
519 .lt => true,
520 .gt => false,
521 .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) {
522 .lt => true,
523 .gt => false,
524 .eq => {
525 // a and b differ only in their ".X" part
526
527 // Sort "<base_name>.<file_ext>" before any "<base_name>.X.<file_ext>"
528 if (a_parts.test_index) |a_index| {
529 if (b_parts.test_index) |b_index| {
530 // Make sure that incremental tests appear in linear order
531 return a_index < b_index;
532 } else {
533 return false;
534 }
535 } else {
536 return b_parts.test_index != null;
537 }
538 },
539 },
540 };
541 }
542 };
543 std.sort.sort([]const u8, filenames, Context{}, Context.lessThan);
544}
545
546pub const TestContext = struct {
547 gpa: Allocator,
548 arena: Allocator,
549 cases: std.ArrayList(Case),
550
551 pub const Update = struct {
552 /// The input to the current update. We simulate an incremental update
553 /// with the file's contents changed to this value each update.
554 ///
555 /// This value can change entirely between updates, which would be akin
556 /// to deleting the source file and creating a new one from scratch; or
557 /// you can keep it mostly consistent, with small changes, testing the
558 /// effects of the incremental compilation.
559 src: [:0]const u8,
560 name: []const u8,
561 case: union(enum) {
562 /// Check the main binary output file against an expected set of bytes.
563 /// This is most useful with, for example, `-ofmt=c`.
564 CompareObjectFile: []const u8,
565 /// An error update attempts to compile bad code, and ensures that it
566 /// fails to compile, and for the expected reasons.
567 /// A slice containing the expected errors *in sequential order*.
568 Error: []const ErrorMsg,
569 /// An execution update compiles and runs the input, testing the
570 /// stdout against the expected results
571 /// This is a slice containing the expected message.
572 Execution: []const u8,
573 /// A header update compiles the input with the equivalent of
574 /// `-femit-h` and tests the produced header against the
575 /// expected result
576 Header: []const u8,
577 },
578 };
579
580 pub const File = struct {
581 /// Contents of the importable file. Doesn't yet support incremental updates.
582 src: [:0]const u8,
583 path: []const u8,
584 };
585
586 pub const DepModule = struct {
587 name: []const u8,
588 path: []const u8,
589 };
590
591 pub const Backend = enum {
592 stage1,
593 stage2,
594 llvm,
595 };
596
597 /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each
598 /// update, so each update's source is treated as a single file being
599 /// updated by the test harness and incrementally compiled.
600 pub const Case = struct {
601 /// The name of the test case. This is shown if a test fails, and
602 /// otherwise ignored.
603 name: []const u8,
604 /// The platform the test targets. For non-native platforms, an emulator
605 /// such as QEMU is required for tests to complete.
606 target: CrossTarget,
607 /// In order to be able to run e.g. Execution updates, this must be set
608 /// to Executable.
609 output_mode: std.builtin.OutputMode,
610 optimize_mode: std.builtin.Mode = .Debug,
611 updates: std.ArrayList(Update),
612 emit_h: bool = false,
613 is_test: bool = false,
614 expect_exact: bool = false,
615 backend: Backend = .stage2,
616 link_libc: bool = false,
617
618 files: std.ArrayList(File),
619 deps: std.ArrayList(DepModule),
620
621 result: anyerror!void = {},
622
623 pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void {
624 case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory");
625 }
626
627 pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void {
628 case.deps.append(.{
629 .name = name,
630 .path = path,
631 }) catch @panic("out of memory");
632 }
633
634 /// Adds a subcase in which the module is updated with `src`, and a C
635 /// header is generated.
636 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
637 self.emit_h = true;
638 self.updates.append(.{
639 .src = src,
640 .name = "update",
641 .case = .{ .Header = result },
642 }) catch @panic("out of memory");
643 }
644
645 /// Adds a subcase in which the module is updated with `src`, compiled,
646 /// run, and the output is tested against `result`.
647 pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
648 self.updates.append(.{
649 .src = src,
650 .name = "update",
651 .case = .{ .Execution = result },
652 }) catch @panic("out of memory");
653 }
654
655 /// Adds a subcase in which the module is updated with `src`, compiled,
656 /// and the object file data is compared against `result`.
657 pub fn addCompareObjectFile(self: *Case, src: [:0]const u8, result: []const u8) void {
658 self.updates.append(.{
659 .src = src,
660 .name = "update",
661 .case = .{ .CompareObjectFile = result },
662 }) catch @panic("out of memory");
663 }
664
665 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
666 return self.addErrorNamed("update", src, errors);
667 }
668
669 /// Adds a subcase in which the module is updated with `src`, which
670 /// should contain invalid input, and ensures that compilation fails
671 /// for the expected reasons, given in sequential order in `errors` in
672 /// the form `:line:column: error: message`.
673 pub fn addErrorNamed(
674 self: *Case,
675 name: []const u8,
676 src: [:0]const u8,
677 errors: []const []const u8,
678 ) void {
679 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch @panic("out of memory");
680 for (errors, 0..) |err_msg_line, i| {
681 if (std.mem.startsWith(u8, err_msg_line, "error: ")) {
682 array[i] = .{
683 .plain = .{
684 .msg = err_msg_line["error: ".len..],
685 .kind = .@"error",
686 .count = 1,
687 },
688 };
689 continue;
690 } else if (std.mem.startsWith(u8, err_msg_line, "note: ")) {
691 array[i] = .{
692 .plain = .{
693 .msg = err_msg_line["note: ".len..],
694 .kind = .note,
695 .count = 1,
696 },
697 };
698 continue;
699 }
700 // example: "file.zig:1:2: error: bad thing happened"
701 var it = std.mem.split(u8, err_msg_line, ":");
702 const src_path = it.first();
703 const line_text = it.next() orelse @panic("missing line");
704 const col_text = it.next() orelse @panic("missing column");
705 const kind_text = it.next() orelse @panic("missing 'error'/'note'");
706 var msg = it.rest()[1..]; // skip over the space at end of "error: "
707
708 const line: ?u32 = if (std.mem.eql(u8, line_text, "?"))
709 null
710 else
711 std.fmt.parseInt(u32, line_text, 10) catch @panic("bad line number");
712 const column: ?u32 = if (std.mem.eql(u8, line_text, "?"))
713 null
714 else
715 std.fmt.parseInt(u32, col_text, 10) catch @panic("bad column number");
716 const kind: ErrorMsg.Kind = if (std.mem.eql(u8, kind_text, " error"))
717 .@"error"
718 else if (std.mem.eql(u8, kind_text, " note"))
719 .note
720 else
721 @panic("expected 'error'/'note'");
722
723 const line_0based: u32 = if (line) |n| blk: {
724 if (n == 0) {
725 print("{s}: line must be specified starting at one\n", .{self.name});
726 return;
727 }
728 break :blk n - 1;
729 } else std.math.maxInt(u32);
730
731 const column_0based: u32 = if (column) |n| blk: {
732 if (n == 0) {
733 print("{s}: line must be specified starting at one\n", .{self.name});
734 return;
735 }
736 break :blk n - 1;
737 } else std.math.maxInt(u32);
738
739 const suffix = " times)";
740 const count = if (std.mem.endsWith(u8, msg, suffix)) count: {
741 const lparen = std.mem.lastIndexOfScalar(u8, msg, '(').?;
742 const count = std.fmt.parseInt(u32, msg[lparen + 1 .. msg.len - suffix.len], 10) catch @panic("bad error note count number");
743 msg = msg[0 .. lparen - 1];
744 break :count count;
745 } else 1;
746
747 array[i] = .{
748 .src = .{
749 .src_path = src_path,
750 .msg = msg,
751 .line = line_0based,
752 .column = column_0based,
753 .kind = kind,
754 .count = count,
755 },
756 };
757 }
758 self.updates.append(.{
759 .src = src,
760 .name = name,
761 .case = .{ .Error = array },
762 }) catch @panic("out of memory");
763 }
764
765 /// Adds a subcase in which the module is updated with `src`, and
766 /// asserts that it compiles without issue
767 pub fn compiles(self: *Case, src: [:0]const u8) void {
768 self.addError(src, &[_][]const u8{});
769 }
770 };
771
772 pub fn addExe(
773 ctx: *TestContext,
774 name: []const u8,
775 target: CrossTarget,
776 ) *Case {
777 ctx.cases.append(Case{
778 .name = name,
779 .target = target,
780 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
781 .output_mode = .Exe,
782 .files = std.ArrayList(File).init(ctx.arena),
783 .deps = std.ArrayList(DepModule).init(ctx.arena),
784 }) catch @panic("out of memory");
785 return &ctx.cases.items[ctx.cases.items.len - 1];
786 }
787
788 /// Adds a test case for Zig input, producing an executable
789 pub fn exe(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
790 return ctx.addExe(name, target);
791 }
792
793 pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
794 const prefixed_name = std.fmt.allocPrint(ctx.arena, "CBE: {s}", .{name}) catch
795 @panic("out of memory");
796 var target_adjusted = target;
797 target_adjusted.ofmt = std.Target.ObjectFormat.c;
798 ctx.cases.append(Case{
799 .name = prefixed_name,
800 .target = target_adjusted,
801 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
802 .output_mode = .Exe,
803 .files = std.ArrayList(File).init(ctx.arena),
804 .deps = std.ArrayList(DepModule).init(ctx.arena),
805 .link_libc = true,
806 }) catch @panic("out of memory");
807 return &ctx.cases.items[ctx.cases.items.len - 1];
808 }
809
810 /// Adds a test case that uses the LLVM backend to emit an executable.
811 /// Currently this implies linking libc, because only then we can generate a testable executable.
812 pub fn exeUsingLlvmBackend(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
813 ctx.cases.append(Case{
814 .name = name,
815 .target = target,
816 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
817 .output_mode = .Exe,
818 .files = std.ArrayList(File).init(ctx.arena),
819 .deps = std.ArrayList(DepModule).init(ctx.arena),
820 .backend = .llvm,
821 .link_libc = true,
822 }) catch @panic("out of memory");
823 return &ctx.cases.items[ctx.cases.items.len - 1];
824 }
825
826 pub fn addObj(
827 ctx: *TestContext,
828 name: []const u8,
829 target: CrossTarget,
830 ) *Case {
831 ctx.cases.append(Case{
832 .name = name,
833 .target = target,
834 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
835 .output_mode = .Obj,
836 .files = std.ArrayList(File).init(ctx.arena),
837 .deps = std.ArrayList(DepModule).init(ctx.arena),
838 }) catch @panic("out of memory");
839 return &ctx.cases.items[ctx.cases.items.len - 1];
840 }
841
842 pub fn addTest(
843 ctx: *TestContext,
844 name: []const u8,
845 target: CrossTarget,
846 ) *Case {
847 ctx.cases.append(Case{
848 .name = name,
849 .target = target,
850 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
851 .output_mode = .Exe,
852 .is_test = true,
853 .files = std.ArrayList(File).init(ctx.arena),
854 .deps = std.ArrayList(DepModule).init(ctx.arena),
855 }) catch @panic("out of memory");
856 return &ctx.cases.items[ctx.cases.items.len - 1];
857 }
858
859 /// Adds a test case for Zig input, producing an object file.
860 pub fn obj(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
861 return ctx.addObj(name, target);
862 }
863
864 /// Adds a test case for ZIR input, producing an object file.
865 pub fn objZIR(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
866 return ctx.addObj(name, target, .ZIR);
867 }
868
869 /// Adds a test case for Zig or ZIR input, producing C code.
870 pub fn addC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
871 var target_adjusted = target;
872 target_adjusted.ofmt = std.Target.ObjectFormat.c;
873 ctx.cases.append(Case{
874 .name = name,
875 .target = target_adjusted,
876 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
877 .output_mode = .Obj,
878 .files = std.ArrayList(File).init(ctx.arena),
879 .deps = std.ArrayList(DepModule).init(ctx.arena),
880 }) catch @panic("out of memory");
881 return &ctx.cases.items[ctx.cases.items.len - 1];
882 }
883
884 pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
885 ctx.addC(name, target).addCompareObjectFile(src, zig_h ++ out);
886 }
887
888 pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
889 ctx.addC(name, target).addHeader(src, zig_h ++ out);
890 }
891
892 pub fn objErrStage1(
893 ctx: *TestContext,
894 name: []const u8,
895 src: [:0]const u8,
896 expected_errors: []const []const u8,
897 ) void {
898 const case = ctx.addObj(name, .{});
899 case.backend = .stage1;
900 case.addError(src, expected_errors);
901 }
902
903 pub fn testErrStage1(
904 ctx: *TestContext,
905 name: []const u8,
906 src: [:0]const u8,
907 expected_errors: []const []const u8,
908 ) void {
909 const case = ctx.addTest(name, .{});
910 case.backend = .stage1;
911 case.addError(src, expected_errors);
912 }
913
914 pub fn exeErrStage1(
915 ctx: *TestContext,
916 name: []const u8,
917 src: [:0]const u8,
918 expected_errors: []const []const u8,
919 ) void {
920 const case = ctx.addExe(name, .{});
921 case.backend = .stage1;
922 case.addError(src, expected_errors);
923 }
924
925 pub fn addCompareOutput(
926 ctx: *TestContext,
927 name: []const u8,
928 src: [:0]const u8,
929 expected_stdout: []const u8,
930 ) void {
931 ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout);
932 }
933
934 /// Adds a test case that compiles the Zig source given in `src`, executes
935 /// it, runs it, and tests the output against `expected_stdout`
936 pub fn compareOutput(
937 ctx: *TestContext,
938 name: []const u8,
939 src: [:0]const u8,
940 expected_stdout: []const u8,
941 ) void {
942 return ctx.addCompareOutput(name, src, expected_stdout);
943 }
944
945 /// Adds a test case that compiles the ZIR source given in `src`, executes
946 /// it, runs it, and tests the output against `expected_stdout`
947 pub fn compareOutputZIR(
948 ctx: *TestContext,
949 name: []const u8,
950 src: [:0]const u8,
951 expected_stdout: []const u8,
952 ) void {
953 ctx.addCompareOutput(name, .ZIR, src, expected_stdout);
954 }
955
956 pub fn addTransform(
957 ctx: *TestContext,
958 name: []const u8,
959 target: CrossTarget,
960 src: [:0]const u8,
961 result: [:0]const u8,
962 ) void {
963 ctx.addObj(name, target).addTransform(src, result);
964 }
965
966 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
967 /// the ZIR against `result`
968 pub fn transform(
969 ctx: *TestContext,
970 name: []const u8,
971 target: CrossTarget,
972 src: [:0]const u8,
973 result: [:0]const u8,
974 ) void {
975 ctx.addTransform(name, target, src, result);
976 }
977
978 pub fn addError(
979 ctx: *TestContext,
980 name: []const u8,
981 target: CrossTarget,
982 src: [:0]const u8,
983 expected_errors: []const []const u8,
984 ) void {
985 ctx.addObj(name, target).addError(src, expected_errors);
986 }
987
988 /// Adds a test case that ensures that the Zig given in `src` fails to
989 /// compile for the expected reasons, given in sequential order in
990 /// `expected_errors` in the form `:line:column: error: message`.
991 pub fn compileError(
992 ctx: *TestContext,
993 name: []const u8,
994 target: CrossTarget,
995 src: [:0]const u8,
996 expected_errors: []const []const u8,
997 ) void {
998 ctx.addError(name, target, src, expected_errors);
999 }
1000
1001 /// Adds a test case that ensures that the ZIR given in `src` fails to
1002 /// compile for the expected reasons, given in sequential order in
1003 /// `expected_errors` in the form `:line:column: error: message`.
1004 pub fn compileErrorZIR(
1005 ctx: *TestContext,
1006 name: []const u8,
1007 target: CrossTarget,
1008 src: [:0]const u8,
1009 expected_errors: []const []const u8,
1010 ) void {
1011 ctx.addError(name, target, .ZIR, src, expected_errors);
1012 }
1013
1014 pub fn addCompiles(
1015 ctx: *TestContext,
1016 name: []const u8,
1017 target: CrossTarget,
1018 src: [:0]const u8,
1019 ) void {
1020 ctx.addObj(name, target).compiles(src);
1021 }
1022
1023 /// Adds a test case that asserts that the Zig given in `src` compiles
1024 /// without any errors.
1025 pub fn compiles(
1026 ctx: *TestContext,
1027 name: []const u8,
1028 target: CrossTarget,
1029 src: [:0]const u8,
1030 ) void {
1031 ctx.addCompiles(name, target, src);
1032 }
1033
1034 /// Adds a test case that asserts that the ZIR given in `src` compiles
1035 /// without any errors.
1036 pub fn compilesZIR(
1037 ctx: *TestContext,
1038 name: []const u8,
1039 target: CrossTarget,
1040 src: [:0]const u8,
1041 ) void {
1042 ctx.addCompiles(name, target, .ZIR, src);
1043 }
1044
1045 /// Adds a test case that first ensures that the Zig given in `src` fails
1046 /// to compile for the reasons given in sequential order in
1047 /// `expected_errors` in the form `:line:column: error: message`, then
1048 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
1049 /// by incremental compilation.
1050 pub fn incrementalFailure(
1051 ctx: *TestContext,
1052 name: []const u8,
1053 target: CrossTarget,
1054 src: [:0]const u8,
1055 expected_errors: []const []const u8,
1056 fixed_src: [:0]const u8,
1057 ) void {
1058 var case = ctx.addObj(name, target);
1059 case.addError(src, expected_errors);
1060 case.compiles(fixed_src);
1061 }
1062
1063 /// Adds a test case that first ensures that the ZIR given in `src` fails
1064 /// to compile for the reasons given in sequential order in
1065 /// `expected_errors` in the form `:line:column: error: message`, then
1066 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
1067 /// by incremental compilation.
1068 pub fn incrementalFailureZIR(
1069 ctx: *TestContext,
1070 name: []const u8,
1071 target: CrossTarget,
1072 src: [:0]const u8,
1073 expected_errors: []const []const u8,
1074 fixed_src: [:0]const u8,
1075 ) void {
1076 var case = ctx.addObj(name, target, .ZIR);
1077 case.addError(src, expected_errors);
1078 case.compiles(fixed_src);
1079 }
1080
1081 /// Adds a test for each file in the provided directory.
1082 /// Testing strategy (TestStrategy) is inferred automatically from filenames.
1083 /// Recurses nested directories.
1084 ///
1085 /// Each file should include a test manifest as a contiguous block of comments at
1086 /// the end of the file. The first line should be the test type, followed by a set of
1087 /// key-value config values, followed by a blank line, then the expected output.
1088 pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.IterableDir) void {
1089 var current_file: []const u8 = "none";
1090 ctx.addTestCasesFromDirInner(dir, &current_file) catch |err| {
1091 std.debug.panic("test harness failed to process file '{s}': {s}\n", .{
1092 current_file, @errorName(err),
1093 });
1094 };
1095 }
1096
1097 fn addTestCasesFromDirInner(
1098 ctx: *TestContext,
1099 iterable_dir: std.fs.IterableDir,
1100 /// This is kept up to date with the currently being processed file so
1101 /// that if any errors occur the caller knows it happened during this file.
1102 current_file: *[]const u8,
1103 ) !void {
1104 var it = try iterable_dir.walk(ctx.arena);
1105 var filenames = std.ArrayList([]const u8).init(ctx.arena);
1106
1107 while (try it.next()) |entry| {
1108 if (entry.kind != .File) continue;
1109
1110 // Ignore stuff such as .swp files
1111 switch (Compilation.classifyFileExt(entry.basename)) {
1112 .unknown => continue,
1113 else => {},
1114 }
1115 try filenames.append(try ctx.arena.dupe(u8, entry.path));
1116 }
1117
1118 // Sort filenames, so that incremental tests are contiguous and in-order
1119 sortTestFilenames(filenames.items);
1120
1121 var test_it = TestIterator{ .filenames = filenames.items };
1122 while (test_it.next()) |maybe_batch| {
1123 const batch = maybe_batch orelse break;
1124 const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent;
1125 var cases = std.ArrayList(usize).init(ctx.arena);
1126
1127 for (batch) |filename| {
1128 current_file.* = filename;
1129
1130 const max_file_size = 10 * 1024 * 1024;
1131 const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);
1132
1133 // Parse the manifest
1134 var manifest = try TestManifest.parse(ctx.arena, src);
1135
1136 if (cases.items.len == 0) {
1137 const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend);
1138 const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget);
1139 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
1140 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);
1141
1142 const name_prefix = blk: {
1143 const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse
1144 return error.InvalidFilename;
1145 const index = std.mem.lastIndexOfScalar(u8, current_file.*[0..ext_index], '.') orelse ext_index;
1146 break :blk current_file.*[0..index];
1147 };
1148
1149 // Cross-product to get all possible test combinations
1150 for (backends) |backend| {
1151 for (targets) |target| {
1152 const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{
1153 name_prefix,
1154 @tagName(backend),
1155 try target.zigTriple(ctx.arena),
1156 });
1157 const next = ctx.cases.items.len;
1158 try ctx.cases.append(.{
1159 .name = name,
1160 .target = target,
1161 .backend = backend,
1162 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
1163 .is_test = is_test,
1164 .output_mode = output_mode,
1165 .link_libc = backend == .llvm,
1166 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
1167 .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
1168 });
1169 try cases.append(next);
1170 }
1171 }
1172 }
1173
1174 for (cases.items) |case_index| {
1175 const case = &ctx.cases.items[case_index];
1176 switch (manifest.type) {
1177 .@"error" => {
1178 const errors = try manifest.trailingAlloc(ctx.arena);
1179 switch (strategy) {
1180 .independent => {
1181 case.addError(src, errors);
1182 },
1183 .incremental => {
1184 case.addErrorNamed("update", src, errors);
1185 },
1186 }
1187 },
1188 .run => {
1189 var output = std.ArrayList(u8).init(ctx.arena);
1190 var trailing_it = manifest.trailing();
1191 while (trailing_it.next()) |line| {
1192 try output.appendSlice(line);
1193 try output.append('\n');
1194 }
1195 if (output.items.len > 0) {
1196 try output.resize(output.items.len - 1);
1197 }
1198 case.addCompareOutput(src, try output.toOwnedSlice());
1199 },
1200 .cli => @panic("TODO cli tests"),
1201 }
1202 }
1203 }
1204 } else |err| {
1205 // make sure the current file is set to the file that produced an error
1206 current_file.* = test_it.currentFilename();
1207 return err;
1208 }
1209 }
1210
1211 fn init(gpa: Allocator, arena: Allocator) TestContext {
1212 return .{
1213 .gpa = gpa,
1214 .cases = std.ArrayList(Case).init(gpa),
1215 .arena = arena,
1216 };
1217 }
1218
1219 fn deinit(self: *TestContext) void {
1220 for (self.cases.items) |case| {
1221 for (case.updates.items) |u| {
1222 if (u.case == .Error) {
1223 case.updates.allocator.free(u.case.Error);
1224 }
1225 }
1226 case.updates.deinit();
1227 }
1228 self.cases.deinit();
1229 self.* = undefined;
1230 }
1231
1232 fn run(self: *TestContext) !void {
1233 const host = try std.zig.system.NativeTargetInfo.detect(.{});
1234 const zig_exe_path = try std.process.getEnvVarOwned(self.arena, "ZIG_EXE");
1235
1236 var progress = std.Progress{};
1237 const root_node = progress.start("compiler", self.cases.items.len);
1238 defer root_node.end();
1239
1240 var zig_lib_directory = try introspect.findZigLibDir(self.gpa);
1241 defer zig_lib_directory.handle.close();
1242 defer self.gpa.free(zig_lib_directory.path.?);
1243
1244 var aux_thread_pool: ThreadPool = undefined;
1245 try aux_thread_pool.init(self.gpa);
1246 defer aux_thread_pool.deinit();
1247
1248 // Use the same global cache dir for all the tests, such that we for example don't have to
1249 // rebuild musl libc for every case (when LLVM backend is enabled).
1250 var global_tmp = std.testing.tmpDir(.{});
1251 defer global_tmp.cleanup();
1252
1253 var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{});
1254 defer cache_dir.close();
1255 const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path });
1256 defer self.gpa.free(tmp_dir_path);
1257
1258 const global_cache_directory: Compilation.Directory = .{
1259 .handle = cache_dir,
1260 .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }),
1261 };
1262 defer self.gpa.free(global_cache_directory.path.?);
1263
1264 {
1265 for (self.cases.items) |*case| {
1266 if (build_options.skip_non_native) {
1267 if (case.target.getCpuArch() != builtin.cpu.arch)
1268 continue;
1269 if (case.target.getObjectFormat() != builtin.object_format)
1270 continue;
1271 }
1272
1273 // Skip tests that require LLVM backend when it is not available
1274 if (!build_options.have_llvm and case.backend == .llvm)
1275 continue;
1276
1277 if (skip_stage1 and case.backend == .stage1)
1278 continue;
1279
1280 if (build_options.test_filter) |test_filter| {
1281 if (std.mem.indexOf(u8, case.name, test_filter) == null) continue;
1282 }
1283
1284 var prg_node = root_node.start(case.name, case.updates.items.len);
1285 prg_node.activate();
1286 defer prg_node.end();
1287
1288 case.result = runOneCase(
1289 self.gpa,
1290 &prg_node,
1291 case.*,
1292 zig_lib_directory,
1293 zig_exe_path,
1294 &aux_thread_pool,
1295 global_cache_directory,
1296 host,
1297 );
1298 }
1299 }
1300
1301 var fail_count: usize = 0;
1302 for (self.cases.items) |*case| {
1303 case.result catch |err| {
1304 fail_count += 1;
1305 print("{s} failed: {s}\n", .{ case.name, @errorName(err) });
1306 };
1307 }
1308
1309 if (fail_count != 0) {
1310 print("{d} tests failed\n", .{fail_count});
1311 return error.TestFailed;
1312 }
1313 }
1314
1315 fn runOneCase(
1316 allocator: Allocator,
1317 root_node: *std.Progress.Node,
1318 case: Case,
1319 zig_lib_directory: Compilation.Directory,
1320 zig_exe_path: []const u8,
1321 thread_pool: *ThreadPool,
1322 global_cache_directory: Compilation.Directory,
1323 host: std.zig.system.NativeTargetInfo,
1324 ) !void {
1325 const target_info = try std.zig.system.NativeTargetInfo.detect(case.target);
1326 const target = target_info.target;
1327
1328 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
1329 defer arena_allocator.deinit();
1330 const arena = arena_allocator.allocator();
1331
1332 var tmp = std.testing.tmpDir(.{});
1333 defer tmp.cleanup();
1334
1335 var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{});
1336 defer cache_dir.close();
1337
1338 const tmp_dir_path = try std.fs.path.join(
1339 arena,
1340 &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path },
1341 );
1342 const tmp_dir_path_plus_slash = try std.fmt.allocPrint(
1343 arena,
1344 "{s}" ++ std.fs.path.sep_str,
1345 .{tmp_dir_path},
1346 );
1347 const local_cache_path = try std.fs.path.join(
1348 arena,
1349 &[_][]const u8{ tmp_dir_path, "zig-cache" },
1350 );
1351
1352 for (case.files.items) |file| {
1353 try tmp.dir.writeFile(file.path, file.src);
1354 }
1355
1356 if (case.backend == .stage1) {
1357 // stage1 backend has limitations:
1358 // * leaks memory
1359 // * calls exit() when a compile error happens
1360 // * cannot handle updates
1361 // because of this we must spawn a child process rather than
1362 // using Compilation directly.
1363
1364 if (!std.process.can_spawn) {
1365 print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)});
1366 return; // Pass test.
1367 }
1368
1369 assert(case.updates.items.len == 1);
1370 const update = case.updates.items[0];
1371 try tmp.dir.writeFile(tmp_src_path, update.src);
1372
1373 var zig_args = std.ArrayList([]const u8).init(arena);
1374 try zig_args.append(zig_exe_path);
1375
1376 if (case.is_test) {
1377 try zig_args.append("test");
1378 } else if (update.case == .Execution) {
1379 try zig_args.append("run");
1380 } else switch (case.output_mode) {
1381 .Obj => try zig_args.append("build-obj"),
1382 .Exe => try zig_args.append("build-exe"),
1383 .Lib => try zig_args.append("build-lib"),
1384 }
1385
1386 try zig_args.append(try std.fs.path.join(arena, &.{ tmp_dir_path, tmp_src_path }));
1387
1388 try zig_args.append("--name");
1389 try zig_args.append("test");
1390
1391 try zig_args.append("--cache-dir");
1392 try zig_args.append(local_cache_path);
1393
1394 try zig_args.append("--global-cache-dir");
1395 try zig_args.append(global_cache_directory.path orelse ".");
1396
1397 if (!case.target.isNative()) {
1398 try zig_args.append("-target");
1399 try zig_args.append(try target.zigTriple(arena));
1400 }
1401
1402 try zig_args.append("-O");
1403 try zig_args.append(@tagName(case.optimize_mode));
1404
1405 // Prevent sub-process progress bar from interfering with the
1406 // one in this parent process.
1407 try zig_args.append("--color");
1408 try zig_args.append("off");
1409
1410 const result = try std.ChildProcess.exec(.{
1411 .allocator = arena,
1412 .argv = zig_args.items,
1413 });
1414 switch (update.case) {
1415 .Error => |case_error_list| {
1416 switch (result.term) {
1417 .Exited => |code| {
1418 if (code == 0) {
1419 dumpArgs(zig_args.items);
1420 return error.CompilationIncorrectlySucceeded;
1421 }
1422 },
1423 else => {
1424 std.debug.print("{s}", .{result.stderr});
1425 dumpArgs(zig_args.items);
1426 return error.CompilationCrashed;
1427 },
1428 }
1429 var ok = true;
1430 if (case.expect_exact) {
1431 var err_iter = std.mem.split(u8, result.stderr, "\n");
1432 var i: usize = 0;
1433 ok = while (err_iter.next()) |line| : (i += 1) {
1434 if (i >= case_error_list.len) break false;
1435 const expected = try std.mem.replaceOwned(
1436 u8,
1437 arena,
1438 try std.fmt.allocPrint(arena, "{s}", .{case_error_list[i]}),
1439 "${DIR}",
1440 tmp_dir_path_plus_slash,
1441 );
1442
1443 if (std.mem.indexOf(u8, line, expected) == null) break false;
1444 continue;
1445 } else true;
1446
1447 ok = ok and i == case_error_list.len;
1448
1449 if (!ok) {
1450 print("\n======== Expected these compile errors: ========\n", .{});
1451 for (case_error_list) |msg| {
1452 const expected = try std.fmt.allocPrint(arena, "{s}", .{msg});
1453 print("{s}\n", .{expected});
1454 }
1455 }
1456 } else {
1457 for (case_error_list) |msg| {
1458 const expected = try std.mem.replaceOwned(
1459 u8,
1460 arena,
1461 try std.fmt.allocPrint(arena, "{s}", .{msg}),
1462 "${DIR}",
1463 tmp_dir_path_plus_slash,
1464 );
1465 if (std.mem.indexOf(u8, result.stderr, expected) == null) {
1466 print(
1467 \\
1468 \\=========== Expected compile error: ============
1469 \\{s}
1470 \\
1471 , .{expected});
1472 ok = false;
1473 break;
1474 }
1475 }
1476 }
1477
1478 if (!ok) {
1479 print(
1480 \\================= Full output: =================
1481 \\{s}
1482 \\================================================
1483 \\
1484 , .{result.stderr});
1485 return error.TestFailed;
1486 }
1487 },
1488 .CompareObjectFile => @panic("TODO implement in the test harness"),
1489 .Execution => |expected_stdout| {
1490 switch (result.term) {
1491 .Exited => |code| {
1492 if (code != 0) {
1493 std.debug.print("{s}", .{result.stderr});
1494 dumpArgs(zig_args.items);
1495 return error.CompilationFailed;
1496 }
1497 },
1498 else => {
1499 std.debug.print("{s}", .{result.stderr});
1500 dumpArgs(zig_args.items);
1501 return error.CompilationCrashed;
1502 },
1503 }
1504 try std.testing.expectEqualStrings("", result.stderr);
1505 try std.testing.expectEqualStrings(expected_stdout, result.stdout);
1506 },
1507 .Header => @panic("TODO implement in the test harness"),
1508 }
1509 return;
1510 }
1511
1512 const zig_cache_directory: Compilation.Directory = .{
1513 .handle = cache_dir,
1514 .path = local_cache_path,
1515 };
1516
1517 var main_pkg: Package = .{
1518 .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir },
1519 .root_src_path = tmp_src_path,
1520 };
1521 defer {
1522 var it = main_pkg.table.iterator();
1523 while (it.next()) |kv| {
1524 allocator.free(kv.key_ptr.*);
1525 kv.value_ptr.*.destroy(allocator);
1526 }
1527 main_pkg.table.deinit(allocator);
1528 }
1529
1530 for (case.deps.items) |dep| {
1531 var pkg = try Package.create(
1532 allocator,
1533 tmp_dir_path,
1534 dep.path,
1535 );
1536 errdefer pkg.destroy(allocator);
1537 try main_pkg.add(allocator, dep.name, pkg);
1538 }
1539
1540 const bin_name = try std.zig.binNameAlloc(arena, .{
1541 .root_name = "test_case",
1542 .target = target,
1543 .output_mode = case.output_mode,
1544 });
1545
1546 const emit_directory: Compilation.Directory = .{
1547 .path = tmp_dir_path,
1548 .handle = tmp.dir,
1549 };
1550 const emit_bin: Compilation.EmitLoc = .{
1551 .directory = emit_directory,
1552 .basename = bin_name,
1553 };
1554 const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{
1555 .directory = emit_directory,
1556 .basename = "test_case.h",
1557 } else null;
1558 const use_llvm: bool = switch (case.backend) {
1559 .llvm => true,
1560 else => false,
1561 };
1562 const comp = try Compilation.create(allocator, .{
1563 .local_cache_directory = zig_cache_directory,
1564 .global_cache_directory = global_cache_directory,
1565 .zig_lib_directory = zig_lib_directory,
1566 .thread_pool = thread_pool,
1567 .root_name = "test_case",
1568 .target = target,
1569 // TODO: support tests for object file building, and library builds
1570 // and linking. This will require a rework to support multi-file
1571 // tests.
1572 .output_mode = case.output_mode,
1573 .is_test = case.is_test,
1574 .optimize_mode = case.optimize_mode,
1575 .emit_bin = emit_bin,
1576 .emit_h = emit_h,
1577 .main_pkg = &main_pkg,
1578 .keep_source_files_loaded = true,
1579 .is_native_os = case.target.isNativeOs(),
1580 .is_native_abi = case.target.isNativeAbi(),
1581 .dynamic_linker = target_info.dynamic_linker.get(),
1582 .link_libc = case.link_libc,
1583 .use_llvm = use_llvm,
1584 .self_exe_path = zig_exe_path,
1585 // TODO instead of turning off color, pass in a std.Progress.Node
1586 .color = .off,
1587 .reference_trace = 0,
1588 // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in
1589 // until the auto-select mechanism deems them worthy
1590 .use_lld = switch (case.backend) {
1591 .stage2 => false,
1592 else => null,
1593 },
1594 });
1595 defer comp.destroy();
1596
1597 update: for (case.updates.items, 0..) |update, update_index| {
1598 var update_node = root_node.start(update.name, 3);
1599 update_node.activate();
1600 defer update_node.end();
1601
1602 var sync_node = update_node.start("write", 0);
1603 sync_node.activate();
1604 try tmp.dir.writeFile(tmp_src_path, update.src);
1605 sync_node.end();
1606
1607 var module_node = update_node.start("parse/analysis/codegen", 0);
1608 module_node.activate();
1609 module_node.context.refresh();
1610 try comp.makeBinFileWritable();
1611 try comp.update();
1612 module_node.end();
1613
1614 if (update.case != .Error) {
1615 var all_errors = try comp.getAllErrorsAlloc();
1616 defer all_errors.deinit(allocator);
1617 if (all_errors.list.len != 0) {
1618 print(
1619 "\nCase '{s}': unexpected errors at update_index={d}:\n{s}\n",
1620 .{ case.name, update_index, hr },
1621 );
1622 for (all_errors.list) |err_msg| {
1623 switch (err_msg) {
1624 .src => |src| {
1625 print("{s}:{d}:{d}: error: {s}\n{s}\n", .{
1626 src.src_path, src.line + 1, src.column + 1, src.msg, hr,
1627 });
1628 },
1629 .plain => |plain| {
1630 print("error: {s}\n{s}\n", .{ plain.msg, hr });
1631 },
1632 }
1633 }
1634 // TODO print generated C code
1635 return error.UnexpectedCompileErrors;
1636 }
1637 }
1638
1639 switch (update.case) {
1640 .Header => |expected_output| {
1641 var file = try tmp.dir.openFile("test_case.h", .{ .mode = .read_only });
1642 defer file.close();
1643 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
1644
1645 try std.testing.expectEqualStrings(expected_output, out);
1646 },
1647 .CompareObjectFile => |expected_output| {
1648 var file = try tmp.dir.openFile(bin_name, .{ .mode = .read_only });
1649 defer file.close();
1650 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
1651
1652 try std.testing.expectEqualStrings(expected_output, out);
1653 },
1654 .Error => |case_error_list| {
1655 var test_node = update_node.start("assert", 0);
1656 test_node.activate();
1657 defer test_node.end();
1658
1659 const handled_errors = try arena.alloc(bool, case_error_list.len);
1660 std.mem.set(bool, handled_errors, false);
1661
1662 var actual_errors = try comp.getAllErrorsAlloc();
1663 defer actual_errors.deinit(allocator);
1664
1665 var any_failed = false;
1666 var notes_to_check = std.ArrayList(*const Compilation.AllErrors.Message).init(allocator);
1667 defer notes_to_check.deinit();
1668
1669 for (actual_errors.list) |actual_error| {
1670 for (case_error_list, 0..) |case_msg, i| {
1671 if (handled_errors[i]) continue;
1672
1673 const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg;
1674 switch (actual_error) {
1675 .src => |actual_msg| {
1676 for (actual_msg.notes) |*note| {
1677 try notes_to_check.append(note);
1678 }
1679
1680 if (ex_tag != .src) continue;
1681
1682 const src_path_ok = case_msg.src.src_path.len == 0 or
1683 std.mem.eql(u8, case_msg.src.src_path, actual_msg.src_path);
1684
1685 const expected_msg = try std.mem.replaceOwned(
1686 u8,
1687 arena,
1688 case_msg.src.msg,
1689 "${DIR}",
1690 tmp_dir_path_plus_slash,
1691 );
1692
1693 var buf: [1024]u8 = undefined;
1694 const rendered_msg = blk: {
1695 var msg: Compilation.AllErrors.Message = actual_error;
1696 msg.src.src_path = case_msg.src.src_path;
1697 msg.src.notes = &.{};
1698 msg.src.source_line = null;
1699 var fib = std.io.fixedBufferStream(&buf);
1700 try msg.renderToWriter(.no_color, fib.writer(), "error", .Red, 0);
1701 var it = std.mem.split(u8, fib.getWritten(), "error: ");
1702 _ = it.first();
1703 const rendered = it.rest();
1704 break :blk rendered[0 .. rendered.len - 1]; // trim final newline
1705 };
1706
1707 if (src_path_ok and
1708 (case_msg.src.line == std.math.maxInt(u32) or
1709 actual_msg.line == case_msg.src.line) and
1710 (case_msg.src.column == std.math.maxInt(u32) or
1711 actual_msg.column == case_msg.src.column) and
1712 std.mem.eql(u8, expected_msg, rendered_msg) and
1713 case_msg.src.kind == .@"error" and
1714 actual_msg.count == case_msg.src.count)
1715 {
1716 handled_errors[i] = true;
1717 break;
1718 }
1719 },
1720 .plain => |plain| {
1721 if (ex_tag != .plain) continue;
1722
1723 if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and
1724 case_msg.plain.kind == .@"error" and
1725 case_msg.plain.count == plain.count)
1726 {
1727 handled_errors[i] = true;
1728 break;
1729 }
1730 },
1731 }
1732 } else {
1733 print(
1734 "\nUnexpected error:\n{s}\n{}\n{s}",
1735 .{ hr, ErrorMsg.init(actual_error, .@"error"), hr },
1736 );
1737 any_failed = true;
1738 }
1739 }
1740 while (notes_to_check.popOrNull()) |note| {
1741 for (case_error_list, 0..) |case_msg, i| {
1742 const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg;
1743 switch (note.*) {
1744 .src => |actual_msg| {
1745 for (actual_msg.notes) |*sub_note| {
1746 try notes_to_check.append(sub_note);
1747 }
1748 if (ex_tag != .src) continue;
1749
1750 const expected_msg = try std.mem.replaceOwned(
1751 u8,
1752 arena,
1753 case_msg.src.msg,
1754 "${DIR}",
1755 tmp_dir_path_plus_slash,
1756 );
1757
1758 if ((case_msg.src.line == std.math.maxInt(u32) or
1759 actual_msg.line == case_msg.src.line) and
1760 (case_msg.src.column == std.math.maxInt(u32) or
1761 actual_msg.column == case_msg.src.column) and
1762 std.mem.eql(u8, expected_msg, actual_msg.msg) and
1763 case_msg.src.kind == .note and
1764 actual_msg.count == case_msg.src.count)
1765 {
1766 handled_errors[i] = true;
1767 break;
1768 }
1769 },
1770 .plain => |plain| {
1771 if (ex_tag != .plain) continue;
1772
1773 if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and
1774 case_msg.plain.kind == .note and
1775 case_msg.plain.count == plain.count)
1776 {
1777 handled_errors[i] = true;
1778 break;
1779 }
1780 },
1781 }
1782 } else {
1783 print(
1784 "\nUnexpected note:\n{s}\n{}\n{s}",
1785 .{ hr, ErrorMsg.init(note.*, .note), hr },
1786 );
1787 any_failed = true;
1788 }
1789 }
1790
1791 for (handled_errors, 0..) |handled, i| {
1792 if (!handled) {
1793 print(
1794 "\nExpected error not found:\n{s}\n{}\n{s}",
1795 .{ hr, case_error_list[i], hr },
1796 );
1797 any_failed = true;
1798 }
1799 }
1800
1801 if (any_failed) {
1802 print("\nupdate_index={d}\n", .{update_index});
1803 return error.WrongCompileErrors;
1804 }
1805 },
1806 .Execution => |expected_stdout| {
1807 if (!std.process.can_spawn) {
1808 print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)});
1809 continue :update; // Pass test.
1810 }
1811
1812 update_node.setEstimatedTotalItems(4);
1813
1814 var argv = std.ArrayList([]const u8).init(allocator);
1815 defer argv.deinit();
1816
1817 var exec_result = x: {
1818 var exec_node = update_node.start("execute", 0);
1819 exec_node.activate();
1820 defer exec_node.end();
1821
1822 // We go out of our way here to use the unique temporary directory name in
1823 // the exe_path so that it makes its way into the cache hash, avoiding
1824 // cache collisions from multiple threads doing `zig run` at the same time
1825 // on the same test_case.c input filename.
1826 const ss = std.fs.path.sep_str;
1827 const exe_path = try std.fmt.allocPrint(
1828 arena,
1829 ".." ++ ss ++ "{s}" ++ ss ++ "{s}",
1830 .{ &tmp.sub_path, bin_name },
1831 );
1832 if (case.target.ofmt != null and case.target.ofmt.? == .c) {
1833 if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) {
1834 // We wouldn't be able to run the compiled C code.
1835 continue :update; // Pass test.
1836 }
1837 try argv.appendSlice(&[_][]const u8{
1838 zig_exe_path,
1839 "run",
1840 "-cflags",
1841 "-std=c99",
1842 "-pedantic",
1843 "-Werror",
1844 "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875
1845 "--",
1846 "-lc",
1847 exe_path,
1848 });
1849 if (zig_lib_directory.path) |p| {
1850 try argv.appendSlice(&.{ "-I", p });
1851 }
1852 } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) {
1853 .native => {
1854 if (case.backend == .stage2 and case.target.getCpuArch() == .arm) {
1855 // https://github.com/ziglang/zig/issues/13623
1856 continue :update; // Pass test.
1857 }
1858 try argv.append(exe_path);
1859 },
1860 .bad_dl, .bad_os_or_cpu => continue :update, // Pass test.
1861
1862 .rosetta => if (enable_rosetta) {
1863 try argv.append(exe_path);
1864 } else {
1865 continue :update; // Rosetta not available, pass test.
1866 },
1867
1868 .qemu => |qemu_bin_name| if (enable_qemu) {
1869 const need_cross_glibc = target.isGnuLibC() and case.link_libc;
1870 const glibc_dir_arg: ?[]const u8 = if (need_cross_glibc)
1871 glibc_runtimes_dir orelse continue :update // glibc dir not available; pass test
1872 else
1873 null;
1874 try argv.append(qemu_bin_name);
1875 if (glibc_dir_arg) |dir| {
1876 const linux_triple = try target.linuxTriple(arena);
1877 const full_dir = try std.fs.path.join(arena, &[_][]const u8{
1878 dir,
1879 linux_triple,
1880 });
1881
1882 try argv.append("-L");
1883 try argv.append(full_dir);
1884 }
1885 try argv.append(exe_path);
1886 } else {
1887 continue :update; // QEMU not available; pass test.
1888 },
1889
1890 .wine => |wine_bin_name| if (enable_wine) {
1891 try argv.append(wine_bin_name);
1892 try argv.append(exe_path);
1893 } else {
1894 continue :update; // Wine not available; pass test.
1895 },
1896
1897 .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
1898 try argv.append(wasmtime_bin_name);
1899 try argv.append("--dir=.");
1900 try argv.append(exe_path);
1901 } else {
1902 continue :update; // wasmtime not available; pass test.
1903 },
1904
1905 .darling => |darling_bin_name| if (enable_darling) {
1906 try argv.append(darling_bin_name);
1907 // Since we use relative to cwd here, we invoke darling with
1908 // "shell" subcommand.
1909 try argv.append("shell");
1910 try argv.append(exe_path);
1911 } else {
1912 continue :update; // Darling not available; pass test.
1913 },
1914 }
1915
1916 try comp.makeBinFileExecutable();
1917
1918 while (true) {
1919 break :x std.ChildProcess.exec(.{
1920 .allocator = allocator,
1921 .argv = argv.items,
1922 .cwd_dir = tmp.dir,
1923 .cwd = tmp_dir_path,
1924 }) catch |err| switch (err) {
1925 error.FileBusy => {
1926 // There is a fundamental design flaw in Unix systems with how
1927 // ETXTBSY interacts with fork+exec.
1928 // https://github.com/golang/go/issues/22315
1929 // https://bugs.openjdk.org/browse/JDK-8068370
1930 // Unfortunately, this could be a real error, but we can't
1931 // tell the difference here.
1932 continue;
1933 },
1934 else => {
1935 print("\n{s}.{d} The following command failed with {s}:\n", .{
1936 case.name, update_index, @errorName(err),
1937 });
1938 dumpArgs(argv.items);
1939 return error.ChildProcessExecution;
1940 },
1941 };
1942 }
1943 };
1944 var test_node = update_node.start("test", 0);
1945 test_node.activate();
1946 defer test_node.end();
1947 defer allocator.free(exec_result.stdout);
1948 defer allocator.free(exec_result.stderr);
1949 switch (exec_result.term) {
1950 .Exited => |code| {
1951 if (code != 0) {
1952 print("\n{s}\n{s}: execution exited with code {d}:\n", .{
1953 exec_result.stderr, case.name, code,
1954 });
1955 dumpArgs(argv.items);
1956 return error.ChildProcessExecution;
1957 }
1958 },
1959 else => {
1960 print("\n{s}\n{s}: execution crashed:\n", .{
1961 exec_result.stderr, case.name,
1962 });
1963 dumpArgs(argv.items);
1964 return error.ChildProcessExecution;
1965 },
1966 }
1967 try std.testing.expectEqualStrings(expected_stdout, exec_result.stdout);
1968 // We allow stderr to have garbage in it because wasmtime prints a
1969 // warning about --invoke even though we don't pass it.
1970 //std.testing.expectEqualStrings("", exec_result.stderr);
1971 },
1972 }
1973 }
1974 }
1975};
1976
1977fn dumpArgs(argv: []const []const u8) void {
1978 for (argv) |arg| {
1979 print("{s} ", .{arg});
1980 }
1981 print("\n", .{});
1982}
1983
1984const tmp_src_path = "tmp.zig";
src/translate_c.zig+15-1
...@@ -784,9 +784,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -784,9 +784,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
784784
785 const qual_type = var_decl.getTypeSourceInfo_getType();785 const qual_type = var_decl.getTypeSourceInfo_getType();
786 const storage_class = var_decl.getStorageClass();786 const storage_class = var_decl.getStorageClass();
787 const is_const = qual_type.isConstQualified();
788 const has_init = var_decl.hasInit();787 const has_init = var_decl.hasInit();
789 const decl_init = var_decl.getInit();788 const decl_init = var_decl.getInit();
789 var is_const = qual_type.isConstQualified();
790790
791 // In C extern variables with initializers behave like Zig exports.791 // In C extern variables with initializers behave like Zig exports.
792 // extern int foo = 2;792 // extern int foo = 2;
...@@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
843843
844 // std.mem.zeroes(T)844 // std.mem.zeroes(T)
845 init_node = try Tag.std_mem_zeroes.create(c.arena, type_node);845 init_node = try Tag.std_mem_zeroes.create(c.arena, type_node);
846 } else if (qual_type.getTypeClass() == .IncompleteArray) {
847 // Oh no, an extern array of unknown size! These are really fun because there's no
848 // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
849 // to the data initialized via @extern.
850
851 const name_str = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{var_name});
852 init_node = try Tag.builtin_extern.create(c.arena, .{
853 .type = type_node,
854 .name = try Tag.string_literal.create(c.arena, name_str),
855 });
856
857 // Since this is really a pointer to the underlying data, we tweak a few properties.
858 is_extern = false;
859 is_const = true;
846 }860 }
847861
848 const linksection_string = blk: {862 const linksection_string = blk: {
src/translate_c/ast.zig+28
...@@ -158,6 +158,8 @@ pub const Node = extern union {...@@ -158,6 +158,8 @@ pub const Node = extern union {
158 vector_zero_init,158 vector_zero_init,
159 /// @shuffle(type, a, b, mask)159 /// @shuffle(type, a, b, mask)
160 shuffle,160 shuffle,
161 /// @extern(ty, .{ .name = n })
162 builtin_extern,
161163
162 /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)164 /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)
163 macro_arithmetic,165 macro_arithmetic,
...@@ -373,6 +375,7 @@ pub const Node = extern union {...@@ -373,6 +375,7 @@ pub const Node = extern union {
373 .field_access => Payload.FieldAccess,375 .field_access => Payload.FieldAccess,
374 .string_slice => Payload.StringSlice,376 .string_slice => Payload.StringSlice,
375 .shuffle => Payload.Shuffle,377 .shuffle => Payload.Shuffle,
378 .builtin_extern => Payload.Extern,
376 .macro_arithmetic => Payload.MacroArithmetic,379 .macro_arithmetic => Payload.MacroArithmetic,
377 };380 };
378 }381 }
...@@ -718,6 +721,14 @@ pub const Payload = struct {...@@ -718,6 +721,14 @@ pub const Payload = struct {
718 },721 },
719 };722 };
720723
724 pub const Extern = struct {
725 base: Payload,
726 data: struct {
727 type: Node,
728 name: Node,
729 },
730 };
731
721 pub const MacroArithmetic = struct {732 pub const MacroArithmetic = struct {
722 base: Payload,733 base: Payload,
723 data: struct {734 data: struct {
...@@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1409 payload.mask_vector,1420 payload.mask_vector,
1410 });1421 });
1411 },1422 },
1423 .builtin_extern => {
1424 const payload = node.castTag(.builtin_extern).?.data;
1425
1426 var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
1427 .{ .name = "name", .value = payload.name },
1428 };
1429 var info_payload: Payload.ContainerInitDot = .{
1430 .base = .{ .tag = .container_init_dot },
1431 .data = &info_inits,
1432 };
1433
1434 return renderBuiltinCall(c, "@extern", &.{
1435 payload.type,
1436 .{ .ptr_otherwise = &info_payload.base },
1437 });
1438 },
1412 .macro_arithmetic => {1439 .macro_arithmetic => {
1413 const payload = node.castTag(.macro_arithmetic).?.data;1440 const payload = node.castTag(.macro_arithmetic).?.data;
1414 const op = @tagName(payload.op);1441 const op = @tagName(payload.op);
...@@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2348 .div_exact,2375 .div_exact,
2349 .offset_of,2376 .offset_of,
2350 .shuffle,2377 .shuffle,
2378 .builtin_extern,
2351 .static_local_var,2379 .static_local_var,
2352 .mut_str,2380 .mut_str,
2353 .macro_arithmetic,2381 .macro_arithmetic,
src/value.zig+60-7
...@@ -1365,6 +1365,17 @@ pub const Value = extern union {...@@ -1365,6 +1365,17 @@ pub const Value = extern union {
1365 if (val.isDeclRef()) return error.ReinterpretDeclRef;1365 if (val.isDeclRef()) return error.ReinterpretDeclRef;
1366 return val.writeToMemory(Type.usize, mod, buffer);1366 return val.writeToMemory(Type.usize, mod, buffer);
1367 },1367 },
1368 .Optional => {
1369 assert(ty.isPtrLikeOptional());
1370 var buf: Type.Payload.ElemType = undefined;
1371 const child = ty.optionalChild(&buf);
1372 const opt_val = val.optionalValue();
1373 if (opt_val) |some| {
1374 return some.writeToMemory(child, mod, buffer);
1375 } else {
1376 return writeToMemory(Value.zero, Type.usize, mod, buffer);
1377 }
1378 },
1368 else => @panic("TODO implement writeToMemory for more types"),1379 else => @panic("TODO implement writeToMemory for more types"),
1369 }1380 }
1370 }1381 }
...@@ -1471,6 +1482,17 @@ pub const Value = extern union {...@@ -1471,6 +1482,17 @@ pub const Value = extern union {
1471 if (val.isDeclRef()) return error.ReinterpretDeclRef;1482 if (val.isDeclRef()) return error.ReinterpretDeclRef;
1472 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);1483 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);
1473 },1484 },
1485 .Optional => {
1486 assert(ty.isPtrLikeOptional());
1487 var buf: Type.Payload.ElemType = undefined;
1488 const child = ty.optionalChild(&buf);
1489 const opt_val = val.optionalValue();
1490 if (opt_val) |some| {
1491 return some.writeToPackedMemory(child, mod, buffer, bit_offset);
1492 } else {
1493 return writeToPackedMemory(Value.zero, Type.usize, mod, buffer, bit_offset);
1494 }
1495 },
1474 else => @panic("TODO implement writeToPackedMemory for more types"),1496 else => @panic("TODO implement writeToPackedMemory for more types"),
1475 }1497 }
1476 }1498 }
...@@ -1579,6 +1601,12 @@ pub const Value = extern union {...@@ -1579,6 +1601,12 @@ pub const Value = extern union {
1579 assert(!ty.isSlice()); // No well defined layout.1601 assert(!ty.isSlice()); // No well defined layout.
1580 return readFromMemory(Type.usize, mod, buffer, arena);1602 return readFromMemory(Type.usize, mod, buffer, arena);
1581 },1603 },
1604 .Optional => {
1605 assert(ty.isPtrLikeOptional());
1606 var buf: Type.Payload.ElemType = undefined;
1607 const child = ty.optionalChild(&buf);
1608 return readFromMemory(child, mod, buffer, arena);
1609 },
1582 else => @panic("TODO implement readFromMemory for more types"),1610 else => @panic("TODO implement readFromMemory for more types"),
1583 }1611 }
1584 }1612 }
...@@ -1670,6 +1698,12 @@ pub const Value = extern union {...@@ -1670,6 +1698,12 @@ pub const Value = extern union {
1670 assert(!ty.isSlice()); // No well defined layout.1698 assert(!ty.isSlice()); // No well defined layout.
1671 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);1699 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
1672 },1700 },
1701 .Optional => {
1702 assert(ty.isPtrLikeOptional());
1703 var buf: Type.Payload.ElemType = undefined;
1704 const child = ty.optionalChild(&buf);
1705 return readFromPackedMemory(child, mod, buffer, bit_offset, arena);
1706 },
1673 else => @panic("TODO implement readFromPackedMemory for more types"),1707 else => @panic("TODO implement readFromPackedMemory for more types"),
1674 }1708 }
1675 }1709 }
...@@ -3144,13 +3178,32 @@ pub const Value = extern union {...@@ -3144,13 +3178,32 @@ pub const Value = extern union {
3144 /// TODO: check for cases such as array that is not marked undef but all the element3178 /// TODO: check for cases such as array that is not marked undef but all the element
3145 /// values are marked undef, or struct that is not marked undef but all fields are marked3179 /// values are marked undef, or struct that is not marked undef but all fields are marked
3146 /// undef, etc.3180 /// undef, etc.
3147 pub fn anyUndef(self: Value) bool {3181 pub fn anyUndef(self: Value, mod: *Module) bool {
3148 if (self.castTag(.aggregate)) |aggregate| {3182 switch (self.tag()) {
3149 for (aggregate.data) |val| {3183 .slice => {
3150 if (val.anyUndef()) return true;3184 const payload = self.castTag(.slice).?;
3151 }3185 const len = payload.data.len.toUnsignedInt(mod.getTarget());
3186
3187 var elem_value_buf: ElemValueBuffer = undefined;
3188 var i: usize = 0;
3189 while (i < len) : (i += 1) {
3190 const elem_val = payload.data.ptr.elemValueBuffer(mod, i, &elem_value_buf);
3191 if (elem_val.anyUndef(mod)) return true;
3192 }
3193 },
3194
3195 .aggregate => {
3196 const payload = self.castTag(.aggregate).?;
3197 for (payload.data) |val| {
3198 if (val.anyUndef(mod)) return true;
3199 }
3200 },
3201
3202 .undef => return true,
3203 else => {},
3152 }3204 }
3153 return self.isUndef();3205
3206 return false;
3154 }3207 }
31553208
3156 /// Asserts the value is not undefined and not unreachable.3209 /// Asserts the value is not undefined and not unreachable.
...@@ -3319,7 +3372,7 @@ pub const Value = extern union {...@@ -3319,7 +3372,7 @@ pub const Value = extern union {
3319 }3372 }
3320 }3373 }
33213374
3322 fn floatToValue(float: f128, arena: Allocator, dest_ty: Type, target: Target) !Value {3375 pub fn floatToValue(float: f128, arena: Allocator, dest_ty: Type, target: Target) !Value {
3323 switch (dest_ty.floatBits(target)) {3376 switch (dest_ty.floatBits(target)) {
3324 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)),3377 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)),
3325 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)),3378 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)),
src/wasi_libc.zig+8-8
...@@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co...@@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co
59 };59 };
60}60}
6161
62pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {62pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
63 if (!build_options.have_llvm) {63 if (!build_options.have_llvm) {
64 return error.ZigCompilerNotBuiltWithLLVMExtensions;64 return error.ZigCompilerNotBuiltWithLLVMExtensions;
65 }65 }
...@@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
74 var args = std.ArrayList([]const u8).init(arena);74 var args = std.ArrayList([]const u8).init(arena);
75 try addCCArgs(comp, arena, &args, false);75 try addCCArgs(comp, arena, &args, false);
76 try addLibcBottomHalfIncludes(comp, arena, &args);76 try addLibcBottomHalfIncludes(comp, arena, &args);
77 return comp.build_crt_file("crt1-reactor", .Obj, &[1]Compilation.CSourceFile{77 return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &.{
78 .{78 .{
79 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{79 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
80 "libc", try sanitize(arena, crt1_reactor_src_file),80 "libc", try sanitize(arena, crt1_reactor_src_file),
...@@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
87 var args = std.ArrayList([]const u8).init(arena);87 var args = std.ArrayList([]const u8).init(arena);
88 try addCCArgs(comp, arena, &args, false);88 try addCCArgs(comp, arena, &args, false);
89 try addLibcBottomHalfIncludes(comp, arena, &args);89 try addLibcBottomHalfIncludes(comp, arena, &args);
90 return comp.build_crt_file("crt1-command", .Obj, &[1]Compilation.CSourceFile{90 return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &.{
91 .{91 .{
92 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{92 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
93 "libc", try sanitize(arena, crt1_command_src_file),93 "libc", try sanitize(arena, crt1_command_src_file),
...@@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
145 }145 }
146 }146 }
147147
148 try comp.build_crt_file("c", .Lib, libc_sources.items);148 try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items);
149 },149 },
150 .libwasi_emulated_process_clocks_a => {150 .libwasi_emulated_process_clocks_a => {
151 var args = std.ArrayList([]const u8).init(arena);151 var args = std.ArrayList([]const u8).init(arena);
...@@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
161 .extra_flags = args.items,161 .extra_flags = args.items,
162 });162 });
163 }163 }
164 try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, emu_clocks_sources.items);164 try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items);
165 },165 },
166 .libwasi_emulated_getpid_a => {166 .libwasi_emulated_getpid_a => {
167 var args = std.ArrayList([]const u8).init(arena);167 var args = std.ArrayList([]const u8).init(arena);
...@@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
177 .extra_flags = args.items,177 .extra_flags = args.items,
178 });178 });
179 }179 }
180 try comp.build_crt_file("wasi-emulated-getpid", .Lib, emu_getpid_sources.items);180 try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items);
181 },181 },
182 .libwasi_emulated_mman_a => {182 .libwasi_emulated_mman_a => {
183 var args = std.ArrayList([]const u8).init(arena);183 var args = std.ArrayList([]const u8).init(arena);
...@@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
193 .extra_flags = args.items,193 .extra_flags = args.items,
194 });194 });
195 }195 }
196 try comp.build_crt_file("wasi-emulated-mman", .Lib, emu_mman_sources.items);196 try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items);
197 },197 },
198 .libwasi_emulated_signal_a => {198 .libwasi_emulated_signal_a => {
199 var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena);199 var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
...@@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
228 }228 }
229 }229 }
230230
231 try comp.build_crt_file("wasi-emulated-signal", .Lib, emu_signal_sources.items);231 try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", prog_node, emu_signal_sources.items);
232 },232 },
233 }233 }
234}234}
stage1/wasi.c-2
...@@ -497,8 +497,6 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov...@@ -497,8 +497,6 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov
497 size_t read_size = 0;497 size_t read_size = 0;
498 if (fds[fd].stream != NULL)498 if (fds[fd].stream != NULL)
499 read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);499 read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);
500 else
501 panic("unimplemented");
502 size += read_size;500 size += read_size;
503 if (read_size < iovs_ptr[i].len) break;501 if (read_size < iovs_ptr[i].len) break;
504 }502 }
stage1/zig.h+1915-904
...@@ -1,8 +1,11 @@...@@ -1,8 +1,11 @@
1#undef linux1#undef linux
22
3#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
3#define __STDC_WANT_IEC_60559_TYPES_EXT__4#define __STDC_WANT_IEC_60559_TYPES_EXT__
5#endif
4#include <float.h>6#include <float.h>
5#include <limits.h>7#include <limits.h>
8#include <stdarg.h>
6#include <stddef.h>9#include <stddef.h>
7#include <stdint.h>10#include <stdint.h>
811
...@@ -34,6 +37,14 @@ typedef char bool;...@@ -34,6 +37,14 @@ typedef char bool;
34#define zig_has_attribute(attribute) 037#define zig_has_attribute(attribute) 0
35#endif38#endif
3639
40#if __LITTLE_ENDIAN__ || _MSC_VER
41#define zig_little_endian 1
42#define zig_big_endian 0
43#else
44#define zig_little_endian 0
45#define zig_big_endian 1
46#endif
47
37#if __STDC_VERSION__ >= 201112L48#if __STDC_VERSION__ >= 201112L
38#define zig_threadlocal _Thread_local49#define zig_threadlocal _Thread_local
39#elif defined(__GNUC__)50#elif defined(__GNUC__)
...@@ -75,6 +86,32 @@ typedef char bool;...@@ -75,6 +86,32 @@ typedef char bool;
75#define zig_cold86#define zig_cold
76#endif87#endif
7788
89#if zig_has_attribute(flatten)
90#define zig_maybe_flatten __attribute__((flatten))
91#else
92#define zig_maybe_flatten
93#endif
94
95#if zig_has_attribute(noinline)
96#define zig_never_inline __attribute__((noinline)) zig_maybe_flatten
97#elif defined(_MSC_VER)
98#define zig_never_inline __declspec(noinline) zig_maybe_flatten
99#else
100#define zig_never_inline zig_never_inline_unavailable
101#endif
102
103#if zig_has_attribute(not_tail_called)
104#define zig_never_tail __attribute__((not_tail_called)) zig_never_inline
105#else
106#define zig_never_tail zig_never_tail_unavailable
107#endif
108
109#if zig_has_attribute(always_inline)
110#define zig_always_tail __attribute__((musttail))
111#else
112#define zig_always_tail zig_always_tail_unavailable
113#endif
114
78#if __STDC_VERSION__ >= 199901L115#if __STDC_VERSION__ >= 199901L
79#define zig_restrict restrict116#define zig_restrict restrict
80#elif defined(__GNUC__)117#elif defined(__GNUC__)
...@@ -151,16 +188,38 @@ typedef char bool;...@@ -151,16 +188,38 @@ typedef char bool;
151#define zig_export(sig, symbol, name) __asm(name " = " symbol)188#define zig_export(sig, symbol, name) __asm(name " = " symbol)
152#endif189#endif
153190
191#if zig_has_builtin(trap)
192#define zig_trap() __builtin_trap()
193#elif _MSC_VER && (_M_IX86 || _M_X64)
194#define zig_trap() __ud2()
195#elif _MSC_VER
196#define zig_trap() __fastfail(0)
197#elif defined(__i386__) || defined(__x86_64__)
198#define zig_trap() __asm__ volatile("ud2");
199#elif defined(__arm__) || defined(__aarch64__)
200#define zig_breakpoint() __asm__ volatile("udf #0");
201#else
202#include <stdlib.h>
203#define zig_trap() abort()
204#endif
205
154#if zig_has_builtin(debugtrap)206#if zig_has_builtin(debugtrap)
155#define zig_breakpoint() __builtin_debugtrap()207#define zig_breakpoint() __builtin_debugtrap()
156#elif zig_has_builtin(trap) || defined(zig_gnuc)
157#define zig_breakpoint() __builtin_trap()
158#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)208#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
159#define zig_breakpoint() __debugbreak()209#define zig_breakpoint() __debugbreak()
160#elif defined(__i386__) || defined(__x86_64__)210#elif defined(__i386__) || defined(__x86_64__)
161#define zig_breakpoint() __asm__ volatile("int $0x03");211#define zig_breakpoint() __asm__ volatile("int $0x03");
212#elif defined(__arm__)
213#define zig_breakpoint() __asm__ volatile("bkpt #0");
214#elif defined(__aarch64__)
215#define zig_breakpoint() __asm__ volatile("brk #0");
162#else216#else
217#include <signal.h>
218#if defined(SIGTRAP)
163#define zig_breakpoint() raise(SIGTRAP)219#define zig_breakpoint() raise(SIGTRAP)
220#else
221#define zig_breakpoint() zig_breakpoint_unavailable
222#endif
164#endif223#endif
165224
166#if zig_has_builtin(return_address) || defined(zig_gnuc)225#if zig_has_builtin(return_address) || defined(zig_gnuc)
...@@ -286,701 +345,656 @@ typedef char bool;...@@ -286,701 +345,656 @@ typedef char bool;
286#endif345#endif
287346
288#if __STDC_VERSION__ >= 201112L347#if __STDC_VERSION__ >= 201112L
289#define zig_noreturn _Noreturn void348#define zig_noreturn _Noreturn
290#elif zig_has_attribute(noreturn) || defined(zig_gnuc)349#elif zig_has_attribute(noreturn) || defined(zig_gnuc)
291#define zig_noreturn __attribute__((noreturn)) void350#define zig_noreturn __attribute__((noreturn))
292#elif _MSC_VER351#elif _MSC_VER
293#define zig_noreturn __declspec(noreturn) void352#define zig_noreturn __declspec(noreturn)
294#else353#else
295#define zig_noreturn void354#define zig_noreturn
296#endif355#endif
297356
298#define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T))357#define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T))
299358
300typedef uintptr_t zig_usize;359#define zig_compiler_rt_abbrev_uint32_t si
301typedef intptr_t zig_isize;360#define zig_compiler_rt_abbrev_int32_t si
302typedef signed short int zig_c_short;361#define zig_compiler_rt_abbrev_uint64_t di
303typedef unsigned short int zig_c_ushort;362#define zig_compiler_rt_abbrev_int64_t di
304typedef signed int zig_c_int;363#define zig_compiler_rt_abbrev_zig_u128 ti
305typedef unsigned int zig_c_uint;364#define zig_compiler_rt_abbrev_zig_i128 ti
306typedef signed long int zig_c_long;365#define zig_compiler_rt_abbrev_zig_f16 hf
307typedef unsigned long int zig_c_ulong;366#define zig_compiler_rt_abbrev_zig_f32 sf
308typedef signed long long int zig_c_longlong;367#define zig_compiler_rt_abbrev_zig_f64 df
309typedef unsigned long long int zig_c_ulonglong;368#define zig_compiler_rt_abbrev_zig_f80 xf
310369#define zig_compiler_rt_abbrev_zig_f128 tf
311typedef uint8_t zig_u8;370
312typedef int8_t zig_i8;371zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t);
313typedef uint16_t zig_u16;372zig_extern void *memset (void *, int, size_t);
314typedef int16_t zig_i16;373
315typedef uint32_t zig_u32;374/* ===================== 8/16/32/64-bit Integer Support ===================== */
316typedef int32_t zig_i32;375
317typedef uint64_t zig_u64;376#if __STDC_VERSION__ >= 199901L || _MSC_VER
318typedef int64_t zig_i64;377#include <stdint.h>
319378#else
320#define zig_as_u8(val) UINT8_C(val)379
321#define zig_as_i8(val) INT8_C(val)380#if SCHAR_MIN == ~0x7F && SCHAR_MAX == 0x7F && UCHAR_MAX == 0xFF
322#define zig_as_u16(val) UINT16_C(val)381typedef unsigned char uint8_t;
323#define zig_as_i16(val) INT16_C(val)382typedef signed char int8_t;
324#define zig_as_u32(val) UINT32_C(val)383#define INT8_C(c) c
325#define zig_as_i32(val) INT32_C(val)384#define UINT8_C(c) c##U
326#define zig_as_u64(val) UINT64_C(val)385#elif SHRT_MIN == ~0x7F && SHRT_MAX == 0x7F && USHRT_MAX == 0xFF
327#define zig_as_i64(val) INT64_C(val)386typedef unsigned short uint8_t;
328387typedef signed short int8_t;
329#define zig_minInt_u8 zig_as_u8(0)388#define INT8_C(c) c
330#define zig_maxInt_u8 UINT8_MAX389#define UINT8_C(c) c##U
390#elif INT_MIN == ~0x7F && INT_MAX == 0x7F && UINT_MAX == 0xFF
391typedef unsigned int uint8_t;
392typedef signed int int8_t;
393#define INT8_C(c) c
394#define UINT8_C(c) c##U
395#elif LONG_MIN == ~0x7F && LONG_MAX == 0x7F && ULONG_MAX == 0xFF
396typedef unsigned long uint8_t;
397typedef signed long int8_t;
398#define INT8_C(c) c##L
399#define UINT8_C(c) c##LU
400#elif LLONG_MIN == ~0x7F && LLONG_MAX == 0x7F && ULLONG_MAX == 0xFF
401typedef unsigned long long uint8_t;
402typedef signed long long int8_t;
403#define INT8_C(c) c##LL
404#define UINT8_C(c) c##LLU
405#endif
406#define INT8_MIN (~INT8_C(0x7F))
407#define INT8_MAX ( INT8_C(0x7F))
408#define UINT8_MAX ( INT8_C(0xFF))
409
410#if SCHAR_MIN == ~0x7FFF && SCHAR_MAX == 0x7FFF && UCHAR_MAX == 0xFFFF
411typedef unsigned char uint16_t;
412typedef signed char int16_t;
413#define INT16_C(c) c
414#define UINT16_C(c) c##U
415#elif SHRT_MIN == ~0x7FFF && SHRT_MAX == 0x7FFF && USHRT_MAX == 0xFFFF
416typedef unsigned short uint16_t;
417typedef signed short int16_t;
418#define INT16_C(c) c
419#define UINT16_C(c) c##U
420#elif INT_MIN == ~0x7FFF && INT_MAX == 0x7FFF && UINT_MAX == 0xFFFF
421typedef unsigned int uint16_t;
422typedef signed int int16_t;
423#define INT16_C(c) c
424#define UINT16_C(c) c##U
425#elif LONG_MIN == ~0x7FFF && LONG_MAX == 0x7FFF && ULONG_MAX == 0xFFFF
426typedef unsigned long uint16_t;
427typedef signed long int16_t;
428#define INT16_C(c) c##L
429#define UINT16_C(c) c##LU
430#elif LLONG_MIN == ~0x7FFF && LLONG_MAX == 0x7FFF && ULLONG_MAX == 0xFFFF
431typedef unsigned long long uint16_t;
432typedef signed long long int16_t;
433#define INT16_C(c) c##LL
434#define UINT16_C(c) c##LLU
435#endif
436#define INT16_MIN (~INT16_C(0x7FFF))
437#define INT16_MAX ( INT16_C(0x7FFF))
438#define UINT16_MAX ( INT16_C(0xFFFF))
439
440#if SCHAR_MIN == ~0x7FFFFFFF && SCHAR_MAX == 0x7FFFFFFF && UCHAR_MAX == 0xFFFFFFFF
441typedef unsigned char uint32_t;
442typedef signed char int32_t;
443#define INT32_C(c) c
444#define UINT32_C(c) c##U
445#elif SHRT_MIN == ~0x7FFFFFFF && SHRT_MAX == 0x7FFFFFFF && USHRT_MAX == 0xFFFFFFFF
446typedef unsigned short uint32_t;
447typedef signed short int32_t;
448#define INT32_C(c) c
449#define UINT32_C(c) c##U
450#elif INT_MIN == ~0x7FFFFFFF && INT_MAX == 0x7FFFFFFF && UINT_MAX == 0xFFFFFFFF
451typedef unsigned int uint32_t;
452typedef signed int int32_t;
453#define INT32_C(c) c
454#define UINT32_C(c) c##U
455#elif LONG_MIN == ~0x7FFFFFFF && LONG_MAX == 0x7FFFFFFF && ULONG_MAX == 0xFFFFFFFF
456typedef unsigned long uint32_t;
457typedef signed long int32_t;
458#define INT32_C(c) c##L
459#define UINT32_C(c) c##LU
460#elif LLONG_MIN == ~0x7FFFFFFF && LLONG_MAX == 0x7FFFFFFF && ULLONG_MAX == 0xFFFFFFFF
461typedef unsigned long long uint32_t;
462typedef signed long long int32_t;
463#define INT32_C(c) c##LL
464#define UINT32_C(c) c##LLU
465#endif
466#define INT32_MIN (~INT32_C(0x7FFFFFFF))
467#define INT32_MAX ( INT32_C(0x7FFFFFFF))
468#define UINT32_MAX ( INT32_C(0xFFFFFFFF))
469
470#if SCHAR_MIN == ~0x7FFFFFFFFFFFFFFF && SCHAR_MAX == 0x7FFFFFFFFFFFFFFF && UCHAR_MAX == 0xFFFFFFFFFFFFFFFF
471typedef unsigned char uint64_t;
472typedef signed char int64_t;
473#define INT64_C(c) c
474#define UINT64_C(c) c##U
475#elif SHRT_MIN == ~0x7FFFFFFFFFFFFFFF && SHRT_MAX == 0x7FFFFFFFFFFFFFFF && USHRT_MAX == 0xFFFFFFFFFFFFFFFF
476typedef unsigned short uint64_t;
477typedef signed short int64_t;
478#define INT64_C(c) c
479#define UINT64_C(c) c##U
480#elif INT_MIN == ~0x7FFFFFFFFFFFFFFF && INT_MAX == 0x7FFFFFFFFFFFFFFF && UINT_MAX == 0xFFFFFFFFFFFFFFFF
481typedef unsigned int uint64_t;
482typedef signed int int64_t;
483#define INT64_C(c) c
484#define UINT64_C(c) c##U
485#elif LONG_MIN == ~0x7FFFFFFFFFFFFFFF && LONG_MAX == 0x7FFFFFFFFFFFFFFF && ULONG_MAX == 0xFFFFFFFFFFFFFFFF
486typedef unsigned long uint64_t;
487typedef signed long int64_t;
488#define INT64_C(c) c##L
489#define UINT64_C(c) c##LU
490#elif LLONG_MIN == ~0x7FFFFFFFFFFFFFFF && LLONG_MAX == 0x7FFFFFFFFFFFFFFF && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF
491typedef unsigned long long uint64_t;
492typedef signed long long int64_t;
493#define INT64_C(c) c##LL
494#define UINT64_C(c) c##LLU
495#endif
496#define INT64_MIN (~INT64_C(0x7FFFFFFFFFFFFFFF))
497#define INT64_MAX ( INT64_C(0x7FFFFFFFFFFFFFFF))
498#define UINT64_MAX ( INT64_C(0xFFFFFFFFFFFFFFFF))
499
500typedef size_t uintptr_t;
501typedef ptrdiff_t intptr_t;
502
503#endif
504
331#define zig_minInt_i8 INT8_MIN505#define zig_minInt_i8 INT8_MIN
332#define zig_maxInt_i8 INT8_MAX506#define zig_maxInt_i8 INT8_MAX
333#define zig_minInt_u16 zig_as_u16(0)507#define zig_minInt_u8 UINT8_C(0)
334#define zig_maxInt_u16 UINT16_MAX508#define zig_maxInt_u8 UINT8_MAX
335#define zig_minInt_i16 INT16_MIN509#define zig_minInt_i16 INT16_MIN
336#define zig_maxInt_i16 INT16_MAX510#define zig_maxInt_i16 INT16_MAX
337#define zig_minInt_u32 zig_as_u32(0)511#define zig_minInt_u16 UINT16_C(0)
338#define zig_maxInt_u32 UINT32_MAX512#define zig_maxInt_u16 UINT16_MAX
339#define zig_minInt_i32 INT32_MIN513#define zig_minInt_i32 INT32_MIN
340#define zig_maxInt_i32 INT32_MAX514#define zig_maxInt_i32 INT32_MAX
341#define zig_minInt_u64 zig_as_u64(0)515#define zig_minInt_u32 UINT32_C(0)
342#define zig_maxInt_u64 UINT64_MAX516#define zig_maxInt_u32 UINT32_MAX
343#define zig_minInt_i64 INT64_MIN517#define zig_minInt_i64 INT64_MIN
344#define zig_maxInt_i64 INT64_MAX518#define zig_maxInt_i64 INT64_MAX
519#define zig_minInt_u64 UINT64_C(0)
520#define zig_maxInt_u64 UINT64_MAX
345521
346#define zig_compiler_rt_abbrev_u32 si522#define zig_intLimit(s, w, limit, bits) zig_shr_##s##w(zig_##limit##Int_##s##w, w - (bits))
347#define zig_compiler_rt_abbrev_i32 si523#define zig_minInt_i(w, bits) zig_intLimit(i, w, min, bits)
348#define zig_compiler_rt_abbrev_u64 di524#define zig_maxInt_i(w, bits) zig_intLimit(i, w, max, bits)
349#define zig_compiler_rt_abbrev_i64 di525#define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits)
350#define zig_compiler_rt_abbrev_u128 ti526#define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits)
351#define zig_compiler_rt_abbrev_i128 ti
352#define zig_compiler_rt_abbrev_f16 hf
353#define zig_compiler_rt_abbrev_f32 sf
354#define zig_compiler_rt_abbrev_f64 df
355#define zig_compiler_rt_abbrev_f80 xf
356#define zig_compiler_rt_abbrev_f128 tf
357
358zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize);
359zig_extern void *memset (void *, int, zig_usize);
360
361/* ==================== 8/16/32/64-bit Integer Routines ===================== */
362
363#define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits))
364#define zig_expand_maxInt(Type, bits) zig_maxInt(Type, bits)
365#define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits)
366#define zig_expand_minInt(Type, bits) zig_minInt(Type, bits)
367527
368#define zig_int_operator(Type, RhsType, operation, operator) \528#define zig_int_operator(Type, RhsType, operation, operator) \
369 static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \529 static inline Type zig_##operation(Type lhs, RhsType rhs) { \
370 return lhs operator rhs; \530 return lhs operator rhs; \
371 }531 }
372#define zig_int_basic_operator(Type, operation, operator) \532#define zig_int_basic_operator(Type, operation, operator) \
373 zig_int_operator(Type, Type, operation, operator)533 zig_int_operator(Type, Type, operation, operator)
374#define zig_int_shift_operator(Type, operation, operator) \534#define zig_int_shift_operator(Type, operation, operator) \
375 zig_int_operator(Type, u8, operation, operator)535 zig_int_operator(Type, uint8_t, operation, operator)
376#define zig_int_helpers(w) \536#define zig_int_helpers(w) \
377 zig_int_basic_operator(u##w, and, &) \537 zig_int_basic_operator(uint##w##_t, and_u##w, &) \
378 zig_int_basic_operator(i##w, and, &) \538 zig_int_basic_operator( int##w##_t, and_i##w, &) \
379 zig_int_basic_operator(u##w, or, |) \539 zig_int_basic_operator(uint##w##_t, or_u##w, |) \
380 zig_int_basic_operator(i##w, or, |) \540 zig_int_basic_operator( int##w##_t, or_i##w, |) \
381 zig_int_basic_operator(u##w, xor, ^) \541 zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \
382 zig_int_basic_operator(i##w, xor, ^) \542 zig_int_basic_operator( int##w##_t, xor_i##w, ^) \
383 zig_int_shift_operator(u##w, shl, <<) \543 zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \
384 zig_int_shift_operator(i##w, shl, <<) \544 zig_int_shift_operator( int##w##_t, shl_i##w, <<) \
385 zig_int_shift_operator(u##w, shr, >>) \545 zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \
386\546\
387 static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \547 static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \
388 zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \548 int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \
389 return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \549 return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \
390 } \550 } \
391\551\
392 static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \552 static inline uint##w##_t zig_not_u##w(uint##w##_t val, uint8_t bits) { \
393 return val ^ zig_maxInt(u##w, bits); \553 return val ^ zig_maxInt_u(w, bits); \
394 } \554 } \
395\555\
396 static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \556 static inline int##w##_t zig_not_i##w(int##w##_t val, uint8_t bits) { \
397 (void)bits; \557 (void)bits; \
398 return ~val; \558 return ~val; \
399 } \559 } \
400\560\
401 static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \561 static inline uint##w##_t zig_wrap_u##w(uint##w##_t val, uint8_t bits) { \
402 return val & zig_maxInt(u##w, bits); \562 return val & zig_maxInt_u(w, bits); \
403 } \563 } \
404\564\
405 static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \565 static inline int##w##_t zig_wrap_i##w(int##w##_t val, uint8_t bits) { \
406 return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \566 return (val & UINT##w##_C(1) << (bits - UINT8_C(1))) != 0 \
407 ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \567 ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \
408 } \568 } \
409\569\
410 zig_int_basic_operator(u##w, div_floor, /) \570 zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \
411\571\
412 static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \572 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
413 return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \573 return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \
414 } \574 } \
415\575\
416 zig_int_basic_operator(u##w, mod, %) \576 zig_int_basic_operator(uint##w##_t, mod_u##w, %) \
417\577\
418 static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \578 static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \
419 zig_i##w rem = lhs % rhs; \579 int##w##_t rem = lhs % rhs; \
420 return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \580 return rem + (((lhs ^ rhs) & rem) < INT##w##_C(0) ? rhs : INT##w##_C(0)); \
421 } \581 } \
422\582\
423 static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \583 static inline uint##w##_t zig_shlw_u##w(uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \
424 return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \584 return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \
425 } \585 } \
426\586\
427 static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \587 static inline int##w##_t zig_shlw_i##w(int##w##_t lhs, uint8_t rhs, uint8_t bits) { \
428 return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \588 return zig_wrap_i##w((int##w##_t)zig_shl_u##w((uint##w##_t)lhs, (uint##w##_t)rhs), bits); \
429 } \589 } \
430\590\
431 static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \591 static inline uint##w##_t zig_addw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
432 return zig_wrap_u##w(lhs + rhs, bits); \592 return zig_wrap_u##w(lhs + rhs, bits); \
433 } \593 } \
434\594\
435 static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \595 static inline int##w##_t zig_addw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
436 return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \596 return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs + (uint##w##_t)rhs), bits); \
437 } \597 } \
438\598\
439 static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \599 static inline uint##w##_t zig_subw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
440 return zig_wrap_u##w(lhs - rhs, bits); \600 return zig_wrap_u##w(lhs - rhs, bits); \
441 } \601 } \
442\602\
443 static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \603 static inline int##w##_t zig_subw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
444 return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \604 return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs - (uint##w##_t)rhs), bits); \
445 } \605 } \
446\606\
447 static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \607 static inline uint##w##_t zig_mulw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
448 return zig_wrap_u##w(lhs * rhs, bits); \608 return zig_wrap_u##w(lhs * rhs, bits); \
449 } \609 } \
450\610\
451 static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \611 static inline int##w##_t zig_mulw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
452 return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \612 return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs * (uint##w##_t)rhs), bits); \
453 }613 }
454zig_int_helpers(8)614zig_int_helpers(8)
455zig_int_helpers(16)615zig_int_helpers(16)
456zig_int_helpers(32)616zig_int_helpers(32)
457zig_int_helpers(64)617zig_int_helpers(64)
458618
459static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) {619static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
460#if zig_has_builtin(add_overflow) || defined(zig_gnuc)620#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
461 zig_u32 full_res;621 uint32_t full_res;
462 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);622 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
463 *res = zig_wrap_u32(full_res, bits);623 *res = zig_wrap_u32(full_res, bits);
464 return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits);624 return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits);
465#else625#else
466 *res = zig_addw_u32(lhs, rhs, bits);626 *res = zig_addw_u32(lhs, rhs, bits);
467 return *res < lhs;627 return *res < lhs;
468#endif628#endif
469}629}
470630
471static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n,631zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow);
472 const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits)632static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
473{
474 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits);
475}
476
477zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow);
478static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) {
479#if zig_has_builtin(add_overflow) || defined(zig_gnuc)633#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
480 zig_i32 full_res;634 int32_t full_res;
481 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);635 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
482#else636#else
483 zig_c_int overflow_int;637 int overflow_int;
484 zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int);638 int32_t full_res = __addosi4(lhs, rhs, &overflow_int);
485 bool overflow = overflow_int != 0;639 bool overflow = overflow_int != 0;
486#endif640#endif
487 *res = zig_wrap_i32(full_res, bits);641 *res = zig_wrap_i32(full_res, bits);
488 return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits);642 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
489}643}
490644
491static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n,645static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
492 const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits)
493{
494 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits);
495}
496
497static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) {
498#if zig_has_builtin(add_overflow) || defined(zig_gnuc)646#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
499 zig_u64 full_res;647 uint64_t full_res;
500 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);648 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
501 *res = zig_wrap_u64(full_res, bits);649 *res = zig_wrap_u64(full_res, bits);
502 return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits);650 return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits);
503#else651#else
504 *res = zig_addw_u64(lhs, rhs, bits);652 *res = zig_addw_u64(lhs, rhs, bits);
505 return *res < lhs;653 return *res < lhs;
506#endif654#endif
507}655}
508656
509static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n,657zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow);
510 const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits)658static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
511{
512 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits);
513}
514
515zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow);
516static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) {
517#if zig_has_builtin(add_overflow) || defined(zig_gnuc)659#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
518 zig_i64 full_res;660 int64_t full_res;
519 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);661 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
520#else662#else
521 zig_c_int overflow_int;663 int overflow_int;
522 zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int);664 int64_t full_res = __addodi4(lhs, rhs, &overflow_int);
523 bool overflow = overflow_int != 0;665 bool overflow = overflow_int != 0;
524#endif666#endif
525 *res = zig_wrap_i64(full_res, bits);667 *res = zig_wrap_i64(full_res, bits);
526 return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits);668 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
527}669}
528670
529static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n,671static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
530 const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits)
531{
532 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits);
533}
534
535static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) {
536#if zig_has_builtin(add_overflow) || defined(zig_gnuc)672#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
537 zig_u8 full_res;673 uint8_t full_res;
538 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);674 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
539 *res = zig_wrap_u8(full_res, bits);675 *res = zig_wrap_u8(full_res, bits);
540 return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits);676 return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits);
541#else677#else
542 zig_u32 full_res;678 uint32_t full_res;
543 bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits);679 bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits);
544 *res = (zig_u8)full_res;680 *res = (uint8_t)full_res;
545 return overflow;681 return overflow;
546#endif682#endif
547}683}
548684
549static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n,685static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
550 const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits)
551{
552 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits);
553}
554
555static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) {
556#if zig_has_builtin(add_overflow) || defined(zig_gnuc)686#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
557 zig_i8 full_res;687 int8_t full_res;
558 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);688 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
559 *res = zig_wrap_i8(full_res, bits);689 *res = zig_wrap_i8(full_res, bits);
560 return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits);690 return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits);
561#else691#else
562 zig_i32 full_res;692 int32_t full_res;
563 bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits);693 bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits);
564 *res = (zig_i8)full_res;694 *res = (int8_t)full_res;
565 return overflow;695 return overflow;
566#endif696#endif
567}697}
568698
569static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n,699static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
570 const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits)
571{
572 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits);
573}
574
575static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) {
576#if zig_has_builtin(add_overflow) || defined(zig_gnuc)700#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
577 zig_u16 full_res;701 uint16_t full_res;
578 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);702 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
579 *res = zig_wrap_u16(full_res, bits);703 *res = zig_wrap_u16(full_res, bits);
580 return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits);704 return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits);
581#else705#else
582 zig_u32 full_res;706 uint32_t full_res;
583 bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits);707 bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits);
584 *res = (zig_u16)full_res;708 *res = (uint16_t)full_res;
585 return overflow;709 return overflow;
586#endif710#endif
587}711}
588712
589static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n,713static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
590 const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits)
591{
592 for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits);
593}
594
595static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) {
596#if zig_has_builtin(add_overflow) || defined(zig_gnuc)714#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
597 zig_i16 full_res;715 int16_t full_res;
598 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);716 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
599 *res = zig_wrap_i16(full_res, bits);717 *res = zig_wrap_i16(full_res, bits);
600 return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits);718 return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits);
601#else719#else
602 zig_i32 full_res;720 int32_t full_res;
603 bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits);721 bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits);
604 *res = (zig_i16)full_res;722 *res = (int16_t)full_res;
605 return overflow;723 return overflow;
606#endif724#endif
607}725}
608726
609static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n,727static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
610 const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits)
611{
612 for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits);
613}
614
615static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) {
616#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)728#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
617 zig_u32 full_res;729 uint32_t full_res;
618 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);730 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
619 *res = zig_wrap_u32(full_res, bits);731 *res = zig_wrap_u32(full_res, bits);
620 return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits);732 return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits);
621#else733#else
622 *res = zig_subw_u32(lhs, rhs, bits);734 *res = zig_subw_u32(lhs, rhs, bits);
623 return *res > lhs;735 return *res > lhs;
624#endif736#endif
625}737}
626738
627static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n,739zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow);
628 const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits)740static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
629{
630 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits);
631}
632
633zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow);
634static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) {
635#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)741#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
636 zig_i32 full_res;742 int32_t full_res;
637 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);743 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
638#else744#else
639 zig_c_int overflow_int;745 int overflow_int;
640 zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int);746 int32_t full_res = __subosi4(lhs, rhs, &overflow_int);
641 bool overflow = overflow_int != 0;747 bool overflow = overflow_int != 0;
642#endif748#endif
643 *res = zig_wrap_i32(full_res, bits);749 *res = zig_wrap_i32(full_res, bits);
644 return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits);750 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
645}
646
647static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n,
648 const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits)
649{
650 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits);
651}751}
652752
653static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) {753static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
654#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)754#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
655 zig_u64 full_res;755 uint64_t full_res;
656 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);756 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
657 *res = zig_wrap_u64(full_res, bits);757 *res = zig_wrap_u64(full_res, bits);
658 return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits);758 return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits);
659#else759#else
660 *res = zig_subw_u64(lhs, rhs, bits);760 *res = zig_subw_u64(lhs, rhs, bits);
661 return *res > lhs;761 return *res > lhs;
662#endif762#endif
663}763}
664764
665static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n,765zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow);
666 const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits)766static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
667{
668 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits);
669}
670
671zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow);
672static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) {
673#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)767#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
674 zig_i64 full_res;768 int64_t full_res;
675 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);769 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
676#else770#else
677 zig_c_int overflow_int;771 int overflow_int;
678 zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int);772 int64_t full_res = __subodi4(lhs, rhs, &overflow_int);
679 bool overflow = overflow_int != 0;773 bool overflow = overflow_int != 0;
680#endif774#endif
681 *res = zig_wrap_i64(full_res, bits);775 *res = zig_wrap_i64(full_res, bits);
682 return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits);776 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
683}
684
685static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n,
686 const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits)
687{
688 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits);
689}777}
690778
691static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) {779static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
692#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)780#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
693 zig_u8 full_res;781 uint8_t full_res;
694 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);782 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
695 *res = zig_wrap_u8(full_res, bits);783 *res = zig_wrap_u8(full_res, bits);
696 return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits);784 return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits);
697#else785#else
698 zig_u32 full_res;786 uint32_t full_res;
699 bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits);787 bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits);
700 *res = (zig_u8)full_res;788 *res = (uint8_t)full_res;
701 return overflow;789 return overflow;
702#endif790#endif
703}791}
704792
705static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n,793static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
706 const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits)
707{
708 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits);
709}
710
711static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) {
712#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)794#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
713 zig_i8 full_res;795 int8_t full_res;
714 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);796 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
715 *res = zig_wrap_i8(full_res, bits);797 *res = zig_wrap_i8(full_res, bits);
716 return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits);798 return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits);
717#else799#else
718 zig_i32 full_res;800 int32_t full_res;
719 bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits);801 bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits);
720 *res = (zig_i8)full_res;802 *res = (int8_t)full_res;
721 return overflow;803 return overflow;
722#endif804#endif
723}805}
724806
725static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n,807static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
726 const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits)
727{
728 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits);
729}
730
731
732static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) {
733#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)808#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
734 zig_u16 full_res;809 uint16_t full_res;
735 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);810 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
736 *res = zig_wrap_u16(full_res, bits);811 *res = zig_wrap_u16(full_res, bits);
737 return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits);812 return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits);
738#else813#else
739 zig_u32 full_res;814 uint32_t full_res;
740 bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits);815 bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits);
741 *res = (zig_u16)full_res;816 *res = (uint16_t)full_res;
742 return overflow;817 return overflow;
743#endif818#endif
744}819}
745820
746static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n,821static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
747 const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits)
748{
749 for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits);
750}
751
752
753static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) {
754#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)822#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
755 zig_i16 full_res;823 int16_t full_res;
756 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);824 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
757 *res = zig_wrap_i16(full_res, bits);825 *res = zig_wrap_i16(full_res, bits);
758 return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits);826 return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits);
759#else827#else
760 zig_i32 full_res;828 int32_t full_res;
761 bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits);829 bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits);
762 *res = (zig_i16)full_res;830 *res = (int16_t)full_res;
763 return overflow;831 return overflow;
764#endif832#endif
765}833}
766834
767static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n,835static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
768 const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits)
769{
770 for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits);
771}
772
773static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) {
774#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)836#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
775 zig_u32 full_res;837 uint32_t full_res;
776 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);838 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
777 *res = zig_wrap_u32(full_res, bits);839 *res = zig_wrap_u32(full_res, bits);
778 return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits);840 return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits);
779#else841#else
780 *res = zig_mulw_u32(lhs, rhs, bits);842 *res = zig_mulw_u32(lhs, rhs, bits);
781 return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs;843 return rhs != UINT32_C(0) && lhs > zig_maxInt_u(32, bits) / rhs;
782#endif844#endif
783}845}
784846
785static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n,847zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow);
786 const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits)848static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
787{
788 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits);
789}
790
791zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow);
792static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) {
793#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)849#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
794 zig_i32 full_res;850 int32_t full_res;
795 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);851 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
796#else852#else
797 zig_c_int overflow_int;853 int overflow_int;
798 zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int);854 int32_t full_res = __mulosi4(lhs, rhs, &overflow_int);
799 bool overflow = overflow_int != 0;855 bool overflow = overflow_int != 0;
800#endif856#endif
801 *res = zig_wrap_i32(full_res, bits);857 *res = zig_wrap_i32(full_res, bits);
802 return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits);858 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
803}
804
805static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n,
806 const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits)
807{
808 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits);
809}859}
810860
811static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) {861static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
812#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)862#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
813 zig_u64 full_res;863 uint64_t full_res;
814 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);864 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
815 *res = zig_wrap_u64(full_res, bits);865 *res = zig_wrap_u64(full_res, bits);
816 return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits);866 return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits);
817#else867#else
818 *res = zig_mulw_u64(lhs, rhs, bits);868 *res = zig_mulw_u64(lhs, rhs, bits);
819 return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs;869 return rhs != UINT64_C(0) && lhs > zig_maxInt_u(64, bits) / rhs;
820#endif870#endif
821}871}
822872
823static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n,873zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow);
824 const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits)874static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
825{
826 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits);
827}
828
829zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow);
830static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) {
831#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)875#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
832 zig_i64 full_res;876 int64_t full_res;
833 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);877 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
834#else878#else
835 zig_c_int overflow_int;879 int overflow_int;
836 zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int);880 int64_t full_res = __mulodi4(lhs, rhs, &overflow_int);
837 bool overflow = overflow_int != 0;881 bool overflow = overflow_int != 0;
838#endif882#endif
839 *res = zig_wrap_i64(full_res, bits);883 *res = zig_wrap_i64(full_res, bits);
840 return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits);884 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
841}885}
842886
843static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n,887static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
844 const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits)
845{
846 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits);
847}
848
849static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) {
850#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)888#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
851 zig_u8 full_res;889 uint8_t full_res;
852 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);890 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
853 *res = zig_wrap_u8(full_res, bits);891 *res = zig_wrap_u8(full_res, bits);
854 return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits);892 return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits);
855#else893#else
856 zig_u32 full_res;894 uint32_t full_res;
857 bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits);895 bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits);
858 *res = (zig_u8)full_res;896 *res = (uint8_t)full_res;
859 return overflow;897 return overflow;
860#endif898#endif
861}899}
862900
863static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n,901static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
864 const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits)
865{
866 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits);
867}
868
869static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) {
870#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)902#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
871 zig_i8 full_res;903 int8_t full_res;
872 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);904 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
873 *res = zig_wrap_i8(full_res, bits);905 *res = zig_wrap_i8(full_res, bits);
874 return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits);906 return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits);
875#else907#else
876 zig_i32 full_res;908 int32_t full_res;
877 bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits);909 bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits);
878 *res = (zig_i8)full_res;910 *res = (int8_t)full_res;
879 return overflow;911 return overflow;
880#endif912#endif
881}913}
882914
883static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n,915static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
884 const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits)
885{
886 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits);
887}
888
889static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) {
890#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)916#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
891 zig_u16 full_res;917 uint16_t full_res;
892 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);918 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
893 *res = zig_wrap_u16(full_res, bits);919 *res = zig_wrap_u16(full_res, bits);
894 return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits);920 return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits);
895#else921#else
896 zig_u32 full_res;922 uint32_t full_res;
897 bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits);923 bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits);
898 *res = (zig_u16)full_res;924 *res = (uint16_t)full_res;
899 return overflow;925 return overflow;
900#endif926#endif
901}927}
902928
903static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n,929static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
904 const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits)
905{
906 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits);
907}
908
909static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) {
910#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)930#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
911 zig_i16 full_res;931 int16_t full_res;
912 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);932 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
913 *res = zig_wrap_i16(full_res, bits);933 *res = zig_wrap_i16(full_res, bits);
914 return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits);934 return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits);
915#else935#else
916 zig_i32 full_res;936 int32_t full_res;
917 bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits);937 bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits);
918 *res = (zig_i16)full_res;938 *res = (int16_t)full_res;
919 return overflow;939 return overflow;
920#endif940#endif
921}941}
922942
923static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n,
924 const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits)
925{
926 for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits);
927}
928
929#define zig_int_builtins(w) \943#define zig_int_builtins(w) \
930 static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \944 static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \
931 *res = zig_shlw_u##w(lhs, rhs, bits); \945 *res = zig_shlw_u##w(lhs, rhs, bits); \
932 return lhs > zig_maxInt(u##w, bits) >> rhs; \946 return lhs > zig_maxInt_u(w, bits) >> rhs; \
933 } \947 } \
934\948\
935 static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \949 static inline bool zig_shlo_i##w(int##w##_t *res, int##w##_t lhs, uint8_t rhs, uint8_t bits) { \
936 *res = zig_shlw_i##w(lhs, rhs, bits); \950 *res = zig_shlw_i##w(lhs, rhs, bits); \
937 zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \951 int##w##_t mask = (int##w##_t)(UINT##w##_MAX << (bits - rhs - 1)); \
938 return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \952 return (lhs & mask) != INT##w##_C(0) && (lhs & mask) != mask; \
939 } \953 } \
940\954\
941 static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \955 static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
942 zig_u##w res; \956 uint##w##_t res; \
943 if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \957 if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \
944 return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \958 return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \
945 } \959 } \
946\960\
947 static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \961 static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
948 zig_i##w res; \962 int##w##_t res; \
949 if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \963 if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \
950 return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \964 return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
951 } \965 } \
952\966\
953 static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \967 static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
954 zig_u##w res; \968 uint##w##_t res; \
955 return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \969 return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \
956 } \970 } \
957\971\
958 static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \972 static inline int##w##_t zig_adds_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
959 zig_i##w res; \973 int##w##_t res; \
960 if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \974 if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \
961 return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \975 return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
962 } \976 } \
963\977\
964 static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \978 static inline uint##w##_t zig_subs_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
965 zig_u##w res; \979 uint##w##_t res; \
966 return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \980 return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt_u(w, bits) : res; \
967 } \981 } \
968\982\
969 static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \983 static inline int##w##_t zig_subs_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
970 zig_i##w res; \984 int##w##_t res; \
971 if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \985 if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \
972 return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \986 return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
973 } \987 } \
974\988\
975 static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \989 static inline uint##w##_t zig_muls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
976 zig_u##w res; \990 uint##w##_t res; \
977 return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \991 return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \
978 } \992 } \
979\993\
980 static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \994 static inline int##w##_t zig_muls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
981 zig_i##w res; \995 int##w##_t res; \
982 if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \996 if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \
983 return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \997 return (lhs ^ rhs) < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
984 }998 }
985zig_int_builtins(8)999zig_int_builtins(8)
986zig_int_builtins(16)1000zig_int_builtins(16)
...@@ -988,89 +1002,89 @@ zig_int_builtins(32)...@@ -988,89 +1002,89 @@ zig_int_builtins(32)
988zig_int_builtins(64)1002zig_int_builtins(64)
9891003
990#define zig_builtin8(name, val) __builtin_##name(val)1004#define zig_builtin8(name, val) __builtin_##name(val)
991typedef zig_c_uint zig_Builtin8;1005typedef unsigned int zig_Builtin8;
9921006
993#define zig_builtin16(name, val) __builtin_##name(val)1007#define zig_builtin16(name, val) __builtin_##name(val)
994typedef zig_c_uint zig_Builtin16;1008typedef unsigned int zig_Builtin16;
9951009
996#if INT_MIN <= INT32_MIN1010#if INT_MIN <= INT32_MIN
997#define zig_builtin32(name, val) __builtin_##name(val)1011#define zig_builtin32(name, val) __builtin_##name(val)
998typedef zig_c_uint zig_Builtin32;1012typedef unsigned int zig_Builtin32;
999#elif LONG_MIN <= INT32_MIN1013#elif LONG_MIN <= INT32_MIN
1000#define zig_builtin32(name, val) __builtin_##name##l(val)1014#define zig_builtin32(name, val) __builtin_##name##l(val)
1001typedef zig_c_ulong zig_Builtin32;1015typedef unsigned long zig_Builtin32;
1002#endif1016#endif
10031017
1004#if INT_MIN <= INT64_MIN1018#if INT_MIN <= INT64_MIN
1005#define zig_builtin64(name, val) __builtin_##name(val)1019#define zig_builtin64(name, val) __builtin_##name(val)
1006typedef zig_c_uint zig_Builtin64;1020typedef unsigned int zig_Builtin64;
1007#elif LONG_MIN <= INT64_MIN1021#elif LONG_MIN <= INT64_MIN
1008#define zig_builtin64(name, val) __builtin_##name##l(val)1022#define zig_builtin64(name, val) __builtin_##name##l(val)
1009typedef zig_c_ulong zig_Builtin64;1023typedef unsigned long zig_Builtin64;
1010#elif LLONG_MIN <= INT64_MIN1024#elif LLONG_MIN <= INT64_MIN
1011#define zig_builtin64(name, val) __builtin_##name##ll(val)1025#define zig_builtin64(name, val) __builtin_##name##ll(val)
1012typedef zig_c_ulonglong zig_Builtin64;1026typedef unsigned long long zig_Builtin64;
1013#endif1027#endif
10141028
1015static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) {1029static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) {
1016 return zig_wrap_u8(val >> (8 - bits), bits);1030 return zig_wrap_u8(val >> (8 - bits), bits);
1017}1031}
10181032
1019static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) {1033static inline int8_t zig_byte_swap_i8(int8_t val, uint8_t bits) {
1020 return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits);1034 return zig_wrap_i8((int8_t)zig_byte_swap_u8((uint8_t)val, bits), bits);
1021}1035}
10221036
1023static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) {1037static inline uint16_t zig_byte_swap_u16(uint16_t val, uint8_t bits) {
1024 zig_u16 full_res;1038 uint16_t full_res;
1025#if zig_has_builtin(bswap16) || defined(zig_gnuc)1039#if zig_has_builtin(bswap16) || defined(zig_gnuc)
1026 full_res = __builtin_bswap16(val);1040 full_res = __builtin_bswap16(val);
1027#else1041#else
1028 full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 |1042 full_res = (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 0), 8) << 8 |
1029 (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0;1043 (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 8), 8) >> 0;
1030#endif1044#endif
1031 return zig_wrap_u16(full_res >> (16 - bits), bits);1045 return zig_wrap_u16(full_res >> (16 - bits), bits);
1032}1046}
10331047
1034static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) {1048static inline int16_t zig_byte_swap_i16(int16_t val, uint8_t bits) {
1035 return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits);1049 return zig_wrap_i16((int16_t)zig_byte_swap_u16((uint16_t)val, bits), bits);
1036}1050}
10371051
1038static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) {1052static inline uint32_t zig_byte_swap_u32(uint32_t val, uint8_t bits) {
1039 zig_u32 full_res;1053 uint32_t full_res;
1040#if zig_has_builtin(bswap32) || defined(zig_gnuc)1054#if zig_has_builtin(bswap32) || defined(zig_gnuc)
1041 full_res = __builtin_bswap32(val);1055 full_res = __builtin_bswap32(val);
1042#else1056#else
1043 full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 |1057 full_res = (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 0), 16) << 16 |
1044 (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0;1058 (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 16), 16) >> 0;
1045#endif1059#endif
1046 return zig_wrap_u32(full_res >> (32 - bits), bits);1060 return zig_wrap_u32(full_res >> (32 - bits), bits);
1047}1061}
10481062
1049static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) {1063static inline int32_t zig_byte_swap_i32(int32_t val, uint8_t bits) {
1050 return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits);1064 return zig_wrap_i32((int32_t)zig_byte_swap_u32((uint32_t)val, bits), bits);
1051}1065}
10521066
1053static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) {1067static inline uint64_t zig_byte_swap_u64(uint64_t val, uint8_t bits) {
1054 zig_u64 full_res;1068 uint64_t full_res;
1055#if zig_has_builtin(bswap64) || defined(zig_gnuc)1069#if zig_has_builtin(bswap64) || defined(zig_gnuc)
1056 full_res = __builtin_bswap64(val);1070 full_res = __builtin_bswap64(val);
1057#else1071#else
1058 full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 |1072 full_res = (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 0), 32) << 32 |
1059 (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0;1073 (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 32), 32) >> 0;
1060#endif1074#endif
1061 return zig_wrap_u64(full_res >> (64 - bits), bits);1075 return zig_wrap_u64(full_res >> (64 - bits), bits);
1062}1076}
10631077
1064static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) {1078static inline int64_t zig_byte_swap_i64(int64_t val, uint8_t bits) {
1065 return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits);1079 return zig_wrap_i64((int64_t)zig_byte_swap_u64((uint64_t)val, bits), bits);
1066}1080}
10671081
1068static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) {1082static inline uint8_t zig_bit_reverse_u8(uint8_t val, uint8_t bits) {
1069 zig_u8 full_res;1083 uint8_t full_res;
1070#if zig_has_builtin(bitreverse8)1084#if zig_has_builtin(bitreverse8)
1071 full_res = __builtin_bitreverse8(val);1085 full_res = __builtin_bitreverse8(val);
1072#else1086#else
1073 static zig_u8 const lut[0x10] = {1087 static uint8_t const lut[0x10] = {
1074 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,1088 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
1075 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf1089 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
1076 };1090 };
...@@ -1079,62 +1093,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) {...@@ -1079,62 +1093,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) {
1079 return zig_wrap_u8(full_res >> (8 - bits), bits);1093 return zig_wrap_u8(full_res >> (8 - bits), bits);
1080}1094}
10811095
1082static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) {1096static inline int8_t zig_bit_reverse_i8(int8_t val, uint8_t bits) {
1083 return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits);1097 return zig_wrap_i8((int8_t)zig_bit_reverse_u8((uint8_t)val, bits), bits);
1084}1098}
10851099
1086static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) {1100static inline uint16_t zig_bit_reverse_u16(uint16_t val, uint8_t bits) {
1087 zig_u16 full_res;1101 uint16_t full_res;
1088#if zig_has_builtin(bitreverse16)1102#if zig_has_builtin(bitreverse16)
1089 full_res = __builtin_bitreverse16(val);1103 full_res = __builtin_bitreverse16(val);
1090#else1104#else
1091 full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 |1105 full_res = (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 0), 8) << 8 |
1092 (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0;1106 (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 8), 8) >> 0;
1093#endif1107#endif
1094 return zig_wrap_u16(full_res >> (16 - bits), bits);1108 return zig_wrap_u16(full_res >> (16 - bits), bits);
1095}1109}
10961110
1097static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) {1111static inline int16_t zig_bit_reverse_i16(int16_t val, uint8_t bits) {
1098 return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits);1112 return zig_wrap_i16((int16_t)zig_bit_reverse_u16((uint16_t)val, bits), bits);
1099}1113}
11001114
1101static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) {1115static inline uint32_t zig_bit_reverse_u32(uint32_t val, uint8_t bits) {
1102 zig_u32 full_res;1116 uint32_t full_res;
1103#if zig_has_builtin(bitreverse32)1117#if zig_has_builtin(bitreverse32)
1104 full_res = __builtin_bitreverse32(val);1118 full_res = __builtin_bitreverse32(val);
1105#else1119#else
1106 full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 |1120 full_res = (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 0), 16) << 16 |
1107 (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0;1121 (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 16), 16) >> 0;
1108#endif1122#endif
1109 return zig_wrap_u32(full_res >> (32 - bits), bits);1123 return zig_wrap_u32(full_res >> (32 - bits), bits);
1110}1124}
11111125
1112static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) {1126static inline int32_t zig_bit_reverse_i32(int32_t val, uint8_t bits) {
1113 return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits);1127 return zig_wrap_i32((int32_t)zig_bit_reverse_u32((uint32_t)val, bits), bits);
1114}1128}
11151129
1116static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) {1130static inline uint64_t zig_bit_reverse_u64(uint64_t val, uint8_t bits) {
1117 zig_u64 full_res;1131 uint64_t full_res;
1118#if zig_has_builtin(bitreverse64)1132#if zig_has_builtin(bitreverse64)
1119 full_res = __builtin_bitreverse64(val);1133 full_res = __builtin_bitreverse64(val);
1120#else1134#else
1121 full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 |1135 full_res = (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 0), 32) << 32 |
1122 (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0;1136 (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 32), 32) >> 0;
1123#endif1137#endif
1124 return zig_wrap_u64(full_res >> (64 - bits), bits);1138 return zig_wrap_u64(full_res >> (64 - bits), bits);
1125}1139}
11261140
1127static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) {1141static inline int64_t zig_bit_reverse_i64(int64_t val, uint8_t bits) {
1128 return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits);1142 return zig_wrap_i64((int64_t)zig_bit_reverse_u64((uint64_t)val, bits), bits);
1129}1143}
11301144
1131#define zig_builtin_popcount_common(w) \1145#define zig_builtin_popcount_common(w) \
1132 static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \1146 static inline uint8_t zig_popcount_i##w(int##w##_t val, uint8_t bits) { \
1133 return zig_popcount_u##w((zig_u##w)val, bits); \1147 return zig_popcount_u##w((uint##w##_t)val, bits); \
1134 }1148 }
1135#if zig_has_builtin(popcount) || defined(zig_gnuc)1149#if zig_has_builtin(popcount) || defined(zig_gnuc)
1136#define zig_builtin_popcount(w) \1150#define zig_builtin_popcount(w) \
1137 static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \1151 static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \
1138 (void)bits; \1152 (void)bits; \
1139 return zig_builtin##w(popcount, val); \1153 return zig_builtin##w(popcount, val); \
1140 } \1154 } \
...@@ -1142,12 +1156,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) {...@@ -1142,12 +1156,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) {
1142 zig_builtin_popcount_common(w)1156 zig_builtin_popcount_common(w)
1143#else1157#else
1144#define zig_builtin_popcount(w) \1158#define zig_builtin_popcount(w) \
1145 static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \1159 static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \
1146 (void)bits; \1160 (void)bits; \
1147 zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \1161 uint##w##_t temp = val - ((val >> 1) & (UINT##w##_MAX / 3)); \
1148 temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \1162 temp = (temp & (UINT##w##_MAX / 5)) + ((temp >> 2) & (UINT##w##_MAX / 5)); \
1149 temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \1163 temp = (temp + (temp >> 4)) & (UINT##w##_MAX / 17); \
1150 return temp * (zig_maxInt_u##w / 255) >> (w - 8); \1164 return temp * (UINT##w##_MAX / 255) >> (w - 8); \
1151 } \1165 } \
1152\1166\
1153 zig_builtin_popcount_common(w)1167 zig_builtin_popcount_common(w)
...@@ -1158,12 +1172,12 @@ zig_builtin_popcount(32)...@@ -1158,12 +1172,12 @@ zig_builtin_popcount(32)
1158zig_builtin_popcount(64)1172zig_builtin_popcount(64)
11591173
1160#define zig_builtin_ctz_common(w) \1174#define zig_builtin_ctz_common(w) \
1161 static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \1175 static inline uint8_t zig_ctz_i##w(int##w##_t val, uint8_t bits) { \
1162 return zig_ctz_u##w((zig_u##w)val, bits); \1176 return zig_ctz_u##w((uint##w##_t)val, bits); \
1163 }1177 }
1164#if zig_has_builtin(ctz) || defined(zig_gnuc)1178#if zig_has_builtin(ctz) || defined(zig_gnuc)
1165#define zig_builtin_ctz(w) \1179#define zig_builtin_ctz(w) \
1166 static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \1180 static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \
1167 if (val == 0) return bits; \1181 if (val == 0) return bits; \
1168 return zig_builtin##w(ctz, val); \1182 return zig_builtin##w(ctz, val); \
1169 } \1183 } \
...@@ -1171,7 +1185,7 @@ zig_builtin_popcount(64)...@@ -1171,7 +1185,7 @@ zig_builtin_popcount(64)
1171 zig_builtin_ctz_common(w)1185 zig_builtin_ctz_common(w)
1172#else1186#else
1173#define zig_builtin_ctz(w) \1187#define zig_builtin_ctz(w) \
1174 static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \1188 static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \
1175 return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \1189 return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \
1176 } \1190 } \
1177\1191\
...@@ -1183,12 +1197,12 @@ zig_builtin_ctz(32)...@@ -1183,12 +1197,12 @@ zig_builtin_ctz(32)
1183zig_builtin_ctz(64)1197zig_builtin_ctz(64)
11841198
1185#define zig_builtin_clz_common(w) \1199#define zig_builtin_clz_common(w) \
1186 static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \1200 static inline uint8_t zig_clz_i##w(int##w##_t val, uint8_t bits) { \
1187 return zig_clz_u##w((zig_u##w)val, bits); \1201 return zig_clz_u##w((uint##w##_t)val, bits); \
1188 }1202 }
1189#if zig_has_builtin(clz) || defined(zig_gnuc)1203#if zig_has_builtin(clz) || defined(zig_gnuc)
1190#define zig_builtin_clz(w) \1204#define zig_builtin_clz(w) \
1191 static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \1205 static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \
1192 if (val == 0) return bits; \1206 if (val == 0) return bits; \
1193 return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \1207 return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \
1194 } \1208 } \
...@@ -1196,7 +1210,7 @@ zig_builtin_ctz(64)...@@ -1196,7 +1210,7 @@ zig_builtin_ctz(64)
1196 zig_builtin_clz_common(w)1210 zig_builtin_clz_common(w)
1197#else1211#else
1198#define zig_builtin_clz(w) \1212#define zig_builtin_clz(w) \
1199 static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \1213 static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \
1200 return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \1214 return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \
1201 } \1215 } \
1202\1216\
...@@ -1207,7 +1221,7 @@ zig_builtin_clz(16)...@@ -1207,7 +1221,7 @@ zig_builtin_clz(16)
1207zig_builtin_clz(32)1221zig_builtin_clz(32)
1208zig_builtin_clz(64)1222zig_builtin_clz(64)
12091223
1210/* ======================== 128-bit Integer Routines ======================== */1224/* ======================== 128-bit Integer Support ========================= */
12111225
1212#if !defined(zig_has_int128)1226#if !defined(zig_has_int128)
1213# if defined(__SIZEOF_INT128__)1227# if defined(__SIZEOF_INT128__)
...@@ -1222,18 +1236,18 @@ zig_builtin_clz(64)...@@ -1222,18 +1236,18 @@ zig_builtin_clz(64)
1222typedef unsigned __int128 zig_u128;1236typedef unsigned __int128 zig_u128;
1223typedef signed __int128 zig_i128;1237typedef signed __int128 zig_i128;
12241238
1225#define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))1239#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
1226#define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo))1240#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo))
1227#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo)1241#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1228#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo)1242#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
1229#define zig_hi_u128(val) ((zig_u64)((val) >> 64))1243#define zig_hi_u128(val) ((uint64_t)((val) >> 64))
1230#define zig_lo_u128(val) ((zig_u64)((val) >> 0))1244#define zig_lo_u128(val) ((uint64_t)((val) >> 0))
1231#define zig_hi_i128(val) ((zig_i64)((val) >> 64))1245#define zig_hi_i128(val) (( int64_t)((val) >> 64))
1232#define zig_lo_i128(val) ((zig_u64)((val) >> 0))1246#define zig_lo_i128(val) ((uint64_t)((val) >> 0))
1233#define zig_bitcast_u128(val) ((zig_u128)(val))1247#define zig_bitcast_u128(val) ((zig_u128)(val))
1234#define zig_bitcast_i128(val) ((zig_i128)(val))1248#define zig_bitcast_i128(val) ((zig_i128)(val))
1235#define zig_cmp_int128(Type) \1249#define zig_cmp_int128(Type) \
1236 static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \1250 static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \
1237 return (lhs > rhs) - (lhs < rhs); \1251 return (lhs > rhs) - (lhs < rhs); \
1238 }1252 }
1239#define zig_bit_int128(Type, operation, operator) \1253#define zig_bit_int128(Type, operation, operator) \
...@@ -1243,32 +1257,32 @@ typedef signed __int128 zig_i128;...@@ -1243,32 +1257,32 @@ typedef signed __int128 zig_i128;
12431257
1244#else /* zig_has_int128 */1258#else /* zig_has_int128 */
12451259
1246#if __LITTLE_ENDIAN__ || _MSC_VER1260#if zig_little_endian
1247typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128;1261typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
1248typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128;1262typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
1249#else1263#else
1250typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128;1264typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128;
1251typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128;1265typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
1252#endif1266#endif
12531267
1254#define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) })1268#define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) })
1255#define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })1269#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })
12561270
1257#if _MSC_VER1271#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */
1258#define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }1272#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1259#define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }1273#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1260#else1274#else /* But non-MSVC doesn't like the unprotected commas */
1261#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo)1275#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1262#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo)1276#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
1263#endif1277#endif
1264#define zig_hi_u128(val) ((val).hi)1278#define zig_hi_u128(val) ((val).hi)
1265#define zig_lo_u128(val) ((val).lo)1279#define zig_lo_u128(val) ((val).lo)
1266#define zig_hi_i128(val) ((val).hi)1280#define zig_hi_i128(val) ((val).hi)
1267#define zig_lo_i128(val) ((val).lo)1281#define zig_lo_i128(val) ((val).lo)
1268#define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo)1282#define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo)
1269#define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo)1283#define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo)
1270#define zig_cmp_int128(Type) \1284#define zig_cmp_int128(Type) \
1271 static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \1285 static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \
1272 return (lhs.hi == rhs.hi) \1286 return (lhs.hi == rhs.hi) \
1273 ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \1287 ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \
1274 : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \1288 : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \
...@@ -1280,10 +1294,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128;...@@ -1280,10 +1294,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128;
12801294
1281#endif /* zig_has_int128 */1295#endif /* zig_has_int128 */
12821296
1283#define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64)1297#define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64)
1284#define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64)1298#define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64)
1285#define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64)1299#define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64)
1286#define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64)1300#define zig_maxInt_i128 zig_make_i128(zig_maxInt_i64, zig_maxInt_u64)
12871301
1288zig_cmp_int128(u128)1302zig_cmp_int128(u128)
1289zig_cmp_int128(i128)1303zig_cmp_int128(i128)
...@@ -1297,28 +1311,33 @@ zig_bit_int128(i128, or, |)...@@ -1297,28 +1311,33 @@ zig_bit_int128(i128, or, |)
1297zig_bit_int128(u128, xor, ^)1311zig_bit_int128(u128, xor, ^)
1298zig_bit_int128(i128, xor, ^)1312zig_bit_int128(i128, xor, ^)
12991313
1300static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs);1314static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs);
13011315
1302#if zig_has_int1281316#if zig_has_int128
13031317
1304static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) {1318static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) {
1305 return val ^ zig_maxInt(u128, bits);1319 return val ^ zig_maxInt_u(128, bits);
1306}1320}
13071321
1308static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) {1322static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) {
1309 (void)bits;1323 (void)bits;
1310 return ~val;1324 return ~val;
1311}1325}
13121326
1313static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) {1327static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) {
1314 return lhs >> rhs;1328 return lhs >> rhs;
1315}1329}
13161330
1317static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) {1331static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) {
1318 return lhs << rhs;1332 return lhs << rhs;
1319}1333}
13201334
1321static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) {1335static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) {
1336 zig_i128 sign_mask = lhs < zig_make_i128(0, 0) ? -zig_make_i128(0, 1) : zig_make_i128(0, 0);
1337 return ((lhs ^ sign_mask) >> rhs) ^ sign_mask;
1338}
1339
1340static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) {
1322 return lhs << rhs;1341 return lhs << rhs;
1323}1342}
13241343
...@@ -1363,40 +1382,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) {...@@ -1363,40 +1382,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) {
1363}1382}
13641383
1365static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {1384static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
1366 return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0));1385 return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_make_i128(0, 0));
1367}1386}
13681387
1369static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {1388static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
1370 zig_i128 rem = zig_rem_i128(lhs, rhs);1389 zig_i128 rem = zig_rem_i128(lhs, rhs);
1371 return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0));1390 return rem + (((lhs ^ rhs) & rem) < zig_make_i128(0, 0) ? rhs : zig_make_i128(0, 0));
1372}1391}
13731392
1374#else /* zig_has_int128 */1393#else /* zig_has_int128 */
13751394
1376static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) {1395static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) {
1377 return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) };1396 return (zig_u128){ .hi = zig_not_u64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) };
1397}
1398
1399static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) {
1400 return (zig_i128){ .hi = zig_not_i64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) };
1378}1401}
13791402
1380static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) {1403static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) {
1381 return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) };1404 if (rhs == UINT8_C(0)) return lhs;
1405 if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - UINT8_C(64)) };
1406 return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (UINT8_C(64) - rhs) | lhs.lo >> rhs };
1382}1407}
13831408
1384static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) {1409static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) {
1385 if (rhs == zig_as_u8(0)) return lhs;1410 if (rhs == UINT8_C(0)) return lhs;
1386 if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) };1411 if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 };
1387 return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs };1412 return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs };
1388}1413}
13891414
1390static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) {1415static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) {
1391 if (rhs == zig_as_u8(0)) return lhs;1416 if (rhs == UINT8_C(0)) return lhs;
1392 if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 };1417 if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = zig_shr_i64(lhs.hi, 63), .lo = zig_shr_i64(lhs.hi, (rhs - UINT8_C(64))) };
1393 return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs };1418 return (zig_i128){ .hi = zig_shr_i64(lhs.hi, rhs), .lo = lhs.lo >> rhs | (uint64_t)lhs.hi << (UINT8_C(64) - rhs) };
1394}1419}
13951420
1396static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) {1421static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) {
1397 if (rhs == zig_as_u8(0)) return lhs;1422 if (rhs == UINT8_C(0)) return lhs;
1398 if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 };1423 if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 };
1399 return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs };1424 return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs };
1400}1425}
14011426
1402static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) {1427static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) {
...@@ -1424,14 +1449,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) {...@@ -1424,14 +1449,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) {
1424}1449}
14251450
1426zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs);1451zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs);
1427static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) {
1428 return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs)));
1429}
1430
1431static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) {1452static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) {
1432 return __multi3(lhs, rhs);1453 return __multi3(lhs, rhs);
1433}1454}
14341455
1456static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) {
1457 return zig_bitcast_u128(zig_mul_i128(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs)));
1458}
1459
1435zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs);1460zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs);
1436static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) {1461static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) {
1437 return __udivti3(lhs, rhs);1462 return __udivti3(lhs, rhs);
...@@ -1454,11 +1479,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) {...@@ -1454,11 +1479,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) {
14541479
1455static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {1480static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
1456 zig_i128 rem = zig_rem_i128(lhs, rhs);1481 zig_i128 rem = zig_rem_i128(lhs, rhs);
1457 return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0)));1482 return zig_add_i128(rem, ((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0));
1458}1483}
14591484
1460static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {1485static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
1461 return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0)));1486 return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_make_i128(0, 0)) < INT32_C(0)));
1462}1487}
14631488
1464#endif /* zig_has_int128 */1489#endif /* zig_has_int128 */
...@@ -1471,326 +1496,1303 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) {...@@ -1471,326 +1496,1303 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) {
1471}1496}
14721497
1473static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) {1498static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) {
1474 return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs;1499 return zig_cmp_u128(lhs, rhs) < INT32_C(0) ? lhs : rhs;
1475}1500}
14761501
1477static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) {1502static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) {
1478 return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs;1503 return zig_cmp_i128(lhs, rhs) < INT32_C(0) ? lhs : rhs;
1479}1504}
14801505
1481static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) {1506static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) {
1482 return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs;1507 return zig_cmp_u128(lhs, rhs) > INT32_C(0) ? lhs : rhs;
1483}1508}
14841509
1485static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) {1510static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) {
1486 return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs;1511 return zig_cmp_i128(lhs, rhs) > INT32_C(0) ? lhs : rhs;
1487}1512}
14881513
1489static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) {1514static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) {
1490 zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0);1515 return zig_and_u128(val, zig_maxInt_u(128, bits));
1491 return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask);
1492}1516}
14931517
1494static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) {1518static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) {
1495 return zig_and_u128(val, zig_maxInt(u128, bits));1519 if (bits > UINT8_C(64)) return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val));
1520 int64_t lo = zig_wrap_i64((int64_t)zig_lo_i128(val), bits);
1521 return zig_make_i128(zig_shr_i64(lo, 63), (uint64_t)lo);
1496}1522}
14971523
1498static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) {1524static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) {
1499 return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val));
1500}
1501
1502static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) {
1503 return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits);1525 return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits);
1504}1526}
15051527
1506static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) {1528static inline zig_i128 zig_shlw_i128(zig_i128 lhs, uint8_t rhs, uint8_t bits) {
1507 return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits);1529 return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits);
1508}1530}
15091531
1510static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1532static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1511 return zig_wrap_u128(zig_add_u128(lhs, rhs), bits);1533 return zig_wrap_u128(zig_add_u128(lhs, rhs), bits);
1512}1534}
15131535
1514static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1536static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1515 return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);1537 return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
1516}1538}
15171539
1518static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1540static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1519 return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits);1541 return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits);
1520}1542}
15211543
1522static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1544static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1523 return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);1545 return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
1524}1546}
15251547
1526static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1548static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1527 return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits);1549 return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits);
1528}1550}
15291551
1530static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1552static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1531 return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);1553 return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
1532}1554}
15331555
1534#if zig_has_int1281556#if zig_has_int128
15351557
1536static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1558static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1537#if zig_has_builtin(add_overflow)1559#if zig_has_builtin(add_overflow)
1538 zig_u128 full_res;1560 zig_u128 full_res;
1539 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);1561 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
1540 *res = zig_wrap_u128(full_res, bits);1562 *res = zig_wrap_u128(full_res, bits);
1541 return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits);1563 return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits);
1542#else1564#else
1543 *res = zig_addw_u128(lhs, rhs, bits);1565 *res = zig_addw_u128(lhs, rhs, bits);
1544 return *res < lhs;1566 return *res < lhs;
1545#endif1567#endif
1546}1568}
15471569
1548zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);1570zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1549static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1571static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1550#if zig_has_builtin(add_overflow)1572#if zig_has_builtin(add_overflow)
1551 zig_i128 full_res;1573 zig_i128 full_res;
1552 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);1574 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
1553#else1575#else
1554 zig_c_int overflow_int;1576 int overflow_int;
1555 zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int);1577 zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int);
1556 bool overflow = overflow_int != 0;1578 bool overflow = overflow_int != 0;
1557#endif1579#endif
1558 *res = zig_wrap_i128(full_res, bits);1580 *res = zig_wrap_i128(full_res, bits);
1559 return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits);1581 return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits);
1560}1582}
15611583
1562static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1584static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1563#if zig_has_builtin(sub_overflow)1585#if zig_has_builtin(sub_overflow)
1564 zig_u128 full_res;1586 zig_u128 full_res;
1565 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);1587 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
1566 *res = zig_wrap_u128(full_res, bits);1588 *res = zig_wrap_u128(full_res, bits);
1567 return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits);1589 return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits);
1568#else1590#else
1569 *res = zig_subw_u128(lhs, rhs, bits);1591 *res = zig_subw_u128(lhs, rhs, bits);
1570 return *res > lhs;1592 return *res > lhs;
1571#endif1593#endif
1572}1594}
15731595
1574zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);1596zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1575static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1597static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1576#if zig_has_builtin(sub_overflow)1598#if zig_has_builtin(sub_overflow)
1577 zig_i128 full_res;1599 zig_i128 full_res;
1578 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);1600 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
1579#else1601#else
1580 zig_c_int overflow_int;1602 int overflow_int;
1581 zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int);1603 zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int);
1582 bool overflow = overflow_int != 0;1604 bool overflow = overflow_int != 0;
1583#endif1605#endif
1584 *res = zig_wrap_i128(full_res, bits);1606 *res = zig_wrap_i128(full_res, bits);
1585 return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits);1607 return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits);
1586}1608}
15871609
1588static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1610static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1589#if zig_has_builtin(mul_overflow)1611#if zig_has_builtin(mul_overflow)
1590 zig_u128 full_res;1612 zig_u128 full_res;
1591 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);1613 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
1592 *res = zig_wrap_u128(full_res, bits);1614 *res = zig_wrap_u128(full_res, bits);
1593 return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits);1615 return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits);
1594#else1616#else
1595 *res = zig_mulw_u128(lhs, rhs, bits);1617 *res = zig_mulw_u128(lhs, rhs, bits);
1596 return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs;1618 return rhs != zig_make_u128(0, 0) && lhs > zig_maxInt_u(128, bits) / rhs;
1597#endif1619#endif
1598}1620}
15991621
1600zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);1622zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1601static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1623static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1602#if zig_has_builtin(mul_overflow)1624#if zig_has_builtin(mul_overflow)
1603 zig_i128 full_res;1625 zig_i128 full_res;
1604 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);1626 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
1605#else1627#else
1606 zig_c_int overflow_int;1628 int overflow_int;
1607 zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int);1629 zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int);
1608 bool overflow = overflow_int != 0;1630 bool overflow = overflow_int != 0;
1609#endif1631#endif
1610 *res = zig_wrap_i128(full_res, bits);1632 *res = zig_wrap_i128(full_res, bits);
1611 return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits);1633 return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits);
1612}1634}
16131635
1614#else /* zig_has_int128 */1636#else /* zig_has_int128 */
16151637
1616static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) {1638static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1617 return overflow ||1639 uint64_t hi;
1618 zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) ||1640 bool overflow = zig_addo_u64(&hi, lhs.hi, rhs.hi, bits - 64);
1619 zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0);1641 return overflow ^ zig_addo_u64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
1620}
1621
1622static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) {
1623 return overflow ||
1624 zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) ||
1625 zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0);
1626}1642}
16271643
1628static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1644static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1629 zig_u128 full_res;1645 int64_t hi;
1630 bool overflow =1646 bool overflow = zig_addo_i64(&hi, lhs.hi, rhs.hi, bits - 64);
1631 zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) |1647 return overflow ^ zig_addo_i64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
1632 zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64);
1633 *res = zig_wrap_u128(full_res, bits);
1634 return zig_overflow_u128(overflow, full_res, bits);
1635}1648}
16361649
1637zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);1650static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1638static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1651 uint64_t hi;
1639 zig_c_int overflow_int;1652 bool overflow = zig_subo_u64(&hi, lhs.hi, rhs.hi, bits - 64);
1640 zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int);1653 return overflow ^ zig_subo_u64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
1641 *res = zig_wrap_i128(full_res, bits);
1642 return zig_overflow_i128(overflow_int, full_res, bits);
1643}1654}
16441655
1645static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1656static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1646 zig_u128 full_res;1657 int64_t hi;
1647 bool overflow =1658 bool overflow = zig_subo_i64(&hi, lhs.hi, rhs.hi, bits - 64);
1648 zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) |1659 return overflow ^ zig_subo_i64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
1649 zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64);
1650 *res = zig_wrap_u128(full_res, bits);
1651 return zig_overflow_u128(overflow, full_res, bits);
1652}1660}
16531661
1654zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);1662static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1655static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
1656 zig_c_int overflow_int;
1657 zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int);
1658 *res = zig_wrap_i128(full_res, bits);
1659 return zig_overflow_i128(overflow_int, full_res, bits);
1660}
1661
1662static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {
1663 *res = zig_mulw_u128(lhs, rhs, bits);1663 *res = zig_mulw_u128(lhs, rhs, bits);
1664 return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) &&1664 return zig_cmp_u128(*res, zig_make_u128(0, 0)) != INT32_C(0) &&
1665 zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0);1665 zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0);
1666}1666}
16671667
1668zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);1668zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1669static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1669static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1670 zig_c_int overflow_int;1670 int overflow_int;
1671 zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int);1671 zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int);
1672 bool overflow = overflow_int != 0 ||
1673 zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) ||
1674 zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0);
1672 *res = zig_wrap_i128(full_res, bits);1675 *res = zig_wrap_i128(full_res, bits);
1673 return zig_overflow_i128(overflow_int, full_res, bits);1676 return overflow;
1674}1677}
16751678
1676#endif /* zig_has_int128 */1679#endif /* zig_has_int128 */
16771680
1678static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) {1681static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, uint8_t rhs, uint8_t bits) {
1679 *res = zig_shlw_u128(lhs, rhs, bits);1682 *res = zig_shlw_u128(lhs, rhs, bits);
1680 return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0);1683 return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0);
1681}1684}
16821685
1683static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) {1686static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8_t bits) {
1684 *res = zig_shlw_i128(lhs, rhs, bits);1687 *res = zig_shlw_i128(lhs, rhs, bits);
1685 zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1)));1688 zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1)));
1686 return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) &&1689 return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) &&
1687 zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0);1690 zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0);
1688}1691}
16891692
1690static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1693static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1691 zig_u128 res;1694 zig_u128 res;
1692 if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0))1695 if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0))
1693 return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs;1696 return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs;
16941697 return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res;
1695#if zig_has_int128
1696 return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res;
1697#else
1698 return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res;
1699#endif
1700}1698}
17011699
1702static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1700static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1703 zig_i128 res;1701 zig_i128 res;
1704 if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res;1702 if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res;
1705 return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits);1703 return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits);
1706}1704}
17071705
1708static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1706static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1709 zig_u128 res;1707 zig_u128 res;
1710 return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res;1708 return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res;
1711}1709}
17121710
1713static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1711static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1714 zig_i128 res;1712 zig_i128 res;
1715 if (!zig_addo_i128(&res, lhs, rhs, bits)) return res;1713 if (!zig_addo_i128(&res, lhs, rhs, bits)) return res;
1716 return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits);1714 return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits);
1717}1715}
17181716
1719static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1717static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1720 zig_u128 res;1718 zig_u128 res;
1721 return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res;1719 return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt_u(128, bits) : res;
1722}1720}
17231721
1724static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1722static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1725 zig_i128 res;1723 zig_i128 res;
1726 if (!zig_subo_i128(&res, lhs, rhs, bits)) return res;1724 if (!zig_subo_i128(&res, lhs, rhs, bits)) return res;
1727 return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits);1725 return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits);
1728}1726}
17291727
1730static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {1728static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1731 zig_u128 res;1729 zig_u128 res;
1732 return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res;1730 return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res;
1733}1731}
17341732
1735static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {1733static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1736 zig_i128 res;1734 zig_i128 res;
1737 if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res;1735 if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res;
1738 return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits);1736 return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits);
1739}1737}
17401738
1741static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) {1739static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) {
1742 if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits);1740 if (bits <= UINT8_C(64)) return zig_clz_u64(zig_lo_u128(val), bits);
1743 if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64));1741 if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - UINT8_C(64));
1744 return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64));1742 return zig_clz_u64(zig_lo_u128(val), UINT8_C(64)) + (bits - UINT8_C(64));
1745}1743}
17461744
1747static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) {1745static inline uint8_t zig_clz_i128(zig_i128 val, uint8_t bits) {
1748 return zig_clz_u128(zig_bitcast_u128(val), bits);1746 return zig_clz_u128(zig_bitcast_u128(val), bits);
1749}1747}
17501748
1751static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) {1749static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) {
1752 if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64));1750 if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), UINT8_C(64));
1753 return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64);1751 return zig_ctz_u64(zig_hi_u128(val), bits - UINT8_C(64)) + UINT8_C(64);
1754}1752}
17551753
1756static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) {1754static inline uint8_t zig_ctz_i128(zig_i128 val, uint8_t bits) {
1757 return zig_ctz_u128(zig_bitcast_u128(val), bits);1755 return zig_ctz_u128(zig_bitcast_u128(val), bits);
1758}1756}
17591757
1760static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) {1758static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) {
1761 return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) +1759 return zig_popcount_u64(zig_hi_u128(val), bits - UINT8_C(64)) +
1762 zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64));1760 zig_popcount_u64(zig_lo_u128(val), UINT8_C(64));
1763}1761}
17641762
1765static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) {1763static inline uint8_t zig_popcount_i128(zig_i128 val, uint8_t bits) {
1766 return zig_popcount_u128(zig_bitcast_u128(val), bits);1764 return zig_popcount_u128(zig_bitcast_u128(val), bits);
1767}1765}
17681766
1769static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) {1767static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) {
1770 zig_u128 full_res;1768 zig_u128 full_res;
1771#if zig_has_builtin(bswap128)1769#if zig_has_builtin(bswap128)
1772 full_res = __builtin_bswap128(val);1770 full_res = __builtin_bswap128(val);
1773#else1771#else
1774 full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)),1772 full_res = zig_make_u128(zig_byte_swap_u64(zig_lo_u128(val), UINT8_C(64)),
1775 zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64)));1773 zig_byte_swap_u64(zig_hi_u128(val), UINT8_C(64)));
1776#endif1774#endif
1777 return zig_shr_u128(full_res, zig_as_u8(128) - bits);1775 return zig_shr_u128(full_res, UINT8_C(128) - bits);
1778}1776}
17791777
1780static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) {1778static inline zig_i128 zig_byte_swap_i128(zig_i128 val, uint8_t bits) {
1781 return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits));1779 return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits));
1782}1780}
17831781
1784static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) {1782static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) {
1785 return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)),1783 return zig_shr_u128(zig_make_u128(zig_bit_reverse_u64(zig_lo_u128(val), UINT8_C(64)),
1786 zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))),1784 zig_bit_reverse_u64(zig_hi_u128(val), UINT8_C(64))),
1787 zig_as_u8(128) - bits);1785 UINT8_C(128) - bits);
1788}1786}
17891787
1790static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) {1788static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
1791 return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));1789 return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));
1792}1790}
17931791
1792/* ========================== Big Integer Support =========================== */
1793
1794static inline uint16_t zig_int_bytes(uint16_t bits) {
1795 uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT;
1796 uint16_t alignment = ZIG_TARGET_MAX_INT_ALIGNMENT;
1797 while (alignment / 2 >= bytes) alignment /= 2;
1798 return (bytes + alignment - 1) / alignment * alignment;
1799}
1800
1801static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
1802 const uint8_t *lhs_bytes = lhs;
1803 const uint8_t *rhs_bytes = rhs;
1804 uint16_t byte_offset = 0;
1805 bool do_signed = is_signed;
1806 uint16_t remaining_bytes = zig_int_bytes(bits);
1807
1808#if zig_little_endian
1809 byte_offset = remaining_bytes;
1810#endif
1811
1812 while (remaining_bytes >= 128 / CHAR_BIT) {
1813 int32_t limb_cmp;
1814
1815#if zig_little_endian
1816 byte_offset -= 128 / CHAR_BIT;
1817#endif
1818
1819 if (do_signed) {
1820 zig_i128 lhs_limb;
1821 zig_i128 rhs_limb;
1822
1823 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1824 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1825 limb_cmp = zig_cmp_i128(lhs_limb, rhs_limb);
1826 do_signed = false;
1827 } else {
1828 zig_u128 lhs_limb;
1829 zig_u128 rhs_limb;
1830
1831 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1832 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1833 limb_cmp = zig_cmp_u128(lhs_limb, rhs_limb);
1834 }
1835
1836 if (limb_cmp != 0) return limb_cmp;
1837 remaining_bytes -= 128 / CHAR_BIT;
1838
1839#if zig_big_endian
1840 byte_offset += 128 / CHAR_BIT;
1841#endif
1842 }
1843
1844 while (remaining_bytes >= 64 / CHAR_BIT) {
1845#if zig_little_endian
1846 byte_offset -= 64 / CHAR_BIT;
1847#endif
1848
1849 if (do_signed) {
1850 int64_t lhs_limb;
1851 int64_t rhs_limb;
1852
1853 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1854 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1855 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1856 do_signed = false;
1857 } else {
1858 uint64_t lhs_limb;
1859 uint64_t rhs_limb;
1860
1861 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1862 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1863 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1864 }
1865
1866 remaining_bytes -= 64 / CHAR_BIT;
1867
1868#if zig_big_endian
1869 byte_offset += 64 / CHAR_BIT;
1870#endif
1871 }
1872
1873 while (remaining_bytes >= 32 / CHAR_BIT) {
1874#if zig_little_endian
1875 byte_offset -= 32 / CHAR_BIT;
1876#endif
1877
1878 if (do_signed) {
1879 int32_t lhs_limb;
1880 int32_t rhs_limb;
1881
1882 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1883 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1884 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1885 do_signed = false;
1886 } else {
1887 uint32_t lhs_limb;
1888 uint32_t rhs_limb;
1889
1890 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1891 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1892 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1893 }
1894
1895 remaining_bytes -= 32 / CHAR_BIT;
1896
1897#if zig_big_endian
1898 byte_offset += 32 / CHAR_BIT;
1899#endif
1900 }
1901
1902 while (remaining_bytes >= 16 / CHAR_BIT) {
1903#if zig_little_endian
1904 byte_offset -= 16 / CHAR_BIT;
1905#endif
1906
1907 if (do_signed) {
1908 int16_t lhs_limb;
1909 int16_t rhs_limb;
1910
1911 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1912 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1913 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1914 do_signed = false;
1915 } else {
1916 uint16_t lhs_limb;
1917 uint16_t rhs_limb;
1918
1919 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1920 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1921 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1922 }
1923
1924 remaining_bytes -= 16 / CHAR_BIT;
1925
1926#if zig_big_endian
1927 byte_offset += 16 / CHAR_BIT;
1928#endif
1929 }
1930
1931 while (remaining_bytes >= 8 / CHAR_BIT) {
1932#if zig_little_endian
1933 byte_offset -= 8 / CHAR_BIT;
1934#endif
1935
1936 if (do_signed) {
1937 int8_t lhs_limb;
1938 int8_t rhs_limb;
1939
1940 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1941 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1942 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1943 do_signed = false;
1944 } else {
1945 uint8_t lhs_limb;
1946 uint8_t rhs_limb;
1947
1948 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1949 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1950 if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
1951 }
1952
1953 remaining_bytes -= 8 / CHAR_BIT;
1954
1955#if zig_big_endian
1956 byte_offset += 8 / CHAR_BIT;
1957#endif
1958 }
1959
1960 return 0;
1961}
1962
1963static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
1964 uint8_t *res_bytes = res;
1965 const uint8_t *lhs_bytes = lhs;
1966 const uint8_t *rhs_bytes = rhs;
1967 uint16_t byte_offset = 0;
1968 uint16_t remaining_bytes = zig_int_bytes(bits);
1969 uint16_t top_bits = remaining_bytes * 8 - bits;
1970 bool overflow = false;
1971
1972#if zig_big_endian
1973 byte_offset = remaining_bytes;
1974#endif
1975
1976 while (remaining_bytes >= 128 / CHAR_BIT) {
1977 uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0);
1978
1979#if zig_big_endian
1980 byte_offset -= 128 / CHAR_BIT;
1981#endif
1982
1983 if (remaining_bytes == 128 / CHAR_BIT && is_signed) {
1984 zig_i128 res_limb;
1985 zig_i128 tmp_limb;
1986 zig_i128 lhs_limb;
1987 zig_i128 rhs_limb;
1988 bool limb_overflow;
1989
1990 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
1991 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
1992 limb_overflow = zig_addo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
1993 overflow = limb_overflow ^ zig_addo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
1994 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
1995 } else {
1996 zig_u128 res_limb;
1997 zig_u128 tmp_limb;
1998 zig_u128 lhs_limb;
1999 zig_u128 rhs_limb;
2000 bool limb_overflow;
2001
2002 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2003 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2004 limb_overflow = zig_addo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2005 overflow = limb_overflow ^ zig_addo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
2006 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2007 }
2008
2009 remaining_bytes -= 128 / CHAR_BIT;
2010
2011#if zig_little_endian
2012 byte_offset += 128 / CHAR_BIT;
2013#endif
2014 }
2015
2016 while (remaining_bytes >= 64 / CHAR_BIT) {
2017 uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0);
2018
2019#if zig_big_endian
2020 byte_offset -= 64 / CHAR_BIT;
2021#endif
2022
2023 if (remaining_bytes == 64 / CHAR_BIT && is_signed) {
2024 int64_t res_limb;
2025 int64_t tmp_limb;
2026 int64_t lhs_limb;
2027 int64_t rhs_limb;
2028 bool limb_overflow;
2029
2030 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2031 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2032 limb_overflow = zig_addo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2033 overflow = limb_overflow ^ zig_addo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits);
2034 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2035 } else {
2036 uint64_t res_limb;
2037 uint64_t tmp_limb;
2038 uint64_t lhs_limb;
2039 uint64_t rhs_limb;
2040 bool limb_overflow;
2041
2042 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2043 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2044 limb_overflow = zig_addo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2045 overflow = limb_overflow ^ zig_addo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits);
2046 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2047 }
2048
2049 remaining_bytes -= 64 / CHAR_BIT;
2050
2051#if zig_little_endian
2052 byte_offset += 64 / CHAR_BIT;
2053#endif
2054 }
2055
2056 while (remaining_bytes >= 32 / CHAR_BIT) {
2057 uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0);
2058
2059#if zig_big_endian
2060 byte_offset -= 32 / CHAR_BIT;
2061#endif
2062
2063 if (remaining_bytes == 32 / CHAR_BIT && is_signed) {
2064 int32_t res_limb;
2065 int32_t tmp_limb;
2066 int32_t lhs_limb;
2067 int32_t rhs_limb;
2068 bool limb_overflow;
2069
2070 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2071 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2072 limb_overflow = zig_addo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2073 overflow = limb_overflow ^ zig_addo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits);
2074 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2075 } else {
2076 uint32_t res_limb;
2077 uint32_t tmp_limb;
2078 uint32_t lhs_limb;
2079 uint32_t rhs_limb;
2080 bool limb_overflow;
2081
2082 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2083 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2084 limb_overflow = zig_addo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2085 overflow = limb_overflow ^ zig_addo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits);
2086 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2087 }
2088
2089 remaining_bytes -= 32 / CHAR_BIT;
2090
2091#if zig_little_endian
2092 byte_offset += 32 / CHAR_BIT;
2093#endif
2094 }
2095
2096 while (remaining_bytes >= 16 / CHAR_BIT) {
2097 uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0);
2098
2099#if zig_big_endian
2100 byte_offset -= 16 / CHAR_BIT;
2101#endif
2102
2103 if (remaining_bytes == 16 / CHAR_BIT && is_signed) {
2104 int16_t res_limb;
2105 int16_t tmp_limb;
2106 int16_t lhs_limb;
2107 int16_t rhs_limb;
2108 bool limb_overflow;
2109
2110 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2111 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2112 limb_overflow = zig_addo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2113 overflow = limb_overflow ^ zig_addo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits);
2114 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2115 } else {
2116 uint16_t res_limb;
2117 uint16_t tmp_limb;
2118 uint16_t lhs_limb;
2119 uint16_t rhs_limb;
2120 bool limb_overflow;
2121
2122 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2123 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2124 limb_overflow = zig_addo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2125 overflow = limb_overflow ^ zig_addo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits);
2126 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2127 }
2128
2129 remaining_bytes -= 16 / CHAR_BIT;
2130
2131#if zig_little_endian
2132 byte_offset += 16 / CHAR_BIT;
2133#endif
2134 }
2135
2136 while (remaining_bytes >= 8 / CHAR_BIT) {
2137 uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0);
2138
2139#if zig_big_endian
2140 byte_offset -= 8 / CHAR_BIT;
2141#endif
2142
2143 if (remaining_bytes == 8 / CHAR_BIT && is_signed) {
2144 int8_t res_limb;
2145 int8_t tmp_limb;
2146 int8_t lhs_limb;
2147 int8_t rhs_limb;
2148 bool limb_overflow;
2149
2150 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2151 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2152 limb_overflow = zig_addo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2153 overflow = limb_overflow ^ zig_addo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits);
2154 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2155 } else {
2156 uint8_t res_limb;
2157 uint8_t tmp_limb;
2158 uint8_t lhs_limb;
2159 uint8_t rhs_limb;
2160 bool limb_overflow;
2161
2162 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2163 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2164 limb_overflow = zig_addo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2165 overflow = limb_overflow ^ zig_addo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits);
2166 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2167 }
2168
2169 remaining_bytes -= 8 / CHAR_BIT;
2170
2171#if zig_little_endian
2172 byte_offset += 8 / CHAR_BIT;
2173#endif
2174 }
2175
2176 return overflow;
2177}
2178
2179static inline bool zig_subo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2180 uint8_t *res_bytes = res;
2181 const uint8_t *lhs_bytes = lhs;
2182 const uint8_t *rhs_bytes = rhs;
2183 uint16_t byte_offset = 0;
2184 uint16_t remaining_bytes = zig_int_bytes(bits);
2185 uint16_t top_bits = remaining_bytes * 8 - bits;
2186 bool overflow = false;
2187
2188#if zig_big_endian
2189 byte_offset = remaining_bytes;
2190#endif
2191
2192 while (remaining_bytes >= 128 / CHAR_BIT) {
2193 uint16_t limb_bits = 128 - (remaining_bytes == 128 / CHAR_BIT ? top_bits : 0);
2194
2195#if zig_big_endian
2196 byte_offset -= 128 / CHAR_BIT;
2197#endif
2198
2199 if (remaining_bytes == 128 / CHAR_BIT && is_signed) {
2200 zig_i128 res_limb;
2201 zig_i128 tmp_limb;
2202 zig_i128 lhs_limb;
2203 zig_i128 rhs_limb;
2204 bool limb_overflow;
2205
2206 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2207 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2208 limb_overflow = zig_subo_i128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2209 overflow = limb_overflow ^ zig_subo_i128(&res_limb, tmp_limb, zig_make_i128(INT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
2210 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2211 } else {
2212 zig_u128 res_limb;
2213 zig_u128 tmp_limb;
2214 zig_u128 lhs_limb;
2215 zig_u128 rhs_limb;
2216 bool limb_overflow;
2217
2218 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2219 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2220 limb_overflow = zig_subo_u128(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2221 overflow = limb_overflow ^ zig_subo_u128(&res_limb, tmp_limb, zig_make_u128(UINT64_C(0), overflow ? UINT64_C(1) : UINT64_C(0)), limb_bits);
2222 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2223 }
2224
2225 remaining_bytes -= 128 / CHAR_BIT;
2226
2227#if zig_little_endian
2228 byte_offset += 128 / CHAR_BIT;
2229#endif
2230 }
2231
2232 while (remaining_bytes >= 64 / CHAR_BIT) {
2233 uint16_t limb_bits = 64 - (remaining_bytes == 64 / CHAR_BIT ? top_bits : 0);
2234
2235#if zig_big_endian
2236 byte_offset -= 64 / CHAR_BIT;
2237#endif
2238
2239 if (remaining_bytes == 64 / CHAR_BIT && is_signed) {
2240 int64_t res_limb;
2241 int64_t tmp_limb;
2242 int64_t lhs_limb;
2243 int64_t rhs_limb;
2244 bool limb_overflow;
2245
2246 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2247 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2248 limb_overflow = zig_subo_i64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2249 overflow = limb_overflow ^ zig_subo_i64(&res_limb, tmp_limb, overflow ? INT64_C(1) : INT64_C(0), limb_bits);
2250 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2251 } else {
2252 uint64_t res_limb;
2253 uint64_t tmp_limb;
2254 uint64_t lhs_limb;
2255 uint64_t rhs_limb;
2256 bool limb_overflow;
2257
2258 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2259 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2260 limb_overflow = zig_subo_u64(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2261 overflow = limb_overflow ^ zig_subo_u64(&res_limb, tmp_limb, overflow ? UINT64_C(1) : UINT64_C(0), limb_bits);
2262 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2263 }
2264
2265 remaining_bytes -= 64 / CHAR_BIT;
2266
2267#if zig_little_endian
2268 byte_offset += 64 / CHAR_BIT;
2269#endif
2270 }
2271
2272 while (remaining_bytes >= 32 / CHAR_BIT) {
2273 uint16_t limb_bits = 32 - (remaining_bytes == 32 / CHAR_BIT ? top_bits : 0);
2274
2275#if zig_big_endian
2276 byte_offset -= 32 / CHAR_BIT;
2277#endif
2278
2279 if (remaining_bytes == 32 / CHAR_BIT && is_signed) {
2280 int32_t res_limb;
2281 int32_t tmp_limb;
2282 int32_t lhs_limb;
2283 int32_t rhs_limb;
2284 bool limb_overflow;
2285
2286 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2287 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2288 limb_overflow = zig_subo_i32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2289 overflow = limb_overflow ^ zig_subo_i32(&res_limb, tmp_limb, overflow ? INT32_C(1) : INT32_C(0), limb_bits);
2290 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2291 } else {
2292 uint32_t res_limb;
2293 uint32_t tmp_limb;
2294 uint32_t lhs_limb;
2295 uint32_t rhs_limb;
2296 bool limb_overflow;
2297
2298 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2299 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2300 limb_overflow = zig_subo_u32(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2301 overflow = limb_overflow ^ zig_subo_u32(&res_limb, tmp_limb, overflow ? UINT32_C(1) : UINT32_C(0), limb_bits);
2302 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2303 }
2304
2305 remaining_bytes -= 32 / CHAR_BIT;
2306
2307#if zig_little_endian
2308 byte_offset += 32 / CHAR_BIT;
2309#endif
2310 }
2311
2312 while (remaining_bytes >= 16 / CHAR_BIT) {
2313 uint16_t limb_bits = 16 - (remaining_bytes == 16 / CHAR_BIT ? top_bits : 0);
2314
2315#if zig_big_endian
2316 byte_offset -= 16 / CHAR_BIT;
2317#endif
2318
2319 if (remaining_bytes == 16 / CHAR_BIT && is_signed) {
2320 int16_t res_limb;
2321 int16_t tmp_limb;
2322 int16_t lhs_limb;
2323 int16_t rhs_limb;
2324 bool limb_overflow;
2325
2326 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2327 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2328 limb_overflow = zig_subo_i16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2329 overflow = limb_overflow ^ zig_subo_i16(&res_limb, tmp_limb, overflow ? INT16_C(1) : INT16_C(0), limb_bits);
2330 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2331 } else {
2332 uint16_t res_limb;
2333 uint16_t tmp_limb;
2334 uint16_t lhs_limb;
2335 uint16_t rhs_limb;
2336 bool limb_overflow;
2337
2338 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2339 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2340 limb_overflow = zig_subo_u16(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2341 overflow = limb_overflow ^ zig_subo_u16(&res_limb, tmp_limb, overflow ? UINT16_C(1) : UINT16_C(0), limb_bits);
2342 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2343 }
2344
2345 remaining_bytes -= 16 / CHAR_BIT;
2346
2347#if zig_little_endian
2348 byte_offset += 16 / CHAR_BIT;
2349#endif
2350 }
2351
2352 while (remaining_bytes >= 8 / CHAR_BIT) {
2353 uint16_t limb_bits = 8 - (remaining_bytes == 8 / CHAR_BIT ? top_bits : 0);
2354
2355#if zig_big_endian
2356 byte_offset -= 8 / CHAR_BIT;
2357#endif
2358
2359 if (remaining_bytes == 8 / CHAR_BIT && is_signed) {
2360 int8_t res_limb;
2361 int8_t tmp_limb;
2362 int8_t lhs_limb;
2363 int8_t rhs_limb;
2364 bool limb_overflow;
2365
2366 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2367 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2368 limb_overflow = zig_subo_i8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2369 overflow = limb_overflow ^ zig_subo_i8(&res_limb, tmp_limb, overflow ? INT8_C(1) : INT8_C(0), limb_bits);
2370 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2371 } else {
2372 uint8_t res_limb;
2373 uint8_t tmp_limb;
2374 uint8_t lhs_limb;
2375 uint8_t rhs_limb;
2376 bool limb_overflow;
2377
2378 memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
2379 memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
2380 limb_overflow = zig_subo_u8(&tmp_limb, lhs_limb, rhs_limb, limb_bits);
2381 overflow = limb_overflow ^ zig_subo_u8(&res_limb, tmp_limb, overflow ? UINT8_C(1) : UINT8_C(0), limb_bits);
2382 memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb));
2383 }
2384
2385 remaining_bytes -= 8 / CHAR_BIT;
2386
2387#if zig_little_endian
2388 byte_offset += 8 / CHAR_BIT;
2389#endif
2390 }
2391
2392 return overflow;
2393}
2394
2395static inline void zig_addw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2396 (void)zig_addo_big(res, lhs, rhs, is_signed, bits);
2397}
2398
2399static inline void zig_subw_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2400 (void)zig_subo_big(res, lhs, rhs, is_signed, bits);
2401}
2402
2403zig_extern void __udivei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
2404static inline void zig_div_trunc_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2405 if (!is_signed) {
2406 __udivei4(res, lhs, rhs, bits);
2407 return;
2408 }
2409
2410 zig_trap();
2411}
2412
2413static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2414 if (!is_signed) {
2415 zig_div_trunc_big(res, lhs, rhs, is_signed, bits);
2416 return;
2417 }
2418
2419 zig_trap();
2420}
2421
2422zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
2423static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2424 if (!is_signed) {
2425 __umodei4(res, lhs, rhs, bits);
2426 return;
2427 }
2428
2429 zig_trap();
2430}
2431
2432static inline void zig_mod_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
2433 if (!is_signed) {
2434 zig_rem_big(res, lhs, rhs, is_signed, bits);
2435 return;
2436 }
2437
2438 zig_trap();
2439}
2440
2441static inline uint16_t zig_clz_big(const void *val, bool is_signed, uint16_t bits) {
2442 const uint8_t *val_bytes = val;
2443 uint16_t byte_offset = 0;
2444 uint16_t remaining_bytes = zig_int_bytes(bits);
2445 uint16_t skip_bits = remaining_bytes * 8 - bits;
2446 uint16_t total_lz = 0;
2447 uint16_t limb_lz;
2448 (void)is_signed;
2449
2450#if zig_little_endian
2451 byte_offset = remaining_bytes;
2452#endif
2453
2454 while (remaining_bytes >= 128 / CHAR_BIT) {
2455#if zig_little_endian
2456 byte_offset -= 128 / CHAR_BIT;
2457#endif
2458
2459 {
2460 zig_u128 val_limb;
2461
2462 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2463 limb_lz = zig_clz_u128(val_limb, 128 - skip_bits);
2464 }
2465
2466 total_lz += limb_lz;
2467 if (limb_lz < 128 - skip_bits) return total_lz;
2468 skip_bits = 0;
2469 remaining_bytes -= 128 / CHAR_BIT;
2470
2471#if zig_big_endian
2472 byte_offset += 128 / CHAR_BIT;
2473#endif
2474 }
2475
2476 while (remaining_bytes >= 64 / CHAR_BIT) {
2477#if zig_little_endian
2478 byte_offset -= 64 / CHAR_BIT;
2479#endif
2480
2481 {
2482 uint64_t val_limb;
2483
2484 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2485 limb_lz = zig_clz_u64(val_limb, 64 - skip_bits);
2486 }
2487
2488 total_lz += limb_lz;
2489 if (limb_lz < 64 - skip_bits) return total_lz;
2490 skip_bits = 0;
2491 remaining_bytes -= 64 / CHAR_BIT;
2492
2493#if zig_big_endian
2494 byte_offset += 64 / CHAR_BIT;
2495#endif
2496 }
2497
2498 while (remaining_bytes >= 32 / CHAR_BIT) {
2499#if zig_little_endian
2500 byte_offset -= 32 / CHAR_BIT;
2501#endif
2502
2503 {
2504 uint32_t val_limb;
2505
2506 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2507 limb_lz = zig_clz_u32(val_limb, 32 - skip_bits);
2508 }
2509
2510 total_lz += limb_lz;
2511 if (limb_lz < 32 - skip_bits) return total_lz;
2512 skip_bits = 0;
2513 remaining_bytes -= 32 / CHAR_BIT;
2514
2515#if zig_big_endian
2516 byte_offset += 32 / CHAR_BIT;
2517#endif
2518 }
2519
2520 while (remaining_bytes >= 16 / CHAR_BIT) {
2521#if zig_little_endian
2522 byte_offset -= 16 / CHAR_BIT;
2523#endif
2524
2525 {
2526 uint16_t val_limb;
2527
2528 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2529 limb_lz = zig_clz_u16(val_limb, 16 - skip_bits);
2530 }
2531
2532 total_lz += limb_lz;
2533 if (limb_lz < 16 - skip_bits) return total_lz;
2534 skip_bits = 0;
2535 remaining_bytes -= 16 / CHAR_BIT;
2536
2537#if zig_big_endian
2538 byte_offset += 16 / CHAR_BIT;
2539#endif
2540 }
2541
2542 while (remaining_bytes >= 8 / CHAR_BIT) {
2543#if zig_little_endian
2544 byte_offset -= 8 / CHAR_BIT;
2545#endif
2546
2547 {
2548 uint8_t val_limb;
2549
2550 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2551 limb_lz = zig_clz_u8(val_limb, 8 - skip_bits);
2552 }
2553
2554 total_lz += limb_lz;
2555 if (limb_lz < 8 - skip_bits) return total_lz;
2556 skip_bits = 0;
2557 remaining_bytes -= 8 / CHAR_BIT;
2558
2559#if zig_big_endian
2560 byte_offset += 8 / CHAR_BIT;
2561#endif
2562 }
2563
2564 return total_lz;
2565}
2566
2567static inline uint16_t zig_ctz_big(const void *val, bool is_signed, uint16_t bits) {
2568 const uint8_t *val_bytes = val;
2569 uint16_t byte_offset = 0;
2570 uint16_t remaining_bytes = zig_int_bytes(bits);
2571 uint16_t total_tz = 0;
2572 uint16_t limb_tz;
2573 (void)is_signed;
2574
2575#if zig_big_endian
2576 byte_offset = remaining_bytes;
2577#endif
2578
2579 while (remaining_bytes >= 128 / CHAR_BIT) {
2580#if zig_big_endian
2581 byte_offset -= 128 / CHAR_BIT;
2582#endif
2583
2584 {
2585 zig_u128 val_limb;
2586
2587 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2588 limb_tz = zig_ctz_u128(val_limb, 128);
2589 }
2590
2591 total_tz += limb_tz;
2592 if (limb_tz < 128) return total_tz;
2593 remaining_bytes -= 128 / CHAR_BIT;
2594
2595#if zig_little_endian
2596 byte_offset += 128 / CHAR_BIT;
2597#endif
2598 }
2599
2600 while (remaining_bytes >= 64 / CHAR_BIT) {
2601#if zig_big_endian
2602 byte_offset -= 64 / CHAR_BIT;
2603#endif
2604
2605 {
2606 uint64_t val_limb;
2607
2608 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2609 limb_tz = zig_ctz_u64(val_limb, 64);
2610 }
2611
2612 total_tz += limb_tz;
2613 if (limb_tz < 64) return total_tz;
2614 remaining_bytes -= 64 / CHAR_BIT;
2615
2616#if zig_little_endian
2617 byte_offset += 64 / CHAR_BIT;
2618#endif
2619 }
2620
2621 while (remaining_bytes >= 32 / CHAR_BIT) {
2622#if zig_big_endian
2623 byte_offset -= 32 / CHAR_BIT;
2624#endif
2625
2626 {
2627 uint32_t val_limb;
2628
2629 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2630 limb_tz = zig_ctz_u32(val_limb, 32);
2631 }
2632
2633 total_tz += limb_tz;
2634 if (limb_tz < 32) return total_tz;
2635 remaining_bytes -= 32 / CHAR_BIT;
2636
2637#if zig_little_endian
2638 byte_offset += 32 / CHAR_BIT;
2639#endif
2640 }
2641
2642 while (remaining_bytes >= 16 / CHAR_BIT) {
2643#if zig_big_endian
2644 byte_offset -= 16 / CHAR_BIT;
2645#endif
2646
2647 {
2648 uint16_t val_limb;
2649
2650 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2651 limb_tz = zig_ctz_u16(val_limb, 16);
2652 }
2653
2654 total_tz += limb_tz;
2655 if (limb_tz < 16) return total_tz;
2656 remaining_bytes -= 16 / CHAR_BIT;
2657
2658#if zig_little_endian
2659 byte_offset += 16 / CHAR_BIT;
2660#endif
2661 }
2662
2663 while (remaining_bytes >= 8 / CHAR_BIT) {
2664#if zig_big_endian
2665 byte_offset -= 8 / CHAR_BIT;
2666#endif
2667
2668 {
2669 uint8_t val_limb;
2670
2671 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2672 limb_tz = zig_ctz_u8(val_limb, 8);
2673 }
2674
2675 total_tz += limb_tz;
2676 if (limb_tz < 8) return total_tz;
2677 remaining_bytes -= 8 / CHAR_BIT;
2678
2679#if zig_little_endian
2680 byte_offset += 8 / CHAR_BIT;
2681#endif
2682 }
2683
2684 return total_tz;
2685}
2686
2687static inline uint16_t zig_popcount_big(const void *val, bool is_signed, uint16_t bits) {
2688 const uint8_t *val_bytes = val;
2689 uint16_t byte_offset = 0;
2690 uint16_t remaining_bytes = zig_int_bytes(bits);
2691 uint16_t total_pc = 0;
2692 (void)is_signed;
2693
2694#if zig_big_endian
2695 byte_offset = remaining_bytes;
2696#endif
2697
2698 while (remaining_bytes >= 128 / CHAR_BIT) {
2699#if zig_big_endian
2700 byte_offset -= 128 / CHAR_BIT;
2701#endif
2702
2703 {
2704 zig_u128 val_limb;
2705
2706 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2707 total_pc += zig_popcount_u128(val_limb, 128);
2708 }
2709
2710 remaining_bytes -= 128 / CHAR_BIT;
2711
2712#if zig_little_endian
2713 byte_offset += 128 / CHAR_BIT;
2714#endif
2715 }
2716
2717 while (remaining_bytes >= 64 / CHAR_BIT) {
2718#if zig_big_endian
2719 byte_offset -= 64 / CHAR_BIT;
2720#endif
2721
2722 {
2723 uint64_t val_limb;
2724
2725 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2726 total_pc += zig_popcount_u64(val_limb, 64);
2727 }
2728
2729 remaining_bytes -= 64 / CHAR_BIT;
2730
2731#if zig_little_endian
2732 byte_offset += 64 / CHAR_BIT;
2733#endif
2734 }
2735
2736 while (remaining_bytes >= 32 / CHAR_BIT) {
2737#if zig_big_endian
2738 byte_offset -= 32 / CHAR_BIT;
2739#endif
2740
2741 {
2742 uint32_t val_limb;
2743
2744 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2745 total_pc += zig_popcount_u32(val_limb, 32);
2746 }
2747
2748 remaining_bytes -= 32 / CHAR_BIT;
2749
2750#if zig_little_endian
2751 byte_offset += 32 / CHAR_BIT;
2752#endif
2753 }
2754
2755 while (remaining_bytes >= 16 / CHAR_BIT) {
2756#if zig_big_endian
2757 byte_offset -= 16 / CHAR_BIT;
2758#endif
2759
2760 {
2761 uint16_t val_limb;
2762
2763 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2764 total_pc = zig_popcount_u16(val_limb, 16);
2765 }
2766
2767 remaining_bytes -= 16 / CHAR_BIT;
2768
2769#if zig_little_endian
2770 byte_offset += 16 / CHAR_BIT;
2771#endif
2772 }
2773
2774 while (remaining_bytes >= 8 / CHAR_BIT) {
2775#if zig_big_endian
2776 byte_offset -= 8 / CHAR_BIT;
2777#endif
2778
2779 {
2780 uint8_t val_limb;
2781
2782 memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb));
2783 total_pc = zig_popcount_u8(val_limb, 8);
2784 }
2785
2786 remaining_bytes -= 8 / CHAR_BIT;
2787
2788#if zig_little_endian
2789 byte_offset += 8 / CHAR_BIT;
2790#endif
2791 }
2792
2793 return total_pc;
2794}
2795
1794/* ========================= Floating Point Support ========================= */2796/* ========================= Floating Point Support ========================= */
17952797
1796#if _MSC_VER2798#if _MSC_VER
...@@ -1810,252 +2812,253 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) {...@@ -1810,252 +2812,253 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) {
18102812
1811#if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc)2813#if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc)
1812#define zig_has_float_builtins 12814#define zig_has_float_builtins 1
1813#define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg)2815#define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg)
1814#define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg)2816#define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg)
1815#define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg)2817#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg)
1816#define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg)2818#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg)
1817#define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg)2819#define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg)
1818#define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg)
1819#else2820#else
1820#define zig_has_float_builtins 02821#define zig_has_float_builtins 0
1821#define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr)2822#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr)
1822#define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr)2823#define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr)
1823#define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)2824#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)
1824#define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)2825#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)
1825#define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr)2826#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr)
1826#define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr)
1827#endif2827#endif
18282828
1829#define zig_has_f16 12829#define zig_has_f16 1
1830#define zig_bitSizeOf_f16 162830#define zig_bitSizeOf_f16 16
2831typedef int16_t zig_repr_f16;
1831#define zig_libc_name_f16(name) __##name##h2832#define zig_libc_name_f16(name) __##name##h
1832#define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr)2833#define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr)
1833#if FLT_MANT_DIG == 112834#if FLT_MANT_DIG == 11
1834typedef float zig_f16;2835typedef float zig_f16;
1835#define zig_as_f16(fp, repr) fp##f2836#define zig_make_f16(fp, repr) fp##f
1836#elif DBL_MANT_DIG == 112837#elif DBL_MANT_DIG == 11
1837typedef double zig_f16;2838typedef double zig_f16;
1838#define zig_as_f16(fp, repr) fp2839#define zig_make_f16(fp, repr) fp
1839#elif LDBL_MANT_DIG == 112840#elif LDBL_MANT_DIG == 11
1840#define zig_bitSizeOf_c_longdouble 162841#define zig_bitSizeOf_c_longdouble 16
2842#ifndef ZIG_TARGET_ABI_MSVC
2843typedef zig_repr_f16 zig_repr_c_longdouble;
2844#endif
1841typedef long double zig_f16;2845typedef long double zig_f16;
1842#define zig_as_f16(fp, repr) fp##l2846#define zig_make_f16(fp, repr) fp##l
1843#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))2847#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))
1844typedef _Float16 zig_f16;2848typedef _Float16 zig_f16;
1845#define zig_as_f16(fp, repr) fp##f162849#define zig_make_f16(fp, repr) fp##f16
1846#elif defined(__SIZEOF_FP16__)2850#elif defined(__SIZEOF_FP16__)
1847typedef __fp16 zig_f16;2851typedef __fp16 zig_f16;
1848#define zig_as_f16(fp, repr) fp##f162852#define zig_make_f16(fp, repr) fp##f16
1849#else2853#else
1850#undef zig_has_f162854#undef zig_has_f16
1851#define zig_has_f16 02855#define zig_has_f16 0
1852#define zig_repr_f16 i162856#define zig_bitSizeOf_repr_f16 16
1853typedef zig_i16 zig_f16;2857typedef int16_t zig_f16;
1854#define zig_as_f16(fp, repr) repr2858#define zig_make_f16(fp, repr) repr
1855#undef zig_as_special_f162859#undef zig_make_special_f16
1856#define zig_as_special_f16(sign, name, arg, repr) repr2860#define zig_make_special_f16(sign, name, arg, repr) repr
1857#undef zig_as_special_constant_f162861#undef zig_init_special_f16
1858#define zig_as_special_constant_f16(sign, name, arg, repr) repr2862#define zig_init_special_f16(sign, name, arg, repr) repr
1859#endif2863#endif
18602864
1861#define zig_has_f32 12865#define zig_has_f32 1
1862#define zig_bitSizeOf_f32 322866#define zig_bitSizeOf_f32 32
2867typedef int32_t zig_repr_f32;
1863#define zig_libc_name_f32(name) name##f2868#define zig_libc_name_f32(name) name##f
1864#if _MSC_VER2869#if _MSC_VER
1865#define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, )2870#define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, )
1866#else2871#else
1867#define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr)2872#define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr)
1868#endif2873#endif
1869#if FLT_MANT_DIG == 242874#if FLT_MANT_DIG == 24
1870typedef float zig_f32;2875typedef float zig_f32;
1871#define zig_as_f32(fp, repr) fp##f2876#define zig_make_f32(fp, repr) fp##f
1872#elif DBL_MANT_DIG == 242877#elif DBL_MANT_DIG == 24
1873typedef double zig_f32;2878typedef double zig_f32;
1874#define zig_as_f32(fp, repr) fp2879#define zig_make_f32(fp, repr) fp
1875#elif LDBL_MANT_DIG == 242880#elif LDBL_MANT_DIG == 24
1876#define zig_bitSizeOf_c_longdouble 322881#define zig_bitSizeOf_c_longdouble 32
2882#ifndef ZIG_TARGET_ABI_MSVC
2883typedef zig_repr_f32 zig_repr_c_longdouble;
2884#endif
1877typedef long double zig_f32;2885typedef long double zig_f32;
1878#define zig_as_f32(fp, repr) fp##l2886#define zig_make_f32(fp, repr) fp##l
1879#elif FLT32_MANT_DIG == 242887#elif FLT32_MANT_DIG == 24
1880typedef _Float32 zig_f32;2888typedef _Float32 zig_f32;
1881#define zig_as_f32(fp, repr) fp##f322889#define zig_make_f32(fp, repr) fp##f32
1882#else2890#else
1883#undef zig_has_f322891#undef zig_has_f32
1884#define zig_has_f32 02892#define zig_has_f32 0
1885#define zig_repr_f32 i322893#define zig_bitSizeOf_repr_f32 32
1886typedef zig_i32 zig_f32;2894typedef int32_t zig_f32;
1887#define zig_as_f32(fp, repr) repr2895#define zig_make_f32(fp, repr) repr
1888#undef zig_as_special_f322896#undef zig_make_special_f32
1889#define zig_as_special_f32(sign, name, arg, repr) repr2897#define zig_make_special_f32(sign, name, arg, repr) repr
1890#undef zig_as_special_constant_f322898#undef zig_init_special_f32
1891#define zig_as_special_constant_f32(sign, name, arg, repr) repr2899#define zig_init_special_f32(sign, name, arg, repr) repr
1892#endif2900#endif
18932901
1894#define zig_has_f64 12902#define zig_has_f64 1
1895#define zig_bitSizeOf_f64 642903#define zig_bitSizeOf_f64 64
2904typedef int64_t zig_repr_f64;
1896#define zig_libc_name_f64(name) name2905#define zig_libc_name_f64(name) name
1897#if _MSC_VER2906#if _MSC_VER
1898#ifdef ZIG_TARGET_ABI_MSVC2907#ifdef ZIG_TARGET_ABI_MSVC
1899#define zig_bitSizeOf_c_longdouble 642908#define zig_bitSizeOf_c_longdouble 64
2909#ifndef ZIG_TARGET_ABI_MSVC
2910typedef zig_repr_f64 zig_repr_c_longdouble;
2911#endif
1900#endif2912#endif
1901#define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, )2913#define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, )
1902#else /* _MSC_VER */2914#else /* _MSC_VER */
1903#define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr)2915#define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr)
1904#endif /* _MSC_VER */2916#endif /* _MSC_VER */
1905#if FLT_MANT_DIG == 532917#if FLT_MANT_DIG == 53
1906typedef float zig_f64;2918typedef float zig_f64;
1907#define zig_as_f64(fp, repr) fp##f2919#define zig_make_f64(fp, repr) fp##f
1908#elif DBL_MANT_DIG == 532920#elif DBL_MANT_DIG == 53
1909typedef double zig_f64;2921typedef double zig_f64;
1910#define zig_as_f64(fp, repr) fp2922#define zig_make_f64(fp, repr) fp
1911#elif LDBL_MANT_DIG == 532923#elif LDBL_MANT_DIG == 53
1912#define zig_bitSizeOf_c_longdouble 642924#define zig_bitSizeOf_c_longdouble 64
2925#ifndef ZIG_TARGET_ABI_MSVC
2926typedef zig_repr_f64 zig_repr_c_longdouble;
2927#endif
1913typedef long double zig_f64;2928typedef long double zig_f64;
1914#define zig_as_f64(fp, repr) fp##l2929#define zig_make_f64(fp, repr) fp##l
1915#elif FLT64_MANT_DIG == 532930#elif FLT64_MANT_DIG == 53
1916typedef _Float64 zig_f64;2931typedef _Float64 zig_f64;
1917#define zig_as_f64(fp, repr) fp##f642932#define zig_make_f64(fp, repr) fp##f64
1918#elif FLT32X_MANT_DIG == 532933#elif FLT32X_MANT_DIG == 53
1919typedef _Float32x zig_f64;2934typedef _Float32x zig_f64;
1920#define zig_as_f64(fp, repr) fp##f32x2935#define zig_make_f64(fp, repr) fp##f32x
1921#else2936#else
1922#undef zig_has_f642937#undef zig_has_f64
1923#define zig_has_f64 02938#define zig_has_f64 0
1924#define zig_repr_f64 i642939#define zig_bitSizeOf_repr_f64 64
1925typedef zig_i64 zig_f64;2940typedef int64_t zig_f64;
1926#define zig_as_f64(fp, repr) repr2941#define zig_make_f64(fp, repr) repr
1927#undef zig_as_special_f642942#undef zig_make_special_f64
1928#define zig_as_special_f64(sign, name, arg, repr) repr2943#define zig_make_special_f64(sign, name, arg, repr) repr
1929#undef zig_as_special_constant_f642944#undef zig_init_special_f64
1930#define zig_as_special_constant_f64(sign, name, arg, repr) repr2945#define zig_init_special_f64(sign, name, arg, repr) repr
1931#endif2946#endif
19322947
1933#define zig_has_f80 12948#define zig_has_f80 1
1934#define zig_bitSizeOf_f80 802949#define zig_bitSizeOf_f80 80
2950typedef zig_i128 zig_repr_f80;
1935#define zig_libc_name_f80(name) __##name##x2951#define zig_libc_name_f80(name) __##name##x
1936#define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr)2952#define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr)
1937#if FLT_MANT_DIG == 642953#if FLT_MANT_DIG == 64
1938typedef float zig_f80;2954typedef float zig_f80;
1939#define zig_as_f80(fp, repr) fp##f2955#define zig_make_f80(fp, repr) fp##f
1940#elif DBL_MANT_DIG == 642956#elif DBL_MANT_DIG == 64
1941typedef double zig_f80;2957typedef double zig_f80;
1942#define zig_as_f80(fp, repr) fp2958#define zig_make_f80(fp, repr) fp
1943#elif LDBL_MANT_DIG == 642959#elif LDBL_MANT_DIG == 64
1944#define zig_bitSizeOf_c_longdouble 802960#define zig_bitSizeOf_c_longdouble 80
2961#ifndef ZIG_TARGET_ABI_MSVC
2962typedef zig_repr_f80 zig_repr_c_longdouble;
2963#endif
1945typedef long double zig_f80;2964typedef long double zig_f80;
1946#define zig_as_f80(fp, repr) fp##l2965#define zig_make_f80(fp, repr) fp##l
1947#elif FLT80_MANT_DIG == 642966#elif FLT80_MANT_DIG == 64
1948typedef _Float80 zig_f80;2967typedef _Float80 zig_f80;
1949#define zig_as_f80(fp, repr) fp##f802968#define zig_make_f80(fp, repr) fp##f80
1950#elif FLT64X_MANT_DIG == 642969#elif FLT64X_MANT_DIG == 64
1951typedef _Float64x zig_f80;2970typedef _Float64x zig_f80;
1952#define zig_as_f80(fp, repr) fp##f64x2971#define zig_make_f80(fp, repr) fp##f64x
1953#elif defined(__SIZEOF_FLOAT80__)2972#elif defined(__SIZEOF_FLOAT80__)
1954typedef __float80 zig_f80;2973typedef __float80 zig_f80;
1955#define zig_as_f80(fp, repr) fp##l2974#define zig_make_f80(fp, repr) fp##l
1956#else2975#else
1957#undef zig_has_f802976#undef zig_has_f80
1958#define zig_has_f80 02977#define zig_has_f80 0
1959#define zig_repr_f80 i1282978#define zig_bitSizeOf_repr_f80 128
1960typedef zig_i128 zig_f80;2979typedef zig_i128 zig_f80;
1961#define zig_as_f80(fp, repr) repr2980#define zig_make_f80(fp, repr) repr
1962#undef zig_as_special_f802981#undef zig_make_special_f80
1963#define zig_as_special_f80(sign, name, arg, repr) repr2982#define zig_make_special_f80(sign, name, arg, repr) repr
1964#undef zig_as_special_constant_f802983#undef zig_init_special_f80
1965#define zig_as_special_constant_f80(sign, name, arg, repr) repr2984#define zig_init_special_f80(sign, name, arg, repr) repr
1966#endif2985#endif
19672986
1968#define zig_has_f128 12987#define zig_has_f128 1
1969#define zig_bitSizeOf_f128 1282988#define zig_bitSizeOf_f128 128
2989typedef zig_i128 zig_repr_f128;
1970#define zig_libc_name_f128(name) name##q2990#define zig_libc_name_f128(name) name##q
1971#define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr)2991#define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr)
1972#if FLT_MANT_DIG == 1132992#if FLT_MANT_DIG == 113
1973typedef float zig_f128;2993typedef float zig_f128;
1974#define zig_as_f128(fp, repr) fp##f2994#define zig_make_f128(fp, repr) fp##f
1975#elif DBL_MANT_DIG == 1132995#elif DBL_MANT_DIG == 113
1976typedef double zig_f128;2996typedef double zig_f128;
1977#define zig_as_f128(fp, repr) fp2997#define zig_make_f128(fp, repr) fp
1978#elif LDBL_MANT_DIG == 1132998#elif LDBL_MANT_DIG == 113
1979#define zig_bitSizeOf_c_longdouble 1282999#define zig_bitSizeOf_c_longdouble 128
3000#ifndef ZIG_TARGET_ABI_MSVC
3001typedef zig_repr_f128 zig_repr_c_longdouble;
3002#endif
1980typedef long double zig_f128;3003typedef long double zig_f128;
1981#define zig_as_f128(fp, repr) fp##l3004#define zig_make_f128(fp, repr) fp##l
1982#elif FLT128_MANT_DIG == 1133005#elif FLT128_MANT_DIG == 113
1983typedef _Float128 zig_f128;3006typedef _Float128 zig_f128;
1984#define zig_as_f128(fp, repr) fp##f1283007#define zig_make_f128(fp, repr) fp##f128
1985#elif FLT64X_MANT_DIG == 1133008#elif FLT64X_MANT_DIG == 113
1986typedef _Float64x zig_f128;3009typedef _Float64x zig_f128;
1987#define zig_as_f128(fp, repr) fp##f64x3010#define zig_make_f128(fp, repr) fp##f64x
1988#elif defined(__SIZEOF_FLOAT128__)3011#elif defined(__SIZEOF_FLOAT128__)
1989typedef __float128 zig_f128;3012typedef __float128 zig_f128;
1990#define zig_as_f128(fp, repr) fp##q3013#define zig_make_f128(fp, repr) fp##q
1991#undef zig_as_special_f1283014#undef zig_make_special_f128
1992#define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg)3015#define zig_make_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg)
1993#else3016#else
1994#undef zig_has_f1283017#undef zig_has_f128
1995#define zig_has_f128 03018#define zig_has_f128 0
1996#define zig_repr_f128 i1283019#define zig_bitSizeOf_repr_f128 128
1997typedef zig_i128 zig_f128;3020typedef zig_i128 zig_f128;
1998#define zig_as_f128(fp, repr) repr3021#define zig_make_f128(fp, repr) repr
1999#undef zig_as_special_f1283022#undef zig_make_special_f128
2000#define zig_as_special_f128(sign, name, arg, repr) repr3023#define zig_make_special_f128(sign, name, arg, repr) repr
2001#undef zig_as_special_constant_f1283024#undef zig_init_special_f128
2002#define zig_as_special_constant_f128(sign, name, arg, repr) repr3025#define zig_init_special_f128(sign, name, arg, repr) repr
2003#endif3026#endif
20043027
2005#define zig_has_c_longdouble 1
2006
2007#ifdef ZIG_TARGET_ABI_MSVC
2008#define zig_libc_name_c_longdouble(name) name
2009#else
2010#define zig_libc_name_c_longdouble(name) name##l
2011#endif
2012
2013#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr)
2014#ifdef zig_bitSizeOf_c_longdouble3028#ifdef zig_bitSizeOf_c_longdouble
20153029
3030#define zig_has_c_longdouble 1
2016#ifdef ZIG_TARGET_ABI_MSVC3031#ifdef ZIG_TARGET_ABI_MSVC
2017typedef double zig_c_longdouble;
2018#undef zig_bitSizeOf_c_longdouble3032#undef zig_bitSizeOf_c_longdouble
2019#define zig_bitSizeOf_c_longdouble 643033#define zig_bitSizeOf_c_longdouble 64
2020#define zig_as_c_longdouble(fp, repr) fp3034typedef zig_f64 zig_c_longdouble;
3035typedef zig_repr_f64 zig_repr_c_longdouble;
2021#else3036#else
2022typedef long double zig_c_longdouble;3037typedef long double zig_c_longdouble;
2023#define zig_as_c_longdouble(fp, repr) fp##l
2024#endif3038#endif
20253039
2026#else /* zig_bitSizeOf_c_longdouble */3040#else /* zig_bitSizeOf_c_longdouble */
20273041
2028#undef zig_has_c_longdouble
2029#define zig_has_c_longdouble 03042#define zig_has_c_longdouble 0
2030#define zig_bitSizeOf_c_longdouble 803043#define zig_bitSizeOf_repr_c_longdouble 128
2031#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f803044typedef zig_f128 zig_c_longdouble;
2032#define zig_repr_c_longdouble i1283045typedef zig_repr_f128 zig_repr_c_longdouble;
2033typedef zig_i128 zig_c_longdouble;
2034#define zig_as_c_longdouble(fp, repr) repr
2035#undef zig_as_special_c_longdouble
2036#define zig_as_special_c_longdouble(sign, name, arg, repr) repr
2037#undef zig_as_special_constant_c_longdouble
2038#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr
20393046
2040#endif /* zig_bitSizeOf_c_longdouble */3047#endif /* zig_bitSizeOf_c_longdouble */
20413048
2042#if !zig_has_float_builtins3049#if !zig_has_float_builtins
2043#define zig_float_from_repr(Type, ReprType) \3050#define zig_float_from_repr(Type) \
2044 static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \3051 static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \
2045 return *((zig_##Type*)&repr); \3052 zig_##Type result; \
3053 memcpy(&result, &repr, sizeof(result)); \
3054 return result; \
2046 }3055 }
20473056
2048zig_float_from_repr(f16, u16)3057zig_float_from_repr(f16)
2049zig_float_from_repr(f32, u32)3058zig_float_from_repr(f32)
2050zig_float_from_repr(f64, u64)3059zig_float_from_repr(f64)
2051zig_float_from_repr(f80, u128)3060zig_float_from_repr(f80)
2052zig_float_from_repr(f128, u128)3061zig_float_from_repr(f128)
2053#if zig_bitSizeOf_c_longdouble == 80
2054zig_float_from_repr(c_longdouble, u128)
2055#else
2056#define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType)
2057zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble))
2058#endif
2059#endif3062#endif
20603063
2061#define zig_cast_f16 (zig_f16)3064#define zig_cast_f16 (zig_f16)
...@@ -2064,41 +3067,42 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo...@@ -2064,41 +3067,42 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo
20643067
2065#if _MSC_VER && !zig_has_f1283068#if _MSC_VER && !zig_has_f128
2066#define zig_cast_f803069#define zig_cast_f80
2067#define zig_cast_c_longdouble
2068#define zig_cast_f1283070#define zig_cast_f128
2069#else3071#else
2070#define zig_cast_f80 (zig_f80)3072#define zig_cast_f80 (zig_f80)
2071#define zig_cast_c_longdouble (zig_c_longdouble)
2072#define zig_cast_f128 (zig_f128)3073#define zig_cast_f128 (zig_f128)
2073#endif3074#endif
20743075
2075#define zig_convert_builtin(ResType, operation, ArgType, version) \3076#define zig_convert_builtin(ResType, operation, ArgType, version) \
2076 zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \3077 zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \
2077 zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType);3078 zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType);
2078zig_convert_builtin(f16, trunc, f32, 2)3079zig_convert_builtin(zig_f16, trunc, zig_f32, 2)
2079zig_convert_builtin(f16, trunc, f64, 2)3080zig_convert_builtin(zig_f16, trunc, zig_f64, 2)
2080zig_convert_builtin(f16, trunc, f80, 2)3081zig_convert_builtin(zig_f16, trunc, zig_f80, 2)
2081zig_convert_builtin(f16, trunc, f128, 2)3082zig_convert_builtin(zig_f16, trunc, zig_f128, 2)
2082zig_convert_builtin(f32, extend, f16, 2)3083zig_convert_builtin(zig_f32, extend, zig_f16, 2)
2083zig_convert_builtin(f32, trunc, f64, 2)3084zig_convert_builtin(zig_f32, trunc, zig_f64, 2)
2084zig_convert_builtin(f32, trunc, f80, 2)3085zig_convert_builtin(zig_f32, trunc, zig_f80, 2)
2085zig_convert_builtin(f32, trunc, f128, 2)3086zig_convert_builtin(zig_f32, trunc, zig_f128, 2)
2086zig_convert_builtin(f64, extend, f16, 2)3087zig_convert_builtin(zig_f64, extend, zig_f16, 2)
2087zig_convert_builtin(f64, extend, f32, 2)3088zig_convert_builtin(zig_f64, extend, zig_f32, 2)
2088zig_convert_builtin(f64, trunc, f80, 2)3089zig_convert_builtin(zig_f64, trunc, zig_f80, 2)
2089zig_convert_builtin(f64, trunc, f128, 2)3090zig_convert_builtin(zig_f64, trunc, zig_f128, 2)
2090zig_convert_builtin(f80, extend, f16, 2)3091zig_convert_builtin(zig_f80, extend, zig_f16, 2)
2091zig_convert_builtin(f80, extend, f32, 2)3092zig_convert_builtin(zig_f80, extend, zig_f32, 2)
2092zig_convert_builtin(f80, extend, f64, 2)3093zig_convert_builtin(zig_f80, extend, zig_f64, 2)
2093zig_convert_builtin(f80, trunc, f128, 2)3094zig_convert_builtin(zig_f80, trunc, zig_f128, 2)
2094zig_convert_builtin(f128, extend, f16, 2)3095zig_convert_builtin(zig_f128, extend, zig_f16, 2)
2095zig_convert_builtin(f128, extend, f32, 2)3096zig_convert_builtin(zig_f128, extend, zig_f32, 2)
2096zig_convert_builtin(f128, extend, f64, 2)3097zig_convert_builtin(zig_f128, extend, zig_f64, 2)
2097zig_convert_builtin(f128, extend, f80, 2)3098zig_convert_builtin(zig_f128, extend, zig_f80, 2)
20983099
2099#define zig_float_negate_builtin_0(Type) \3100#define zig_float_negate_builtin_0(Type) \
2100 static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \3101 static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \
2101 return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \3102 return zig_expand_concat(zig_xor_i, zig_bitSizeOf_repr_##Type)( \
3103 arg, \
3104 zig_minInt_i(zig_bitSizeOf_repr_##Type, zig_bitSizeOf_##Type) \
3105 ); \
2102 }3106 }
2103#define zig_float_negate_builtin_1(Type) \3107#define zig_float_negate_builtin_1(Type) \
2104 static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \3108 static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \
...@@ -2106,28 +3110,28 @@ zig_convert_builtin(f128, extend, f80, 2)...@@ -2106,28 +3110,28 @@ zig_convert_builtin(f128, extend, f80, 2)
2106 }3110 }
21073111
2108#define zig_float_less_builtin_0(Type, operation) \3112#define zig_float_less_builtin_0(Type, operation) \
2109 zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \3113 zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \
2110 zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \3114 zig_compiler_rt_abbrev_zig_##Type), 2)(zig_##Type, zig_##Type); \
2111 static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \3115 static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
2112 return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \3116 return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 2)(lhs, rhs); \
2113 }3117 }
2114#define zig_float_less_builtin_1(Type, operation) \3118#define zig_float_less_builtin_1(Type, operation) \
2115 static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \3119 static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
2116 return (!(lhs <= rhs) - (lhs < rhs)); \3120 return (!(lhs <= rhs) - (lhs < rhs)); \
2117 }3121 }
21183122
2119#define zig_float_greater_builtin_0(Type, operation) \3123#define zig_float_greater_builtin_0(Type, operation) \
2120 zig_float_less_builtin_0(Type, operation)3124 zig_float_less_builtin_0(Type, operation)
2121#define zig_float_greater_builtin_1(Type, operation) \3125#define zig_float_greater_builtin_1(Type, operation) \
2122 static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \3126 static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
2123 return ((lhs > rhs) - !(lhs >= rhs)); \3127 return ((lhs > rhs) - !(lhs >= rhs)); \
2124 }3128 }
21253129
2126#define zig_float_binary_builtin_0(Type, operation, operator) \3130#define zig_float_binary_builtin_0(Type, operation, operator) \
2127 zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \3131 zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \
2128 zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \3132 zig_compiler_rt_abbrev_zig_##Type), 3)(zig_##Type, zig_##Type); \
2129 static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \3133 static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
2130 return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \3134 return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 3)(lhs, rhs); \
2131 }3135 }
2132#define zig_float_binary_builtin_1(Type, operation, operator) \3136#define zig_float_binary_builtin_1(Type, operation, operator) \
2133 static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \3137 static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
...@@ -2135,18 +3139,18 @@ zig_convert_builtin(f128, extend, f80, 2)...@@ -2135,18 +3139,18 @@ zig_convert_builtin(f128, extend, f80, 2)
2135 }3139 }
21363140
2137#define zig_float_builtins(Type) \3141#define zig_float_builtins(Type) \
2138 zig_convert_builtin(i32, fix, Type, ) \3142 zig_convert_builtin( int32_t, fix, zig_##Type, ) \
2139 zig_convert_builtin(u32, fixuns, Type, ) \3143 zig_convert_builtin(uint32_t, fixuns, zig_##Type, ) \
2140 zig_convert_builtin(i64, fix, Type, ) \3144 zig_convert_builtin( int64_t, fix, zig_##Type, ) \
2141 zig_convert_builtin(u64, fixuns, Type, ) \3145 zig_convert_builtin(uint64_t, fixuns, zig_##Type, ) \
2142 zig_convert_builtin(i128, fix, Type, ) \3146 zig_convert_builtin(zig_i128, fix, zig_##Type, ) \
2143 zig_convert_builtin(u128, fixuns, Type, ) \3147 zig_convert_builtin(zig_u128, fixuns, zig_##Type, ) \
2144 zig_convert_builtin(Type, float, i32, ) \3148 zig_convert_builtin(zig_##Type, float, int32_t, ) \
2145 zig_convert_builtin(Type, floatun, u32, ) \3149 zig_convert_builtin(zig_##Type, floatun, uint32_t, ) \
2146 zig_convert_builtin(Type, float, i64, ) \3150 zig_convert_builtin(zig_##Type, float, int64_t, ) \
2147 zig_convert_builtin(Type, floatun, u64, ) \3151 zig_convert_builtin(zig_##Type, floatun, uint64_t, ) \
2148 zig_convert_builtin(Type, float, i128, ) \3152 zig_convert_builtin(zig_##Type, float, zig_i128, ) \
2149 zig_convert_builtin(Type, floatun, u128, ) \3153 zig_convert_builtin(zig_##Type, floatun, zig_u128, ) \
2150 zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \3154 zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \
2151 zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \3155 zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \
2152 zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \3156 zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \
...@@ -2194,155 +3198,162 @@ zig_float_builtins(f32)...@@ -2194,155 +3198,162 @@ zig_float_builtins(f32)
2194zig_float_builtins(f64)3198zig_float_builtins(f64)
2195zig_float_builtins(f80)3199zig_float_builtins(f80)
2196zig_float_builtins(f128)3200zig_float_builtins(f128)
2197zig_float_builtins(c_longdouble)
21983201
2199#if _MSC_VER && (_M_IX86 || _M_X64)3202#if _MSC_VER && (_M_IX86 || _M_X64)
22003203
2201// TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x643204// TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64
22023205
2203#define zig_msvc_atomics(Type, suffix) \3206#define zig_msvc_atomics(ZigType, Type, suffix) \
2204 static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \3207 static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \
2205 zig_##Type comparand = *expected; \3208 Type comparand = *expected; \
2206 zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \3209 Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \
2207 bool exchanged = initial == comparand; \3210 bool exchanged = initial == comparand; \
2208 if (!exchanged) { \3211 if (!exchanged) { \
2209 *expected = initial; \3212 *expected = initial; \
2210 } \3213 } \
2211 return exchanged; \3214 return exchanged; \
2212 } \3215 } \
2213 static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \3216 static inline Type zig_msvc_atomicrmw_xchg_##ZigType(Type volatile* obj, Type value) { \
2214 return _InterlockedExchange##suffix(obj, value); \3217 return _InterlockedExchange##suffix(obj, value); \
2215 } \3218 } \
2216 static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \3219 static inline Type zig_msvc_atomicrmw_add_##ZigType(Type volatile* obj, Type value) { \
2217 return _InterlockedExchangeAdd##suffix(obj, value); \3220 return _InterlockedExchangeAdd##suffix(obj, value); \
2218 } \3221 } \
2219 static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \3222 static inline Type zig_msvc_atomicrmw_sub_##ZigType(Type volatile* obj, Type value) { \
2220 bool success = false; \3223 bool success = false; \
2221 zig_##Type new; \3224 Type new; \
2222 zig_##Type prev; \3225 Type prev; \
2223 while (!success) { \3226 while (!success) { \
2224 prev = *obj; \3227 prev = *obj; \
2225 new = prev - value; \3228 new = prev - value; \
2226 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \3229 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
2227 } \3230 } \
2228 return prev; \3231 return prev; \
2229 } \3232 } \
2230 static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \3233 static inline Type zig_msvc_atomicrmw_or_##ZigType(Type volatile* obj, Type value) { \
2231 return _InterlockedOr##suffix(obj, value); \3234 return _InterlockedOr##suffix(obj, value); \
2232 } \3235 } \
2233 static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \3236 static inline Type zig_msvc_atomicrmw_xor_##ZigType(Type volatile* obj, Type value) { \
2234 return _InterlockedXor##suffix(obj, value); \3237 return _InterlockedXor##suffix(obj, value); \
2235 } \3238 } \
2236 static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \3239 static inline Type zig_msvc_atomicrmw_and_##ZigType(Type volatile* obj, Type value) { \
2237 return _InterlockedAnd##suffix(obj, value); \3240 return _InterlockedAnd##suffix(obj, value); \
2238 } \3241 } \
2239 static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \3242 static inline Type zig_msvc_atomicrmw_nand_##ZigType(Type volatile* obj, Type value) { \
2240 bool success = false; \3243 bool success = false; \
2241 zig_##Type new; \3244 Type new; \
2242 zig_##Type prev; \3245 Type prev; \
2243 while (!success) { \3246 while (!success) { \
2244 prev = *obj; \3247 prev = *obj; \
2245 new = ~(prev & value); \3248 new = ~(prev & value); \
2246 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \3249 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
2247 } \3250 } \
2248 return prev; \3251 return prev; \
2249 } \3252 } \
2250 static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \3253 static inline Type zig_msvc_atomicrmw_min_##ZigType(Type volatile* obj, Type value) { \
2251 bool success = false; \3254 bool success = false; \
2252 zig_##Type new; \3255 Type new; \
2253 zig_##Type prev; \3256 Type prev; \
2254 while (!success) { \3257 while (!success) { \
2255 prev = *obj; \3258 prev = *obj; \
2256 new = value < prev ? value : prev; \3259 new = value < prev ? value : prev; \
2257 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \3260 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
2258 } \3261 } \
2259 return prev; \3262 return prev; \
2260 } \3263 } \
2261 static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \3264 static inline Type zig_msvc_atomicrmw_max_##ZigType(Type volatile* obj, Type value) { \
2262 bool success = false; \3265 bool success = false; \
2263 zig_##Type new; \3266 Type new; \
2264 zig_##Type prev; \3267 Type prev; \
2265 while (!success) { \3268 while (!success) { \
2266 prev = *obj; \3269 prev = *obj; \
2267 new = value > prev ? value : prev; \3270 new = value > prev ? value : prev; \
2268 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \3271 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
2269 } \3272 } \
2270 return prev; \3273 return prev; \
2271 } \3274 } \
2272 static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \3275 static inline void zig_msvc_atomic_store_##ZigType(Type volatile* obj, Type value) { \
2273 _InterlockedExchange##suffix(obj, value); \3276 _InterlockedExchange##suffix(obj, value); \
2274 } \3277 } \
2275 static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \3278 static inline Type zig_msvc_atomic_load_##ZigType(Type volatile* obj) { \
2276 return _InterlockedOr##suffix(obj, 0); \3279 return _InterlockedOr##suffix(obj, 0); \
2277 }3280 }
22783281
2279zig_msvc_atomics(u8, 8)3282zig_msvc_atomics( u8, uint8_t, 8)
2280zig_msvc_atomics(i8, 8)3283zig_msvc_atomics( i8, int8_t, 8)
2281zig_msvc_atomics(u16, 16)3284zig_msvc_atomics(u16, uint16_t, 16)
2282zig_msvc_atomics(i16, 16)3285zig_msvc_atomics(i16, int16_t, 16)
2283zig_msvc_atomics(u32, )3286zig_msvc_atomics(u32, uint32_t, )
2284zig_msvc_atomics(i32, )3287zig_msvc_atomics(i32, int32_t, )
22853288
2286#if _M_X643289#if _M_X64
2287zig_msvc_atomics(u64, 64)3290zig_msvc_atomics(u64, uint64_t, 64)
2288zig_msvc_atomics(i64, 64)3291zig_msvc_atomics(i64, int64_t, 64)
2289#endif3292#endif
22903293
2291#define zig_msvc_flt_atomics(Type, ReprType, suffix) \3294#define zig_msvc_flt_atomics(Type, ReprType, suffix) \
2292 static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \3295 static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \
2293 zig_##ReprType comparand = *((zig_##ReprType*)expected); \3296 ReprType exchange; \
2294 zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \3297 ReprType comparand; \
2295 bool exchanged = initial == comparand; \3298 ReprType initial; \
2296 if (!exchanged) { \3299 bool success; \
2297 *expected = *((zig_##Type*)&initial); \3300 memcpy(&comparand, expected, sizeof(comparand)); \
2298 } \3301 memcpy(&exchange, &desired, sizeof(exchange)); \
2299 return exchanged; \3302 initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \
3303 success = initial == comparand; \
3304 if (!success) memcpy(expected, &initial, sizeof(*expected)); \
3305 return success; \
2300 } \3306 } \
2301 static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \3307 static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \
2302 zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \3308 ReprType repr; \
2303 return *((zig_##Type*)&initial); \3309 ReprType initial; \
3310 zig_##Type result; \
3311 memcpy(&repr, &value, sizeof(repr)); \
3312 initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \
3313 memcpy(&result, &initial, sizeof(result)); \
3314 return result; \
2304 } \3315 } \
2305 static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \3316 static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \
2306 bool success = false; \3317 ReprType repr; \
2307 zig_##ReprType new; \3318 zig_##Type expected; \
2308 zig_##Type prev; \3319 zig_##Type desired; \
2309 while (!success) { \3320 repr = *(ReprType volatile*)obj; \
2310 prev = *obj; \3321 memcpy(&expected, &repr, sizeof(expected)); \
2311 new = prev + value; \3322 do { \
2312 success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \3323 desired = expected + value; \
2313 } \3324 } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \
2314 return prev; \3325 return expected; \
2315 } \3326 } \
2316 static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \3327 static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \
2317 bool success = false; \3328 ReprType repr; \
2318 zig_##ReprType new; \3329 zig_##Type expected; \
2319 zig_##Type prev; \3330 zig_##Type desired; \
2320 while (!success) { \3331 repr = *(ReprType volatile*)obj; \
2321 prev = *obj; \3332 memcpy(&expected, &repr, sizeof(expected)); \
2322 new = prev - value; \3333 do { \
2323 success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \3334 desired = expected - value; \
2324 } \3335 } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \
2325 return prev; \3336 return expected; \
2326 }3337 }
23273338
2328zig_msvc_flt_atomics(f32, u32, )3339zig_msvc_flt_atomics(f32, uint32_t, )
2329#if _M_X643340#if _M_X64
2330zig_msvc_flt_atomics(f64, u64, 64)3341zig_msvc_flt_atomics(f64, uint64_t, 64)
2331#endif3342#endif
23323343
2333#if _M_IX863344#if _M_IX86
2334static inline void zig_msvc_atomic_barrier() {3345static inline void zig_msvc_atomic_barrier() {
2335 zig_i32 barrier;3346 int32_t barrier;
2336 __asm {3347 __asm {
2337 xchg barrier, eax3348 xchg barrier, eax
2338 }3349 }
2339}3350}
23403351
2341static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) {3352static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, void* arg) {
2342 return _InterlockedExchangePointer(obj, arg);3353 return _InterlockedExchangePointer(obj, arg);
2343}3354}
23443355
2345static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) {3356static inline void zig_msvc_atomic_store_p32(void** obj, void* arg) {
2346 _InterlockedExchangePointer(obj, arg);3357 _InterlockedExchangePointer(obj, arg);
2347}3358}
23483359
...@@ -2360,11 +3371,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir...@@ -2360,11 +3371,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir
2360 return exchanged;3371 return exchanged;
2361}3372}
2362#else /* _M_IX86 */3373#else /* _M_IX86 */
2363static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) {3374static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, void* arg) {
2364 return _InterlockedExchangePointer(obj, arg);3375 return _InterlockedExchangePointer(obj, arg);
2365}3376}
23663377
2367static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) {3378static inline void zig_msvc_atomic_store_p64(void** obj, void* arg) {
2368 _InterlockedExchangePointer(obj, arg);3379 _InterlockedExchangePointer(obj, arg);
2369}3380}
23703381
...@@ -2383,11 +3394,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir...@@ -2383,11 +3394,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir
2383}3394}
23843395
2385static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) {3396static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) {
2386 return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected);3397 return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (int64_t*)expected);
2387}3398}
23883399
2389static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) {3400static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) {
2390 return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected);3401 return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (uint64_t*)expected);
2391}3402}
23923403
2393#define zig_msvc_atomics_128xchg(Type) \3404#define zig_msvc_atomics_128xchg(Type) \
...@@ -2429,7 +3440,7 @@ zig_msvc_atomics_128op(u128, max)...@@ -2429,7 +3440,7 @@ zig_msvc_atomics_128op(u128, max)
24293440
2430#endif /* _MSC_VER && (_M_IX86 || _M_X64) */3441#endif /* _MSC_VER && (_M_IX86 || _M_X64) */
24313442
2432/* ========================= Special Case Intrinsics ========================= */3443/* ======================== Special Case Intrinsics ========================= */
24333444
2434#if (_MSC_VER && _M_X64) || defined(__x86_64__)3445#if (_MSC_VER && _M_X64) || defined(__x86_64__)
24353446
...@@ -2459,8 +3470,8 @@ static inline void* zig_x86_windows_teb(void) {...@@ -2459,8 +3470,8 @@ static inline void* zig_x86_windows_teb(void) {
24593470
2460#if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__)3471#if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__)
24613472
2462static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) {3473static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) {
2463 zig_u32 cpu_info[4];3474 uint32_t cpu_info[4];
2464#if _MSC_VER3475#if _MSC_VER
2465 __cpuidex(cpu_info, leaf_id, subid);3476 __cpuidex(cpu_info, leaf_id, subid);
2466#else3477#else
...@@ -2472,12 +3483,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z...@@ -2472,12 +3483,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z
2472 *edx = cpu_info[3];3483 *edx = cpu_info[3];
2473}3484}
24743485
2475static inline zig_u32 zig_x86_get_xcr0(void) {3486static inline uint32_t zig_x86_get_xcr0(void) {
2476#if _MSC_VER3487#if _MSC_VER
2477 return (zig_u32)_xgetbv(0);3488 return (uint32_t)_xgetbv(0);
2478#else3489#else
2479 zig_u32 eax;3490 uint32_t eax;
2480 zig_u32 edx;3491 uint32_t edx;
2481 __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));3492 __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
2482 return eax;3493 return eax;
2483#endif3494#endif
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior.zig+1
...@@ -141,6 +141,7 @@ test {...@@ -141,6 +141,7 @@ test {
141 _ = @import("behavior/bugs/13664.zig");141 _ = @import("behavior/bugs/13664.zig");
142 _ = @import("behavior/bugs/13714.zig");142 _ = @import("behavior/bugs/13714.zig");
143 _ = @import("behavior/bugs/13785.zig");143 _ = @import("behavior/bugs/13785.zig");
144 _ = @import("behavior/bugs/14854.zig");
144 _ = @import("behavior/byteswap.zig");145 _ = @import("behavior/byteswap.zig");
145 _ = @import("behavior/byval_arg_var.zig");146 _ = @import("behavior/byval_arg_var.zig");
146 _ = @import("behavior/call.zig");147 _ = @import("behavior/call.zig");
test/behavior/align.zig-1
...@@ -568,7 +568,6 @@ fn overaligned_fn() align(0x1000) i32 {...@@ -568,7 +568,6 @@ fn overaligned_fn() align(0x1000) i32 {
568}568}
569569
570test "comptime alloc alignment" {570test "comptime alloc alignment" {
571 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
572 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO571 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
573 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO572 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
574 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO573 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
test/behavior/array.zig+18-11
...@@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize {...@@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize {
48test "array concat with undefined" {48test "array concat with undefined" {
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5050
51 {51 const S = struct {
52 var array = "hello".* ++ @as([5]u8, undefined);52 fn doTheTest() !void {
53 array[5..10].* = "world".*;53 {
54 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));54 var array = "hello".* ++ @as([5]u8, undefined);
55 }55 array[5..10].* = "world".*;
56 {56 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
57 var array = @as([5]u8, undefined) ++ "world".*;57 }
58 array[0..5].* = "hello".*;58 {
59 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));59 var array = @as([5]u8, undefined) ++ "world".*;
60 }60 array[0..5].* = "hello".*;
61 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
62 }
63 }
64 };
65
66 try S.doTheTest();
67 comptime try S.doTheTest();
61}68}
6269
63test "array concat with tuple" {70test "array concat with tuple" {
...@@ -77,6 +84,7 @@ test "array concat with tuple" {...@@ -77,6 +84,7 @@ test "array concat with tuple" {
77}84}
7885
79test "array init with concat" {86test "array init with concat" {
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
80 const a = 'a';88 const a = 'a';
81 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };89 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };
82 try expect(std.mem.eql(u8, &i, "abcd"));90 try expect(std.mem.eql(u8, &i, "abcd"));
...@@ -584,7 +592,6 @@ test "type coercion of anon struct literal to array" {...@@ -584,7 +592,6 @@ test "type coercion of anon struct literal to array" {
584test "type coercion of pointer to anon struct literal to pointer to array" {592test "type coercion of pointer to anon struct literal to pointer to array" {
585 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO593 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
586 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
587 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
588 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO595 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
589596
590 const S = struct {597 const S = struct {
test/behavior/bit_shifting.zig-1
...@@ -63,7 +63,6 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt...@@ -63,7 +63,6 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt
63test "sharded table" {63test "sharded table" {
64 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;64 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;65 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
66 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
67 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO66 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
6867
69 // realistic 16-way sharding68 // realistic 16-way sharding
test/behavior/bitcast.zig-3
...@@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" {...@@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" {
3434
35test "@bitCast iX -> uX exotic integers" {35test "@bitCast iX -> uX exotic integers" {
36 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;36 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
37 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
38 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;37 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;38 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;39 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -81,7 +80,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe...@@ -81,7 +80,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe
8180
82test "bitcast uX to bytes" {81test "bitcast uX to bytes" {
83 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;82 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
85 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;83 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
86 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;84 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;85 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -368,7 +366,6 @@ test "comptime @bitCast packed struct to int and back" {...@@ -368,7 +366,6 @@ test "comptime @bitCast packed struct to int and back" {
368}366}
369367
370test "comptime bitcast with fields following f80" {368test "comptime bitcast with fields following f80" {
371 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
372 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;369 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
373 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;370 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
374 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;371 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
test/behavior/bitreverse.zig-4
...@@ -96,7 +96,6 @@ fn vector8() !void {...@@ -96,7 +96,6 @@ fn vector8() !void {
9696
97test "bitReverse vectors u8" {97test "bitReverse vectors u8" {
98 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;98 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
100 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;99 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
101 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;100 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
102 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -115,7 +114,6 @@ fn vector16() !void {...@@ -115,7 +114,6 @@ fn vector16() !void {
115114
116test "bitReverse vectors u16" {115test "bitReverse vectors u16" {
117 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;116 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
118 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
119 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;117 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
120 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;118 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
121 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;119 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -134,7 +132,6 @@ fn vector24() !void {...@@ -134,7 +132,6 @@ fn vector24() !void {
134132
135test "bitReverse vectors u24" {133test "bitReverse vectors u24" {
136 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;134 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
137 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
138 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;135 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
139 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;136 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
140 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;137 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -153,7 +150,6 @@ fn vector0() !void {...@@ -153,7 +150,6 @@ fn vector0() !void {
153150
154test "bitReverse vectors u0" {151test "bitReverse vectors u0" {
155 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;152 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
156 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
157153
158 comptime try vector0();154 comptime try vector0();
159 try vector0();155 try vector0();
test/behavior/bugs/10147.zig-1
...@@ -6,7 +6,6 @@ test "test calling @clz on both vector and scalar inputs" {...@@ -6,7 +6,6 @@ test "test calling @clz on both vector and scalar inputs" {
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1110
12 var x: u32 = 0x1;11 var x: u32 = 0x1;
test/behavior/bugs/1076.zig-1
...@@ -4,7 +4,6 @@ const mem = std.mem;...@@ -4,7 +4,6 @@ const mem = std.mem;
4const expect = std.testing.expect;4const expect = std.testing.expect;
55
6test "comptime code should not modify constant data" {6test "comptime code should not modify constant data" {
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
109
test/behavior/bugs/11046.zig-1
...@@ -10,7 +10,6 @@ fn bar() !void {...@@ -10,7 +10,6 @@ fn bar() !void {
10}10}
1111
12test "fixed" {12test "fixed" {
13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO13 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO15 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/bugs/11787.zig-1
...@@ -4,7 +4,6 @@ const testing = std.testing;...@@ -4,7 +4,6 @@ const testing = std.testing;
44
5test "slicing zero length array field of struct" {5test "slicing zero length array field of struct" {
6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/12000.zig-1
...@@ -7,7 +7,6 @@ const T = struct {...@@ -7,7 +7,6 @@ const T = struct {
77
8test {8test {
9 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/12119.zig-1
...@@ -6,7 +6,6 @@ const u32x8 = @Vector(8, u32);...@@ -6,7 +6,6 @@ const u32x8 = @Vector(8, u32);
66
7test {7test {
8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/12169.zig-1
...@@ -3,7 +3,6 @@ const builtin = @import("builtin");...@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33
4test {4test {
5 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO5 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/12890.zig-1
...@@ -10,7 +10,6 @@ fn a(b: []u3, c: u3) void {...@@ -10,7 +10,6 @@ fn a(b: []u3, c: u3) void {
10}10}
11test {11test {
12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO13 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1514
16 var arr: [8]u3 = undefined;15 var arr: [8]u3 = undefined;
test/behavior/bugs/13113.zig-1
...@@ -8,7 +8,6 @@ const Foo = extern struct {...@@ -8,7 +8,6 @@ const Foo = extern struct {
88
9test {9test {
10 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/1421.zig-1
...@@ -9,7 +9,6 @@ const S = struct {...@@ -9,7 +9,6 @@ const S = struct {
9};9};
1010
11test "functions with return type required to be comptime are generic" {11test "functions with return type required to be comptime are generic" {
12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
13 const ti = S.method();12 const ti = S.method();
14 try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct);13 try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct);
15}14}
test/behavior/bugs/14854.zig created+13
...@@ -0,0 +1,13 @@
1const testing = @import("std").testing;
2
3test {
4 try testing.expect(getGeneric(u8, getU8) == 123);
5}
6
7fn getU8() callconv(.C) u8 {
8 return 123;
9}
10
11fn getGeneric(comptime T: type, supplier: fn () callconv(.C) T) T {
12 return supplier();
13}
test/behavior/bugs/1607.zig-1
...@@ -13,7 +13,6 @@ fn checkAddress(s: []const u8) !void {...@@ -13,7 +13,6 @@ fn checkAddress(s: []const u8) !void {
13test "slices pointing at the same address as global array." {13test "slices pointing at the same address as global array." {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
18 try checkAddress(&a);17 try checkAddress(&a);
19 comptime try checkAddress(&a);18 comptime try checkAddress(&a);
test/behavior/bugs/2622.zig-1
...@@ -4,7 +4,6 @@ var buf: []u8 = undefined;...@@ -4,7 +4,6 @@ var buf: []u8 = undefined;
44
5test "reslice of undefined global var slice" {5test "reslice of undefined global var slice" {
6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/2727.zig-1
...@@ -6,7 +6,6 @@ fn t() bool {...@@ -6,7 +6,6 @@ fn t() bool {
66
7test {7test {
8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/3742.zig-1
...@@ -37,7 +37,6 @@ pub const ArgSerializer = struct {...@@ -37,7 +37,6 @@ pub const ArgSerializer = struct {
37test "fixed" {37test "fixed" {
38 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;38 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
41 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO40 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
42 if (builtin.zig_backend == .stage2_llvm and41 if (builtin.zig_backend == .stage2_llvm and
43 builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest;42 builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest;
test/behavior/bugs/394.zig-1
...@@ -11,7 +11,6 @@ const expect = @import("std").testing.expect;...@@ -11,7 +11,6 @@ const expect = @import("std").testing.expect;
11const builtin = @import("builtin");11const builtin = @import("builtin");
1212
13test "fixed" {13test "fixed" {
14 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
15 const x = S{14 const x = S{
16 .x = 3,15 .x = 3,
17 .y = E{ .B = 1 },16 .y = E{ .B = 1 },
test/behavior/bugs/421.zig-1
...@@ -2,7 +2,6 @@ const builtin = @import("builtin");...@@ -2,7 +2,6 @@ const builtin = @import("builtin");
2const expect = @import("std").testing.expect;2const expect = @import("std").testing.expect;
33
4test "bitCast to array" {4test "bitCast to array" {
5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/bugs/5398.zig-1
...@@ -21,7 +21,6 @@ var renderable: Renderable = undefined;...@@ -21,7 +21,6 @@ var renderable: Renderable = undefined;
21test "assignment of field with padding" {21test "assignment of field with padding" {
22 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;22 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;23 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
25 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO24 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2625
27 renderable = Renderable{26 renderable = Renderable{
test/behavior/bugs/5487.zig-1
...@@ -12,7 +12,6 @@ pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_ty...@@ -12,7 +12,6 @@ pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_ty
12test "crash" {12test "crash" {
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO15 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
17 _ = io.multiWriter(.{writer()});16 _ = io.multiWriter(.{writer()});
18}17}
test/behavior/bugs/656.zig-1
...@@ -13,7 +13,6 @@ const Value = struct {...@@ -13,7 +13,6 @@ const Value = struct {
13test "optional if after an if in a switch prong of a switch with 2 prongs in an else" {13test "optional if after an if in a switch prong of a switch with 2 prongs in an else" {
14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
18 try foo(false, true);17 try foo(false, true);
19}18}
test/behavior/bugs/6947.zig-1
...@@ -6,7 +6,6 @@ fn destroy(ptr: *void) void {...@@ -6,7 +6,6 @@ fn destroy(ptr: *void) void {
66
7test {7test {
8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/builtin_functions_returning_void_or_noreturn.zig created+32
...@@ -0,0 +1,32 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4
5var x: u8 = 1;
6
7// This excludes builtin functions that return void or noreturn that cannot be tested.
8test {
9 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
15 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
16
17 var val: u8 = undefined;
18 try testing.expectEqual({}, @atomicStore(u8, &val, 0, .Unordered));
19 try testing.expectEqual(void, @TypeOf(@breakpoint()));
20 try testing.expectEqual({}, @export(x, .{ .name = "x" }));
21 try testing.expectEqual({}, @fence(.Acquire));
22 try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1), @intToPtr([*]u8, 1), 0));
23 try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1), undefined, 0));
24 try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));
25 try testing.expectEqual({}, @prefetch(&val, .{}));
26 try testing.expectEqual({}, @setAlignStack(16));
27 try testing.expectEqual({}, @setCold(true));
28 try testing.expectEqual({}, @setEvalBranchQuota(0));
29 try testing.expectEqual({}, @setFloatMode(.Optimized));
30 try testing.expectEqual({}, @setRuntimeSafety(true));
31 try testing.expectEqual(noreturn, @TypeOf(if (true) @trap() else {}));
32}
test/behavior/byteswap.zig-4
...@@ -62,7 +62,6 @@ fn vector8() !void {...@@ -62,7 +62,6 @@ fn vector8() !void {
6262
63test "@byteSwap vectors u8" {63test "@byteSwap vectors u8" {
64 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;64 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
66 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;65 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
67 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
68 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;67 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -81,7 +80,6 @@ fn vector16() !void {...@@ -81,7 +80,6 @@ fn vector16() !void {
8180
82test "@byteSwap vectors u16" {81test "@byteSwap vectors u16" {
83 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;82 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
85 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;83 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
86 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;84 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;85 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -100,7 +98,6 @@ fn vector24() !void {...@@ -100,7 +98,6 @@ fn vector24() !void {
10098
101test "@byteSwap vectors u24" {99test "@byteSwap vectors u24" {
102 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;100 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
104 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;101 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
105 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
106 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -119,7 +116,6 @@ fn vector0() !void {...@@ -119,7 +116,6 @@ fn vector0() !void {
119116
120test "@byteSwap vectors u0" {117test "@byteSwap vectors u0" {
121 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;118 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
122 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
123119
124 comptime try vector0();120 comptime try vector0();
125 try vector0();121 try vector0();
test/behavior/byval_arg_var.zig-1
...@@ -4,7 +4,6 @@ const builtin = @import("builtin");...@@ -4,7 +4,6 @@ const builtin = @import("builtin");
4var result: []const u8 = "wrong";4var result: []const u8 = "wrong";
55
6test "pass string literal byvalue to a generic var param" {6test "pass string literal byvalue to a generic var param" {
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
98
10 start();9 start();
test/behavior/call.zig-2
...@@ -21,7 +21,6 @@ test "super basic invocations" {...@@ -21,7 +21,6 @@ test "super basic invocations" {
21test "basic invocations" {21test "basic invocations" {
22 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO22 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO24 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO26 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -56,7 +55,6 @@ test "basic invocations" {...@@ -56,7 +55,6 @@ test "basic invocations" {
56test "tuple parameters" {55test "tuple parameters" {
57 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO56 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO57 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
59 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
60 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO58 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
6159
62 const add = struct {60 const add = struct {
test/behavior/cast.zig-10
...@@ -176,7 +176,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {...@@ -176,7 +176,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
176176
177test "implicitly cast indirect pointer to maybe-indirect pointer" {177test "implicitly cast indirect pointer to maybe-indirect pointer" {
178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
179 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
180 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO179 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
181180
182 const S = struct {181 const S = struct {
...@@ -318,7 +317,6 @@ test "peer result null and comptime_int" {...@@ -318,7 +317,6 @@ test "peer result null and comptime_int" {
318317
319test "*const ?[*]const T to [*c]const [*c]const T" {318test "*const ?[*]const T to [*c]const [*c]const T" {
320 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;319 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
321 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
322 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO320 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
323321
324 var array = [_]u8{ 'o', 'k' };322 var array = [_]u8{ 'o', 'k' };
...@@ -588,7 +586,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {...@@ -588,7 +586,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {
588586
589test "cast *[1][*]const u8 to [*]const ?[*]const u8" {587test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
590 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;588 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
591 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
592 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO589 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
593590
594 const window_name = [1][*]const u8{"window name"};591 const window_name = [1][*]const u8{"window name"};
...@@ -598,7 +595,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {...@@ -598,7 +595,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
598595
599test "vector casts" {596test "vector casts" {
600 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO597 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
601 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
602 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO598 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
603 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO599 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
604 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO600 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -928,7 +924,6 @@ test "peer cast *[N:x]T to *[N]T" {...@@ -928,7 +924,6 @@ test "peer cast *[N:x]T to *[N]T" {
928924
929test "peer cast [*:x]T to [*]T" {925test "peer cast [*:x]T to [*]T" {
930 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO926 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
931 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
932 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO927 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
933928
934 const S = struct {929 const S = struct {
...@@ -949,7 +944,6 @@ test "peer cast [*:x]T to [*]T" {...@@ -949,7 +944,6 @@ test "peer cast [*:x]T to [*]T" {
949test "peer cast [:x]T to [*:x]T" {944test "peer cast [:x]T to [*:x]T" {
950 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;945 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
951 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO946 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
952 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
953 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO947 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
954948
955 const S = struct {949 const S = struct {
...@@ -990,7 +984,6 @@ test "peer type resolution implicit cast to return type" {...@@ -990,7 +984,6 @@ test "peer type resolution implicit cast to return type" {
990984
991test "peer type resolution implicit cast to variable type" {985test "peer type resolution implicit cast to variable type" {
992 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;986 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
993 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
994 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO987 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
995988
996 const S = struct {989 const S = struct {
...@@ -1113,8 +1106,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void {...@@ -1113,8 +1106,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void {
1113}1106}
11141107
1115test "compile time int to ptr of function" {1108test "compile time int to ptr of function" {
1116 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1117
1118 try foobar(FUNCTION_CONSTANT);1109 try foobar(FUNCTION_CONSTANT);
1119}1110}
11201111
...@@ -1506,7 +1497,6 @@ test "implicit cast from [:0]T to [*c]T" {...@@ -1506,7 +1497,6 @@ test "implicit cast from [:0]T to [*c]T" {
1506test "bitcast packed struct with u0" {1497test "bitcast packed struct with u0" {
1507 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1498 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1508 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1499 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1509 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15101500
1511 const S = packed struct(u2) { a: u0, b: u2 };1501 const S = packed struct(u2) { a: u0, b: u2 };
1512 const s = @bitCast(S, @as(u2, 2));1502 const s = @bitCast(S, @as(u2, 2));
test/behavior/comptime_memory.zig+8
...@@ -412,3 +412,11 @@ test "bitcast packed union to integer" {...@@ -412,3 +412,11 @@ test "bitcast packed union to integer" {
412 try testing.expectEqual(@as(u2, 2), cast_b);412 try testing.expectEqual(@as(u2, 2), cast_b);
413 }413 }
414}414}
415
416test "mutate entire slice at comptime" {
417 comptime {
418 var buf: [3]u8 = undefined;
419 const x: [2]u8 = .{ 1, 2 }; // Avoid RLS
420 buf[1..3].* = x;
421 }
422}
test/behavior/defer.zig-1
...@@ -50,7 +50,6 @@ fn testNestedFnErrDefer() anyerror!void {...@@ -50,7 +50,6 @@ fn testNestedFnErrDefer() anyerror!void {
50}50}
5151
52test "return variable while defer expression in scope to modify it" {52test "return variable while defer expression in scope to modify it" {
53 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
54 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;53 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
55 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;54 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
56 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO55 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/enum.zig-2
...@@ -937,7 +937,6 @@ test "enum literal casting to error union with payload enum" {...@@ -937,7 +937,6 @@ test "enum literal casting to error union with payload enum" {
937}937}
938938
939test "constant enum initialization with differing sizes" {939test "constant enum initialization with differing sizes" {
940 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
941 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;940 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
942 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;941 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
943 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO942 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1137,7 +1136,6 @@ test "tag name functions are unique" {...@@ -1137,7 +1136,6 @@ test "tag name functions are unique" {
1137}1136}
11381137
1139test "size of enum with only one tag which has explicit integer tag type" {1138test "size of enum with only one tag which has explicit integer tag type" {
1140 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1141 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1139 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1140 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11431141
test/behavior/error.zig-10
...@@ -144,15 +144,11 @@ test "implicit cast to optional to error union to return result loc" {...@@ -144,15 +144,11 @@ test "implicit cast to optional to error union to return result loc" {
144}144}
145145
146test "fn returning empty error set can be passed as fn returning any error" {146test "fn returning empty error set can be passed as fn returning any error" {
147 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
148
149 entry();147 entry();
150 comptime entry();148 comptime entry();
151}149}
152150
153test "fn returning empty error set can be passed as fn returning any error - pointer" {151test "fn returning empty error set can be passed as fn returning any error - pointer" {
154 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
155
156 entryPtr();152 entryPtr();
157 comptime entryPtr();153 comptime entryPtr();
158}154}
...@@ -219,7 +215,6 @@ fn testErrorSetType() !void {...@@ -219,7 +215,6 @@ fn testErrorSetType() !void {
219}215}
220216
221test "explicit error set cast" {217test "explicit error set cast" {
222 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
223 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO218 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
224219
225 try testExplicitErrorSetCast(Set1.A);220 try testExplicitErrorSetCast(Set1.A);
...@@ -238,7 +233,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void {...@@ -238,7 +233,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void {
238}233}
239234
240test "comptime test error for empty error set" {235test "comptime test error for empty error set" {
241 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
242 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO236 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
243237
244 try testComptimeTestErrorEmptySet(1234);238 try testComptimeTestErrorEmptySet(1234);
...@@ -255,8 +249,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {...@@ -255,8 +249,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
255}249}
256250
257test "comptime err to int of error set with only 1 possible value" {251test "comptime err to int of error set with only 1 possible value" {
258 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
259
260 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));252 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
261 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));253 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
262}254}
...@@ -426,7 +418,6 @@ test "nested error union function call in optional unwrap" {...@@ -426,7 +418,6 @@ test "nested error union function call in optional unwrap" {
426}418}
427419
428test "return function call to error set from error union function" {420test "return function call to error set from error union function" {
429 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
430 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO421 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
431 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO422 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
432423
...@@ -486,7 +477,6 @@ test "nested catch" {...@@ -486,7 +477,6 @@ test "nested catch" {
486}477}
487478
488test "function pointer with return type that is error union with payload which is pointer of parent struct" {479test "function pointer with return type that is error union with payload which is pointer of parent struct" {
489 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
490 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO480 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
491481
492 const S = struct {482 const S = struct {
test/behavior/eval.zig-2
...@@ -138,7 +138,6 @@ test "pointer to type" {...@@ -138,7 +138,6 @@ test "pointer to type" {
138}138}
139139
140test "a type constructed in a global expression" {140test "a type constructed in a global expression" {
141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO141 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
143 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
144143
...@@ -1515,7 +1514,6 @@ test "x or true is comptime-known true" {...@@ -1515,7 +1514,6 @@ test "x or true is comptime-known true" {
1515}1514}
15161515
1517test "non-optional and optional array elements concatenated" {1516test "non-optional and optional array elements concatenated" {
1518 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1519 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1517 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1520 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1518 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1521 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1519 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
test/behavior/field_parent_ptr.zig+5
...@@ -3,6 +3,7 @@ const builtin = @import("builtin");...@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33
4test "@fieldParentPtr non-first field" {4test "@fieldParentPtr non-first field" {
5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7 try testParentFieldPtr(&foo.c);8 try testParentFieldPtr(&foo.c);
8 comptime try testParentFieldPtr(&foo.c);9 comptime try testParentFieldPtr(&foo.c);
...@@ -10,6 +11,7 @@ test "@fieldParentPtr non-first field" {...@@ -10,6 +11,7 @@ test "@fieldParentPtr non-first field" {
1011
11test "@fieldParentPtr first field" {12test "@fieldParentPtr first field" {
12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO15 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14 try testParentFieldPtrFirst(&foo.a);16 try testParentFieldPtrFirst(&foo.a);
15 comptime try testParentFieldPtrFirst(&foo.a);17 comptime try testParentFieldPtrFirst(&foo.a);
...@@ -47,6 +49,7 @@ fn testParentFieldPtrFirst(a: *const bool) !void {...@@ -47,6 +49,7 @@ fn testParentFieldPtrFirst(a: *const bool) !void {
4749
48test "@fieldParentPtr untagged union" {50test "@fieldParentPtr untagged union" {
49 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;51 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO53 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO54 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5255
...@@ -73,6 +76,7 @@ fn testFieldParentPtrUnion(c: *const i32) !void {...@@ -73,6 +76,7 @@ fn testFieldParentPtrUnion(c: *const i32) !void {
7376
74test "@fieldParentPtr tagged union" {77test "@fieldParentPtr tagged union" {
75 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;78 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
79 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO80 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
77 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO81 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7882
...@@ -99,6 +103,7 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void {...@@ -99,6 +103,7 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void {
99103
100test "@fieldParentPtr extern union" {104test "@fieldParentPtr extern union" {
101 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;105 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
106 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
102 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO107 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO108 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
104109
test/behavior/floatop.zig-19
...@@ -21,7 +21,6 @@ fn epsForType(comptime T: type) T {...@@ -21,7 +21,6 @@ fn epsForType(comptime T: type) T {
2121
22test "floating point comparisons" {22test "floating point comparisons" {
23 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO24 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2625
27 try testFloatComparisons();26 try testFloatComparisons();
...@@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void {...@@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void {
9190
92test "negative f128 floatToInt at compile-time" {91test "negative f128 floatToInt at compile-time" {
93 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO92 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
94 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
95 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO93 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
96 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO94 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9795
...@@ -141,7 +139,6 @@ fn testSqrt() !void {...@@ -141,7 +139,6 @@ fn testSqrt() !void {
141139
142test "@sqrt with vectors" {140test "@sqrt with vectors" {
143 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO141 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
144 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
145 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO142 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
146 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
147 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO144 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -234,7 +231,6 @@ fn testSin() !void {...@@ -234,7 +231,6 @@ fn testSin() !void {
234231
235test "@sin with vectors" {232test "@sin with vectors" {
236 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO233 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
237 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
238 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO234 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
239 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO235 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
240 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO236 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -275,7 +271,6 @@ fn testCos() !void {...@@ -275,7 +271,6 @@ fn testCos() !void {
275271
276test "@cos with vectors" {272test "@cos with vectors" {
277 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO273 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
278 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
279 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO274 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
280 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO275 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
281 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -315,7 +310,6 @@ fn testExp() !void {...@@ -315,7 +310,6 @@ fn testExp() !void {
315310
316test "@exp with vectors" {311test "@exp with vectors" {
317 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO312 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
319 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO313 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
320 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO314 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
321 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO315 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -335,7 +329,6 @@ fn testExpWithVectors() !void {...@@ -335,7 +329,6 @@ fn testExpWithVectors() !void {
335329
336test "@exp2" {330test "@exp2" {
337 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO331 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
338 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO333 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
341 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO334 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -355,7 +348,6 @@ fn testExp2() !void {...@@ -355,7 +348,6 @@ fn testExp2() !void {
355348
356test "@exp2" {349test "@exp2" {
357 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO350 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
358 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
359 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO351 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
360 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO352 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
361 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO353 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -409,7 +401,6 @@ test "@log with @vectors" {...@@ -409,7 +401,6 @@ test "@log with @vectors" {
409 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO401 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
410 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO402 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
411 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO403 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
412 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
413 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO404 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
414405
415 {406 {
...@@ -447,7 +438,6 @@ test "@log2 with vectors" {...@@ -447,7 +438,6 @@ test "@log2 with vectors" {
447 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO438 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
448 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
449 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
450 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
451 // https://github.com/ziglang/zig/issues/13681441 // https://github.com/ziglang/zig/issues/13681
452 if (builtin.zig_backend == .stage2_llvm and442 if (builtin.zig_backend == .stage2_llvm and
453 builtin.cpu.arch == .aarch64 and443 builtin.cpu.arch == .aarch64 and
...@@ -491,7 +481,6 @@ test "@log10 with vectors" {...@@ -491,7 +481,6 @@ test "@log10 with vectors" {
491 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO481 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
492 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO482 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
493 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO483 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
494 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
495484
496 comptime try testLog10WithVectors();485 comptime try testLog10WithVectors();
497 try testLog10WithVectors();486 try testLog10WithVectors();
...@@ -508,7 +497,6 @@ fn testLog10WithVectors() !void {...@@ -508,7 +497,6 @@ fn testLog10WithVectors() !void {
508497
509test "@fabs" {498test "@fabs" {
510 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO499 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
511 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
512 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO500 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO501 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
514 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO502 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -537,7 +525,6 @@ fn testFabs() !void {...@@ -537,7 +525,6 @@ fn testFabs() !void {
537525
538test "@fabs with vectors" {526test "@fabs with vectors" {
539 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO527 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
540 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
541 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO528 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
542 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO529 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
543 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO530 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -636,7 +623,6 @@ test "a third @fabs test, surely there should not be three fabs tests" {...@@ -636,7 +623,6 @@ test "a third @fabs test, surely there should not be three fabs tests" {
636623
637test "@floor" {624test "@floor" {
638 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO625 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
639 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
640 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO626 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO627 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
642 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO628 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -660,7 +646,6 @@ fn testFloor() !void {...@@ -660,7 +646,6 @@ fn testFloor() !void {
660646
661test "@floor with vectors" {647test "@floor with vectors" {
662 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO648 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
663 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
664 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO649 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
665 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO650 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
666 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO651 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -730,7 +715,6 @@ fn testFloorLegacy(comptime T: type, x: T) !void {...@@ -730,7 +715,6 @@ fn testFloorLegacy(comptime T: type, x: T) !void {
730715
731test "@ceil" {716test "@ceil" {
732 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO717 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
733 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
734 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO718 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
735 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO719 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
736 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO720 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -754,7 +738,6 @@ fn testCeil() !void {...@@ -754,7 +738,6 @@ fn testCeil() !void {
754738
755test "@ceil with vectors" {739test "@ceil with vectors" {
756 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO740 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
757 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
758 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO741 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
759 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO742 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
760 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO743 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -824,7 +807,6 @@ fn testCeilLegacy(comptime T: type, x: T) !void {...@@ -824,7 +807,6 @@ fn testCeilLegacy(comptime T: type, x: T) !void {
824807
825test "@trunc" {808test "@trunc" {
826 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO809 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
827 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
828 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO810 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
829 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO811 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
830 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO812 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -848,7 +830,6 @@ fn testTrunc() !void {...@@ -848,7 +830,6 @@ fn testTrunc() !void {
848830
849test "@trunc with vectors" {831test "@trunc with vectors" {
850 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO832 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
851 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
852 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO833 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
853 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO834 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
854 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO835 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/fn.zig-2
...@@ -275,7 +275,6 @@ test "implicit cast fn call result to optional in field result" {...@@ -275,7 +275,6 @@ test "implicit cast fn call result to optional in field result" {
275}275}
276276
277test "void parameters" {277test "void parameters" {
278 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
279 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;278 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
280 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;279 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
281280
...@@ -300,7 +299,6 @@ fn acceptsString(foo: []u8) void {...@@ -300,7 +299,6 @@ fn acceptsString(foo: []u8) void {
300}299}
301300
302test "function pointers" {301test "function pointers" {
303 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
304 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
305 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO303 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
306 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO304 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/for.zig-8
...@@ -110,7 +110,6 @@ test "basic for loop" {...@@ -110,7 +110,6 @@ test "basic for loop" {
110}110}
111111
112test "for with null and T peer types and inferred result location type" {112test "for with null and T peer types and inferred result location type" {
113 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
114 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;113 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
115 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;114 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO115 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -152,7 +151,6 @@ test "2 break statements and an else" {...@@ -152,7 +151,6 @@ test "2 break statements and an else" {
152}151}
153152
154test "for loop with pointer elem var" {153test "for loop with pointer elem var" {
155 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
156 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO154 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
157 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO155 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
158 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO156 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -198,7 +196,6 @@ test "for copies its payload" {...@@ -198,7 +196,6 @@ test "for copies its payload" {
198}196}
199197
200test "for on slice with allowzero ptr" {198test "for on slice with allowzero ptr" {
201 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
202 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO199 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
203 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO200 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
204 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO201 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -253,7 +250,6 @@ test "for loop with else branch" {...@@ -253,7 +250,6 @@ test "for loop with else branch" {
253test "count over fixed range" {250test "count over fixed range" {
254 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO251 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
255 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO252 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
256 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
257253
258 var sum: usize = 0;254 var sum: usize = 0;
259 for (0..6) |i| {255 for (0..6) |i| {
...@@ -266,7 +262,6 @@ test "count over fixed range" {...@@ -266,7 +262,6 @@ test "count over fixed range" {
266test "two counters" {262test "two counters" {
267 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO263 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
268 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO264 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
269 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
270265
271 var sum: usize = 0;266 var sum: usize = 0;
272 for (0..10, 10..20) |i, j| {267 for (0..10, 10..20) |i, j| {
...@@ -280,7 +275,6 @@ test "two counters" {...@@ -280,7 +275,6 @@ test "two counters" {
280test "1-based counter and ptr to array" {275test "1-based counter and ptr to array" {
281 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO276 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
282 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
283 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
284278
285 var ok: usize = 0;279 var ok: usize = 0;
286280
...@@ -401,7 +395,6 @@ test "raw pointer and counter" {...@@ -401,7 +395,6 @@ test "raw pointer and counter" {
401test "inline for with slice as the comptime-known" {395test "inline for with slice as the comptime-known" {
402 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO396 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
403 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO397 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
404 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
405 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO398 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
406399
407 const comptime_slice = "hello";400 const comptime_slice = "hello";
...@@ -432,7 +425,6 @@ test "inline for with slice as the comptime-known" {...@@ -432,7 +425,6 @@ test "inline for with slice as the comptime-known" {
432test "inline for with counter as the comptime-known" {425test "inline for with counter as the comptime-known" {
433 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO426 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
434 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
435 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
436 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO428 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
437429
438 var runtime_slice = "hello";430 var runtime_slice = "hello";
test/behavior/generics.zig-3
...@@ -56,7 +56,6 @@ fn sameButWithFloats(a: f64, b: f64) f64 {...@@ -56,7 +56,6 @@ fn sameButWithFloats(a: f64, b: f64) f64 {
56}56}
5757
58test "fn with comptime args" {58test "fn with comptime args" {
59 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
60 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;59 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
61 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;60 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
62 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO61 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -67,7 +66,6 @@ test "fn with comptime args" {...@@ -67,7 +66,6 @@ test "fn with comptime args" {
67}66}
6867
69test "anytype params" {68test "anytype params" {
70 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
71 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;69 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
72 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;70 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
73 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO71 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -93,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 {...@@ -93,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 {
93}91}
9492
95test "type constructed by comptime function call" {93test "type constructed by comptime function call" {
96 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
97 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;94 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
98 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO95 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9996
test/behavior/if.zig-1
...@@ -78,7 +78,6 @@ test "const result loc, runtime if cond, else unreachable" {...@@ -78,7 +78,6 @@ test "const result loc, runtime if cond, else unreachable" {
78}78}
7979
80test "if copies its payload" {80test "if copies its payload" {
81 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
82 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;81 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
83 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO82 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8483
test/behavior/inline_switch.zig-1
...@@ -46,7 +46,6 @@ const U = union(E) { a: void, b: u2, c: u3, d: u4 };...@@ -46,7 +46,6 @@ const U = union(E) { a: void, b: u2, c: u3, d: u4 };
46test "inline switch unions" {46test "inline switch unions" {
47 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO47 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
48 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO48 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5150
52 var x: U = .a;51 var x: U = .a;
test/behavior/int_div.zig+19
...@@ -91,3 +91,22 @@ fn mod(comptime T: type, a: T, b: T) T {...@@ -91,3 +91,22 @@ fn mod(comptime T: type, a: T, b: T) T {
91fn rem(comptime T: type, a: T, b: T) T {91fn rem(comptime T: type, a: T, b: T) T {
92 return @rem(a, b);92 return @rem(a, b);
93}93}
94
95test "large integer division" {
96 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
97 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
98 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
100 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
101
102 {
103 var numerator: u256 = 99999999999999999997315645440;
104 var divisor: u256 = 10000000000000000000000000000;
105 try expect(numerator / divisor == 9);
106 }
107 {
108 var numerator: u256 = 99999999999999999999000000000000000000000;
109 var divisor: u256 = 10000000000000000000000000000000000000000;
110 try expect(numerator / divisor == 9);
111 }
112}
test/behavior/math.zig+6-3
...@@ -100,7 +100,6 @@ test "@clz vectors" {...@@ -100,7 +100,6 @@ test "@clz vectors" {
100 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO100 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
104 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO103 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
105104
106 try testClzVectors();105 try testClzVectors();
...@@ -163,7 +162,6 @@ test "@ctz vectors" {...@@ -163,7 +162,6 @@ test "@ctz vectors" {
163 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO162 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
165 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
166 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
167 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO165 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
168166
169 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {167 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
...@@ -1526,7 +1524,6 @@ fn testNanEqNan(comptime F: type) !void {...@@ -1526,7 +1524,6 @@ fn testNanEqNan(comptime F: type) !void {
1526}1524}
15271525
1528test "vector comparison" {1526test "vector comparison" {
1529 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1530 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1527 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1531 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1528 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1532 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1529 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -1563,6 +1560,12 @@ test "signed zeros are represented properly" {...@@ -1563,6 +1560,12 @@ test "signed zeros are represented properly" {
1563 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1560 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1564 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1561 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
15651562
1563 if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and
1564 builtin.zig_backend == .stage2_c)
1565 {
1566 return error.SkipZigTest;
1567 }
1568
1566 const S = struct {1569 const S = struct {
1567 fn doTheTest() !void {1570 fn doTheTest() !void {
1568 try testOne(f16);1571 try testOne(f16);
test/behavior/maximum_minimum.zig-2
...@@ -25,7 +25,6 @@ test "@max" {...@@ -25,7 +25,6 @@ test "@max" {
25test "@max on vectors" {25test "@max on vectors" {
26 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO26 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO27 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
29 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO28 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
30 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
31 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO30 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -75,7 +74,6 @@ test "@min for vectors" {...@@ -75,7 +74,6 @@ test "@min for vectors" {
75 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO74 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO75 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
77 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO76 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
78 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
79 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO77 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8078
81 const S = struct {79 const S = struct {
test/behavior/member_func.zig-2
...@@ -27,7 +27,6 @@ const HasFuncs = struct {...@@ -27,7 +27,6 @@ const HasFuncs = struct {
27};27};
2828
29test "standard field calls" {29test "standard field calls" {
30 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;30 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
32 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;31 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
33 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO32 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -71,7 +70,6 @@ test "standard field calls" {...@@ -71,7 +70,6 @@ test "standard field calls" {
71}70}
7271
73test "@field field calls" {72test "@field field calls" {
74 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
75 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;73 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;74 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
77 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO75 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/muladd.zig+7-5
...@@ -100,7 +100,6 @@ fn vector16() !void {...@@ -100,7 +100,6 @@ fn vector16() !void {
100}100}
101101
102test "vector f16" {102test "vector f16" {
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
104 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO103 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
105 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO104 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
106 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO105 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -124,7 +123,6 @@ fn vector32() !void {...@@ -124,7 +123,6 @@ fn vector32() !void {
124}123}
125124
126test "vector f32" {125test "vector f32" {
127 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
128 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO126 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
129 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO127 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
130 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO128 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -148,7 +146,6 @@ fn vector64() !void {...@@ -148,7 +146,6 @@ fn vector64() !void {
148}146}
149147
150test "vector f64" {148test "vector f64" {
151 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
152 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO149 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
153 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO150 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
154 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -171,7 +168,6 @@ fn vector80() !void {...@@ -171,7 +168,6 @@ fn vector80() !void {
171}168}
172169
173test "vector f80" {170test "vector f80" {
174 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
175 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO171 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
176 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO172 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
177 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO173 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -195,13 +191,19 @@ fn vector128() !void {...@@ -195,13 +191,19 @@ fn vector128() !void {
195}191}
196192
197test "vector f128" {193test "vector f128" {
198 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
199 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO194 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
200 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO195 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
201 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO196 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
202 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO197 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
203 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO198 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
204199
200 if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and
201 builtin.zig_backend == .stage2_c)
202 {
203 // https://github.com/ziglang/zig/issues/13876
204 return error.SkipZigTest;
205 }
206
205 comptime try vector128();207 comptime try vector128();
206 try vector128();208 try vector128();
207}209}
test/behavior/null.zig-3
...@@ -50,7 +50,6 @@ test "rhs maybe unwrap return" {...@@ -50,7 +50,6 @@ test "rhs maybe unwrap return" {
50}50}
5151
52test "maybe return" {52test "maybe return" {
53 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
54 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;53 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
55 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;54 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
56 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO55 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -71,7 +70,6 @@ fn foo(x: ?i32) ?bool {...@@ -71,7 +70,6 @@ fn foo(x: ?i32) ?bool {
71}70}
7271
73test "test null runtime" {72test "test null runtime" {
74 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
75 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;73 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO74 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7775
...@@ -184,7 +182,6 @@ const SillyStruct = struct {...@@ -184,7 +182,6 @@ const SillyStruct = struct {
184const here_is_a_null_literal = SillyStruct{ .context = null };182const here_is_a_null_literal = SillyStruct{ .context = null };
185183
186test "unwrap optional which is field of global var" {184test "unwrap optional which is field of global var" {
187 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
188 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;185 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
189 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;186 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
190 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO187 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/optional.zig-1
...@@ -274,7 +274,6 @@ test "0-bit child type coerced to optional return ptr result location" {...@@ -274,7 +274,6 @@ test "0-bit child type coerced to optional return ptr result location" {
274}274}
275275
276test "0-bit child type coerced to optional" {276test "0-bit child type coerced to optional" {
277 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
278 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO277 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
279278
280 const S = struct {279 const S = struct {
test/behavior/pointers.zig-2
...@@ -190,7 +190,6 @@ test "compare equality of optional and non-optional pointer" {...@@ -190,7 +190,6 @@ test "compare equality of optional and non-optional pointer" {
190test "allowzero pointer and slice" {190test "allowzero pointer and slice" {
191 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO191 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
192 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO192 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
193 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
194 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;193 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
195 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO194 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
196195
...@@ -510,7 +509,6 @@ test "ptrToInt on a generic function" {...@@ -510,7 +509,6 @@ test "ptrToInt on a generic function" {
510 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO509 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
511 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO510 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
512 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO511 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
514512
515 const S = struct {513 const S = struct {
516 fn generic(i: anytype) @TypeOf(i) {514 fn generic(i: anytype) @TypeOf(i) {
test/behavior/popcount.zig-1
...@@ -67,7 +67,6 @@ fn testPopCountIntegers() !void {...@@ -67,7 +67,6 @@ fn testPopCountIntegers() !void {
67}67}
6868
69test "@popCount vectors" {69test "@popCount vectors" {
70 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
71 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO70 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
72 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO71 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
73 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO72 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/ptrcast.zig+2-1
...@@ -53,7 +53,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void {...@@ -53,7 +53,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void {
53}53}
5454
55test "reinterpret bytes of an array into an extern struct" {55test "reinterpret bytes of an array into an extern struct" {
56 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO56 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO57 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5958
...@@ -171,6 +170,7 @@ test "lower reinterpreted comptime field ptr" {...@@ -171,6 +170,7 @@ test "lower reinterpreted comptime field ptr" {
171170
172test "reinterpret struct field at comptime" {171test "reinterpret struct field at comptime" {
173 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO172 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
173 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
174174
175 const numNative = comptime Bytes.init(0x12345678);175 const numNative = comptime Bytes.init(0x12345678);
176 if (native_endian != .Little) {176 if (native_endian != .Little) {
...@@ -233,6 +233,7 @@ test "ptrcast of const integer has the correct object size" {...@@ -233,6 +233,7 @@ test "ptrcast of const integer has the correct object size" {
233test "implicit optional pointer to optional anyopaque pointer" {233test "implicit optional pointer to optional anyopaque pointer" {
234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
235 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO235 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
236 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
236237
237 var buf: [4]u8 = "aoeu".*;238 var buf: [4]u8 = "aoeu".*;
238 var x: ?[*]u8 = &buf;239 var x: ?[*]u8 = &buf;
test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig-1
...@@ -5,7 +5,6 @@ const mem = std.mem;...@@ -5,7 +5,6 @@ const mem = std.mem;
55
6var ok: bool = false;6var ok: bool = false;
7test "reference a variable in an if after an if in the 2nd switch prong" {7test "reference a variable in an if after an if in the 2nd switch prong" {
8 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/reflection.zig-1
...@@ -27,7 +27,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 {...@@ -27,7 +27,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 {
2727
28test "reflection: @field" {28test "reflection: @field" {
29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
30 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
31 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO30 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3231
33 var f = Foo{32 var f = Foo{
test/behavior/select.zig-2
...@@ -4,7 +4,6 @@ const mem = std.mem;...@@ -4,7 +4,6 @@ const mem = std.mem;
4const expect = std.testing.expect;4const expect = std.testing.expect;
55
6test "@select vectors" {6test "@select vectors" {
7 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -33,7 +32,6 @@ fn selectVectors() !void {...@@ -33,7 +32,6 @@ fn selectVectors() !void {
33}32}
3433
35test "@select arrays" {34test "@select arrays" {
36 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
37 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO35 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
38 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO36 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
39 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/shuffle.zig+1-4
...@@ -8,7 +8,6 @@ test "@shuffle int" {...@@ -8,7 +8,6 @@ test "@shuffle int" {
8 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1312
14 const S = struct {13 const S = struct {
...@@ -50,7 +49,6 @@ test "@shuffle bool 1" {...@@ -50,7 +49,6 @@ test "@shuffle bool 1" {
50 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO50 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO51 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
53 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
54 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO52 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5553
56 const S = struct {54 const S = struct {
...@@ -71,7 +69,6 @@ test "@shuffle bool 2" {...@@ -71,7 +69,6 @@ test "@shuffle bool 2" {
71 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO69 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
72 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO70 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
73 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO71 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
74 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO72 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7673
77 if (builtin.zig_backend == .stage2_llvm) {74 if (builtin.zig_backend == .stage2_llvm) {
...@@ -83,7 +80,7 @@ test "@shuffle bool 2" {...@@ -83,7 +80,7 @@ test "@shuffle bool 2" {
83 fn doTheTest() !void {80 fn doTheTest() !void {
84 var x: @Vector(3, bool) = [3]bool{ false, true, false };81 var x: @Vector(3, bool) = [3]bool{ false, true, false };
85 var v: @Vector(2, bool) = [2]bool{ true, false };82 var v: @Vector(2, bool) = [2]bool{ true, false };
86 const mask: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };83 const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
87 var res = @shuffle(bool, x, v, mask);84 var res = @shuffle(bool, x, v, mask);
88 try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));85 try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));
89 }86 }
test/behavior/sizeof_and_typeof.zig-3
...@@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" {...@@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" {
18}18}
1919
20test "@TypeOf() with multiple arguments" {20test "@TypeOf() with multiple arguments" {
21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
22 {21 {
23 var var_1: u32 = undefined;22 var var_1: u32 = undefined;
24 var var_2: u8 = undefined;23 var var_2: u8 = undefined;
...@@ -138,7 +137,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {...@@ -138,7 +137,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {
138}137}
139138
140test "@TypeOf() has no runtime side effects" {139test "@TypeOf() has no runtime side effects" {
141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
142 const S = struct {140 const S = struct {
143 fn foo(comptime T: type, ptr: *T) T {141 fn foo(comptime T: type, ptr: *T) T {
144 ptr.* += 1;142 ptr.* += 1;
...@@ -153,7 +151,6 @@ test "@TypeOf() has no runtime side effects" {...@@ -153,7 +151,6 @@ test "@TypeOf() has no runtime side effects" {
153151
154test "branching logic inside @TypeOf" {152test "branching logic inside @TypeOf" {
155 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
156 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
157 const S = struct {154 const S = struct {
158 var data: i32 = 0;155 var data: i32 = 0;
159 fn foo() anyerror!i32 {156 fn foo() anyerror!i32 {
test/behavior/slice.zig+21-9
...@@ -119,7 +119,6 @@ test "slice of type" {...@@ -119,7 +119,6 @@ test "slice of type" {
119}119}
120120
121test "generic malloc free" {121test "generic malloc free" {
122 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
123 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO122 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
124123
125 const a = memAlloc(u8, 10) catch unreachable;124 const a = memAlloc(u8, 10) catch unreachable;
...@@ -171,7 +170,6 @@ test "comptime pointer cast array and then slice" {...@@ -171,7 +170,6 @@ test "comptime pointer cast array and then slice" {
171170
172test "slicing zero length array" {171test "slicing zero length array" {
173 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;172 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
174 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
175 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO173 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
176174
177 const s1 = ""[0..];175 const s1 = ""[0..];
...@@ -185,8 +183,6 @@ test "slicing zero length array" {...@@ -185,8 +183,6 @@ test "slicing zero length array" {
185const x = @intToPtr([*]i32, 0x1000)[0..0x500];183const x = @intToPtr([*]i32, 0x1000)[0..0x500];
186const y = x[0x100..];184const y = x[0x100..];
187test "compile time slice of pointer to hard coded address" {185test "compile time slice of pointer to hard coded address" {
188 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
189
190 try expect(@ptrToInt(x) == 0x1000);186 try expect(@ptrToInt(x) == 0x1000);
191 try expect(x.len == 0x500);187 try expect(x.len == 0x500);
192188
...@@ -231,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {...@@ -231,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
231227
232test "C pointer" {228test "C pointer" {
233 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO229 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
230 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
234231
235 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";232 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
236 var len: u32 = 10;233 var len: u32 = 10;
...@@ -342,7 +339,6 @@ test "@ptrCast slice to pointer" {...@@ -342,7 +339,6 @@ test "@ptrCast slice to pointer" {
342}339}
343340
344test "slice syntax resulting in pointer-to-array" {341test "slice syntax resulting in pointer-to-array" {
345 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
346 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO342 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
347 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO343 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
348 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO344 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -477,7 +473,6 @@ test "slice syntax resulting in pointer-to-array" {...@@ -477,7 +473,6 @@ test "slice syntax resulting in pointer-to-array" {
477}473}
478474
479test "slice pointer-to-array null terminated" {475test "slice pointer-to-array null terminated" {
480 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
481 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO476 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
482 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO477 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
483478
...@@ -497,7 +492,6 @@ test "slice pointer-to-array null terminated" {...@@ -497,7 +492,6 @@ test "slice pointer-to-array null terminated" {
497}492}
498493
499test "slice pointer-to-array zero length" {494test "slice pointer-to-array zero length" {
500 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
501 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO495 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
502496
503 comptime {497 comptime {
...@@ -530,7 +524,6 @@ test "slice pointer-to-array zero length" {...@@ -530,7 +524,6 @@ test "slice pointer-to-array zero length" {
530}524}
531525
532test "type coercion of pointer to anon struct literal to pointer to slice" {526test "type coercion of pointer to anon struct literal to pointer to slice" {
533 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
534 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO527 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
535 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO528 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
536 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO529 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -730,7 +723,6 @@ test "slice with dereferenced value" {...@@ -730,7 +723,6 @@ test "slice with dereferenced value" {
730723
731test "empty slice ptr is non null" {724test "empty slice ptr is non null" {
732 if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest; // TODO725 if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest; // TODO
733 if (builtin.zig_backend == .stage2_x86_64 and (builtin.os.tag == .macos or builtin.os.tag == .windows)) return error.SkipZigTest; // TODO
734726
735 const empty_slice: []u8 = &[_]u8{};727 const empty_slice: []u8 = &[_]u8{};
736 const p: [*]u8 = empty_slice.ptr + 0;728 const p: [*]u8 = empty_slice.ptr + 0;
...@@ -747,3 +739,23 @@ test "slice decays to many pointer" {...@@ -747,3 +739,23 @@ test "slice decays to many pointer" {
747 const p: [*:0]const u8 = buf[0..7 :0];739 const p: [*:0]const u8 = buf[0..7 :0];
748 try expectEqualStrings(buf[0..7], std.mem.span(p));740 try expectEqualStrings(buf[0..7], std.mem.span(p));
749}741}
742
743test "write through pointer to optional slice arg" {
744 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
745 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
746 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
747 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
748
749 const S = struct {
750 fn bar(foo: *?[]const u8) !void {
751 foo.* = try baz();
752 }
753
754 fn baz() ![]const u8 {
755 return "ok";
756 }
757 };
758 var foo: ?[]const u8 = null;
759 try S.bar(&foo);
760 try expectEqualStrings(foo.?, "ok");
761}
test/behavior/struct.zig-5
...@@ -342,7 +342,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {...@@ -342,7 +342,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {
342test "self-referencing struct via array member" {342test "self-referencing struct via array member" {
343 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;343 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
345 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
346 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO345 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
347346
348 const T = struct {347 const T = struct {
...@@ -536,7 +535,6 @@ test "implicit cast packed struct field to const ptr" {...@@ -536,7 +535,6 @@ test "implicit cast packed struct field to const ptr" {
536}535}
537536
538test "zero-bit field in packed struct" {537test "zero-bit field in packed struct" {
539 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
540 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO538 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
541539
542 const S = packed struct {540 const S = packed struct {
...@@ -719,7 +717,6 @@ test "pointer to packed struct member in a stack variable" {...@@ -719,7 +717,6 @@ test "pointer to packed struct member in a stack variable" {
719}717}
720718
721test "packed struct with u0 field access" {719test "packed struct with u0 field access" {
722 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
723 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO720 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
724721
725 const S = packed struct {722 const S = packed struct {
...@@ -1018,7 +1015,6 @@ test "type coercion of anon struct literal to struct" {...@@ -1018,7 +1015,6 @@ test "type coercion of anon struct literal to struct" {
1018}1015}
10191016
1020test "type coercion of pointer to anon struct literal to pointer to struct" {1017test "type coercion of pointer to anon struct literal to pointer to struct" {
1021 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1022 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1018 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1023 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1019 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1024 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1020 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1088,7 +1084,6 @@ test "packed struct with undefined initializers" {...@@ -1088,7 +1084,6 @@ test "packed struct with undefined initializers" {
1088}1084}
10891085
1090test "for loop over pointers to struct, getting field from struct pointer" {1086test "for loop over pointers to struct, getting field from struct pointer" {
1091 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1092 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1087 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1093 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1088 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10941089
test/behavior/struct_contains_null_ptr_itself.zig-1
...@@ -4,7 +4,6 @@ const builtin = @import("builtin");...@@ -4,7 +4,6 @@ const builtin = @import("builtin");
44
5test "struct contains null pointer which contains original struct" {5test "struct contains null pointer which contains original struct" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
98
10 var x: ?*NodeLineComment = null;9 var x: ?*NodeLineComment = null;
test/behavior/switch.zig-2
...@@ -403,7 +403,6 @@ fn return_a_number() anyerror!i32 {...@@ -403,7 +403,6 @@ fn return_a_number() anyerror!i32 {
403}403}
404404
405test "switch on integer with else capturing expr" {405test "switch on integer with else capturing expr" {
406 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
407 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO406 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
408407
409 const S = struct {408 const S = struct {
...@@ -577,7 +576,6 @@ test "switch prongs with cases with identical payload types" {...@@ -577,7 +576,6 @@ test "switch prongs with cases with identical payload types" {
577}576}
578577
579test "switch on pointer type" {578test "switch on pointer type" {
580 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
581 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO579 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
582 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO580 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
583581
test/behavior/threadlocal.zig-2
...@@ -4,7 +4,6 @@ const expect = std.testing.expect;...@@ -4,7 +4,6 @@ const expect = std.testing.expect;
44
5test "thread local variable" {5test "thread local variable" {
6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO6 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {9 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
...@@ -40,7 +39,6 @@ threadlocal var buffer: [11]u8 = undefined;...@@ -40,7 +39,6 @@ threadlocal var buffer: [11]u8 = undefined;
4039
41test "reference a global threadlocal variable" {40test "reference a global threadlocal variable" {
42 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO41 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO42 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO43 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
46 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {44 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
test/behavior/translate_c_macros.zig-1
...@@ -79,7 +79,6 @@ test "casting to union with a macro" {...@@ -79,7 +79,6 @@ test "casting to union with a macro" {
7979
80test "casting or calling a value with a paren-surrounded macro" {80test "casting or calling a value with a paren-surrounded macro" {
81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
82 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
83 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO82 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
84 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO83 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
85 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO84 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/truncate.zig-1
...@@ -60,7 +60,6 @@ test "truncate on comptime integer" {...@@ -60,7 +60,6 @@ test "truncate on comptime integer" {
60}60}
6161
62test "truncate on vectors" {62test "truncate on vectors" {
63 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
64 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;63 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;64 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
66 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
test/behavior/tuple.zig+19-1
...@@ -126,7 +126,6 @@ test "tuple initializer for var" {...@@ -126,7 +126,6 @@ test "tuple initializer for var" {
126}126}
127127
128test "array-like initializer for tuple types" {128test "array-like initializer for tuple types" {
129 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
130 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO129 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
131 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO130 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
132131
...@@ -397,3 +396,22 @@ test "nested runtime conditionals in tuple initializer" {...@@ -397,3 +396,22 @@ test "nested runtime conditionals in tuple initializer" {
397 };396 };
398 try expectEqualStrings("up", x[0]);397 try expectEqualStrings("up", x[0]);
399}398}
399
400test "sentinel slice in tuple with other fields" {
401 const S = struct {
402 a: u32,
403 b: u32,
404 };
405
406 const Submission = union(enum) {
407 open: struct { *S, [:0]const u8, u32 },
408 };
409
410 _ = Submission;
411}
412
413test "sentinel slice in tuple" {
414 const S = struct { [:0]const u8 };
415
416 _ = S;
417}
test/behavior/type.zig-4
...@@ -449,8 +449,6 @@ test "Type.Union" {...@@ -449,8 +449,6 @@ test "Type.Union" {
449}449}
450450
451test "Type.Union from Type.Enum" {451test "Type.Union from Type.Enum" {
452 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
453
454 const Tag = @Type(.{452 const Tag = @Type(.{
455 .Enum = .{453 .Enum = .{
456 .tag_type = u0,454 .tag_type = u0,
...@@ -475,8 +473,6 @@ test "Type.Union from Type.Enum" {...@@ -475,8 +473,6 @@ test "Type.Union from Type.Enum" {
475}473}
476474
477test "Type.Union from regular enum" {475test "Type.Union from regular enum" {
478 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
479
480 const E = enum { working_as_expected };476 const E = enum { working_as_expected };
481 const T = @Type(.{477 const T = @Type(.{
482 .Union = .{478 .Union = .{
test/behavior/type_info.zig-2
...@@ -509,7 +509,6 @@ test "type info for async frames" {...@@ -509,7 +509,6 @@ test "type info for async frames" {
509}509}
510510
511test "Declarations are returned in declaration order" {511test "Declarations are returned in declaration order" {
512 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
513 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO512 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
514513
515 const S = struct {514 const S = struct {
...@@ -532,7 +531,6 @@ test "Struct.is_tuple for anon list literal" {...@@ -532,7 +531,6 @@ test "Struct.is_tuple for anon list literal" {
532}531}
533532
534test "Struct.is_tuple for anon struct literal" {533test "Struct.is_tuple for anon struct literal" {
535 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
536 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO534 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
537535
538 const info = @typeInfo(@TypeOf(.{ .a = 0 }));536 const info = @typeInfo(@TypeOf(.{ .a = 0 }));
test/behavior/union.zig-11
...@@ -11,7 +11,6 @@ const FooWithFloats = union {...@@ -11,7 +11,6 @@ const FooWithFloats = union {
11};11};
1212
13test "basic unions with floats" {13test "basic unions with floats" {
14 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -215,7 +214,6 @@ const Payload = union(Letter) {...@@ -215,7 +214,6 @@ const Payload = union(Letter) {
215};214};
216215
217test "union with specified enum tag" {216test "union with specified enum tag" {
218 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;217 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
220 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
221 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO219 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -225,7 +223,6 @@ test "union with specified enum tag" {...@@ -225,7 +223,6 @@ test "union with specified enum tag" {
225}223}
226224
227test "packed union generates correctly aligned type" {225test "packed union generates correctly aligned type" {
228 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
229 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;226 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
230 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;227 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
231 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO228 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -379,7 +376,6 @@ test "union with only 1 field which is void should be zero bits" {...@@ -379,7 +376,6 @@ test "union with only 1 field which is void should be zero bits" {
379}376}
380377
381test "tagged union initialization with runtime void" {378test "tagged union initialization with runtime void" {
382 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
383 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
384 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;380 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
385 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO381 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -400,7 +396,6 @@ fn testTaggedUnionInit(x: anytype) bool {...@@ -400,7 +396,6 @@ fn testTaggedUnionInit(x: anytype) bool {
400pub const UnionEnumNoPayloads = union(enum) { A, B };396pub const UnionEnumNoPayloads = union(enum) { A, B };
401397
402test "tagged union with no payloads" {398test "tagged union with no payloads" {
403 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
404 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;399 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
405 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
406 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO401 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -466,7 +461,6 @@ pub const FooUnion = union(enum) {...@@ -466,7 +461,6 @@ pub const FooUnion = union(enum) {
466var glbl_array: [2]FooUnion = undefined;461var glbl_array: [2]FooUnion = undefined;
467462
468test "initialize global array of union" {463test "initialize global array of union" {
469 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
470 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;464 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
471 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;465 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
472 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO466 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -494,7 +488,6 @@ test "update the tag value for zero-sized unions" {...@@ -494,7 +488,6 @@ test "update the tag value for zero-sized unions" {
494488
495test "union initializer generates padding only if needed" {489test "union initializer generates padding only if needed" {
496 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;490 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
497 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
498 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;491 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
499 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO492 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
500 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO493 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -561,7 +554,6 @@ const FooNoVoid = union(enum) {...@@ -561,7 +554,6 @@ const FooNoVoid = union(enum) {
561const Baz = enum { A, B, C, D };554const Baz = enum { A, B, C, D };
562555
563test "tagged union type" {556test "tagged union type" {
564 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
565 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO557 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
566558
567 const foo1 = TaggedFoo{ .One = 13 };559 const foo1 = TaggedFoo{ .One = 13 };
...@@ -598,7 +590,6 @@ fn returnAnInt(x: i32) TaggedFoo {...@@ -598,7 +590,6 @@ fn returnAnInt(x: i32) TaggedFoo {
598}590}
599591
600test "tagged union with all void fields but a meaningful tag" {592test "tagged union with all void fields but a meaningful tag" {
601 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
602 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;593 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
603 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;594 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
604 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO595 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -777,7 +768,6 @@ fn Setter(comptime attr: Attribute) type {...@@ -777,7 +768,6 @@ fn Setter(comptime attr: Attribute) type {
777}768}
778769
779test "return union init with void payload" {770test "return union init with void payload" {
780 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
781 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;771 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
782 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;772 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
783 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO773 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
...@@ -913,7 +903,6 @@ test "extern union doesn't trigger field check at comptime" {...@@ -913,7 +903,6 @@ test "extern union doesn't trigger field check at comptime" {
913test "anonymous union literal syntax" {903test "anonymous union literal syntax" {
914 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO904 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
915 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO905 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
916 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
917 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO906 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
918907
919 const S = struct {908 const S = struct {
test/behavior/union_with_members.zig-1
...@@ -18,7 +18,6 @@ const ET = union(enum) {...@@ -18,7 +18,6 @@ const ET = union(enum) {
1818
19test "enum with members" {19test "enum with members" {
20 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO20 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO22 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/vector.zig+17-37
...@@ -25,7 +25,6 @@ test "implicit cast vector to array - bool" {...@@ -25,7 +25,6 @@ test "implicit cast vector to array - bool" {
2525
26test "vector wrap operators" {26test "vector wrap operators" {
27 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO27 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
29 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO28 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
30 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO30 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -48,7 +47,6 @@ test "vector wrap operators" {...@@ -48,7 +47,6 @@ test "vector wrap operators" {
4847
49test "vector bin compares with mem.eql" {48test "vector bin compares with mem.eql" {
50 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
52 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO50 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
53 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO51 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
54 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -93,12 +91,18 @@ test "vector int operators" {...@@ -93,12 +91,18 @@ test "vector int operators" {
9391
94test "vector float operators" {92test "vector float operators" {
95 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO93 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
96 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
97 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO94 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO95 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO96 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
100 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO97 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10198
99 if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and
100 builtin.zig_backend == .stage2_c)
101 {
102 // https://github.com/ziglang/zig/issues/13876
103 return error.SkipZigTest;
104 }
105
102 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {106 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
103 const S = struct {107 const S = struct {
104 fn doTheTest() !void {108 fn doTheTest() !void {
...@@ -117,7 +121,6 @@ test "vector float operators" {...@@ -117,7 +121,6 @@ test "vector float operators" {
117121
118test "vector bit operators" {122test "vector bit operators" {
119 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO123 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
120 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
121 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO124 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
122 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO125 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
123 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO126 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -237,7 +240,6 @@ test "vector casts of sizes not divisible by 8" {...@@ -237,7 +240,6 @@ test "vector casts of sizes not divisible by 8" {
237}240}
238241
239test "vector @splat" {242test "vector @splat" {
240 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
241 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO243 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
242 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO244 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO245 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -380,7 +382,6 @@ test "store vector elements via runtime index" {...@@ -380,7 +382,6 @@ test "store vector elements via runtime index" {
380}382}
381383
382test "initialize vector which is a struct field" {384test "initialize vector which is a struct field" {
383 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
384 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO385 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
385 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO386 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
386 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO387 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -403,7 +404,6 @@ test "initialize vector which is a struct field" {...@@ -403,7 +404,6 @@ test "initialize vector which is a struct field" {
403404
404test "vector comparison operators" {405test "vector comparison operators" {
405 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO406 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
406 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
407 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO407 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
408 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO408 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
409 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO409 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -444,7 +444,6 @@ test "vector comparison operators" {...@@ -444,7 +444,6 @@ test "vector comparison operators" {
444444
445test "vector division operators" {445test "vector division operators" {
446 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO446 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
447 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
448 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO447 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
449 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO448 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
450 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO449 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -527,7 +526,6 @@ test "vector division operators" {...@@ -527,7 +526,6 @@ test "vector division operators" {
527526
528test "vector bitwise not operator" {527test "vector bitwise not operator" {
529 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO528 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
530 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
531 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO529 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
532 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO530 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
533 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO531 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -559,7 +557,6 @@ test "vector bitwise not operator" {...@@ -559,7 +557,6 @@ test "vector bitwise not operator" {
559557
560test "vector shift operators" {558test "vector shift operators" {
561 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO559 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
562 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
563 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO560 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
564 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO561 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
565 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO562 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -653,7 +650,6 @@ test "vector shift operators" {...@@ -653,7 +650,6 @@ test "vector shift operators" {
653650
654test "vector reduce operation" {651test "vector reduce operation" {
655 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO652 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
656 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
657 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO653 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
658 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO654 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
659 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO655 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -709,7 +705,7 @@ test "vector reduce operation" {...@@ -709,7 +705,7 @@ test "vector reduce operation" {
709705
710 // LLVM 11 ERROR: Cannot select type706 // LLVM 11 ERROR: Cannot select type
711 // https://github.com/ziglang/zig/issues/7138707 // https://github.com/ziglang/zig/issues/7138
712 if (builtin.target.cpu.arch != .aarch64) {708 if (builtin.zig_backend != .stage2_llvm or builtin.target.cpu.arch != .aarch64) {
713 try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386));709 try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386));
714 try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9));710 try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9));
715 }711 }
...@@ -727,7 +723,7 @@ test "vector reduce operation" {...@@ -727,7 +723,7 @@ test "vector reduce operation" {
727723
728 // LLVM 11 ERROR: Cannot select type724 // LLVM 11 ERROR: Cannot select type
729 // https://github.com/ziglang/zig/issues/7138725 // https://github.com/ziglang/zig/issues/7138
730 if (builtin.target.cpu.arch != .aarch64) {726 if (builtin.zig_backend != .stage2_llvm or builtin.target.cpu.arch != .aarch64) {
731 try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567));727 try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567));
732 try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999));728 try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999));
733 }729 }
...@@ -775,14 +771,14 @@ test "vector reduce operation" {...@@ -775,14 +771,14 @@ test "vector reduce operation" {
775771
776 // LLVM 11 ERROR: Cannot select type772 // LLVM 11 ERROR: Cannot select type
777 // https://github.com/ziglang/zig/issues/7138773 // https://github.com/ziglang/zig/issues/7138
778 if (false) {774 if (builtin.zig_backend != .stage2_llvm) {
779 try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);775 try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, -1.9));
780 try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);776 try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, -1.9));
781 try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);777 try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, -1.9));
782778
783 try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);779 try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, 100.0));
784 try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);780 try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, 100.0));
785 try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);781 try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, 100.0));
786 }782 }
787783
788 try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);784 try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
...@@ -797,7 +793,6 @@ test "vector reduce operation" {...@@ -797,7 +793,6 @@ test "vector reduce operation" {
797793
798test "vector @reduce comptime" {794test "vector @reduce comptime" {
799 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO795 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
800 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
801 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO796 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
802 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO797 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
803 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO798 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -813,7 +808,6 @@ test "vector @reduce comptime" {...@@ -813,7 +808,6 @@ test "vector @reduce comptime" {
813808
814test "mask parameter of @shuffle is comptime scope" {809test "mask parameter of @shuffle is comptime scope" {
815 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO810 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
816 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
817 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO811 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
818 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO812 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
819 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO813 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -833,7 +827,6 @@ test "mask parameter of @shuffle is comptime scope" {...@@ -833,7 +827,6 @@ test "mask parameter of @shuffle is comptime scope" {
833827
834test "saturating add" {828test "saturating add" {
835 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO829 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
836 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
837 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO830 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
838 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO831 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
839 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO832 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -865,7 +858,6 @@ test "saturating add" {...@@ -865,7 +858,6 @@ test "saturating add" {
865858
866test "saturating subtraction" {859test "saturating subtraction" {
867 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO860 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
868 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
869 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO861 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
870 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO862 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
871 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO863 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -888,7 +880,6 @@ test "saturating subtraction" {...@@ -888,7 +880,6 @@ test "saturating subtraction" {
888880
889test "saturating multiplication" {881test "saturating multiplication" {
890 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO882 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
891 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
892 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO883 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
893 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO884 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
894 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO885 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -915,7 +906,6 @@ test "saturating multiplication" {...@@ -915,7 +906,6 @@ test "saturating multiplication" {
915906
916test "saturating shift-left" {907test "saturating shift-left" {
917 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO908 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
918 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
919 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO909 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO910 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
921 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO911 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -1049,7 +1039,6 @@ test "@mulWithOverflow" {...@@ -1049,7 +1039,6 @@ test "@mulWithOverflow" {
1049}1039}
10501040
1051test "@shlWithOverflow" {1041test "@shlWithOverflow" {
1052 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1053 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1042 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1054 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1043 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1055 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1044 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -1078,7 +1067,6 @@ test "alignment of vectors" {...@@ -1078,7 +1067,6 @@ test "alignment of vectors" {
10781067
1079test "loading the second vector from a slice of vectors" {1068test "loading the second vector from a slice of vectors" {
1080 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1069 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1081 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1082 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1070 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1083 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1071 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1084 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1072 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1131,7 +1119,6 @@ test "byte vector initialized in inline function" {...@@ -1131,7 +1119,6 @@ test "byte vector initialized in inline function" {
1131}1119}
11321120
1133test "byte vector initialized in inline function" {1121test "byte vector initialized in inline function" {
1134 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1135 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1122 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1136 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1123 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1137 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1124 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -1160,7 +1147,6 @@ test "byte vector initialized in inline function" {...@@ -1160,7 +1147,6 @@ test "byte vector initialized in inline function" {
11601147
1161test "zero divisor" {1148test "zero divisor" {
1162 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1149 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1163 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1164 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1150 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1165 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1166 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1152 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1181,7 +1167,6 @@ test "zero divisor" {...@@ -1181,7 +1167,6 @@ test "zero divisor" {
11811167
1182test "zero multiplicand" {1168test "zero multiplicand" {
1183 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1169 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1184 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1185 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1186 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1171 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1187 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1172 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -1204,7 +1189,6 @@ test "zero multiplicand" {...@@ -1204,7 +1189,6 @@ test "zero multiplicand" {
12041189
1205test "@intCast to u0" {1190test "@intCast to u0" {
1206 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1191 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1207 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1208 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1192 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1209 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1193 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1210 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1194 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -1228,7 +1212,6 @@ test "modRem with zero divisor" {...@@ -1228,7 +1212,6 @@ test "modRem with zero divisor" {
12281212
1229test "array operands to shuffle are coerced to vectors" {1213test "array operands to shuffle are coerced to vectors" {
1230 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1214 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1231 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1232 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1215 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1233 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1216 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1217 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -1246,8 +1229,6 @@ test "load packed vector element" {...@@ -1246,8 +1229,6 @@ test "load packed vector element" {
1246 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1229 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1247 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1230 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1248 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1231 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1249 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1250 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12511232
1252 var x: @Vector(2, u15) = .{ 1, 4 };1233 var x: @Vector(2, u15) = .{ 1, 4 };
1253 try expect((&x[0]).* == 1);1234 try expect((&x[0]).* == 1);
...@@ -1260,7 +1241,6 @@ test "store packed vector element" {...@@ -1260,7 +1241,6 @@ test "store packed vector element" {
1260 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1241 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1261 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1242 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1262 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1243 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1263 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12641244
1265 var v = @Vector(4, u1){ 1, 1, 1, 1 };1245 var v = @Vector(4, u1){ 1, 1, 1, 1 };
1266 try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v);1246 try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v);
test/behavior/void.zig-1
...@@ -33,7 +33,6 @@ fn times(n: usize) []const void {...@@ -33,7 +33,6 @@ fn times(n: usize) []const void {
33}33}
3434
35test "void optional" {35test "void optional" {
36 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;36 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
38 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;37 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO38 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/while.zig-8
...@@ -104,7 +104,6 @@ fn testBreakOuter() void {...@@ -104,7 +104,6 @@ fn testBreakOuter() void {
104}104}
105105
106test "while copies its payload" {106test "while copies its payload" {
107 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
108 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
109 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO108 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
110109
...@@ -143,7 +142,6 @@ fn runContinueAndBreakTest() !void {...@@ -143,7 +142,6 @@ fn runContinueAndBreakTest() !void {
143}142}
144143
145test "while with optional as condition" {144test "while with optional as condition" {
146 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
147 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;145 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
148 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO146 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
149147
...@@ -156,7 +154,6 @@ test "while with optional as condition" {...@@ -156,7 +154,6 @@ test "while with optional as condition" {
156}154}
157155
158test "while with optional as condition with else" {156test "while with optional as condition with else" {
159 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
160 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;157 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
161 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO158 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
162159
...@@ -174,7 +171,6 @@ test "while with optional as condition with else" {...@@ -174,7 +171,6 @@ test "while with optional as condition with else" {
174}171}
175172
176test "while with error union condition" {173test "while with error union condition" {
177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
178 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO174 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
179175
180 numbers_left = 10;176 numbers_left = 10;
...@@ -205,7 +201,6 @@ test "while on bool with else result follow break prong" {...@@ -205,7 +201,6 @@ test "while on bool with else result follow break prong" {
205}201}
206202
207test "while on optional with else result follow else prong" {203test "while on optional with else result follow else prong" {
208 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
209 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;204 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
210 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;205 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
211 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO206 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -217,7 +212,6 @@ test "while on optional with else result follow else prong" {...@@ -217,7 +212,6 @@ test "while on optional with else result follow else prong" {
217}212}
218213
219test "while on optional with else result follow break prong" {214test "while on optional with else result follow break prong" {
220 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;215 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
222 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;216 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
223 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO217 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -290,7 +284,6 @@ test "while bool 2 break statements and an else" {...@@ -290,7 +284,6 @@ test "while bool 2 break statements and an else" {
290}284}
291285
292test "while optional 2 break statements and an else" {286test "while optional 2 break statements and an else" {
293 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
294 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO287 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
295 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO288 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
296 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO289 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -310,7 +303,6 @@ test "while optional 2 break statements and an else" {...@@ -310,7 +303,6 @@ test "while optional 2 break statements and an else" {
310}303}
311304
312test "while error 2 break statements and an else" {305test "while error 2 break statements and an else" {
313 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
314 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO306 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
315 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO307 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
316 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO308 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/widening.zig-1
...@@ -28,7 +28,6 @@ test "integer widening u0 to u8" {...@@ -28,7 +28,6 @@ test "integer widening u0 to u8" {
28}28}
2929
30test "implicit unsigned integer to signed integer" {30test "implicit unsigned integer to signed integer" {
31 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
32 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO31 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
33 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO32 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3433
test/cases.zig+5-5
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const TestContext = @import("../src/test.zig").TestContext;2const Cases = @import("src/Cases.zig");
33
4pub fn addCases(ctx: *TestContext) !void {4pub fn addCases(cases: *Cases) !void {
5 try @import("compile_errors.zig").addCases(ctx);5 try @import("compile_errors.zig").addCases(cases);
6 try @import("stage2/cbe.zig").addCases(ctx);6 try @import("cbe.zig").addCases(cases);
7 try @import("stage2/nvptx.zig").addCases(ctx);7 try @import("nvptx.zig").addCases(cases);
8}8}
test/cases/compile_errors/access_inactive_union_field_comptime.zig+1
...@@ -21,3 +21,4 @@ pub export fn entry1() void {...@@ -21,3 +21,4 @@ pub export fn entry1() void {
21// :9:15: error: access of union field 'a' while field 'b' is active21// :9:15: error: access of union field 'a' while field 'b' is active
22// :2:21: note: union declared here22// :2:21: note: union declared here
23// :14:16: error: access of union field 'a' while field 'b' is active23// :14:16: error: access of union field 'a' while field 'b' is active
24// :2:21: note: union declared here
test/cases/compile_errors/attempted_implicit_cast_from_T_to_long_array_ptr.zig created+18
...@@ -0,0 +1,18 @@
1export fn entry0(single: *u32) void {
2 _ = @as(*const [0]u32, single);
3}
4export fn entry1(single: *u32) void {
5 _ = @as(*const [1]u32, single);
6}
7export fn entry2(single: *u32) void {
8 _ = @as(*const [2]u32, single);
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :2:28: error: expected type '*const [0]u32', found '*u32'
16// :2:28: note: pointer type child 'u32' cannot cast into pointer type child '[0]u32'
17// :8:28: error: expected type '*const [2]u32', found '*u32'
18// :8:28: note: pointer type child 'u32' cannot cast into pointer type child '[2]u32'
test/cases/compile_errors/bad_import.zig+1-1
...@@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",);...@@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",);
4// backend=stage24// backend=stage2
5// target=native5// target=native
6//6//
7// :1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound7// bogus-does-not-exist.zig': FileNotFound
test/cases/compile_errors/break_void_result_location.zig created+32
...@@ -0,0 +1,32 @@
1export fn f1() void {
2 const x: usize = for ("hello") |_| {};
3 _ = x;
4}
5export fn f2() void {
6 const x: usize = for ("hello") |_| {
7 break;
8 };
9 _ = x;
10}
11export fn f3() void {
12 var t: bool = true;
13 const x: usize = while (t) {
14 break;
15 };
16 _ = x;
17}
18export fn f4() void {
19 const x: usize = blk: {
20 break :blk;
21 };
22 _ = x;
23}
24
25// error
26// backend=stage2
27// target=native
28//
29// :2:22: error: expected type 'usize', found 'void'
30// :7:9: error: expected type 'usize', found 'void'
31// :14:9: error: expected type 'usize', found 'void'
32// :18:1: error: expected type 'usize', found 'void'
test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig+4
...@@ -15,3 +15,7 @@ pub export fn entry() void {...@@ -15,3 +15,7 @@ pub export fn entry() void {
15// target=native15// target=native
16//16//
17// :6:5: error: found compile log statement17// :6:5: error: found compile log statement
18//
19// Compile Log Output:
20// @as(tmp.Bar, .{ .X = 123 })
21// @as(tmp.Bar, [runtime value])
test/cases/compile_errors/compile_log.zig+9
...@@ -17,3 +17,12 @@ export fn baz() void {...@@ -17,3 +17,12 @@ export fn baz() void {
17//17//
18// :5:5: error: found compile log statement18// :5:5: error: found compile log statement
19// :11:5: note: also here19// :11:5: note: also here
20//
21// Compile Log Output:
22// @as(*const [5:0]u8, "begin")
23// @as(*const [1:0]u8, "a"), @as(i32, 12), @as(*const [1:0]u8, "b"), @as([]const u8, "hi")
24// @as(*const [3:0]u8, "end")
25// @as(comptime_int, 4)
26// @as(*const [5:0]u8, "begin")
27// @as(*const [1:0]u8, "a"), @as(i32, [runtime value]), @as(*const [1:0]u8, "b"), @as([]const u8, [runtime value])
28// @as(*const [3:0]u8, "end")
test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig+4-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1export fn entry() void {1export fn entry() void {
2 @compileLog(@ptrCast(*const anyopaque, &entry));2 @compileLog(@as(*align(1) const anyopaque, @ptrCast(*const anyopaque, &entry)));
3}3}
44
5// error5// error
...@@ -7,3 +7,6 @@ export fn entry() void {...@@ -7,3 +7,6 @@ export fn entry() void {
7// target=native7// target=native
8//8//
9// :2:5: error: found compile log statement9// :2:5: error: found compile log statement
10//
11// Compile Log Output:
12// @as(*const anyopaque, (function 'entry'))
test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig+3
...@@ -12,3 +12,6 @@ export fn entry() void {...@@ -12,3 +12,6 @@ export fn entry() void {
12// target=native12// target=native
13//13//
14// :2:5: error: found compile log statement14// :2:5: error: found compile log statement
15//
16// Compile Log Output:
17// @as(*const [3:0]u8, "i32\x00")
test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig+5
...@@ -13,3 +13,8 @@ fn inner(comptime n: usize) void {...@@ -13,3 +13,8 @@ fn inner(comptime n: usize) void {
13//13//
14// :7:39: error: found compile log statement14// :7:39: error: found compile log statement
15// :7:39: note: also here15// :7:39: note: also here
16//
17// Compile Log Output:
18// @as(*const [4:0]u8, "!@#$")
19// @as(*const [4:0]u8, "!@#$")
20// @as(*const [4:0]u8, "!@#$")
test/cases/compile_errors/comptime_try_non_error.zig created+18
...@@ -0,0 +1,18 @@
1comptime {
2 foo();
3}
4
5fn foo() void {
6 try bar();
7}
8
9pub fn bar() u8 {
10 return 0;
11}
12
13// error
14// backend=stage2
15// target=native
16//
17// :6:12: error: expected error union type, found 'u8'
18// :2:8: note: called from here
test/cases/compile_errors/condition_comptime_reason_explained.zig+2
...@@ -45,4 +45,6 @@ pub export fn entry2() void {...@@ -45,4 +45,6 @@ pub export fn entry2() void {
45// :22:13: error: unable to resolve comptime value45// :22:13: error: unable to resolve comptime value
46// :22:13: note: condition in comptime switch must be comptime-known46// :22:13: note: condition in comptime switch must be comptime-known
47// :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S'47// :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S'
48// :2:12: note: struct requires comptime because of this field
49// :2:12: note: use '*const fn() void' for a function pointer type
48// :32:19: note: called from here50// :32:19: note: called from here
test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig+1
...@@ -32,6 +32,7 @@ export fn d() void {...@@ -32,6 +32,7 @@ export fn d() void {
32// :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs32// :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs
33// :1:11: note: opaque declared here33// :1:11: note: opaque declared here
34// :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions34// :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions
35// :1:11: note: opaque declared here
35// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs36// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs
36// :18:22: note: opaque declared here37// :18:22: note: opaque declared here
37// :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs38// :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs
test/cases/compile_errors/exporting_primitive_values.zig created+29
...@@ -0,0 +1,29 @@
1pub export fn entry1() void {
2 @export(u100, .{ .name = "a" });
3}
4pub export fn entry3() void {
5 @export(undefined, .{ .name = "b" });
6}
7pub export fn entry4() void {
8 @export(null, .{ .name = "c" });
9}
10pub export fn entry5() void {
11 @export(false, .{ .name = "d" });
12}
13pub export fn entry6() void {
14 @export(u8, .{ .name = "e" });
15}
16pub export fn entry7() void {
17 @export(u65535, .{ .name = "f" });
18}
19
20// error
21// backend=llvm
22// target=native
23//
24// :2:13: error: unable to export primitive value
25// :5:13: error: unable to export primitive value
26// :8:13: error: unable to export primitive value
27// :11:13: error: unable to export primitive value
28// :14:13: error: unable to export primitive value
29// :17:13: error: unable to export primitive value
test/cases/compile_errors/extern_function_with_comptime_parameter.zig+1-1
...@@ -12,6 +12,6 @@ comptime { _ = entry2; }...@@ -12,6 +12,6 @@ comptime { _ = entry2; }
12// backend=stage212// backend=stage2
13// target=native13// target=native
14//14//
15// :1:15: error: comptime parameters not allowed in function with calling convention 'C'
16// :5:30: error: comptime parameters not allowed in function with calling convention 'C'15// :5:30: error: comptime parameters not allowed in function with calling convention 'C'
17// :6:30: error: generic parameters not allowed in function with calling convention 'C'16// :6:30: error: generic parameters not allowed in function with calling convention 'C'
17// :1:15: error: comptime parameters not allowed in function with calling convention 'C'
test/cases/compile_errors/function_parameter_is_opaque.zig+1
...@@ -27,4 +27,5 @@ export fn entry4() void {...@@ -27,4 +27,5 @@ export fn entry4() void {
27// :1:17: note: opaque declared here27// :1:17: note: opaque declared here
28// :8:28: error: parameter of type '@TypeOf(null)' not allowed28// :8:28: error: parameter of type '@TypeOf(null)' not allowed
29// :12:8: error: parameter of opaque type 'tmp.FooType' not allowed29// :12:8: error: parameter of opaque type 'tmp.FooType' not allowed
30// :1:17: note: opaque declared here
30// :17:8: error: parameter of type '@TypeOf(null)' not allowed31// :17:8: error: parameter of type '@TypeOf(null)' not allowed
test/cases/compile_errors/helpful_return_type_error_message.zig+1-1
...@@ -24,9 +24,9 @@ export fn quux() u32 {...@@ -24,9 +24,9 @@ export fn quux() u32 {
24// :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set'24// :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set'
25// :7:17: note: function cannot return an error25// :7:17: note: function cannot return an error
26// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'26// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
27// :10:17: note: function cannot return an error
28// :11:15: note: cannot convert error union to payload type27// :11:15: note: cannot convert error union to payload type
29// :11:15: note: consider using 'try', 'catch', or 'if'28// :11:15: note: consider using 'try', 'catch', or 'if'
29// :10:17: note: function cannot return an error
30// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'30// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
31// :15:14: note: cannot convert error union to payload type31// :15:14: note: cannot convert error union to payload type
32// :15:14: note: consider using 'try', 'catch', or 'if'32// :15:14: note: consider using 'try', 'catch', or 'if'
test/cases/compile_errors/implicit_semicolon-block_expr.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = {}4 _ = {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-block_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 ({})4 ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-comptime_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = comptime {}4 _ = comptime {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-comptime_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 comptime ({})4 comptime ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-defer.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 defer ({})4 defer ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-for_expression.zig+3
...@@ -3,7 +3,10 @@ export fn entry() void {...@@ -3,7 +3,10 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = for(foo()) |_| {}4 _ = for(foo()) |_| {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
9fn foo() void {}
710
8// error11// error
9// backend=stage212// backend=stage2
test/cases/compile_errors/implicit_semicolon-for_statement.zig+3
...@@ -3,7 +3,10 @@ export fn entry() void {...@@ -3,7 +3,10 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 for(foo()) |_| ({})4 for(foo()) |_| ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
9fn foo() void {}
710
8// error11// error
9// backend=stage212// backend=stage2
test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = if(true) {} else if(true) {} else {}4 _ = if(true) {} else if(true) {} else {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 if(true) ({}) else if(true) ({}) else ({})4 if(true) ({}) else if(true) ({}) else ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = if(true) {} else if(true) {}4 _ = if(true) {} else if(true) {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 if(true) ({}) else if(true) ({})4 if(true) ({}) else if(true) ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if-else_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = if(true) {} else {}4 _ = if(true) {} else {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if-else_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 if(true) ({}) else ({})4 if(true) ({}) else ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = if(true) {}4 _ = if(true) {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-if_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 if(true) ({})4 if(true) ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-test_expression.zig+3
...@@ -3,7 +3,10 @@ export fn entry() void {...@@ -3,7 +3,10 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = if (foo()) |_| {}4 _ = if (foo()) |_| {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
9fn foo() void {}
710
8// error11// error
9// backend=stage212// backend=stage2
test/cases/compile_errors/implicit_semicolon-test_statement.zig+3
...@@ -3,7 +3,10 @@ export fn entry() void {...@@ -3,7 +3,10 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 if (foo()) |_| ({})4 if (foo()) |_| ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
9fn foo() void {}
710
8// error11// error
9// backend=stage212// backend=stage2
test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = while(true):({}) {}4 _ = while(true):({}) {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 while(true):({}) ({})4 while(true):({}) ({})
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-while_expression.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 _ = while(true) {}4 _ = while(true) {}
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/implicit_semicolon-while_statement.zig+2
...@@ -3,6 +3,8 @@ export fn entry() void {...@@ -3,6 +3,8 @@ export fn entry() void {
3 var good = {};3 var good = {};
4 while(true) 14 while(true) 1
5 var bad = {};5 var bad = {};
6 _ = good;
7 _ = bad;
6}8}
79
8// error10// error
test/cases/compile_errors/invalid_member_of_builtin_enum.zig+1-1
...@@ -9,4 +9,4 @@ export fn entry() void {...@@ -9,4 +9,4 @@ export fn entry() void {
9// target=native9// target=native
10//10//
11// :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86'11// :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86'
12// :?:18: note: enum declared here12// : note: enum declared here
test/cases/compile_errors/invalid_store_to_comptime_field.zig+1-1
...@@ -73,11 +73,11 @@ pub export fn entry8() void {...@@ -73,11 +73,11 @@ pub export fn entry8() void {
73//73//
74// :6:19: error: value stored in comptime field does not match the default value of the field74// :6:19: error: value stored in comptime field does not match the default value of the field
75// :14:19: error: value stored in comptime field does not match the default value of the field75// :14:19: error: value stored in comptime field does not match the default value of the field
76// :53:16: error: value stored in comptime field does not match the default value of the field
77// :19:38: error: value stored in comptime field does not match the default value of the field76// :19:38: error: value stored in comptime field does not match the default value of the field
78// :31:19: error: value stored in comptime field does not match the default value of the field77// :31:19: error: value stored in comptime field does not match the default value of the field
79// :25:29: note: default value set here78// :25:29: note: default value set here
80// :41:16: error: value stored in comptime field does not match the default value of the field79// :41:16: error: value stored in comptime field does not match the default value of the field
81// :45:12: error: value stored in comptime field does not match the default value of the field80// :45:12: error: value stored in comptime field does not match the default value of the field
81// :53:16: error: value stored in comptime field does not match the default value of the field
82// :66:43: error: value stored in comptime field does not match the default value of the field82// :66:43: error: value stored in comptime field does not match the default value of the field
83// :59:35: error: value stored in comptime field does not match the default value of the field83// :59:35: error: value stored in comptime field does not match the default value of the field
test/cases/compile_errors/invalid_struct_field.zig+1
...@@ -25,5 +25,6 @@ export fn e() void {...@@ -25,5 +25,6 @@ export fn e() void {
25// :4:7: error: no field named 'foo' in struct 'tmp.A'25// :4:7: error: no field named 'foo' in struct 'tmp.A'
26// :1:11: note: struct declared here26// :1:11: note: struct declared here
27// :10:17: error: no field named 'bar' in struct 'tmp.A'27// :10:17: error: no field named 'bar' in struct 'tmp.A'
28// :1:11: note: struct declared here
28// :18:45: error: no field named 'f' in struct 'tmp.e.B'29// :18:45: error: no field named 'f' in struct 'tmp.e.B'
29// :14:15: note: struct declared here30// :14:15: note: struct declared here
test/cases/compile_errors/method_call_with_first_arg_type_primitive.zig+2-1
...@@ -2,7 +2,7 @@ const Foo = struct {...@@ -2,7 +2,7 @@ const Foo = struct {
2 x: i32,2 x: i32,
33
4 fn init(x: i32) Foo {4 fn init(x: i32) Foo {
5 return Foo {5 return Foo{
6 .x = x,6 .x = x,
7 };7 };
8 }8 }
...@@ -20,3 +20,4 @@ export fn f() void {...@@ -20,3 +20,4 @@ export fn f() void {
20//20//
21// :14:9: error: no field or member function named 'init' in 'tmp.Foo'21// :14:9: error: no field or member function named 'init' in 'tmp.Foo'
22// :1:13: note: struct declared here22// :1:13: note: struct declared here
23// :4:5: note: 'init' is not a member function
test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig+1
...@@ -29,3 +29,4 @@ export fn foo() void {...@@ -29,3 +29,4 @@ export fn foo() void {
29//29//
30// :23:6: error: no field or member function named 'init' in 'tmp.List'30// :23:6: error: no field or member function named 'init' in 'tmp.List'
31// :1:18: note: struct declared here31// :1:18: note: struct declared here
32// :5:9: note: 'init' is not a member function
test/cases/compile_errors/missing_main_fn_in_executable.zig+4-2
...@@ -5,5 +5,7 @@...@@ -5,5 +5,7 @@
5// target=x86_64-linux5// target=x86_64-linux
6// output_mode=Exe6// output_mode=Exe
7//7//
8// :?:?: error: root struct of file 'tmp' has no member named 'main'8// : error: root struct of file 'tmp' has no member named 'main'
9// :?:?: note: called from here9// : note: called from here
10// : note: called from here
11// : note: called from here
test/cases/compile_errors/private_main_fn.zig+4-2
...@@ -5,6 +5,8 @@ fn main() void {}...@@ -5,6 +5,8 @@ fn main() void {}
5// target=x86_64-linux5// target=x86_64-linux
6// output_mode=Exe6// output_mode=Exe
7//7//
8// :?:?: error: 'main' is not marked 'pub'8// : error: 'main' is not marked 'pub'
9// :1:1: note: declared here9// :1:1: note: declared here
10// :?:?: note: called from here10// : note: called from here
11// : note: called from here
12// : note: called from here
test/cases/compile_errors/reify_type_with_undefined.zig+13
...@@ -11,6 +11,18 @@ comptime {...@@ -11,6 +11,18 @@ comptime {
11 },11 },
12 });12 });
13}13}
14comptime {
15 const std = @import("std");
16 const fields: [1]std.builtin.Type.StructField = undefined;
17 _ = @Type(.{
18 .Struct = .{
19 .layout = .Auto,
20 .fields = &fields,
21 .decls = &.{},
22 .is_tuple = false,
23 },
24 });
25}
1426
15// error27// error
16// backend=stage228// backend=stage2
...@@ -18,3 +30,4 @@ comptime {...@@ -18,3 +30,4 @@ comptime {
18//30//
19// :2:9: error: use of undefined value here causes undefined behavior31// :2:9: error: use of undefined value here causes undefined behavior
20// :5:9: error: use of undefined value here causes undefined behavior32// :5:9: error: use of undefined value here causes undefined behavior
33// :17:9: error: use of undefined value here causes undefined behavior
test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig+3-2
...@@ -15,5 +15,6 @@ export fn entry() void {...@@ -15,5 +15,6 @@ export fn entry() void {
15// target=native15// target=native
16//16//
17// :9:51: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known17// :9:51: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known
18// :?:21: note: struct requires comptime because of this field18// : note: struct requires comptime because of this field
19// :?:21: note: types are not available at runtime19// : note: types are not available at runtime
20// : struct requires comptime because of this field
test/cases/compile_errors/struct_type_mismatch_in_arg.zig+1-1
...@@ -13,6 +13,6 @@ comptime {...@@ -13,6 +13,6 @@ comptime {
13// target=native13// target=native
14//14//
15// :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar'15// :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar'
16// :1:13: note: struct declared here
17// :2:13: note: struct declared here16// :2:13: note: struct declared here
17// :1:13: note: struct declared here
18// :4:18: note: parameter type declared here18// :4:18: note: parameter type declared here
test/cases/compile_errors/undefined_as_field_type_is_rejected.zig+8-4
...@@ -1,9 +1,13 @@...@@ -1,9 +1,13 @@
1export fn a() void {1const Foo = struct {
2 b();2 a: undefined,
3};
4export fn entry1() void {
5 const foo: Foo = undefined;
6 _ = foo;
3}7}
48
5// error9// error
6// backend=stage210// backend=stage1
7// target=native11// target=native
8//12//
9// :2:5: error: use of undeclared identifier 'b'13// tmp.zig:2:8: error: use of undefined value here causes undefined behavior
test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig+2-1
...@@ -28,10 +28,11 @@ export fn u2m() void {...@@ -28,10 +28,11 @@ export fn u2m() void {
28// target=native28// target=native
29//29//
30// :9:1: error: union initializer must initialize one field30// :9:1: error: union initializer must initialize one field
31// :1:12: note: union declared here
31// :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field32// :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field
32// :14:31: note: additional initializer here33// :14:31: note: additional initializer here
34// :1:12: note: union declared here
33// :18:21: error: union initializer must initialize one field35// :18:21: error: union initializer must initialize one field
34// :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field36// :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field
35// :22:31: note: additional initializer here37// :22:31: note: additional initializer here
36// :1:12: note: union declared here
37// :5:12: note: union declared here38// :5:12: note: union declared here
test/cases/compile_log.0.zig+5
...@@ -15,3 +15,8 @@ fn x() void {}...@@ -15,3 +15,8 @@ fn x() void {}
15// error15// error
16//16//
17// :6:23: error: expected type 'usize', found 'bool'17// :6:23: error: expected type 'usize', found 'bool'
18//
19// Compile Log Output:
20// @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x'))
21// @as(comptime_int, 1000)
22// @as(comptime_int, 1234)
test/cases/compile_log.1.zig+4
...@@ -14,3 +14,7 @@ fn x() void {}...@@ -14,3 +14,7 @@ fn x() void {}
14//14//
15// :9:5: error: found compile log statement15// :9:5: error: found compile log statement
16// :4:5: note: also here16// :4:5: note: also here
17//
18// Compile Log Output:
19// @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x'))
20// @as(comptime_int, 1000)
test/cases/f32_passed_to_variadic_fn.zig+2-1
...@@ -9,7 +9,8 @@ pub fn main() void {...@@ -9,7 +9,8 @@ pub fn main() void {
9// run9// run
10// backend=llvm10// backend=llvm
11// target=x86_64-linux-gnu11// target=x86_64-linux-gnu
12// link_libc=1
12//13//
13// f64: 2.00000014// f64: 2.000000
14// f32: 10.00000015// f32: 10.000000
15//
\ No newline at end of file
16//
test/cases/fn_typeinfo_passed_to_comptime_fn.zig+1
...@@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void {...@@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void {
1414
15// run15// run
16// is_test=116// is_test=1
17// backend=llvm
17//18//
test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig+1-1
...@@ -5,7 +5,7 @@ pub fn main() void {...@@ -5,7 +5,7 @@ pub fn main() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// compile
9// output_mode=Exe9// output_mode=Exe
10// backend=llvm10// backend=llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig+1-1
...@@ -5,7 +5,7 @@ pub fn main() void {...@@ -5,7 +5,7 @@ pub fn main() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// compile
9// output_mode=Exe9// output_mode=Exe
10// backend=stage2,llvm10// backend=stage2,llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig+1-1
...@@ -6,7 +6,7 @@ pub fn main() void {...@@ -6,7 +6,7 @@ pub fn main() void {
6 _ = entry;6 _ = entry;
7}7}
88
9// error9// compile
10// output_mode=Exe10// output_mode=Exe
11// backend=llvm11// backend=llvm
12// target=x86_64-linux,x86_64-macos12// target=x86_64-linux,x86_64-macos
test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig+1-1
...@@ -6,7 +6,7 @@ pub fn main() void {...@@ -6,7 +6,7 @@ pub fn main() void {
6 _ = entry;6 _ = entry;
7}7}
88
9// error9// compile
10// output_mode=Exe10// output_mode=Exe
11// backend=stage2,llvm11// backend=stage2,llvm
12// target=x86_64-linux,x86_64-macos12// target=x86_64-linux,x86_64-macos
test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig+1-1
...@@ -5,7 +5,7 @@ pub fn main() void {...@@ -5,7 +5,7 @@ pub fn main() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// compile
9// output_mode=Exe9// output_mode=Exe
10// backend=stage2,llvm10// backend=stage2,llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
test/cases/llvm/hello_world.zig+1
...@@ -7,6 +7,7 @@ pub fn main() void {...@@ -7,6 +7,7 @@ pub fn main() void {
7// run7// run
8// backend=llvm8// backend=llvm
9// target=x86_64-linux,x86_64-macos9// target=x86_64-linux,x86_64-macos
10// link_libc=1
10//11//
11// hello world!12// hello world!
12//13//
test/cases/llvm/pointer_keeps_address_space.zig+1-1
...@@ -5,7 +5,7 @@ pub fn main() void {...@@ -5,7 +5,7 @@ pub fn main() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// compile
9// output_mode=Exe9// output_mode=Exe
10// backend=stage2,llvm10// backend=stage2,llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig+1-1
...@@ -5,7 +5,7 @@ pub fn main() void {...@@ -5,7 +5,7 @@ pub fn main() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// compile
9// output_mode=Exe9// output_mode=Exe
10// backend=stage2,llvm10// backend=stage2,llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig+1-1
...@@ -5,7 +5,7 @@ pub fn main() void {...@@ -5,7 +5,7 @@ pub fn main() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// compile
9// output_mode=Exe9// output_mode=Exe
10// backend=stage2,llvm10// backend=stage2,llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
test/cbe.zig created+950
...@@ -0,0 +1,950 @@
1const std = @import("std");
2const Cases = @import("src/Cases.zig");
3
4// These tests should work with all platforms, but we're using linux_x64 for
5// now for consistency. Will be expanded eventually.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
10
11pub fn addCases(ctx: *Cases) !void {
12 {
13 var case = ctx.exeFromCompiledC("hello world with updates", .{});
14
15 // Regular old hello world
16 case.addCompareOutput(
17 \\extern fn puts(s: [*:0]const u8) c_int;
18 \\pub export fn main() c_int {
19 \\ _ = puts("hello world!");
20 \\ return 0;
21 \\}
22 , "hello world!" ++ std.cstr.line_sep);
23
24 // Now change the message only
25 case.addCompareOutput(
26 \\extern fn puts(s: [*:0]const u8) c_int;
27 \\pub export fn main() c_int {
28 \\ _ = puts("yo");
29 \\ return 0;
30 \\}
31 , "yo" ++ std.cstr.line_sep);
32
33 // Add an unused Decl
34 case.addCompareOutput(
35 \\extern fn puts(s: [*:0]const u8) c_int;
36 \\pub export fn main() c_int {
37 \\ _ = puts("yo!");
38 \\ return 0;
39 \\}
40 \\fn unused() void {}
41 , "yo!" ++ std.cstr.line_sep);
42
43 // Comptime return type and calling convention expected.
44 case.addError(
45 \\var x: i32 = 1234;
46 \\pub export fn main() x {
47 \\ return 0;
48 \\}
49 \\export fn foo() callconv(y) c_int {
50 \\ return 0;
51 \\}
52 \\var y: @import("std").builtin.CallingConvention = .C;
53 , &.{
54 ":2:22: error: expected type 'type', found 'i32'",
55 ":5:26: error: unable to resolve comptime value",
56 ":5:26: note: calling convention must be comptime-known",
57 });
58 }
59
60 {
61 var case = ctx.exeFromCompiledC("var args", .{});
62
63 case.addCompareOutput(
64 \\extern fn printf(format: [*:0]const u8, ...) c_int;
65 \\
66 \\pub export fn main() c_int {
67 \\ _ = printf("Hello, %s!\n", "world");
68 \\ return 0;
69 \\}
70 , "Hello, world!" ++ std.cstr.line_sep);
71 }
72
73 {
74 var case = ctx.exeFromCompiledC("intToError", .{});
75
76 case.addCompareOutput(
77 \\pub export fn main() c_int {
78 \\ // comptime checks
79 \\ const a = error.A;
80 \\ const b = error.B;
81 \\ const c = @intToError(2);
82 \\ const d = @intToError(1);
83 \\ if (!(c == b)) unreachable;
84 \\ if (!(a == d)) unreachable;
85 \\ // runtime checks
86 \\ var x = error.A;
87 \\ var y = error.B;
88 \\ var z = @intToError(2);
89 \\ var f = @intToError(1);
90 \\ if (!(y == z)) unreachable;
91 \\ if (!(x == f)) unreachable;
92 \\ return 0;
93 \\}
94 , "");
95 case.addError(
96 \\pub export fn main() c_int {
97 \\ _ = @intToError(0);
98 \\ return 0;
99 \\}
100 , &.{":2:21: error: integer value '0' represents no error"});
101 case.addError(
102 \\pub export fn main() c_int {
103 \\ _ = @intToError(3);
104 \\ return 0;
105 \\}
106 , &.{":2:21: error: integer value '3' represents no error"});
107 }
108
109 {
110 var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
111
112 // Exit with 0
113 case.addCompareOutput(
114 \\fn exitGood() noreturn {
115 \\ asm volatile ("syscall"
116 \\ :
117 \\ : [number] "{rax}" (231),
118 \\ [arg1] "{rdi}" (0)
119 \\ );
120 \\ unreachable;
121 \\}
122 \\
123 \\pub export fn main() c_int {
124 \\ exitGood();
125 \\}
126 , "");
127
128 // Pass a usize parameter to exit
129 case.addCompareOutput(
130 \\pub export fn main() c_int {
131 \\ exit(0);
132 \\}
133 \\
134 \\fn exit(code: usize) noreturn {
135 \\ asm volatile ("syscall"
136 \\ :
137 \\ : [number] "{rax}" (231),
138 \\ [arg1] "{rdi}" (code)
139 \\ );
140 \\ unreachable;
141 \\}
142 , "");
143
144 // Change the parameter to u8
145 case.addCompareOutput(
146 \\pub export fn main() c_int {
147 \\ exit(0);
148 \\}
149 \\
150 \\fn exit(code: u8) noreturn {
151 \\ asm volatile ("syscall"
152 \\ :
153 \\ : [number] "{rax}" (231),
154 \\ [arg1] "{rdi}" (code)
155 \\ );
156 \\ unreachable;
157 \\}
158 , "");
159
160 // Do some arithmetic at the exit callsite
161 case.addCompareOutput(
162 \\pub export fn main() c_int {
163 \\ exitMath(1);
164 \\}
165 \\
166 \\fn exitMath(a: u8) noreturn {
167 \\ exit(0 + a - a);
168 \\}
169 \\
170 \\fn exit(code: u8) noreturn {
171 \\ asm volatile ("syscall"
172 \\ :
173 \\ : [number] "{rax}" (231),
174 \\ [arg1] "{rdi}" (code)
175 \\ );
176 \\ unreachable;
177 \\}
178 \\
179 , "");
180
181 // Invert the arithmetic
182 case.addCompareOutput(
183 \\pub export fn main() c_int {
184 \\ exitMath(1);
185 \\}
186 \\
187 \\fn exitMath(a: u8) noreturn {
188 \\ exit(a + 0 - a);
189 \\}
190 \\
191 \\fn exit(code: u8) noreturn {
192 \\ asm volatile ("syscall"
193 \\ :
194 \\ : [number] "{rax}" (231),
195 \\ [arg1] "{rdi}" (code)
196 \\ );
197 \\ unreachable;
198 \\}
199 \\
200 , "");
201 }
202
203 {
204 var case = ctx.exeFromCompiledC("alloc and retptr", .{});
205
206 case.addCompareOutput(
207 \\fn add(a: i32, b: i32) i32 {
208 \\ return a + b;
209 \\}
210 \\
211 \\fn addIndirect(a: i32, b: i32) i32 {
212 \\ return add(a, b);
213 \\}
214 \\
215 \\pub export fn main() c_int {
216 \\ return addIndirect(1, 2) - 3;
217 \\}
218 , "");
219 }
220
221 {
222 var case = ctx.exeFromCompiledC("inferred local const and var", .{});
223
224 case.addCompareOutput(
225 \\fn add(a: i32, b: i32) i32 {
226 \\ return a + b;
227 \\}
228 \\
229 \\pub export fn main() c_int {
230 \\ const x = add(1, 2);
231 \\ var y = add(3, 0);
232 \\ y -= x;
233 \\ return y;
234 \\}
235 , "");
236 }
237 {
238 var case = ctx.exeFromCompiledC("control flow", .{});
239
240 // Simple while loop
241 case.addCompareOutput(
242 \\pub export fn main() c_int {
243 \\ var a: c_int = 0;
244 \\ while (a < 5) : (a+=1) {}
245 \\ return a - 5;
246 \\}
247 , "");
248 case.addCompareOutput(
249 \\pub export fn main() c_int {
250 \\ var a = true;
251 \\ while (!a) {}
252 \\ return 0;
253 \\}
254 , "");
255
256 // If expression
257 case.addCompareOutput(
258 \\pub export fn main() c_int {
259 \\ var cond: c_int = 0;
260 \\ var a: c_int = @as(c_int, if (cond == 0)
261 \\ 2
262 \\ else
263 \\ 3) + 9;
264 \\ return a - 11;
265 \\}
266 , "");
267
268 // If expression with breakpoint that does not get hit
269 case.addCompareOutput(
270 \\pub export fn main() c_int {
271 \\ var x: i32 = 1;
272 \\ if (x != 1) @breakpoint();
273 \\ return 0;
274 \\}
275 , "");
276
277 // Switch expression
278 case.addCompareOutput(
279 \\pub export fn main() c_int {
280 \\ var cond: c_int = 0;
281 \\ var a: c_int = switch (cond) {
282 \\ 1 => 1,
283 \\ 2 => 2,
284 \\ 99...300, 12 => 3,
285 \\ 0 => 4,
286 \\ else => 5,
287 \\ };
288 \\ return a - 4;
289 \\}
290 , "");
291
292 // Switch expression missing else case.
293 case.addError(
294 \\pub export fn main() c_int {
295 \\ var cond: c_int = 0;
296 \\ const a: c_int = switch (cond) {
297 \\ 1 => 1,
298 \\ 2 => 2,
299 \\ 3 => 3,
300 \\ 4 => 4,
301 \\ };
302 \\ return a - 4;
303 \\}
304 , &.{":3:22: error: switch must handle all possibilities"});
305
306 // Switch expression, has an unreachable prong.
307 case.addCompareOutput(
308 \\pub export fn main() c_int {
309 \\ var cond: c_int = 0;
310 \\ const a: c_int = switch (cond) {
311 \\ 1 => 1,
312 \\ 2 => 2,
313 \\ 99...300, 12 => 3,
314 \\ 0 => 4,
315 \\ 13 => unreachable,
316 \\ else => 5,
317 \\ };
318 \\ return a - 4;
319 \\}
320 , "");
321
322 // Switch expression, has an unreachable prong and prongs write
323 // to result locations.
324 case.addCompareOutput(
325 \\pub export fn main() c_int {
326 \\ var cond: c_int = 0;
327 \\ var a: c_int = switch (cond) {
328 \\ 1 => 1,
329 \\ 2 => 2,
330 \\ 99...300, 12 => 3,
331 \\ 0 => 4,
332 \\ 13 => unreachable,
333 \\ else => 5,
334 \\ };
335 \\ return a - 4;
336 \\}
337 , "");
338
339 // Integer switch expression has duplicate case value.
340 case.addError(
341 \\pub export fn main() c_int {
342 \\ var cond: c_int = 0;
343 \\ const a: c_int = switch (cond) {
344 \\ 1 => 1,
345 \\ 2 => 2,
346 \\ 96, 11...13, 97 => 3,
347 \\ 0 => 4,
348 \\ 90, 12 => 100,
349 \\ else => 5,
350 \\ };
351 \\ return a - 4;
352 \\}
353 , &.{
354 ":8:13: error: duplicate switch value",
355 ":6:15: note: previous value here",
356 });
357
358 // Boolean switch expression has duplicate case value.
359 case.addError(
360 \\pub export fn main() c_int {
361 \\ var a: bool = false;
362 \\ const b: c_int = switch (a) {
363 \\ false => 1,
364 \\ true => 2,
365 \\ false => 3,
366 \\ };
367 \\ _ = b;
368 \\}
369 , &.{
370 ":6:9: error: duplicate switch value",
371 });
372
373 // Sparse (no range capable) switch expression has duplicate case value.
374 case.addError(
375 \\pub export fn main() c_int {
376 \\ const A: type = i32;
377 \\ const b: c_int = switch (A) {
378 \\ i32 => 1,
379 \\ bool => 2,
380 \\ f64, i32 => 3,
381 \\ else => 4,
382 \\ };
383 \\ _ = b;
384 \\}
385 , &.{
386 ":6:14: error: duplicate switch value",
387 ":4:9: note: previous value here",
388 });
389
390 // Ranges not allowed for some kinds of switches.
391 case.addError(
392 \\pub export fn main() c_int {
393 \\ const A: type = i32;
394 \\ const b: c_int = switch (A) {
395 \\ i32 => 1,
396 \\ bool => 2,
397 \\ f16...f64 => 3,
398 \\ else => 4,
399 \\ };
400 \\ _ = b;
401 \\}
402 , &.{
403 ":3:30: error: ranges not allowed when switching on type 'type'",
404 ":6:12: note: range here",
405 });
406
407 // Switch expression has unreachable else prong.
408 case.addError(
409 \\pub export fn main() c_int {
410 \\ var a: u2 = 0;
411 \\ const b: i32 = switch (a) {
412 \\ 0 => 10,
413 \\ 1 => 20,
414 \\ 2 => 30,
415 \\ 3 => 40,
416 \\ else => 50,
417 \\ };
418 \\ _ = b;
419 \\}
420 , &.{
421 ":8:14: error: unreachable else prong; all cases already handled",
422 });
423 }
424 //{
425 // var case = ctx.exeFromCompiledC("optionals", .{});
426
427 // // Simple while loop
428 // case.addCompareOutput(
429 // \\pub export fn main() c_int {
430 // \\ var count: c_int = 0;
431 // \\ var opt_ptr: ?*c_int = &count;
432 // \\ while (opt_ptr) |_| : (count += 1) {
433 // \\ if (count == 4) opt_ptr = null;
434 // \\ }
435 // \\ return count - 5;
436 // \\}
437 // , "");
438
439 // // Same with non pointer optionals
440 // case.addCompareOutput(
441 // \\pub export fn main() c_int {
442 // \\ var count: c_int = 0;
443 // \\ var opt_ptr: ?c_int = count;
444 // \\ while (opt_ptr) |_| : (count += 1) {
445 // \\ if (count == 4) opt_ptr = null;
446 // \\ }
447 // \\ return count - 5;
448 // \\}
449 // , "");
450 //}
451
452 {
453 var case = ctx.exeFromCompiledC("errors", .{});
454 case.addCompareOutput(
455 \\pub export fn main() c_int {
456 \\ var e1 = error.Foo;
457 \\ var e2 = error.Bar;
458 \\ assert(e1 != e2);
459 \\ assert(e1 == error.Foo);
460 \\ assert(e2 == error.Bar);
461 \\ return 0;
462 \\}
463 \\fn assert(b: bool) void {
464 \\ if (!b) unreachable;
465 \\}
466 , "");
467 case.addCompareOutput(
468 \\pub export fn main() c_int {
469 \\ var e: anyerror!c_int = 0;
470 \\ const i = e catch 69;
471 \\ return i;
472 \\}
473 , "");
474 case.addCompareOutput(
475 \\pub export fn main() c_int {
476 \\ var e: anyerror!c_int = error.Foo;
477 \\ const i = e catch 69;
478 \\ return 69 - i;
479 \\}
480 , "");
481 case.addCompareOutput(
482 \\const E = error{e};
483 \\const S = struct { x: u32 };
484 \\fn f() E!u32 {
485 \\ const x = (try @as(E!S, S{ .x = 1 })).x;
486 \\ return x;
487 \\}
488 \\pub export fn main() c_int {
489 \\ const x = f() catch @as(u32, 0);
490 \\ if (x != 1) unreachable;
491 \\ return 0;
492 \\}
493 , "");
494 }
495
496 {
497 var case = ctx.exeFromCompiledC("structs", .{});
498 case.addError(
499 \\const Point = struct { x: i32, y: i32 };
500 \\pub export fn main() c_int {
501 \\ var p: Point = .{
502 \\ .y = 24,
503 \\ .x = 12,
504 \\ .y = 24,
505 \\ };
506 \\ return p.y - p.x - p.x;
507 \\}
508 , &.{
509 ":6:10: error: duplicate field",
510 ":4:10: note: other field here",
511 });
512 case.addError(
513 \\const Point = struct { x: i32, y: i32 };
514 \\pub export fn main() c_int {
515 \\ var p: Point = .{
516 \\ .y = 24,
517 \\ };
518 \\ return p.y - p.x - p.x;
519 \\}
520 , &.{
521 ":3:21: error: missing struct field: x",
522 ":1:15: note: struct 'tmp.Point' declared here",
523 });
524 case.addError(
525 \\const Point = struct { x: i32, y: i32 };
526 \\pub export fn main() c_int {
527 \\ var p: Point = .{
528 \\ .x = 12,
529 \\ .y = 24,
530 \\ .z = 48,
531 \\ };
532 \\ return p.y - p.x - p.x;
533 \\}
534 , &.{
535 ":6:10: error: no field named 'z' in struct 'tmp.Point'",
536 ":1:15: note: struct declared here",
537 });
538 case.addCompareOutput(
539 \\const Point = struct { x: i32, y: i32 };
540 \\pub export fn main() c_int {
541 \\ var p: Point = .{
542 \\ .x = 12,
543 \\ .y = 24,
544 \\ };
545 \\ return p.y - p.x - p.x;
546 \\}
547 , "");
548 case.addCompareOutput(
549 \\const Point = struct { x: i32, y: i32, z: i32, a: i32, b: i32 };
550 \\pub export fn main() c_int {
551 \\ var p: Point = .{
552 \\ .x = 18,
553 \\ .y = 24,
554 \\ .z = 1,
555 \\ .a = 2,
556 \\ .b = 3,
557 \\ };
558 \\ return p.y - p.x - p.z - p.a - p.b;
559 \\}
560 , "");
561 }
562
563 {
564 var case = ctx.exeFromCompiledC("unions", .{});
565
566 case.addError(
567 \\const U = union {
568 \\ a: u32,
569 \\ b
570 \\};
571 , &.{
572 ":3:5: error: union field missing type",
573 });
574
575 case.addError(
576 \\const E = enum { a, b };
577 \\const U = union(E) {
578 \\ a: u32 = 1,
579 \\ b: f32 = 2,
580 \\};
581 , &.{
582 ":2:11: error: explicitly valued tagged union requires inferred enum tag type",
583 ":3:14: note: tag value specified here",
584 });
585
586 case.addError(
587 \\const U = union(enum) {
588 \\ a: u32 = 1,
589 \\ b: f32 = 2,
590 \\};
591 , &.{
592 ":1:11: error: explicitly valued tagged union missing integer tag type",
593 ":2:14: note: tag value specified here",
594 });
595 }
596
597 {
598 var case = ctx.exeFromCompiledC("enums", .{});
599
600 case.addError(
601 \\const E1 = packed enum { a, b, c };
602 \\const E2 = extern enum { a, b, c };
603 \\export fn foo() void {
604 \\ _ = E1.a;
605 \\}
606 \\export fn bar() void {
607 \\ _ = E2.a;
608 \\}
609 , &.{
610 ":1:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type",
611 ":2:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type",
612 });
613
614 // comptime and types are caught in AstGen.
615 case.addError(
616 \\const E1 = enum {
617 \\ a,
618 \\ comptime b,
619 \\ c,
620 \\};
621 \\const E2 = enum {
622 \\ a,
623 \\ b: i32,
624 \\ c,
625 \\};
626 \\export fn foo() void {
627 \\ _ = E1.a;
628 \\}
629 \\export fn bar() void {
630 \\ _ = E2.a;
631 \\}
632 , &.{
633 ":3:5: error: enum fields cannot be marked comptime",
634 ":8:8: error: enum fields do not have types",
635 ":6:12: note: consider 'union(enum)' here to make it a tagged union",
636 });
637
638 // @enumToInt, @intToEnum, enum literal coercion, field access syntax, comparison, switch
639 case.addCompareOutput(
640 \\const Number = enum { One, Two, Three };
641 \\
642 \\pub export fn main() c_int {
643 \\ var number1 = Number.One;
644 \\ var number2: Number = .Two;
645 \\ const number3 = @intToEnum(Number, 2);
646 \\ if (number1 == number2) return 1;
647 \\ if (number2 == number3) return 1;
648 \\ if (@enumToInt(number1) != 0) return 1;
649 \\ if (@enumToInt(number2) != 1) return 1;
650 \\ if (@enumToInt(number3) != 2) return 1;
651 \\ var x: Number = .Two;
652 \\ if (number2 != x) return 1;
653 \\ switch (x) {
654 \\ .One => return 1,
655 \\ .Two => return 0,
656 \\ number3 => return 2,
657 \\ }
658 \\}
659 , "");
660
661 // Specifying alignment is a parse error.
662 // This also tests going from a successful build to a parse error.
663 case.addError(
664 \\const E1 = enum {
665 \\ a,
666 \\ b align(4),
667 \\ c,
668 \\};
669 \\export fn foo() void {
670 \\ _ = E1.a;
671 \\}
672 , &.{
673 ":3:13: error: enum fields cannot be aligned",
674 });
675
676 // Redundant non-exhaustive enum mark.
677 // This also tests going from a parse error to an AstGen error.
678 case.addError(
679 \\const E1 = enum {
680 \\ a,
681 \\ _,
682 \\ b,
683 \\ c,
684 \\ _,
685 \\};
686 \\export fn foo() void {
687 \\ _ = E1.a;
688 \\}
689 , &.{
690 ":6:5: error: redundant non-exhaustive enum mark",
691 ":3:5: note: other mark here",
692 });
693
694 case.addError(
695 \\const E1 = enum {
696 \\ a,
697 \\ b,
698 \\ c,
699 \\ _ = 10,
700 \\};
701 \\export fn foo() void {
702 \\ _ = E1.a;
703 \\}
704 , &.{
705 ":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value",
706 });
707
708 case.addError(
709 \\const E1 = enum { a, b, _ };
710 \\export fn foo() void {
711 \\ _ = E1.a;
712 \\}
713 , &.{
714 ":1:12: error: non-exhaustive enum missing integer tag type",
715 ":1:25: note: marked non-exhaustive here",
716 });
717
718 case.addError(
719 \\const E1 = enum { a, b, c, b, d };
720 \\pub export fn main() c_int {
721 \\ _ = E1.a;
722 \\}
723 , &.{
724 ":1:28: error: duplicate enum field 'b'",
725 ":1:22: note: other field here",
726 });
727
728 case.addError(
729 \\pub export fn main() c_int {
730 \\ const a = true;
731 \\ _ = @enumToInt(a);
732 \\}
733 , &.{
734 ":3:20: error: expected enum or tagged union, found 'bool'",
735 });
736
737 case.addError(
738 \\pub export fn main() c_int {
739 \\ const a = 1;
740 \\ _ = @intToEnum(bool, a);
741 \\}
742 , &.{
743 ":3:20: error: expected enum, found 'bool'",
744 });
745
746 case.addError(
747 \\const E = enum { a, b, c };
748 \\pub export fn main() c_int {
749 \\ _ = @intToEnum(E, 3);
750 \\}
751 , &.{
752 ":3:9: error: enum 'tmp.E' has no tag with value '3'",
753 ":1:11: note: enum declared here",
754 });
755
756 case.addError(
757 \\const E = enum { a, b, c };
758 \\pub export fn main() c_int {
759 \\ var x: E = .a;
760 \\ switch (x) {
761 \\ .a => {},
762 \\ .c => {},
763 \\ }
764 \\}
765 , &.{
766 ":4:5: error: switch must handle all possibilities",
767 ":1:21: note: unhandled enumeration value: 'b'",
768 ":1:11: note: enum 'tmp.E' declared here",
769 });
770
771 case.addError(
772 \\const E = enum { a, b, c };
773 \\pub export fn main() c_int {
774 \\ var x: E = .a;
775 \\ switch (x) {
776 \\ .a => {},
777 \\ .b => {},
778 \\ .b => {},
779 \\ .c => {},
780 \\ }
781 \\}
782 , &.{
783 ":7:10: error: duplicate switch value",
784 ":6:10: note: previous value here",
785 });
786
787 case.addError(
788 \\const E = enum { a, b, c };
789 \\pub export fn main() c_int {
790 \\ var x: E = .a;
791 \\ switch (x) {
792 \\ .a => {},
793 \\ .b => {},
794 \\ .c => {},
795 \\ else => {},
796 \\ }
797 \\}
798 , &.{
799 ":8:14: error: unreachable else prong; all cases already handled",
800 });
801
802 case.addError(
803 \\const E = enum { a, b, c };
804 \\pub export fn main() c_int {
805 \\ var x: E = .a;
806 \\ switch (x) {
807 \\ .a => {},
808 \\ .b => {},
809 \\ _ => {},
810 \\ }
811 \\}
812 , &.{
813 ":4:5: error: '_' prong only allowed when switching on non-exhaustive enums",
814 ":7:11: note: '_' prong here",
815 });
816
817 case.addError(
818 \\const E = enum { a, b, c };
819 \\pub export fn main() c_int {
820 \\ _ = E.d;
821 \\}
822 , &.{
823 ":3:11: error: enum 'tmp.E' has no member named 'd'",
824 ":1:11: note: enum declared here",
825 });
826
827 case.addError(
828 \\const E = enum { a, b, c };
829 \\pub export fn main() c_int {
830 \\ var x: E = .d;
831 \\ _ = x;
832 \\}
833 , &.{
834 ":3:17: error: no field named 'd' in enum 'tmp.E'",
835 ":1:11: note: enum declared here",
836 });
837 }
838
839 {
840 var case = ctx.exeFromCompiledC("shift right and left", .{});
841 case.addCompareOutput(
842 \\pub export fn main() c_int {
843 \\ var i: u32 = 16;
844 \\ assert(i >> 1, 8);
845 \\ return 0;
846 \\}
847 \\fn assert(a: u32, b: u32) void {
848 \\ if (a != b) unreachable;
849 \\}
850 , "");
851
852 case.addCompareOutput(
853 \\pub export fn main() c_int {
854 \\ var i: u32 = 16;
855 \\ assert(i << 1, 32);
856 \\ return 0;
857 \\}
858 \\fn assert(a: u32, b: u32) void {
859 \\ if (a != b) unreachable;
860 \\}
861 , "");
862 }
863
864 {
865 var case = ctx.exeFromCompiledC("inferred error sets", .{});
866
867 case.addCompareOutput(
868 \\pub export fn main() c_int {
869 \\ if (foo()) |_| {
870 \\ @panic("test fail");
871 \\ } else |err| {
872 \\ if (err != error.ItBroke) {
873 \\ @panic("test fail");
874 \\ }
875 \\ }
876 \\ return 0;
877 \\}
878 \\fn foo() !void {
879 \\ return error.ItBroke;
880 \\}
881 , "");
882 }
883
884 {
885 // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)
886 var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{});
887 case.addCompareOutput(
888 \\pub export fn main() c_int {
889 \\ // Addition
890 \\ if (!add_u3(1, 1, 2)) return 1;
891 \\ if (!add_u3(7, 1, 0)) return 1;
892 \\ if (!add_i3(1, 1, 2)) return 1;
893 \\ if (!add_i3(3, 2, -3)) return 1;
894 \\ if (!add_i3(-3, -2, 3)) return 1;
895 \\ if (!add_c_int(1, 1, 2)) return 1;
896 \\ // TODO enable these when stage2 supports std.math.maxInt
897 \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1;
898 \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1;
899 \\
900 \\ // Subtraction
901 \\ if (!sub_u3(2, 1, 1)) return 1;
902 \\ if (!sub_u3(0, 1, 7)) return 1;
903 \\ if (!sub_i3(2, 1, 1)) return 1;
904 \\ if (!sub_i3(3, -2, -3)) return 1;
905 \\ if (!sub_i3(-3, 2, 3)) return 1;
906 \\ if (!sub_c_int(2, 1, 1)) return 1;
907 \\ // TODO enable these when stage2 supports std.math.maxInt
908 \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1;
909 \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1;
910 \\
911 \\ return 0;
912 \\}
913 \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool {
914 \\ return expected == lhs +% rhs;
915 \\}
916 \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool {
917 \\ return expected == lhs +% rhs;
918 \\}
919 \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
920 \\ return expected == lhs +% rhs;
921 \\}
922 \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool {
923 \\ return expected == lhs -% rhs;
924 \\}
925 \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool {
926 \\ return expected == lhs -% rhs;
927 \\}
928 \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
929 \\ return expected == lhs -% rhs;
930 \\}
931 , "");
932 }
933
934 {
935 var case = ctx.exeFromCompiledC("rem", linux_x64);
936 case.addCompareOutput(
937 \\fn assert(ok: bool) void {
938 \\ if (!ok) unreachable;
939 \\}
940 \\fn rem(lhs: i32, rhs: i32, expected: i32) bool {
941 \\ return @rem(lhs, rhs) == expected;
942 \\}
943 \\pub export fn main() c_int {
944 \\ assert(rem(-5, 3, -2));
945 \\ assert(rem(5, 3, 2));
946 \\ return 0;
947 \\}
948 , "");
949 }
950}
test/cli.zig deleted-195
...@@ -1,195 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4const process = std.process;
5const fs = std.fs;
6const ChildProcess = std.ChildProcess;
7
8var a: std.mem.Allocator = undefined;
9
10pub fn main() !void {
11 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
12 defer _ = gpa.deinit();
13 var arena = std.heap.ArenaAllocator.init(gpa.allocator());
14 defer arena.deinit();
15
16 a = arena.allocator();
17 var arg_it = try process.argsWithAllocator(a);
18
19 // skip my own exe name
20 _ = arg_it.skip();
21
22 const zig_exe_rel = arg_it.next() orelse {
23 std.debug.print("Expected first argument to be path to zig compiler\n", .{});
24 return error.InvalidArgs;
25 };
26 const cache_root = arg_it.next() orelse {
27 std.debug.print("Expected second argument to be cache root directory path\n", .{});
28 return error.InvalidArgs;
29 };
30 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
31
32 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
33 defer fs.cwd().deleteTree(dir_path) catch {};
34
35 const TestFn = fn ([]const u8, []const u8) anyerror!void;
36 const Test = struct {
37 func: TestFn,
38 name: []const u8,
39 };
40 const tests = [_]Test{
41 .{ .func = testZigInitLib, .name = "zig init-lib" },
42 .{ .func = testZigInitExe, .name = "zig init-exe" },
43 .{ .func = testGodboltApi, .name = "godbolt API" },
44 .{ .func = testMissingOutputPath, .name = "missing output path" },
45 .{ .func = testZigFmt, .name = "zig fmt" },
46 };
47 inline for (tests) |t| {
48 try fs.cwd().deleteTree(dir_path);
49 try fs.cwd().makeDir(dir_path);
50 t.func(zig_exe, dir_path) catch |err| {
51 std.debug.print("test '{s}' failed: {s}\n", .{
52 t.name, @errorName(err),
53 });
54 return err;
55 };
56 }
57}
58
59fn printCmd(cwd: []const u8, argv: []const []const u8) void {
60 std.debug.print("cd {s} && ", .{cwd});
61 for (argv) |arg| {
62 std.debug.print("{s} ", .{arg});
63 }
64 std.debug.print("\n", .{});
65}
66
67fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult {
68 const max_output_size = 100 * 1024;
69 const result = ChildProcess.exec(.{
70 .allocator = a,
71 .argv = argv,
72 .cwd = cwd,
73 .max_output_bytes = max_output_size,
74 }) catch |err| {
75 std.debug.print("The following command failed:\n", .{});
76 printCmd(cwd, argv);
77 return err;
78 };
79 switch (result.term) {
80 .Exited => |code| {
81 if ((code != 0) == expect_0) {
82 std.debug.print("The following command exited with error code {}:\n", .{code});
83 printCmd(cwd, argv);
84 std.debug.print("stderr:\n{s}\n", .{result.stderr});
85 return error.CommandFailed;
86 }
87 },
88 else => {
89 std.debug.print("The following command terminated unexpectedly:\n", .{});
90 printCmd(cwd, argv);
91 std.debug.print("stderr:\n{s}\n", .{result.stderr});
92 return error.CommandFailed;
93 },
94 }
95 return result;
96}
97
98fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
99 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });
100 const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });
101 try testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n");
102}
103
104fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
105 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
106 const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });
107 try testing.expectEqualStrings("All your codebase are belong to us.\n", run_result.stderr);
108 try testing.expectEqualStrings("Run `zig build test` to run the tests.\n", run_result.stdout);
109}
110
111fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
112 if (builtin.os.tag != .linux or builtin.cpu.arch != .x86_64) return;
113
114 const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" });
115 const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" });
116
117 try fs.cwd().writeFile(example_zig_path,
118 \\// Type your code here, or load an example.
119 \\export fn square(num: i32) i32 {
120 \\ return num * num;
121 \\}
122 \\extern fn zig_panic() noreturn;
123 \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn {
124 \\ _ = msg;
125 \\ _ = error_return_trace;
126 \\ zig_panic();
127 \\}
128 );
129
130 var args = std.ArrayList([]const u8).init(a);
131 try args.appendSlice(&[_][]const u8{
132 zig_exe, "build-obj",
133 "--cache-dir", dir_path,
134 "--name", "example",
135 "-fno-emit-bin", "-fno-emit-h",
136 "-fstrip", "-OReleaseFast",
137 example_zig_path,
138 });
139
140 const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path});
141 try args.append(emit_asm_arg);
142
143 _ = try exec(dir_path, true, args.items);
144
145 const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize));
146 try testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);
147 try testing.expect(std.mem.indexOf(u8, out_asm, "mov\teax, edi") != null);
148 try testing.expect(std.mem.indexOf(u8, out_asm, "imul\teax, edi") != null);
149}
150
151fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
152 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
153 const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" });
154 const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path});
155 const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" });
156 const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg });
157 const s = std.fs.path.sep_str;
158 const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n";
159 try testing.expectEqualStrings(expected, result.stderr);
160}
161
162fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
163 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
164
165 const unformatted_code = " // no reason for indent";
166
167 const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
168 try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
169
170 const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
171 // stderr should be file path + \n
172 try testing.expect(std.mem.startsWith(u8, run_result1.stdout, fmt1_zig_path));
173 try testing.expect(run_result1.stdout.len == fmt1_zig_path.len + 1 and run_result1.stdout[run_result1.stdout.len - 1] == '\n');
174
175 const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
176 try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
177
178 const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
179 // running it on the dir, only the new file should be changed
180 try testing.expect(std.mem.startsWith(u8, run_result2.stdout, fmt2_zig_path));
181 try testing.expect(run_result2.stdout.len == fmt2_zig_path.len + 1 and run_result2.stdout[run_result2.stdout.len - 1] == '\n');
182
183 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
184 // both files have been formatted, nothing should change now
185 try testing.expect(run_result3.stdout.len == 0);
186
187 // Check UTF-16 decoding
188 const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" });
189 var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
190 try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16);
191
192 const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
193 try testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path));
194 try testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n');
195}
test/compile_errors.zig+25-199
...@@ -1,146 +1,10 @@...@@ -1,146 +1,10 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const TestContext = @import("../src/test.zig").TestContext;3const Cases = @import("src/Cases.zig");
4
5pub fn addCases(ctx: *TestContext) !void {
6 {
7 const case = ctx.obj("wrong same named struct", .{});
8 case.backend = .stage1;
9
10 case.addSourceFile("a.zig",
11 \\pub const Foo = struct {
12 \\ x: i32,
13 \\};
14 );
15
16 case.addSourceFile("b.zig",
17 \\pub const Foo = struct {
18 \\ z: f64,
19 \\};
20 );
21
22 case.addError(
23 \\const a = @import("a.zig");
24 \\const b = @import("b.zig");
25 \\
26 \\export fn entry() void {
27 \\ var a1: a.Foo = undefined;
28 \\ bar(&a1);
29 \\}
30 \\
31 \\fn bar(x: *b.Foo) void {_ = x;}
32 , &[_][]const u8{
33 "tmp.zig:6:10: error: expected type '*b.Foo', found '*a.Foo'",
34 "tmp.zig:6:10: note: pointer type child 'a.Foo' cannot cast into pointer type child 'b.Foo'",
35 "a.zig:1:17: note: a.Foo declared here",
36 "b.zig:1:17: note: b.Foo declared here",
37 });
38 }
39
40 {
41 const case = ctx.obj("multiple files with private function error", .{});
42 case.backend = .stage1;
43
44 case.addSourceFile("foo.zig",
45 \\fn privateFunction() void { }
46 );
47
48 case.addError(
49 \\const foo = @import("foo.zig",);
50 \\
51 \\export fn callPrivFunction() void {
52 \\ foo.privateFunction();
53 \\}
54 , &[_][]const u8{
55 "tmp.zig:4:8: error: 'privateFunction' is private",
56 "foo.zig:1:1: note: declared here",
57 });
58 }
59
60 {
61 const case = ctx.obj("multiple files with private member instance function (canonical invocation) error", .{});
62 case.backend = .stage1;
63
64 case.addSourceFile("foo.zig",
65 \\pub const Foo = struct {
66 \\ fn privateFunction(self: *Foo) void { _ = self; }
67 \\};
68 );
69
70 case.addError(
71 \\const Foo = @import("foo.zig",).Foo;
72 \\
73 \\export fn callPrivFunction() void {
74 \\ var foo = Foo{};
75 \\ Foo.privateFunction(foo);
76 \\}
77 , &[_][]const u8{
78 "tmp.zig:5:8: error: 'privateFunction' is private",
79 "foo.zig:2:5: note: declared here",
80 });
81 }
82
83 {
84 const case = ctx.obj("multiple files with private member instance function error", .{});
85 case.backend = .stage1;
86
87 case.addSourceFile("foo.zig",
88 \\pub const Foo = struct {
89 \\ fn privateFunction(self: *Foo) void { _ = self; }
90 \\};
91 );
92
93 case.addError(
94 \\const Foo = @import("foo.zig",).Foo;
95 \\
96 \\export fn callPrivFunction() void {
97 \\ var foo = Foo{};
98 \\ foo.privateFunction();
99 \\}
100 , &[_][]const u8{
101 "tmp.zig:5:8: error: 'privateFunction' is private",
102 "foo.zig:2:5: note: declared here",
103 });
104 }
105
106 {
107 const case = ctx.obj("export collision", .{});
108 case.backend = .stage1;
109
110 case.addSourceFile("foo.zig",
111 \\export fn bar() void {}
112 \\pub const baz = 1234;
113 );
114
115 case.addError(
116 \\const foo = @import("foo.zig",);
117 \\
118 \\export fn bar() usize {
119 \\ return foo.baz;
120 \\}
121 , &[_][]const u8{
122 "foo.zig:1:1: error: exported symbol collision: 'bar'",
123 "tmp.zig:3:1: note: other symbol here",
124 });
125 }
126
127 ctx.objErrStage1("non-printable invalid character", "\xff\xfe" ++
128 "fn foo() bool {\r\n" ++
129 " return true;\r\n" ++
130 "}\r\n", &[_][]const u8{
131 "tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid bytes'",
132 "tmp.zig:1:1: note: invalid byte: '\\xff'",
133 });
134
135 ctx.objErrStage1("non-printable invalid character with escape alternative", "fn foo() bool {\n" ++
136 "\treturn true;\n" ++
137 "}\n", &[_][]const u8{
138 "tmp.zig:2:1: error: invalid character: '\\t'",
139 });
1404
5pub fn addCases(ctx: *Cases) !void {
141 {6 {
142 const case = ctx.obj("multiline error messages", .{});7 const case = ctx.obj("multiline error messages", .{});
143 case.backend = .stage2;
1448
145 case.addError(9 case.addError(
146 \\comptime {10 \\comptime {
...@@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void {
17640
177 {41 {
178 const case = ctx.obj("isolated carriage return in multiline string literal", .{});42 const case = ctx.obj("isolated carriage return in multiline string literal", .{});
179 case.backend = .stage2;
18043
181 case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{44 case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{
182 ":1:19: error: expected ';' after declaration",45 ":1:19: error: expected ';' after declaration",
...@@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void {
19558
196 {59 {
197 const case = ctx.obj("argument causes error", .{});60 const case = ctx.obj("argument causes error", .{});
198 case.backend = .stage2;
199
200 case.addSourceFile("b.zig",
201 \\pub const ElfDynLib = struct {
202 \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T {
203 \\ _ = self;
204 \\ return undefined;
205 \\ }
206 \\};
207 );
20861
209 case.addError(62 case.addError(
210 \\pub export fn entry() void {63 \\pub export fn entry() void {
...@@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void {
216 ":3:12: note: argument to function being called at comptime must be comptime-known",69 ":3:12: note: argument to function being called at comptime must be comptime-known",
217 ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type",70 ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type",
218 });71 });
72 case.addSourceFile("b.zig",
73 \\pub const ElfDynLib = struct {
74 \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T {
75 \\ _ = self;
76 \\ return undefined;
77 \\ }
78 \\};
79 );
219 }80 }
22081
221 {82 {
222 const case = ctx.obj("astgen failure in file struct", .{});83 const case = ctx.obj("astgen failure in file struct", .{});
223 case.backend = .stage2;
224
225 case.addSourceFile("b.zig",
226 \\+
227 );
22884
229 case.addError(85 case.addError(
230 \\pub export fn entry() void {86 \\pub export fn entry() void {
...@@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void {
233 , &[_][]const u8{89 , &[_][]const u8{
234 ":1:1: error: expected type expression, found '+'",90 ":1:1: error: expected type expression, found '+'",
235 });91 });
92 case.addSourceFile("b.zig",
93 \\+
94 );
236 }95 }
23796
238 {97 {
239 const case = ctx.obj("invalid store to comptime field", .{});98 const case = ctx.obj("invalid store to comptime field", .{});
240 case.backend = .stage2;
241
242 case.addSourceFile("a.zig",
243 \\pub const S = struct {
244 \\ comptime foo: u32 = 1,
245 \\ bar: u32,
246 \\ pub fn foo(x: @This()) void {
247 \\ _ = x;
248 \\ }
249 \\};
250 );
25199
252 case.addError(100 case.addError(
253 \\const a = @import("a.zig");101 \\const a = @import("a.zig");
...@@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void {
259 ":4:23: error: value stored in comptime field does not match the default value of the field",107 ":4:23: error: value stored in comptime field does not match the default value of the field",
260 ":2:25: note: default value set here",108 ":2:25: note: default value set here",
261 });109 });
110 case.addSourceFile("a.zig",
111 \\pub const S = struct {
112 \\ comptime foo: u32 = 1,
113 \\ bar: u32,
114 \\ pub fn foo(x: @This()) void {
115 \\ _ = x;
116 \\ }
117 \\};
118 );
262 }119 }
263120
264 // TODO test this in stage2, but we won't even try in stage1
265 //ctx.objErrStage1("inline fn calls itself indirectly",
266 // \\export fn foo() void {
267 // \\ bar();
268 // \\}
269 // \\fn bar() callconv(.Inline) void {
270 // \\ baz();
271 // \\ quux();
272 // \\}
273 // \\fn baz() callconv(.Inline) void {
274 // \\ bar();
275 // \\ quux();
276 // \\}
277 // \\extern fn quux() void;
278 //, &[_][]const u8{
279 // "tmp.zig:4:1: error: unable to inline function",
280 //});
281
282 //ctx.objErrStage1("save reference to inline function",
283 // \\export fn foo() void {
284 // \\ quux(@ptrToInt(bar));
285 // \\}
286 // \\fn bar() callconv(.Inline) void { }
287 // \\extern fn quux(usize) void;
288 //, &[_][]const u8{
289 // "tmp.zig:4:1: error: unable to inline function",
290 //});
291
292 {121 {
293 const case = ctx.obj("file in multiple modules", .{});122 const case = ctx.obj("file in multiple modules", .{});
294 case.backend = .stage2;
295
296 case.addSourceFile("foo.zig",
297 \\const dummy = 0;
298 );
299
300 case.addDepModule("foo", "foo.zig");123 case.addDepModule("foo", "foo.zig");
301124
302 case.addError(125 case.addError(
...@@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void {
309 ":1:1: note: root of module root.foo",132 ":1:1: note: root of module root.foo",
310 ":3:17: note: imported from module root",133 ":3:17: note: imported from module root",
311 });134 });
135 case.addSourceFile("foo.zig",
136 \\const dummy = 0;
137 );
312 }138 }
313}139}
test/link.zig+172-213
...@@ -1,213 +1,172 @@...@@ -1,213 +1,172 @@
1const std = @import("std");1pub const Case = struct {
2const builtin = @import("builtin");2 build_root: []const u8,
3const tests = @import("tests.zig");3 import: type,
44};
5pub fn addCases(cases: *tests.StandaloneContext) void {5
6 cases.addBuildFile("test/link/bss/build.zig", .{6pub const cases = [_]Case{
7 .build_modes = false, // we only guarantee zerofill for undefined in Debug7 .{
8 });8 .build_root = "test/link/bss",
99 .import = @import("link/bss/build.zig"),
10 cases.addBuildFile("test/link/common_symbols/build.zig", .{10 },
11 .build_modes = true,11 .{
12 });12 .build_root = "test/link/common_symbols",
1313 .import = @import("link/common_symbols/build.zig"),
14 cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{14 },
15 .build_modes = true,15 .{
16 });16 .build_root = "test/link/common_symbols_alignment",
1717 .import = @import("link/common_symbols_alignment/build.zig"),
18 cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{18 },
19 .build_modes = true,19 .{
20 });20 .build_root = "test/link/interdependent_static_c_libs",
2121 .import = @import("link/interdependent_static_c_libs/build.zig"),
22 cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{22 },
23 .build_modes = true,23
24 });24 // WASM Cases
2525 .{
26 addWasmCases(cases);26 .build_root = "test/link/wasm/archive",
27 addMachOCases(cases);27 .import = @import("link/wasm/archive/build.zig"),
28}28 },
2929 .{
30fn addWasmCases(cases: *tests.StandaloneContext) void {30 .build_root = "test/link/wasm/basic-features",
31 cases.addBuildFile("test/link/wasm/archive/build.zig", .{31 .import = @import("link/wasm/basic-features/build.zig"),
32 .build_modes = true,32 },
33 .requires_stage2 = true,33 .{
34 });34 .build_root = "test/link/wasm/bss",
3535 .import = @import("link/wasm/bss/build.zig"),
36 cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{36 },
37 .requires_stage2 = true,37 .{
38 });38 .build_root = "test/link/wasm/export",
3939 .import = @import("link/wasm/export/build.zig"),
40 cases.addBuildFile("test/link/wasm/bss/build.zig", .{40 },
41 .build_modes = false,41 .{
42 .requires_stage2 = true,42 .build_root = "test/link/wasm/export-data",
43 });43 .import = @import("link/wasm/export-data/build.zig"),
4444 },
45 cases.addBuildFile("test/link/wasm/export/build.zig", .{45 .{
46 .build_modes = true,46 .build_root = "test/link/wasm/extern",
47 .requires_stage2 = true,47 .import = @import("link/wasm/extern/build.zig"),
48 });48 },
4949 .{
50 // TODO: Fix open handle in wasm-linker refraining rename from working on Windows.50 .build_root = "test/link/wasm/extern-mangle",
51 if (builtin.os.tag != .windows) {51 .import = @import("link/wasm/extern-mangle/build.zig"),
52 cases.addBuildFile("test/link/wasm/export-data/build.zig", .{});52 },
53 }53 .{
5454 .build_root = "test/link/wasm/function-table",
55 cases.addBuildFile("test/link/wasm/extern/build.zig", .{55 .import = @import("link/wasm/function-table/build.zig"),
56 .build_modes = true,56 },
57 .requires_stage2 = true,57 .{
58 .use_emulation = true,58 .build_root = "test/link/wasm/infer-features",
59 });59 .import = @import("link/wasm/infer-features/build.zig"),
6060 },
61 cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{61 .{
62 .build_modes = true,62 .build_root = "test/link/wasm/producers",
63 .requires_stage2 = true,63 .import = @import("link/wasm/producers/build.zig"),
64 });64 },
6565 .{
66 cases.addBuildFile("test/link/wasm/function-table/build.zig", .{66 .build_root = "test/link/wasm/segments",
67 .build_modes = true,67 .import = @import("link/wasm/segments/build.zig"),
68 .requires_stage2 = true,68 },
69 });69 .{
7070 .build_root = "test/link/wasm/stack_pointer",
71 cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{71 .import = @import("link/wasm/stack_pointer/build.zig"),
72 .requires_stage2 = true,72 },
73 });73 .{
7474 .build_root = "test/link/wasm/type",
75 cases.addBuildFile("test/link/wasm/producers/build.zig", .{75 .import = @import("link/wasm/type/build.zig"),
76 .build_modes = true,76 },
77 .requires_stage2 = true,77
78 });78 // Mach-O Cases
7979 .{
80 cases.addBuildFile("test/link/wasm/segments/build.zig", .{80 .build_root = "test/link/macho/bugs/13056",
81 .build_modes = true,81 .import = @import("link/macho/bugs/13056/build.zig"),
82 .requires_stage2 = true,82 },
83 });83 .{
8484 .build_root = "test/link/macho/bugs/13457",
85 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{85 .import = @import("link/macho/bugs/13457/build.zig"),
86 .build_modes = true,86 },
87 .requires_stage2 = true,87 .{
88 });88 .build_root = "test/link/macho/dead_strip",
8989 .import = @import("link/macho/dead_strip/build.zig"),
90 cases.addBuildFile("test/link/wasm/type/build.zig", .{90 },
91 .build_modes = true,91 .{
92 .requires_stage2 = true,92 .build_root = "test/link/macho/dead_strip_dylibs",
93 });93 .import = @import("link/macho/dead_strip_dylibs/build.zig"),
94}94 },
9595 .{
96fn addMachOCases(cases: *tests.StandaloneContext) void {96 .build_root = "test/link/macho/dylib",
97 cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{97 .import = @import("link/macho/dylib/build.zig"),
98 .build_modes = true,98 },
99 .requires_macos_sdk = true,99 .{
100 .requires_symlinks = true,100 .build_root = "test/link/macho/empty",
101 });101 .import = @import("link/macho/empty/build.zig"),
102102 },
103 cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{103 .{
104 .build_modes = true,104 .build_root = "test/link/macho/entry",
105 .requires_symlinks = true,105 .import = @import("link/macho/entry/build.zig"),
106 });106 },
107107 .{
108 cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{108 .build_root = "test/link/macho/headerpad",
109 .build_modes = false,109 .import = @import("link/macho/headerpad/build.zig"),
110 .requires_symlinks = true,110 },
111 });111 .{
112112 .build_root = "test/link/macho/linksection",
113 cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{113 .import = @import("link/macho/linksection/build.zig"),
114 .build_modes = true,114 },
115 .requires_macos_sdk = true,115 .{
116 .requires_symlinks = true,116 .build_root = "test/link/macho/needed_framework",
117 });117 .import = @import("link/macho/needed_framework/build.zig"),
118118 },
119 cases.addBuildFile("test/link/macho/dylib/build.zig", .{119 .{
120 .build_modes = true,120 .build_root = "test/link/macho/needed_library",
121 .requires_symlinks = true,121 .import = @import("link/macho/needed_library/build.zig"),
122 });122 },
123123 .{
124 cases.addBuildFile("test/link/macho/empty/build.zig", .{124 .build_root = "test/link/macho/objc",
125 .build_modes = true,125 .import = @import("link/macho/objc/build.zig"),
126 .requires_symlinks = true,126 },
127 });127 .{
128128 .build_root = "test/link/macho/objcpp",
129 cases.addBuildFile("test/link/macho/entry/build.zig", .{129 .import = @import("link/macho/objcpp/build.zig"),
130 .build_modes = true,130 },
131 .requires_symlinks = true,131 .{
132 });132 .build_root = "test/link/macho/pagezero",
133133 .import = @import("link/macho/pagezero/build.zig"),
134 cases.addBuildFile("test/link/macho/headerpad/build.zig", .{134 },
135 .build_modes = true,135 .{
136 .requires_macos_sdk = true,136 .build_root = "test/link/macho/search_strategy",
137 .requires_symlinks = true,137 .import = @import("link/macho/search_strategy/build.zig"),
138 });138 },
139139 .{
140 cases.addBuildFile("test/link/macho/linksection/build.zig", .{140 .build_root = "test/link/macho/stack_size",
141 .build_modes = true,141 .import = @import("link/macho/stack_size/build.zig"),
142 .requires_symlinks = true,142 },
143 });143 .{
144144 .build_root = "test/link/macho/strict_validation",
145 cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{145 .import = @import("link/macho/strict_validation/build.zig"),
146 .build_modes = true,146 },
147 .requires_macos_sdk = true,147 .{
148 .requires_symlinks = true,148 .build_root = "test/link/macho/tls",
149 });149 .import = @import("link/macho/tls/build.zig"),
150150 },
151 cases.addBuildFile("test/link/macho/needed_library/build.zig", .{151 .{
152 .build_modes = true,152 .build_root = "test/link/macho/unwind_info",
153 .requires_symlinks = true,153 .import = @import("link/macho/unwind_info/build.zig"),
154 });154 },
155155 // TODO: re-enable this test. It currently has some incompatibilities with
156 cases.addBuildFile("test/link/macho/objc/build.zig", .{156 // the new build system API. In particular, it depends on installing the build
157 .build_modes = true,157 // artifacts, which should be unnecessary, and it has a custom build step that
158 .requires_macos_sdk = true,158 // prints directly to stderr instead of failing the step with an error message.
159 .requires_symlinks = true,159 //.{
160 });160 // .build_root = "test/link/macho/uuid",
161161 // .import = @import("link/macho/uuid/build.zig"),
162 cases.addBuildFile("test/link/macho/objcpp/build.zig", .{162 //},
163 .build_modes = true,163
164 .requires_macos_sdk = true,164 .{
165 .requires_symlinks = true,165 .build_root = "test/link/macho/weak_library",
166 });166 .import = @import("link/macho/weak_library/build.zig"),
167167 },
168 cases.addBuildFile("test/link/macho/pagezero/build.zig", .{168 .{
169 .build_modes = false,169 .build_root = "test/link/macho/weak_framework",
170 .requires_symlinks = true,170 .import = @import("link/macho/weak_framework/build.zig"),
171 });171 },
172172};
173 cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{
174 .build_modes = true,
175 .requires_symlinks = true,
176 });
177
178 cases.addBuildFile("test/link/macho/stack_size/build.zig", .{
179 .build_modes = true,
180 .requires_symlinks = true,
181 });
182
183 cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{
184 .build_modes = true,
185 .requires_symlinks = true,
186 });
187
188 cases.addBuildFile("test/link/macho/tls/build.zig", .{
189 .build_modes = true,
190 .requires_symlinks = true,
191 });
192
193 cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{
194 .build_modes = true,
195 .requires_symlinks = true,
196 });
197
198 cases.addBuildFile("test/link/macho/uuid/build.zig", .{
199 .build_modes = false,
200 .requires_symlinks = true,
201 });
202
203 cases.addBuildFile("test/link/macho/weak_library/build.zig", .{
204 .build_modes = true,
205 .requires_symlinks = true,
206 });
207
208 cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{
209 .build_modes = true,
210 .requires_macos_sdk = true,
211 .requires_symlinks = true,
212 });
213}
test/link/bss/build.zig+3-3
...@@ -1,17 +1,17 @@...@@ -1,17 +1,17 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
66
7 const exe = b.addExecutable(.{7 const exe = b.addExecutable(.{
8 .name = "bss",8 .name = "bss",
9 .root_source_file = .{ .path = "main.zig" },9 .root_source_file = .{ .path = "main.zig" },
10 .optimize = optimize,10 .optimize = .Debug,
11 });11 });
12 b.default_step.dependOn(&exe.step);
1312
14 const run = exe.run();13 const run = exe.run();
15 run.expectStdOutEqual("0, 1, 0\n");14 run.expectStdOutEqual("0, 1, 0\n");
15
16 test_step.dependOn(&run.step);16 test_step.dependOn(&run.step);
17}17}
test/link/bss/main.zig+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
22
3// Stress test zerofill layout3// Stress test zerofill layout
4var buffer: [0x1000000]u64 = undefined;4var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000;
55
6pub fn main() anyerror!void {6pub fn main() anyerror!void {
7 buffer[0x10] = 1;7 buffer[0x10] = 1;
test/link/common_symbols/build.zig+10-3
...@@ -1,8 +1,16 @@...@@ -1,8 +1,16 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
512
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
6 const lib_a = b.addStaticLibrary(.{14 const lib_a = b.addStaticLibrary(.{
7 .name = "a",15 .name = "a",
8 .optimize = optimize,16 .optimize = optimize,
...@@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void {...@@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void {
16 });24 });
17 test_exe.linkLibrary(lib_a);25 test_exe.linkLibrary(lib_a);
1826
19 const test_step = b.step("test", "Test it");27 test_step.dependOn(&test_exe.run().step);
20 test_step.dependOn(&test_exe.step);
21}28}
test/link/common_symbols_alignment/build.zig+11-6
...@@ -1,23 +1,28 @@...@@ -1,23 +1,28 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 const target = b.standardTargetOptions(.{});5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
612
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 const lib_a = b.addStaticLibrary(.{14 const lib_a = b.addStaticLibrary(.{
8 .name = "a",15 .name = "a",
9 .optimize = optimize,16 .optimize = optimize,
10 .target = target,17 .target = .{},
11 });18 });
12 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});19 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});
1320
14 const test_exe = b.addTest(.{21 const test_exe = b.addTest(.{
15 .root_source_file = .{ .path = "main.zig" },22 .root_source_file = .{ .path = "main.zig" },
16 .optimize = optimize,23 .optimize = optimize,
17 .target = target,
18 });24 });
19 test_exe.linkLibrary(lib_a);25 test_exe.linkLibrary(lib_a);
2026
21 const test_step = b.step("test", "Test it");27 test_step.dependOn(&test_exe.run().step);
22 test_step.dependOn(&test_exe.step);
23}28}
test/link/interdependent_static_c_libs/build.zig+12-7
...@@ -1,13 +1,20 @@...@@ -1,13 +1,20 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 const target = b.standardTargetOptions(.{});5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
612
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 const lib_a = b.addStaticLibrary(.{14 const lib_a = b.addStaticLibrary(.{
8 .name = "a",15 .name = "a",
9 .optimize = optimize,16 .optimize = optimize,
10 .target = target,17 .target = .{},
11 });18 });
12 lib_a.addCSourceFile("a.c", &[_][]const u8{});19 lib_a.addCSourceFile("a.c", &[_][]const u8{});
13 lib_a.addIncludePath(".");20 lib_a.addIncludePath(".");
...@@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void {...@@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void {
15 const lib_b = b.addStaticLibrary(.{22 const lib_b = b.addStaticLibrary(.{
16 .name = "b",23 .name = "b",
17 .optimize = optimize,24 .optimize = optimize,
18 .target = target,25 .target = .{},
19 });26 });
20 lib_b.addCSourceFile("b.c", &[_][]const u8{});27 lib_b.addCSourceFile("b.c", &[_][]const u8{});
21 lib_b.addIncludePath(".");28 lib_b.addIncludePath(".");
...@@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void {...@@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void {
23 const test_exe = b.addTest(.{30 const test_exe = b.addTest(.{
24 .root_source_file = .{ .path = "main.zig" },31 .root_source_file = .{ .path = "main.zig" },
25 .optimize = optimize,32 .optimize = optimize,
26 .target = target,
27 });33 });
28 test_exe.linkLibrary(lib_a);34 test_exe.linkLibrary(lib_a);
29 test_exe.linkLibrary(lib_b);35 test_exe.linkLibrary(lib_b);
30 test_exe.addIncludePath(".");36 test_exe.addIncludePath(".");
3137
32 const test_step = b.step("test", "Test it");38 test_step.dependOn(&test_exe.run().step);
33 test_step.dependOn(&test_exe.step);
34}39}
test/link/macho/bugs/13056/build.zig+12-4
...@@ -1,20 +1,28 @@...@@ -1,20 +1,28 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_macos_sdk = true;
4pub const requires_symlinks = true;
5
3pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
9
10 add(b, test_step, .Debug);
11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
515
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };17 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
7 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;18 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
8 const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse19 const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse
9 @panic("macOS SDK is required to run the test");20 @panic("macOS SDK is required to run the test");
1021
11 const test_step = b.step("test", "Test the program");
12
13 const exe = b.addExecutable(.{22 const exe = b.addExecutable(.{
14 .name = "test",23 .name = "test",
15 .optimize = optimize,24 .optimize = optimize,
16 });25 });
17 b.default_step.dependOn(&exe.step);
18 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);26 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);
19 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);27 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);
20 exe.addCSourceFile("test.cpp", &.{28 exe.addCSourceFile("test.cpp", &.{
test/link/macho/bugs/13457/build.zig+16-4
...@@ -1,10 +1,19 @@...@@ -1,10 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
614
7 const test_step = b.step("test", "Test the program");15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
817
9 const exe = b.addExecutable(.{18 const exe = b.addExecutable(.{
10 .name = "test",19 .name = "test",
...@@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void {...@@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void {
13 .target = target,22 .target = target,
14 });23 });
1524
16 const run = exe.runEmulatable();25 const run = b.addRunArtifact(exe);
26 run.skip_foreign_checks = true;
27 run.expectStdOutEqual("");
28
17 test_step.dependOn(&run.step);29 test_step.dependOn(&run.step);
18}30}
test/link/macho/dead_strip/build.zig+10-7
...@@ -1,17 +1,19 @@...@@ -1,17 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const optimize: std.builtin.OptimizeMode = .Debug;
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
68
7 const test_step = b.step("test", "Test the program");9 const test_step = b.step("test", "Test the program");
8 test_step.dependOn(b.getInstallStep());10 b.default_step = test_step;
911
10 {12 {
11 // Without -dead_strip, we expect `iAmUnused` symbol present13 // Without -dead_strip, we expect `iAmUnused` symbol present
12 const exe = createScenario(b, optimize, target);14 const exe = createScenario(b, optimize, target, "no-gc");
1315
14 const check = exe.checkObject(.macho);16 const check = exe.checkObject();
15 check.checkInSymtab();17 check.checkInSymtab();
16 check.checkNext("{*} (__TEXT,__text) external _iAmUnused");18 check.checkNext("{*} (__TEXT,__text) external _iAmUnused");
1719
...@@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void {...@@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void {
2224
23 {25 {
24 // With -dead_strip, no `iAmUnused` symbol should be present26 // With -dead_strip, no `iAmUnused` symbol should be present
25 const exe = createScenario(b, optimize, target);27 const exe = createScenario(b, optimize, target, "yes-gc");
26 exe.link_gc_sections = true;28 exe.link_gc_sections = true;
2729
28 const check = exe.checkObject(.macho);30 const check = exe.checkObject();
29 check.checkInSymtab();31 check.checkInSymtab();
30 check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused");32 check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused");
3133
...@@ -39,9 +41,10 @@ fn createScenario(...@@ -39,9 +41,10 @@ fn createScenario(
39 b: *std.Build,41 b: *std.Build,
40 optimize: std.builtin.OptimizeMode,42 optimize: std.builtin.OptimizeMode,
41 target: std.zig.CrossTarget,43 target: std.zig.CrossTarget,
44 name: []const u8,
42) *std.Build.CompileStep {45) *std.Build.CompileStep {
43 const exe = b.addExecutable(.{46 const exe = b.addExecutable(.{
44 .name = "test",47 .name = name,
45 .optimize = optimize,48 .optimize = optimize,
46 .target = target,49 .target = target,
47 });50 });
test/link/macho/dead_strip_dylibs/build.zig+22-10
...@@ -1,16 +1,24 @@...@@ -1,16 +1,24 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_macos_sdk = true;
4pub const requires_symlinks = true;
5
3pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
59
6 const test_step = b.step("test", "Test the program");10 add(b, test_step, .Debug);
7 test_step.dependOn(b.getInstallStep());11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
815
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
9 {17 {
10 // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable18 // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable
11 const exe = createScenario(b, optimize);19 const exe = createScenario(b, optimize, "no-dead-strip");
1220
13 const check = exe.checkObject(.macho);21 const check = exe.checkObject();
14 check.checkStart("cmd LOAD_DYLIB");22 check.checkStart("cmd LOAD_DYLIB");
15 check.checkNext("name {*}Cocoa");23 check.checkNext("name {*}Cocoa");
1624
...@@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void {...@@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void {
2533
26 {34 {
27 // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable35 // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable
28 const exe = createScenario(b, optimize);36 const exe = createScenario(b, optimize, "yes-dead-strip");
29 exe.dead_strip_dylibs = true;37 exe.dead_strip_dylibs = true;
3038
31 const run_cmd = exe.run();39 const run_cmd = b.addRunArtifact(exe);
32 run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail40 run_cmd.expectExitCode(@bitCast(u8, @as(i8, -2))); // should fail
33 test_step.dependOn(&run_cmd.step);41 test_step.dependOn(&run_cmd.step);
34 }42 }
35}43}
3644
37fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {45fn createScenario(
46 b: *std.Build,
47 optimize: std.builtin.OptimizeMode,
48 name: []const u8,
49) *std.Build.CompileStep {
38 const exe = b.addExecutable(.{50 const exe = b.addExecutable(.{
39 .name = "test",51 .name = name,
40 .optimize = optimize,52 .optimize = optimize,
41 });53 });
42 exe.addCSourceFile("main.c", &[0][]const u8{});54 exe.addCSourceFile("main.c", &[0][]const u8{});
test/link/macho/dylib/build.zig+20-11
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
10 const dylib = b.addSharedLibrary(.{18 const dylib = b.addSharedLibrary(.{
11 .name = "a",19 .name = "a",
...@@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void {...@@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void {
15 });23 });
16 dylib.addCSourceFile("a.c", &.{});24 dylib.addCSourceFile("a.c", &.{});
17 dylib.linkLibC();25 dylib.linkLibC();
18 dylib.install();
1926
20 const check_dylib = dylib.checkObject(.macho);27 const check_dylib = dylib.checkObject();
21 check_dylib.checkStart("cmd ID_DYLIB");28 check_dylib.checkStart("cmd ID_DYLIB");
22 check_dylib.checkNext("name @rpath/liba.dylib");29 check_dylib.checkNext("name @rpath/liba.dylib");
23 check_dylib.checkNext("timestamp 2");30 check_dylib.checkNext("timestamp 2");
...@@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void {...@@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void {
33 });40 });
34 exe.addCSourceFile("main.c", &.{});41 exe.addCSourceFile("main.c", &.{});
35 exe.linkSystemLibrary("a");42 exe.linkSystemLibrary("a");
43 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
44 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
36 exe.linkLibC();45 exe.linkLibC();
37 exe.addLibraryPath(b.pathFromRoot("zig-out/lib/"));
38 exe.addRPath(b.pathFromRoot("zig-out/lib"));
3946
40 const check_exe = exe.checkObject(.macho);47 const check_exe = exe.checkObject();
41 check_exe.checkStart("cmd LOAD_DYLIB");48 check_exe.checkStart("cmd LOAD_DYLIB");
42 check_exe.checkNext("name @rpath/liba.dylib");49 check_exe.checkNext("name @rpath/liba.dylib");
43 check_exe.checkNext("timestamp 2");50 check_exe.checkNext("timestamp 2");
...@@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void {...@@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void {
45 check_exe.checkNext("compatibility version 10000");52 check_exe.checkNext("compatibility version 10000");
4653
47 check_exe.checkStart("cmd RPATH");54 check_exe.checkStart("cmd RPATH");
48 check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable);55 // TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`)
56 //check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{
57 // b.pathFromRoot("zig-out/lib"),
58 //}) catch unreachable);
4959
50 const run = check_exe.runAndCompare();60 const run = check_exe.runAndCompare();
51 run.cwd = b.pathFromRoot(".");
52 run.expectStdOutEqual("Hello world");61 run.expectStdOutEqual("Hello world");
53 test_step.dependOn(&run.step);62 test_step.dependOn(&run.step);
54}63}
test/link/macho/empty/build.zig+14-5
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test the program");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
10 const exe = b.addExecutable(.{18 const exe = b.addExecutable(.{
11 .name = "test",19 .name = "test",
...@@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void {...@@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void {
16 exe.addCSourceFile("empty.c", &[0][]const u8{});24 exe.addCSourceFile("empty.c", &[0][]const u8{});
17 exe.linkLibC();25 exe.linkLibC();
1826
19 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);27 const run_cmd = b.addRunArtifact(exe);
28 run_cmd.skip_foreign_checks = true;
20 run_cmd.expectStdOutEqual("Hello!\n");29 run_cmd.expectStdOutEqual("Hello!\n");
21 test_step.dependOn(&run_cmd.step);30 test_step.dependOn(&run_cmd.step);
22}31}
test/link/macho/entry/build.zig+11-4
...@@ -1,11 +1,18 @@...@@ -1,11 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
58
6 const test_step = b.step("test", "Test");9 add(b, test_step, .Debug);
7 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
814
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
9 const exe = b.addExecutable(.{16 const exe = b.addExecutable(.{
10 .name = "main",17 .name = "main",
11 .optimize = optimize,18 .optimize = optimize,
...@@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void {...@@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void {
15 exe.linkLibC();22 exe.linkLibC();
16 exe.entry_symbol_name = "_non_main";23 exe.entry_symbol_name = "_non_main";
1724
18 const check_exe = exe.checkObject(.macho);25 const check_exe = exe.checkObject();
1926
20 check_exe.checkStart("segname __TEXT");27 check_exe.checkStart("segname __TEXT");
21 check_exe.checkNext("vmaddr {vmaddr}");28 check_exe.checkNext("vmaddr {vmaddr}");
test/link/macho/headerpad/build.zig+25-13
...@@ -1,18 +1,26 @@...@@ -1,18 +1,26 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub const requires_symlinks = true;
5pub const requires_macos_sdk = true;
6
4pub fn build(b: *std.Build) void {7pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});8 const test_step = b.step("test", "Test it");
9 b.default_step = test_step;
610
7 const test_step = b.step("test", "Test");11 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());12 add(b, test_step, .ReleaseFast);
13 add(b, test_step, .ReleaseSmall);
14 add(b, test_step, .ReleaseSafe);
15}
916
17fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
10 {18 {
11 // Test -headerpad_max_install_names19 // Test -headerpad_max_install_names
12 const exe = simpleExe(b, optimize);20 const exe = simpleExe(b, optimize, "headerpad_max_install_names");
13 exe.headerpad_max_install_names = true;21 exe.headerpad_max_install_names = true;
1422
15 const check = exe.checkObject(.macho);23 const check = exe.checkObject();
16 check.checkStart("sectname __text");24 check.checkStart("sectname __text");
17 check.checkNext("offset {offset}");25 check.checkNext("offset {offset}");
1826
...@@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void {...@@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void {
3442
35 {43 {
36 // Test -headerpad44 // Test -headerpad
37 const exe = simpleExe(b, optimize);45 const exe = simpleExe(b, optimize, "headerpad");
38 exe.headerpad_size = 0x10000;46 exe.headerpad_size = 0x10000;
3947
40 const check = exe.checkObject(.macho);48 const check = exe.checkObject();
41 check.checkStart("sectname __text");49 check.checkStart("sectname __text");
42 check.checkNext("offset {offset}");50 check.checkNext("offset {offset}");
43 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });51 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
...@@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void {...@@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void {
5058
51 {59 {
52 // Test both flags with -headerpad overriding -headerpad_max_install_names60 // Test both flags with -headerpad overriding -headerpad_max_install_names
53 const exe = simpleExe(b, optimize);61 const exe = simpleExe(b, optimize, "headerpad_overriding");
54 exe.headerpad_max_install_names = true;62 exe.headerpad_max_install_names = true;
55 exe.headerpad_size = 0x10000;63 exe.headerpad_size = 0x10000;
5664
57 const check = exe.checkObject(.macho);65 const check = exe.checkObject();
58 check.checkStart("sectname __text");66 check.checkStart("sectname __text");
59 check.checkNext("offset {offset}");67 check.checkNext("offset {offset}");
60 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });68 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
...@@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void {...@@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void {
6775
68 {76 {
69 // Test both flags with -headerpad_max_install_names overriding -headerpad77 // Test both flags with -headerpad_max_install_names overriding -headerpad
70 const exe = simpleExe(b, optimize);78 const exe = simpleExe(b, optimize, "headerpad_max_install_names_overriding");
71 exe.headerpad_size = 0x1000;79 exe.headerpad_size = 0x1000;
72 exe.headerpad_max_install_names = true;80 exe.headerpad_max_install_names = true;
7381
74 const check = exe.checkObject(.macho);82 const check = exe.checkObject();
75 check.checkStart("sectname __text");83 check.checkStart("sectname __text");
76 check.checkNext("offset {offset}");84 check.checkNext("offset {offset}");
7785
...@@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void {...@@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void {
92 }100 }
93}101}
94102
95fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {103fn simpleExe(
104 b: *std.Build,
105 optimize: std.builtin.OptimizeMode,
106 name: []const u8,
107) *std.Build.CompileStep {
96 const exe = b.addExecutable(.{108 const exe = b.addExecutable(.{
97 .name = "main",109 .name = name,
98 .optimize = optimize,110 .optimize = optimize,
99 });111 });
100 exe.addCSourceFile("main.c", &.{});112 exe.addCSourceFile("main.c", &.{});
test/link/macho/linksection/build.zig+13-5
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target = std.zig.CrossTarget{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target = std.zig.CrossTarget{ .os_tag = .macos };
917
10 const obj = b.addObject(.{18 const obj = b.addObject(.{
11 .name = "test",19 .name = "test",
...@@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void {...@@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void {
14 .target = target,22 .target = target,
15 });23 });
1624
17 const check = obj.checkObject(.macho);25 const check = obj.checkObject();
1826
19 check.checkInSymtab();27 check.checkInSymtab();
20 check.checkNext("{*} (__DATA,__TestGlobal) external _test_global");28 check.checkNext("{*} (__DATA,__TestGlobal) external _test_global");
test/link/macho/needed_framework/build.zig+12-4
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
3pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
59
6 const test_step = b.step("test", "Test the program");10 add(b, test_step, .Debug);
7 test_step.dependOn(b.getInstallStep());11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
815
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
9 // -dead_strip_dylibs17 // -dead_strip_dylibs
10 // -needed_framework Cocoa18 // -needed_framework Cocoa
11 const exe = b.addExecutable(.{19 const exe = b.addExecutable(.{
...@@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void {...@@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void {
17 exe.linkFrameworkNeeded("Cocoa");25 exe.linkFrameworkNeeded("Cocoa");
18 exe.dead_strip_dylibs = true;26 exe.dead_strip_dylibs = true;
1927
20 const check = exe.checkObject(.macho);28 const check = exe.checkObject();
21 check.checkStart("cmd LOAD_DYLIB");29 check.checkStart("cmd LOAD_DYLIB");
22 check.checkNext("name {*}Cocoa");30 check.checkNext("name {*}Cocoa");
23 test_step.dependOn(&check.step);31 test_step.dependOn(&check.step);
test/link/macho/needed_library/build.zig+16-8
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test the program");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
10 const dylib = b.addSharedLibrary(.{18 const dylib = b.addSharedLibrary(.{
11 .name = "a",19 .name = "a",
...@@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void {...@@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void {
15 });23 });
16 dylib.addCSourceFile("a.c", &.{});24 dylib.addCSourceFile("a.c", &.{});
17 dylib.linkLibC();25 dylib.linkLibC();
18 dylib.install();
1926
20 // -dead_strip_dylibs27 // -dead_strip_dylibs
21 // -needed-la28 // -needed-la
...@@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void {...@@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void {
27 exe.addCSourceFile("main.c", &[0][]const u8{});34 exe.addCSourceFile("main.c", &[0][]const u8{});
28 exe.linkLibC();35 exe.linkLibC();
29 exe.linkSystemLibraryNeeded("a");36 exe.linkSystemLibraryNeeded("a");
30 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));37 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
31 exe.addRPath(b.pathFromRoot("zig-out/lib"));38 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
32 exe.dead_strip_dylibs = true;39 exe.dead_strip_dylibs = true;
3340
34 const check = exe.checkObject(.macho);41 const check = exe.checkObject();
35 check.checkStart("cmd LOAD_DYLIB");42 check.checkStart("cmd LOAD_DYLIB");
36 check.checkNext("name @rpath/liba.dylib");43 check.checkNext("name @rpath/liba.dylib");
3744
38 const run_cmd = check.runAndCompare();45 const run_cmd = check.runAndCompare();
46 run_cmd.expectStdOutEqual("");
39 test_step.dependOn(&run_cmd.step);47 test_step.dependOn(&run_cmd.step);
40}48}
test/link/macho/objc/build.zig+14-3
...@@ -1,10 +1,19 @@...@@ -1,10 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
3pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
59
6 const test_step = b.step("test", "Test the program");10 add(b, test_step, .Debug);
11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
715
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
8 const exe = b.addExecutable(.{17 const exe = b.addExecutable(.{
9 .name = "test",18 .name = "test",
10 .optimize = optimize,19 .optimize = optimize,
...@@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void {...@@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void {
17 // populate paths to the sysroot here.26 // populate paths to the sysroot here.
18 exe.linkFramework("Foundation");27 exe.linkFramework("Foundation");
1928
20 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);29 const run_cmd = b.addRunArtifact(exe);
30 run_cmd.skip_foreign_checks = true;
31 run_cmd.expectStdOutEqual("");
21 test_step.dependOn(&run_cmd.step);32 test_step.dependOn(&run_cmd.step);
22}33}
test/link/macho/objcpp/build.zig+11-2
...@@ -1,10 +1,19 @@...@@ -1,10 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
3pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
59
6 const test_step = b.step("test", "Test the program");10 add(b, test_step, .Debug);
11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
715
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
8 const exe = b.addExecutable(.{17 const exe = b.addExecutable(.{
9 .name = "test",18 .name = "test",
10 .optimize = optimize,19 .optimize = optimize,
test/link/macho/pagezero/build.zig+8-6
...@@ -1,11 +1,13 @@...@@ -1,11 +1,13 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");9 const optimize: std.builtin.OptimizeMode = .Debug;
8 test_step.dependOn(b.getInstallStep());10 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
911
10 {12 {
11 const exe = b.addExecutable(.{13 const exe = b.addExecutable(.{
...@@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void {...@@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void {
17 exe.linkLibC();19 exe.linkLibC();
18 exe.pagezero_size = 0x4000;20 exe.pagezero_size = 0x4000;
1921
20 const check = exe.checkObject(.macho);22 const check = exe.checkObject();
21 check.checkStart("LC 0");23 check.checkStart("LC 0");
22 check.checkNext("segname __PAGEZERO");24 check.checkNext("segname __PAGEZERO");
23 check.checkNext("vmaddr 0");25 check.checkNext("vmaddr 0");
...@@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void {...@@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void {
39 exe.linkLibC();41 exe.linkLibC();
40 exe.pagezero_size = 0;42 exe.pagezero_size = 0;
4143
42 const check = exe.checkObject(.macho);44 const check = exe.checkObject();
43 check.checkStart("LC 0");45 check.checkStart("LC 0");
44 check.checkNext("segname __TEXT");46 check.checkNext("segname __TEXT");
45 check.checkNext("vmaddr 0");47 check.checkNext("vmaddr 0");
test/link/macho/search_strategy/build.zig+26-20
...@@ -1,34 +1,41 @@...@@ -1,34 +1,41 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
10 {18 {
11 // -search_dylibs_first19 // -search_dylibs_first
12 const exe = createScenario(b, optimize, target);20 const exe = createScenario(b, optimize, target, "search_dylibs_first");
13 exe.search_strategy = .dylibs_first;21 exe.search_strategy = .dylibs_first;
1422
15 const check = exe.checkObject(.macho);23 const check = exe.checkObject();
16 check.checkStart("cmd LOAD_DYLIB");24 check.checkStart("cmd LOAD_DYLIB");
17 check.checkNext("name @rpath/liba.dylib");25 check.checkNext("name @rpath/libsearch_dylibs_first.dylib");
1826
19 const run = check.runAndCompare();27 const run = check.runAndCompare();
20 run.cwd = b.pathFromRoot(".");
21 run.expectStdOutEqual("Hello world");28 run.expectStdOutEqual("Hello world");
22 test_step.dependOn(&run.step);29 test_step.dependOn(&run.step);
23 }30 }
2431
25 {32 {
26 // -search_paths_first33 // -search_paths_first
27 const exe = createScenario(b, optimize, target);34 const exe = createScenario(b, optimize, target, "search_paths_first");
28 exe.search_strategy = .paths_first;35 exe.search_strategy = .paths_first;
2936
30 const run = std.Build.EmulatableRunStep.create(b, "run", exe);37 const run = b.addRunArtifact(exe);
31 run.cwd = b.pathFromRoot(".");38 run.skip_foreign_checks = true;
32 run.expectStdOutEqual("Hello world");39 run.expectStdOutEqual("Hello world");
33 test_step.dependOn(&run.step);40 test_step.dependOn(&run.step);
34 }41 }
...@@ -38,9 +45,10 @@ fn createScenario(...@@ -38,9 +45,10 @@ fn createScenario(
38 b: *std.Build,45 b: *std.Build,
39 optimize: std.builtin.OptimizeMode,46 optimize: std.builtin.OptimizeMode,
40 target: std.zig.CrossTarget,47 target: std.zig.CrossTarget,
48 name: []const u8,
41) *std.Build.CompileStep {49) *std.Build.CompileStep {
42 const static = b.addStaticLibrary(.{50 const static = b.addStaticLibrary(.{
43 .name = "a",51 .name = name,
44 .optimize = optimize,52 .optimize = optimize,
45 .target = target,53 .target = target,
46 });54 });
...@@ -49,10 +57,9 @@ fn createScenario(...@@ -49,10 +57,9 @@ fn createScenario(
49 static.override_dest_dir = std.Build.InstallDir{57 static.override_dest_dir = std.Build.InstallDir{
50 .custom = "static",58 .custom = "static",
51 };59 };
52 static.install();
5360
54 const dylib = b.addSharedLibrary(.{61 const dylib = b.addSharedLibrary(.{
55 .name = "a",62 .name = name,
56 .version = .{ .major = 1, .minor = 0 },63 .version = .{ .major = 1, .minor = 0 },
57 .optimize = optimize,64 .optimize = optimize,
58 .target = target,65 .target = target,
...@@ -62,18 +69,17 @@ fn createScenario(...@@ -62,18 +69,17 @@ fn createScenario(
62 dylib.override_dest_dir = std.Build.InstallDir{69 dylib.override_dest_dir = std.Build.InstallDir{
63 .custom = "dynamic",70 .custom = "dynamic",
64 };71 };
65 dylib.install();
6672
67 const exe = b.addExecutable(.{73 const exe = b.addExecutable(.{
68 .name = "main",74 .name = name,
69 .optimize = optimize,75 .optimize = optimize,
70 .target = target,76 .target = target,
71 });77 });
72 exe.addCSourceFile("main.c", &.{});78 exe.addCSourceFile("main.c", &.{});
73 exe.linkSystemLibraryName("a");79 exe.linkSystemLibraryName(name);
74 exe.linkLibC();80 exe.linkLibC();
75 exe.addLibraryPath(b.pathFromRoot("zig-out/static"));81 exe.addLibraryPathDirectorySource(static.getOutputDirectorySource());
76 exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic"));82 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
77 exe.addRPath(b.pathFromRoot("zig-out/dynamic"));83 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
78 return exe;84 return exe;
79}85}
test/link/macho/stack_size/build.zig+14-5
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
10 const exe = b.addExecutable(.{18 const exe = b.addExecutable(.{
11 .name = "main",19 .name = "main",
...@@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void {...@@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void {
16 exe.linkLibC();24 exe.linkLibC();
17 exe.stack_size = 0x100000000;25 exe.stack_size = 0x100000000;
1826
19 const check_exe = exe.checkObject(.macho);27 const check_exe = exe.checkObject();
20 check_exe.checkStart("cmd MAIN");28 check_exe.checkStart("cmd MAIN");
21 check_exe.checkNext("stacksize 100000000");29 check_exe.checkNext("stacksize 100000000");
2230
23 const run = check_exe.runAndCompare();31 const run = check_exe.runAndCompare();
32 run.expectStdOutEqual("");
24 test_step.dependOn(&run.step);33 test_step.dependOn(&run.step);
25}34}
test/link/macho/strict_validation/build.zig+13-5
...@@ -1,12 +1,20 @@...@@ -1,12 +1,20 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub const requires_symlinks = true;
5
4pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };8 b.default_step = test_step;
79
8 const test_step = b.step("test", "Test");10 add(b, test_step, .Debug);
9 test_step.dependOn(b.getInstallStep());11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
15
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
17 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
1018
11 const exe = b.addExecutable(.{19 const exe = b.addExecutable(.{
12 .name = "main",20 .name = "main",
...@@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void {...@@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void {
16 });24 });
17 exe.linkLibC();25 exe.linkLibC();
1826
19 const check_exe = exe.checkObject(.macho);27 const check_exe = exe.checkObject();
2028
21 check_exe.checkStart("cmd SEGMENT_64");29 check_exe.checkStart("cmd SEGMENT_64");
22 check_exe.checkNext("segname __LINKEDIT");30 check_exe.checkNext("segname __LINKEDIT");
test/link/macho/tls/build.zig+16-3
...@@ -1,7 +1,18 @@...@@ -1,7 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
617
7 const lib = b.addSharedLibrary(.{18 const lib = b.addSharedLibrary(.{
...@@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void {...@@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void {
21 test_exe.linkLibrary(lib);32 test_exe.linkLibrary(lib);
22 test_exe.linkLibC();33 test_exe.linkLibC();
2334
24 const test_step = b.step("test", "Test it");35 const run = test_exe.run();
25 test_step.dependOn(&test_exe.step);36 run.skip_foreign_checks = true;
37
38 test_step.dependOn(&run.step);
26}39}
test/link/macho/unwind_info/build.zig+19-8
...@@ -1,14 +1,23 @@...@@ -1,14 +1,23 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub const requires_symlinks = true;
5
4pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };8 b.default_step = test_step;
79
8 const test_step = b.step("test", "Test the program");10 add(b, test_step, .Debug);
11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
15
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
17 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
918
10 testUnwindInfo(b, test_step, optimize, target, false);19 testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip");
11 testUnwindInfo(b, test_step, optimize, target, true);20 testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip");
12}21}
1322
14fn testUnwindInfo(23fn testUnwindInfo(
...@@ -17,11 +26,12 @@ fn testUnwindInfo(...@@ -17,11 +26,12 @@ fn testUnwindInfo(
17 optimize: std.builtin.OptimizeMode,26 optimize: std.builtin.OptimizeMode,
18 target: std.zig.CrossTarget,27 target: std.zig.CrossTarget,
19 dead_strip: bool,28 dead_strip: bool,
29 name: []const u8,
20) void {30) void {
21 const exe = createScenario(b, optimize, target);31 const exe = createScenario(b, optimize, target, name);
22 exe.link_gc_sections = dead_strip;32 exe.link_gc_sections = dead_strip;
2333
24 const check = exe.checkObject(.macho);34 const check = exe.checkObject();
25 check.checkStart("segname __TEXT");35 check.checkStart("segname __TEXT");
26 check.checkNext("sectname __gcc_except_tab");36 check.checkNext("sectname __gcc_except_tab");
27 check.checkNext("sectname __unwind_info");37 check.checkNext("sectname __unwind_info");
...@@ -54,9 +64,10 @@ fn createScenario(...@@ -54,9 +64,10 @@ fn createScenario(
54 b: *std.Build,64 b: *std.Build,
55 optimize: std.builtin.OptimizeMode,65 optimize: std.builtin.OptimizeMode,
56 target: std.zig.CrossTarget,66 target: std.zig.CrossTarget,
67 name: []const u8,
57) *std.Build.CompileStep {68) *std.Build.CompileStep {
58 const exe = b.addExecutable(.{69 const exe = b.addExecutable(.{
59 .name = "test",70 .name = name,
60 .optimize = optimize,71 .optimize = optimize,
61 .target = target,72 .target = target,
62 });73 });
test/link/macho/uuid/build.zig+23-62
...@@ -1,14 +1,16 @@...@@ -1,14 +1,16 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.Build.Builder;
3const CompileStep = std.Build.CompileStep;2const CompileStep = std.Build.CompileStep;
4const FileSource = std.Build.FileSource;3const FileSource = std.Build.FileSource;
5const Step = std.Build.Step;4const Step = std.Build.Step;
65
6pub const requires_symlinks = true;
7
7pub fn build(b: *std.Build) void {8pub fn build(b: *std.Build) void {
8 const test_step = b.step("test", "Test");9 const test_step = b.step("test", "Test");
9 test_step.dependOn(b.getInstallStep());10 b.default_step = test_step;
1011
11 // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features.12 // We force cross-compilation to ensure we always pick a generic CPU with
13 // constant set of CPU features.
12 const aarch64_macos = std.zig.CrossTarget{14 const aarch64_macos = std.zig.CrossTarget{
13 .cpu_arch = .aarch64,15 .cpu_arch = .aarch64,
14 .os_tag = .macos,16 .os_tag = .macos,
...@@ -38,13 +40,15 @@ fn testUuid(...@@ -38,13 +40,15 @@ fn testUuid(
38 // stay the same across builds.40 // stay the same across builds.
39 {41 {
40 const dylib = simpleDylib(b, optimize, target);42 const dylib = simpleDylib(b, optimize, target);
41 const install_step = installWithRename(dylib, "test1.dylib");43 const install_step = b.addInstallArtifact(dylib);
44 install_step.dest_sub_path = "test1.dylib";
42 install_step.step.dependOn(&dylib.step);45 install_step.step.dependOn(&dylib.step);
43 }46 }
44 {47 {
45 const dylib = simpleDylib(b, optimize, target);48 const dylib = simpleDylib(b, optimize, target);
46 dylib.strip = true;49 dylib.strip = true;
47 const install_step = installWithRename(dylib, "test2.dylib");50 const install_step = b.addInstallArtifact(dylib);
51 install_step.dest_sub_path = "test2.dylib";
48 install_step.step.dependOn(&dylib.step);52 install_step.step.dependOn(&dylib.step);
49 }53 }
5054
...@@ -68,86 +72,43 @@ fn simpleDylib(...@@ -68,86 +72,43 @@ fn simpleDylib(
68 return dylib;72 return dylib;
69}73}
7074
71fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename {
72 const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name);
73 cs.builder.getInstallStep().dependOn(&step.step);
74 return step;
75}
76
77const InstallWithRename = struct {
78 pub const base_id = .custom;
79
80 step: Step,
81 builder: *Builder,
82 source: FileSource,
83 name: []const u8,
84
85 pub fn create(
86 builder: *Builder,
87 source: FileSource,
88 name: []const u8,
89 ) *InstallWithRename {
90 const self = builder.allocator.create(InstallWithRename) catch @panic("OOM");
91 self.* = InstallWithRename{
92 .builder = builder,
93 .step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{
94 source.getDisplayName(),
95 name,
96 }), builder.allocator, make),
97 .source = source,
98 .name = builder.dupe(name),
99 };
100 return self;
101 }
102
103 fn make(step: *Step) anyerror!void {
104 const self = @fieldParentPtr(InstallWithRename, "step", step);
105 const source_path = self.source.getPath(self.builder);
106 const target_path = self.builder.getInstallPath(.lib, self.name);
107 self.builder.updateFile(source_path, target_path) catch |err| {
108 std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path });
109 return err;
110 };
111 }
112};
113
114const CompareUuid = struct {75const CompareUuid = struct {
115 pub const base_id = .custom;76 pub const base_id = .custom;
11677
117 step: Step,78 step: Step,
118 builder: *Builder,
119 lhs: []const u8,79 lhs: []const u8,
120 rhs: []const u8,80 rhs: []const u8,
12181
122 pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid {82 pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid {
123 const self = builder.allocator.create(CompareUuid) catch @panic("OOM");83 const self = owner.allocator.create(CompareUuid) catch @panic("OOM");
124 self.* = CompareUuid{84 self.* = CompareUuid{
125 .builder = builder,85 .step = Step.init(.{
126 .step = Step.init(86 .id = base_id,
127 .custom,87 .name = owner.fmt("compare uuid: {s} and {s}", .{
128 builder.fmt("compare uuid: {s} and {s}", .{
129 lhs,88 lhs,
130 rhs,89 rhs,
131 }),90 }),
132 builder.allocator,91 .owner = owner,
133 make,92 .makeFn = make,
134 ),93 }),
135 .lhs = lhs,94 .lhs = lhs,
136 .rhs = rhs,95 .rhs = rhs,
137 };96 };
138 return self;97 return self;
139 }98 }
14099
141 fn make(step: *Step) anyerror!void {100 fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void {
101 _ = prog_node;
102 const b = step.owner;
142 const self = @fieldParentPtr(CompareUuid, "step", step);103 const self = @fieldParentPtr(CompareUuid, "step", step);
143 const gpa = self.builder.allocator;104 const gpa = b.allocator;
144105
145 var lhs_uuid: [16]u8 = undefined;106 var lhs_uuid: [16]u8 = undefined;
146 const lhs_path = self.builder.getInstallPath(.lib, self.lhs);107 const lhs_path = b.getInstallPath(.lib, self.lhs);
147 try parseUuid(gpa, lhs_path, &lhs_uuid);108 try parseUuid(gpa, lhs_path, &lhs_uuid);
148109
149 var rhs_uuid: [16]u8 = undefined;110 var rhs_uuid: [16]u8 = undefined;
150 const rhs_path = self.builder.getInstallPath(.lib, self.rhs);111 const rhs_path = b.getInstallPath(.lib, self.rhs);
151 try parseUuid(gpa, rhs_path, &rhs_uuid);112 try parseUuid(gpa, rhs_path, &rhs_uuid);
152113
153 try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid);114 try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid);
test/link/macho/weak_framework/build.zig+12-4
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
3pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
59
6 const test_step = b.step("test", "Test the program");10 add(b, test_step, .Debug);
7 test_step.dependOn(b.getInstallStep());11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
815
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
9 const exe = b.addExecutable(.{17 const exe = b.addExecutable(.{
10 .name = "test",18 .name = "test",
11 .optimize = optimize,19 .optimize = optimize,
...@@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void {...@@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void {
14 exe.linkLibC();22 exe.linkLibC();
15 exe.linkFrameworkWeak("Cocoa");23 exe.linkFrameworkWeak("Cocoa");
1624
17 const check = exe.checkObject(.macho);25 const check = exe.checkObject();
18 check.checkStart("cmd LOAD_WEAK_DYLIB");26 check.checkStart("cmd LOAD_WEAK_DYLIB");
19 check.checkNext("name {*}Cocoa");27 check.checkNext("name {*}Cocoa");
20 test_step.dependOn(&check.step);28 test_step.dependOn(&check.step);
test/link/macho/weak_library/build.zig+15-7
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_symlinks = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test the program");9 add(b, test_step, .Debug);
8 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
10 const dylib = b.addSharedLibrary(.{18 const dylib = b.addSharedLibrary(.{
11 .name = "a",19 .name = "a",
...@@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void {...@@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void {
25 exe.addCSourceFile("main.c", &[0][]const u8{});33 exe.addCSourceFile("main.c", &[0][]const u8{});
26 exe.linkLibC();34 exe.linkLibC();
27 exe.linkSystemLibraryWeak("a");35 exe.linkSystemLibraryWeak("a");
28 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));36 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
29 exe.addRPath(b.pathFromRoot("zig-out/lib"));37 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
3038
31 const check = exe.checkObject(.macho);39 const check = exe.checkObject();
32 check.checkStart("cmd LOAD_WEAK_DYLIB");40 check.checkStart("cmd LOAD_WEAK_DYLIB");
33 check.checkNext("name @rpath/liba.dylib");41 check.checkNext("name @rpath/liba.dylib");
3442
test/link/static_lib_as_system_lib/a.c deleted-4
...@@ -1,4 +0,0 @@
1#include "a.h"
2int32_t add(int32_t a, int32_t b) {
3 return a + b;
4}
test/link/static_lib_as_system_lib/a.h deleted-2
...@@ -1,2 +0,0 @@
1#include <stdint.h>
2int32_t add(int32_t a, int32_t b);
test/link/static_lib_as_system_lib/build.zig deleted-29
...@@ -1,29 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
6
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
12 lib_a.addCSourceFile("a.c", &[_][]const u8{});
13 lib_a.addIncludePath(".");
14 lib_a.install();
15
16 const test_exe = b.addTest(.{
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
21 test_exe.linkSystemLibrary("a"); // force linking liba.a as -la
22 test_exe.addSystemIncludePath(".");
23 const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable;
24 test_exe.addLibraryPath(search_path);
25
26 const test_step = b.step("test", "Test it");
27 test_step.dependOn(b.getInstallStep());
28 test_step.dependOn(&test_exe.step);
29}
test/link/static_lib_as_system_lib/main.zig deleted-8
...@@ -1,8 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const c = @cImport(@cInclude("a.h"));
4
5test "import C add" {
6 const result = c.add(2, 1);
7 try expect(result == 3);
8}
test/link/wasm/archive/build.zig+13-4
...@@ -1,22 +1,31 @@...@@ -1,22 +1,31 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test it");
5 test_step.dependOn(b.getInstallStep());7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 // The code in question will pull-in compiler-rt,16 // The code in question will pull-in compiler-rt,
8 // and therefore link with its archive file.17 // and therefore link with its archive file.
9 const lib = b.addSharedLibrary(.{18 const lib = b.addSharedLibrary(.{
10 .name = "main",19 .name = "main",
11 .root_source_file = .{ .path = "main.zig" },20 .root_source_file = .{ .path = "main.zig" },
12 .optimize = b.standardOptimizeOption(.{}),21 .optimize = optimize,
13 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
14 });23 });
15 lib.use_llvm = false;24 lib.use_llvm = false;
16 lib.use_lld = false;25 lib.use_lld = false;
17 lib.strip = false;26 lib.strip = false;
1827
19 const check = lib.checkObject(.wasm);28 const check = lib.checkObject();
20 check.checkStart("Section custom");29 check.checkStart("Section custom");
21 check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved30 check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved
2231
test/link/wasm/basic-features/build.zig+5-2
...@@ -1,11 +1,13 @@...@@ -1,11 +1,13 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 // Library with explicitly set cpu features6 // Library with explicitly set cpu features
5 const lib = b.addSharedLibrary(.{7 const lib = b.addSharedLibrary(.{
6 .name = "lib",8 .name = "lib",
7 .root_source_file = .{ .path = "main.zig" },9 .root_source_file = .{ .path = "main.zig" },
8 .optimize = b.standardOptimizeOption(.{}),10 .optimize = .Debug,
9 .target = .{11 .target = .{
10 .cpu_arch = .wasm32,12 .cpu_arch = .wasm32,
11 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },13 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
...@@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void {...@@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void {
17 lib.use_lld = false;19 lib.use_lld = false;
1820
19 // Verify the result contains the features explicitly set on the target for the library.21 // Verify the result contains the features explicitly set on the target for the library.
20 const check = lib.checkObject(.wasm);22 const check = lib.checkObject();
21 check.checkStart("name target_features");23 check.checkStart("name target_features");
22 check.checkNext("features 1");24 check.checkNext("features 1");
23 check.checkNext("+ atomics");25 check.checkNext("+ atomics");
2426
25 const test_step = b.step("test", "Run linker test");27 const test_step = b.step("test", "Run linker test");
26 test_step.dependOn(&check.step);28 test_step.dependOn(&check.step);
29 b.default_step = test_step;
27}30}
test/link/wasm/bss/build.zig+6-3
...@@ -1,14 +1,16 @@...@@ -1,14 +1,16 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());7 b.default_step = test_step;
68
7 const lib = b.addSharedLibrary(.{9 const lib = b.addSharedLibrary(.{
8 .name = "lib",10 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },11 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },12 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),13 .optimize = .Debug,
12 });14 });
13 lib.use_llvm = false;15 lib.use_llvm = false;
14 lib.use_lld = false;16 lib.use_lld = false;
...@@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void {...@@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void {
17 lib.import_memory = true;19 lib.import_memory = true;
18 lib.install();20 lib.install();
1921
20 const check_lib = lib.checkObject(.wasm);22 const check_lib = lib.checkObject();
2123
22 // since we import memory, make sure it exists with the correct naming24 // since we import memory, make sure it exists with the correct naming
23 check_lib.checkStart("Section import");25 check_lib.checkStart("Section import");
...@@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void {...@@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void {
36 check_lib.checkNext("name .rodata");38 check_lib.checkNext("name .rodata");
37 check_lib.checkNext("index 1"); // bss section always last39 check_lib.checkNext("index 1"); // bss section always last
38 check_lib.checkNext("name .bss");40 check_lib.checkNext("name .bss");
41
39 test_step.dependOn(&check_lib.step);42 test_step.dependOn(&check_lib.step);
40}43}
test/link/wasm/export-data/build.zig+7-2
...@@ -2,7 +2,12 @@ const std = @import("std");...@@ -2,7 +2,12 @@ const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());5 b.default_step = test_step;
6
7 if (@import("builtin").os.tag == .windows) {
8 // TODO: Fix open handle in wasm-linker refraining rename from working on Windows.
9 return;
10 }
611
7 const lib = b.addSharedLibrary(.{12 const lib = b.addSharedLibrary(.{
8 .name = "lib",13 .name = "lib",
...@@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void {...@@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void {
14 lib.export_symbol_names = &.{ "foo", "bar" };19 lib.export_symbol_names = &.{ "foo", "bar" };
15 lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse20 lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse
1621
17 const check_lib = lib.checkObject(.wasm);22 const check_lib = lib.checkObject();
1823
19 check_lib.checkStart("Section global");24 check_lib.checkStart("Section global");
20 check_lib.checkNext("entries 3");25 check_lib.checkNext("entries 3");
test/link/wasm/export/build.zig+14-5
...@@ -1,8 +1,18 @@...@@ -1,8 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
514
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
6 const no_export = b.addSharedLibrary(.{16 const no_export = b.addSharedLibrary(.{
7 .name = "no-export",17 .name = "no-export",
8 .root_source_file = .{ .path = "main.zig" },18 .root_source_file = .{ .path = "main.zig" },
...@@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void {...@@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void {
32 force_export.use_llvm = false;42 force_export.use_llvm = false;
33 force_export.use_lld = false;43 force_export.use_lld = false;
3444
35 const check_no_export = no_export.checkObject(.wasm);45 const check_no_export = no_export.checkObject();
36 check_no_export.checkStart("Section export");46 check_no_export.checkStart("Section export");
37 check_no_export.checkNext("entries 1");47 check_no_export.checkNext("entries 1");
38 check_no_export.checkNext("name memory");48 check_no_export.checkNext("name memory");
39 check_no_export.checkNext("kind memory");49 check_no_export.checkNext("kind memory");
4050
41 const check_dynamic_export = dynamic_export.checkObject(.wasm);51 const check_dynamic_export = dynamic_export.checkObject();
42 check_dynamic_export.checkStart("Section export");52 check_dynamic_export.checkStart("Section export");
43 check_dynamic_export.checkNext("entries 2");53 check_dynamic_export.checkNext("entries 2");
44 check_dynamic_export.checkNext("name foo");54 check_dynamic_export.checkNext("name foo");
45 check_dynamic_export.checkNext("kind function");55 check_dynamic_export.checkNext("kind function");
4656
47 const check_force_export = force_export.checkObject(.wasm);57 const check_force_export = force_export.checkObject();
48 check_force_export.checkStart("Section export");58 check_force_export.checkStart("Section export");
49 check_force_export.checkNext("entries 2");59 check_force_export.checkNext("entries 2");
50 check_force_export.checkNext("name foo");60 check_force_export.checkNext("name foo");
51 check_force_export.checkNext("kind function");61 check_force_export.checkNext("kind function");
5262
53 const test_step = b.step("test", "Run linker test");
54 test_step.dependOn(&check_no_export.step);63 test_step.dependOn(&check_no_export.step);
55 test_step.dependOn(&check_dynamic_export.step);64 test_step.dependOn(&check_dynamic_export.step);
56 test_step.dependOn(&check_force_export.step);65 test_step.dependOn(&check_force_export.step);
test/link/wasm/extern-mangle/build.zig+11-5
...@@ -1,20 +1,26 @@...@@ -1,20 +1,26 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test it");
5 test_step.dependOn(b.getInstallStep());5 b.default_step = test_step;
66
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 const lib = b.addSharedLibrary(.{14 const lib = b.addSharedLibrary(.{
8 .name = "lib",15 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },16 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },17 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),18 .optimize = optimize,
12 });19 });
13 lib.import_symbols = true; // import `a` and `b`20 lib.import_symbols = true; // import `a` and `b`
14 lib.rdynamic = true; // export `foo`21 lib.rdynamic = true; // export `foo`
15 lib.install();
1622
17 const check_lib = lib.checkObject(.wasm);23 const check_lib = lib.checkObject();
18 check_lib.checkStart("Section import");24 check_lib.checkStart("Section import");
19 check_lib.checkNext("entries 2"); // a.hello & b.hello25 check_lib.checkNext("entries 2"); // a.hello & b.hello
20 check_lib.checkNext("module a");26 check_lib.checkNext("module a");
test/link/wasm/extern/build.zig+15-3
...@@ -1,19 +1,31 @@...@@ -1,19 +1,31 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
4 const exe = b.addExecutable(.{16 const exe = b.addExecutable(.{
5 .name = "extern",17 .name = "extern",
6 .root_source_file = .{ .path = "main.zig" },18 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),19 .optimize = optimize,
8 .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },20 .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },
9 });21 });
10 exe.addCSourceFile("foo.c", &.{});22 exe.addCSourceFile("foo.c", &.{});
11 exe.use_llvm = false;23 exe.use_llvm = false;
12 exe.use_lld = false;24 exe.use_lld = false;
1325
14 const run = exe.runEmulatable();26 const run = b.addRunArtifact(exe);
27 run.skip_foreign_checks = true;
15 run.expectStdOutEqual("Result: 30");28 run.expectStdOutEqual("Result: 30");
1629
17 const test_step = b.step("test", "Run linker test");
18 test_step.dependOn(&run.step);30 test_step.dependOn(&run.step);
19}31}
test/link/wasm/function-table/build.zig+16-9
...@@ -1,13 +1,20 @@...@@ -1,13 +1,20 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
58
6 const test_step = b.step("test", "Test");9 add(b, test_step, .Debug);
7 test_step.dependOn(b.getInstallStep());10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
814
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
9 const import_table = b.addSharedLibrary(.{16 const import_table = b.addSharedLibrary(.{
10 .name = "lib",17 .name = "import_table",
11 .root_source_file = .{ .path = "lib.zig" },18 .root_source_file = .{ .path = "lib.zig" },
12 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
13 .optimize = optimize,20 .optimize = optimize,
...@@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void {...@@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void {
17 import_table.import_table = true;24 import_table.import_table = true;
1825
19 const export_table = b.addSharedLibrary(.{26 const export_table = b.addSharedLibrary(.{
20 .name = "lib",27 .name = "export_table",
21 .root_source_file = .{ .path = "lib.zig" },28 .root_source_file = .{ .path = "lib.zig" },
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },29 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
23 .optimize = optimize,30 .optimize = optimize,
...@@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void {...@@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void {
27 export_table.export_table = true;34 export_table.export_table = true;
2835
29 const regular_table = b.addSharedLibrary(.{36 const regular_table = b.addSharedLibrary(.{
30 .name = "lib",37 .name = "regular_table",
31 .root_source_file = .{ .path = "lib.zig" },38 .root_source_file = .{ .path = "lib.zig" },
32 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },39 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
33 .optimize = optimize,40 .optimize = optimize,
...@@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void {...@@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void {
35 regular_table.use_llvm = false;42 regular_table.use_llvm = false;
36 regular_table.use_lld = false;43 regular_table.use_lld = false;
3744
38 const check_import = import_table.checkObject(.wasm);45 const check_import = import_table.checkObject();
39 const check_export = export_table.checkObject(.wasm);46 const check_export = export_table.checkObject();
40 const check_regular = regular_table.checkObject(.wasm);47 const check_regular = regular_table.checkObject();
4148
42 check_import.checkStart("Section import");49 check_import.checkStart("Section import");
43 check_import.checkNext("entries 1");50 check_import.checkNext("entries 1");
test/link/wasm/infer-features/build.zig+6-5
...@@ -1,12 +1,12 @@...@@ -1,12 +1,12 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub const requires_stage2 = true;
4 const optimize = b.standardOptimizeOption(.{});
54
5pub fn build(b: *std.Build) void {
6 // Wasm Object file which we will use to infer the features from6 // Wasm Object file which we will use to infer the features from
7 const c_obj = b.addObject(.{7 const c_obj = b.addObject(.{
8 .name = "c_obj",8 .name = "c_obj",
9 .optimize = optimize,9 .optimize = .Debug,
10 .target = .{10 .target = .{
11 .cpu_arch = .wasm32,11 .cpu_arch = .wasm32,
12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
...@@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void {...@@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void {
20 const lib = b.addSharedLibrary(.{20 const lib = b.addSharedLibrary(.{
21 .name = "lib",21 .name = "lib",
22 .root_source_file = .{ .path = "main.zig" },22 .root_source_file = .{ .path = "main.zig" },
23 .optimize = optimize,23 .optimize = .Debug,
24 .target = .{24 .target = .{
25 .cpu_arch = .wasm32,25 .cpu_arch = .wasm32,
26 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },26 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
...@@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void {...@@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void {
32 lib.addObject(c_obj);32 lib.addObject(c_obj);
3333
34 // Verify the result contains the features from the C Object file.34 // Verify the result contains the features from the C Object file.
35 const check = lib.checkObject(.wasm);35 const check = lib.checkObject();
36 check.checkStart("name target_features");36 check.checkStart("name target_features");
37 check.checkNext("features 7");37 check.checkNext("features 7");
38 check.checkNext("+ atomics");38 check.checkNext("+ atomics");
...@@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void {...@@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void {
4545
46 const test_step = b.step("test", "Run linker test");46 const test_step = b.step("test", "Run linker test");
47 test_step.dependOn(&check.step);47 test_step.dependOn(&check.step);
48 b.default_step = test_step;
48}49}
test/link/wasm/producers/build.zig+14-7
...@@ -1,26 +1,33 @@...@@ -1,26 +1,33 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub const requires_stage2 = true;
5
4pub fn build(b: *std.Build) void {6pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test it");
6 test_step.dependOn(b.getInstallStep());8 b.default_step = test_step;
9
10 add(b, test_step, .Debug);
11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
715
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
8 const lib = b.addSharedLibrary(.{17 const lib = b.addSharedLibrary(.{
9 .name = "lib",18 .name = "lib",
10 .root_source_file = .{ .path = "lib.zig" },19 .root_source_file = .{ .path = "lib.zig" },
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },20 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 .optimize = b.standardOptimizeOption(.{}),21 .optimize = optimize,
13 });22 });
14 lib.use_llvm = false;23 lib.use_llvm = false;
15 lib.use_lld = false;24 lib.use_lld = false;
16 lib.strip = false;25 lib.strip = false;
17 lib.install();26 lib.install();
1827
19 const zig_version = builtin.zig_version;28 const version_fmt = "version " ++ builtin.zig_version_string;
20 var version_buf: [100]u8 = undefined;
21 const version_fmt = std.fmt.bufPrint(&version_buf, "version {}", .{zig_version}) catch unreachable;
2229
23 const check_lib = lib.checkObject(.wasm);30 const check_lib = lib.checkObject();
24 check_lib.checkStart("name producers");31 check_lib.checkStart("name producers");
25 check_lib.checkNext("fields 2");32 check_lib.checkNext("fields 2");
26 check_lib.checkNext("field_name language");33 check_lib.checkNext("field_name language");
test/link/wasm/segments/build.zig+13-4
...@@ -1,21 +1,30 @@...@@ -1,21 +1,30 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test it");
5 test_step.dependOn(b.getInstallStep());7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 const lib = b.addSharedLibrary(.{16 const lib = b.addSharedLibrary(.{
8 .name = "lib",17 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },18 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),20 .optimize = optimize,
12 });21 });
13 lib.use_llvm = false;22 lib.use_llvm = false;
14 lib.use_lld = false;23 lib.use_lld = false;
15 lib.strip = false;24 lib.strip = false;
16 lib.install();25 lib.install();
1726
18 const check_lib = lib.checkObject(.wasm);27 const check_lib = lib.checkObject();
19 check_lib.checkStart("Section data");28 check_lib.checkStart("Section data");
20 check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory29 check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory
2130
test/link/wasm/stack_pointer/build.zig+13-4
...@@ -1,14 +1,23 @@...@@ -1,14 +1,23 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test it");
5 test_step.dependOn(b.getInstallStep());7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 const lib = b.addSharedLibrary(.{16 const lib = b.addSharedLibrary(.{
8 .name = "lib",17 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },18 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),20 .optimize = optimize,
12 });21 });
13 lib.use_llvm = false;22 lib.use_llvm = false;
14 lib.use_lld = false;23 lib.use_lld = false;
...@@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void {...@@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void {
16 lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size25 lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size
17 lib.install();26 lib.install();
1827
19 const check_lib = lib.checkObject(.wasm);28 const check_lib = lib.checkObject();
2029
21 // ensure global exists and its initial value is equal to explitic stack size30 // ensure global exists and its initial value is equal to explitic stack size
22 check_lib.checkStart("Section global");31 check_lib.checkStart("Section global");
test/link/wasm/type/build.zig+13-4
...@@ -1,21 +1,30 @@...@@ -1,21 +1,30 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test it");
5 test_step.dependOn(b.getInstallStep());7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
7 const lib = b.addSharedLibrary(.{16 const lib = b.addSharedLibrary(.{
8 .name = "lib",17 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },18 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),20 .optimize = optimize,
12 });21 });
13 lib.use_llvm = false;22 lib.use_llvm = false;
14 lib.use_lld = false;23 lib.use_lld = false;
15 lib.strip = false;24 lib.strip = false;
16 lib.install();25 lib.install();
1726
18 const check_lib = lib.checkObject(.wasm);27 const check_lib = lib.checkObject();
19 check_lib.checkStart("Section type");28 check_lib.checkStart("Section type");
20 // only 2 entries, although we have 3 functions.29 // only 2 entries, although we have 3 functions.
21 // This is to test functions with the same function signature30 // This is to test functions with the same function signature
test/nvptx.zig created+106
...@@ -0,0 +1,106 @@
1const std = @import("std");
2const Cases = @import("src/Cases.zig");
3
4pub fn addCases(ctx: *Cases) !void {
5 {
6 var case = addPtx(ctx, "simple addition and subtraction");
7
8 case.addCompile(
9 \\fn add(a: i32, b: i32) i32 {
10 \\ return a + b;
11 \\}
12 \\
13 \\pub export fn add_and_substract(a: i32, out: *i32) callconv(.PtxKernel) void {
14 \\ const x = add(a, 7);
15 \\ var y = add(2, 0);
16 \\ y -= x;
17 \\ out.* = y;
18 \\}
19 );
20 }
21
22 {
23 var case = addPtx(ctx, "read special registers");
24
25 case.addCompile(
26 \\fn threadIdX() u32 {
27 \\ return asm ("mov.u32 \t%[r], %tid.x;"
28 \\ : [r] "=r" (-> u32),
29 \\ );
30 \\}
31 \\
32 \\pub export fn special_reg(a: []const i32, out: []i32) callconv(.PtxKernel) void {
33 \\ const i = threadIdX();
34 \\ out[i] = a[i] + 7;
35 \\}
36 );
37 }
38
39 {
40 var case = addPtx(ctx, "address spaces");
41
42 case.addCompile(
43 \\var x: i32 addrspace(.global) = 0;
44 \\
45 \\pub export fn increment(out: *i32) callconv(.PtxKernel) void {
46 \\ x += 1;
47 \\ out.* = x;
48 \\}
49 );
50 }
51
52 {
53 var case = addPtx(ctx, "reduce in shared mem");
54 case.addCompile(
55 \\fn threadIdX() u32 {
56 \\ return asm ("mov.u32 \t%[r], %tid.x;"
57 \\ : [r] "=r" (-> u32),
58 \\ );
59 \\}
60 \\
61 \\ var _sdata: [1024]f32 addrspace(.shared) = undefined;
62 \\ pub export fn reduceSum(d_x: []const f32, out: *f32) callconv(.PtxKernel) void {
63 \\ var sdata = @addrSpaceCast(.generic, &_sdata);
64 \\ const tid: u32 = threadIdX();
65 \\ var sum = d_x[tid];
66 \\ sdata[tid] = sum;
67 \\ asm volatile ("bar.sync \t0;");
68 \\ var s: u32 = 512;
69 \\ while (s > 0) : (s = s >> 1) {
70 \\ if (tid < s) {
71 \\ sum += sdata[tid + s];
72 \\ sdata[tid] = sum;
73 \\ }
74 \\ asm volatile ("bar.sync \t0;");
75 \\ }
76 \\
77 \\ if (tid == 0) {
78 \\ out.* = sum;
79 \\ }
80 \\ }
81 );
82 }
83}
84
85const nvptx_target = std.zig.CrossTarget{
86 .cpu_arch = .nvptx64,
87 .os_tag = .cuda,
88};
89
90pub fn addPtx(
91 ctx: *Cases,
92 name: []const u8,
93) *Cases.Case {
94 ctx.cases.append(.{
95 .name = name,
96 .target = nvptx_target,
97 .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator),
98 .output_mode = .Obj,
99 .deps = std.ArrayList(Cases.DepModule).init(ctx.cases.allocator),
100 .link_libc = false,
101 .backend = .llvm,
102 // Bug in Debug mode
103 .optimize_mode = .ReleaseSafe,
104 }) catch @panic("out of memory");
105 return &ctx.cases.items[ctx.cases.items.len - 1];
106}
test/src/Cases.zig created+1587
...@@ -0,0 +1,1587 @@
1gpa: Allocator,
2arena: Allocator,
3cases: std.ArrayList(Case),
4incremental_cases: std.ArrayList(IncrementalCase),
5
6pub const IncrementalCase = struct {
7 base_path: []const u8,
8};
9
10pub const Update = struct {
11 /// The input to the current update. We simulate an incremental update
12 /// with the file's contents changed to this value each update.
13 ///
14 /// This value can change entirely between updates, which would be akin
15 /// to deleting the source file and creating a new one from scratch; or
16 /// you can keep it mostly consistent, with small changes, testing the
17 /// effects of the incremental compilation.
18 files: std.ArrayList(File),
19 /// This is a description of what happens with the update, for debugging
20 /// purposes.
21 name: []const u8,
22 case: union(enum) {
23 /// Check that it compiles with no errors.
24 Compile: void,
25 /// Check the main binary output file against an expected set of bytes.
26 /// This is most useful with, for example, `-ofmt=c`.
27 CompareObjectFile: []const u8,
28 /// An error update attempts to compile bad code, and ensures that it
29 /// fails to compile, and for the expected reasons.
30 /// A slice containing the expected stderr template, which
31 /// gets some values substituted.
32 Error: []const []const u8,
33 /// An execution update compiles and runs the input, testing the
34 /// stdout against the expected results
35 /// This is a slice containing the expected message.
36 Execution: []const u8,
37 /// A header update compiles the input with the equivalent of
38 /// `-femit-h` and tests the produced header against the
39 /// expected result
40 Header: []const u8,
41 },
42
43 pub fn addSourceFile(update: *Update, name: []const u8, src: [:0]const u8) void {
44 update.files.append(.{ .path = name, .src = src }) catch @panic("out of memory");
45 }
46};
47
48pub const File = struct {
49 src: [:0]const u8,
50 path: []const u8,
51};
52
53pub const DepModule = struct {
54 name: []const u8,
55 path: []const u8,
56};
57
58pub const Backend = enum {
59 stage1,
60 stage2,
61 llvm,
62};
63
64/// A `Case` consists of a list of `Update`. The same `Compilation` is used for each
65/// update, so each update's source is treated as a single file being
66/// updated by the test harness and incrementally compiled.
67pub const Case = struct {
68 /// The name of the test case. This is shown if a test fails, and
69 /// otherwise ignored.
70 name: []const u8,
71 /// The platform the test targets. For non-native platforms, an emulator
72 /// such as QEMU is required for tests to complete.
73 target: CrossTarget,
74 /// In order to be able to run e.g. Execution updates, this must be set
75 /// to Executable.
76 output_mode: std.builtin.OutputMode,
77 optimize_mode: std.builtin.Mode = .Debug,
78 updates: std.ArrayList(Update),
79 emit_h: bool = false,
80 is_test: bool = false,
81 expect_exact: bool = false,
82 backend: Backend = .stage2,
83 link_libc: bool = false,
84
85 deps: std.ArrayList(DepModule),
86
87 pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void {
88 const update = &case.updates.items[case.updates.items.len - 1];
89 update.files.append(.{ .path = name, .src = src }) catch @panic("OOM");
90 }
91
92 pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void {
93 case.deps.append(.{
94 .name = name,
95 .path = path,
96 }) catch @panic("out of memory");
97 }
98
99 /// Adds a subcase in which the module is updated with `src`, compiled,
100 /// run, and the output is tested against `result`.
101 pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
102 self.updates.append(.{
103 .files = std.ArrayList(File).init(self.updates.allocator),
104 .name = "update",
105 .case = .{ .Execution = result },
106 }) catch @panic("out of memory");
107 addSourceFile(self, "tmp.zig", src);
108 }
109
110 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
111 return self.addErrorNamed("update", src, errors);
112 }
113
114 /// Adds a subcase in which the module is updated with `src`, which
115 /// should contain invalid input, and ensures that compilation fails
116 /// for the expected reasons, given in sequential order in `errors` in
117 /// the form `:line:column: error: message`.
118 pub fn addErrorNamed(
119 self: *Case,
120 name: []const u8,
121 src: [:0]const u8,
122 errors: []const []const u8,
123 ) void {
124 assert(errors.len != 0);
125 self.updates.append(.{
126 .files = std.ArrayList(File).init(self.updates.allocator),
127 .name = name,
128 .case = .{ .Error = errors },
129 }) catch @panic("out of memory");
130 addSourceFile(self, "tmp.zig", src);
131 }
132
133 /// Adds a subcase in which the module is updated with `src`, and
134 /// asserts that it compiles without issue
135 pub fn addCompile(self: *Case, src: [:0]const u8) void {
136 self.updates.append(.{
137 .files = std.ArrayList(File).init(self.updates.allocator),
138 .name = "compile",
139 .case = .{ .Compile = {} },
140 }) catch @panic("out of memory");
141 addSourceFile(self, "tmp.zig", src);
142 }
143};
144
145pub fn addExe(
146 ctx: *Cases,
147 name: []const u8,
148 target: CrossTarget,
149) *Case {
150 ctx.cases.append(Case{
151 .name = name,
152 .target = target,
153 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
154 .output_mode = .Exe,
155 .deps = std.ArrayList(DepModule).init(ctx.arena),
156 }) catch @panic("out of memory");
157 return &ctx.cases.items[ctx.cases.items.len - 1];
158}
159
160/// Adds a test case for Zig input, producing an executable
161pub fn exe(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
162 return ctx.addExe(name, target);
163}
164
165pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
166 var target_adjusted = target;
167 target_adjusted.ofmt = .c;
168 ctx.cases.append(Case{
169 .name = name,
170 .target = target_adjusted,
171 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
172 .output_mode = .Exe,
173 .deps = std.ArrayList(DepModule).init(ctx.arena),
174 .link_libc = true,
175 }) catch @panic("out of memory");
176 return &ctx.cases.items[ctx.cases.items.len - 1];
177}
178
179/// Adds a test case that uses the LLVM backend to emit an executable.
180/// Currently this implies linking libc, because only then we can generate a testable executable.
181pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
182 ctx.cases.append(Case{
183 .name = name,
184 .target = target,
185 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
186 .output_mode = .Exe,
187 .deps = std.ArrayList(DepModule).init(ctx.arena),
188 .backend = .llvm,
189 .link_libc = true,
190 }) catch @panic("out of memory");
191 return &ctx.cases.items[ctx.cases.items.len - 1];
192}
193
194pub fn addObj(
195 ctx: *Cases,
196 name: []const u8,
197 target: CrossTarget,
198) *Case {
199 ctx.cases.append(Case{
200 .name = name,
201 .target = target,
202 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
203 .output_mode = .Obj,
204 .deps = std.ArrayList(DepModule).init(ctx.arena),
205 }) catch @panic("out of memory");
206 return &ctx.cases.items[ctx.cases.items.len - 1];
207}
208
209pub fn addTest(
210 ctx: *Cases,
211 name: []const u8,
212 target: CrossTarget,
213) *Case {
214 ctx.cases.append(Case{
215 .name = name,
216 .target = target,
217 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
218 .output_mode = .Exe,
219 .is_test = true,
220 .deps = std.ArrayList(DepModule).init(ctx.arena),
221 }) catch @panic("out of memory");
222 return &ctx.cases.items[ctx.cases.items.len - 1];
223}
224
225/// Adds a test case for Zig input, producing an object file.
226pub fn obj(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
227 return ctx.addObj(name, target);
228}
229
230/// Adds a test case for ZIR input, producing an object file.
231pub fn objZIR(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
232 return ctx.addObj(name, target, .ZIR);
233}
234
235/// Adds a test case for Zig or ZIR input, producing C code.
236pub fn addC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
237 var target_adjusted = target;
238 target_adjusted.ofmt = std.Target.ObjectFormat.c;
239 ctx.cases.append(Case{
240 .name = name,
241 .target = target_adjusted,
242 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
243 .output_mode = .Obj,
244 .deps = std.ArrayList(DepModule).init(ctx.arena),
245 }) catch @panic("out of memory");
246 return &ctx.cases.items[ctx.cases.items.len - 1];
247}
248
249pub fn addCompareOutput(
250 ctx: *Cases,
251 name: []const u8,
252 src: [:0]const u8,
253 expected_stdout: []const u8,
254) void {
255 ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout);
256}
257
258/// Adds a test case that compiles the Zig source given in `src`, executes
259/// it, runs it, and tests the output against `expected_stdout`
260pub fn compareOutput(
261 ctx: *Cases,
262 name: []const u8,
263 src: [:0]const u8,
264 expected_stdout: []const u8,
265) void {
266 return ctx.addCompareOutput(name, src, expected_stdout);
267}
268
269pub fn addTransform(
270 ctx: *Cases,
271 name: []const u8,
272 target: CrossTarget,
273 src: [:0]const u8,
274 result: [:0]const u8,
275) void {
276 ctx.addObj(name, target).addTransform(src, result);
277}
278
279/// Adds a test case that compiles the Zig given in `src` to ZIR and tests
280/// the ZIR against `result`
281pub fn transform(
282 ctx: *Cases,
283 name: []const u8,
284 target: CrossTarget,
285 src: [:0]const u8,
286 result: [:0]const u8,
287) void {
288 ctx.addTransform(name, target, src, result);
289}
290
291pub fn addError(
292 ctx: *Cases,
293 name: []const u8,
294 target: CrossTarget,
295 src: [:0]const u8,
296 expected_errors: []const []const u8,
297) void {
298 ctx.addObj(name, target).addError(src, expected_errors);
299}
300
301/// Adds a test case that ensures that the Zig given in `src` fails to
302/// compile for the expected reasons, given in sequential order in
303/// `expected_errors` in the form `:line:column: error: message`.
304pub fn compileError(
305 ctx: *Cases,
306 name: []const u8,
307 target: CrossTarget,
308 src: [:0]const u8,
309 expected_errors: []const []const u8,
310) void {
311 ctx.addError(name, target, src, expected_errors);
312}
313
314/// Adds a test case that asserts that the Zig given in `src` compiles
315/// without any errors.
316pub fn addCompile(
317 ctx: *Cases,
318 name: []const u8,
319 target: CrossTarget,
320 src: [:0]const u8,
321) void {
322 ctx.addObj(name, target).addCompile(src);
323}
324
325/// Adds a test for each file in the provided directory.
326/// Testing strategy (TestStrategy) is inferred automatically from filenames.
327/// Recurses nested directories.
328///
329/// Each file should include a test manifest as a contiguous block of comments at
330/// the end of the file. The first line should be the test type, followed by a set of
331/// key-value config values, followed by a blank line, then the expected output.
332pub fn addFromDir(ctx: *Cases, dir: std.fs.IterableDir) void {
333 var current_file: []const u8 = "none";
334 ctx.addFromDirInner(dir, &current_file) catch |err| {
335 std.debug.panic("test harness failed to process file '{s}': {s}\n", .{
336 current_file, @errorName(err),
337 });
338 };
339}
340
341fn addFromDirInner(
342 ctx: *Cases,
343 iterable_dir: std.fs.IterableDir,
344 /// This is kept up to date with the currently being processed file so
345 /// that if any errors occur the caller knows it happened during this file.
346 current_file: *[]const u8,
347) !void {
348 var it = try iterable_dir.walk(ctx.arena);
349 var filenames = std.ArrayList([]const u8).init(ctx.arena);
350
351 while (try it.next()) |entry| {
352 if (entry.kind != .File) continue;
353
354 // Ignore stuff such as .swp files
355 switch (Compilation.classifyFileExt(entry.basename)) {
356 .unknown => continue,
357 else => {},
358 }
359 try filenames.append(try ctx.arena.dupe(u8, entry.path));
360 }
361
362 // Sort filenames, so that incremental tests are contiguous and in-order
363 sortTestFilenames(filenames.items);
364
365 var test_it = TestIterator{ .filenames = filenames.items };
366 while (test_it.next()) |maybe_batch| {
367 const batch = maybe_batch orelse break;
368 const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent;
369 const filename = batch[0];
370 current_file.* = filename;
371 if (strategy == .incremental) {
372 try ctx.incremental_cases.append(.{ .base_path = filename });
373 continue;
374 }
375
376 const max_file_size = 10 * 1024 * 1024;
377 const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);
378
379 // Parse the manifest
380 var manifest = try TestManifest.parse(ctx.arena, src);
381
382 const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend);
383 const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget);
384 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
385 const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool);
386 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);
387
388 var cases = std.ArrayList(usize).init(ctx.arena);
389
390 // Cross-product to get all possible test combinations
391 for (backends) |backend| {
392 for (targets) |target| {
393 const next = ctx.cases.items.len;
394 try ctx.cases.append(.{
395 .name = std.fs.path.stem(filename),
396 .target = target,
397 .backend = backend,
398 .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator),
399 .is_test = is_test,
400 .output_mode = output_mode,
401 .link_libc = link_libc,
402 .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
403 });
404 try cases.append(next);
405 }
406 }
407
408 for (cases.items) |case_index| {
409 const case = &ctx.cases.items[case_index];
410 switch (manifest.type) {
411 .compile => {
412 case.addCompile(src);
413 },
414 .@"error" => {
415 const errors = try manifest.trailingAlloc(ctx.arena);
416 case.addError(src, errors);
417 },
418 .run => {
419 var output = std.ArrayList(u8).init(ctx.arena);
420 var trailing_it = manifest.trailing();
421 while (trailing_it.next()) |line| {
422 try output.appendSlice(line);
423 try output.append('\n');
424 }
425 if (output.items.len > 0) {
426 try output.resize(output.items.len - 1);
427 }
428 case.addCompareOutput(src, try output.toOwnedSlice());
429 },
430 .cli => @panic("TODO cli tests"),
431 }
432 }
433 } else |err| {
434 // make sure the current file is set to the file that produced an error
435 current_file.* = test_it.currentFilename();
436 return err;
437 }
438}
439
440pub fn init(gpa: Allocator, arena: Allocator) Cases {
441 return .{
442 .gpa = gpa,
443 .cases = std.ArrayList(Case).init(gpa),
444 .incremental_cases = std.ArrayList(IncrementalCase).init(gpa),
445 .arena = arena,
446 };
447}
448
449pub fn lowerToBuildSteps(
450 self: *Cases,
451 b: *std.Build,
452 parent_step: *std.Build.Step,
453 opt_test_filter: ?[]const u8,
454 cases_dir_path: []const u8,
455 incremental_exe: *std.Build.CompileStep,
456) void {
457 for (self.incremental_cases.items) |incr_case| {
458 if (opt_test_filter) |test_filter| {
459 if (std.mem.indexOf(u8, incr_case.base_path, test_filter) == null) continue;
460 }
461 const case_base_path_with_dir = std.fs.path.join(b.allocator, &.{
462 cases_dir_path, incr_case.base_path,
463 }) catch @panic("OOM");
464 const run = b.addRunArtifact(incremental_exe);
465 run.setName(incr_case.base_path);
466 run.addArgs(&.{
467 case_base_path_with_dir,
468 b.zig_exe,
469 });
470 run.expectStdOutEqual("");
471 parent_step.dependOn(&run.step);
472 }
473
474 for (self.cases.items) |case| {
475 if (case.updates.items.len != 1) continue; // handled with incremental_cases above
476 assert(case.updates.items.len == 1);
477 const update = case.updates.items[0];
478
479 if (opt_test_filter) |test_filter| {
480 if (std.mem.indexOf(u8, case.name, test_filter) == null) continue;
481 }
482
483 const writefiles = b.addWriteFiles();
484 for (update.files.items) |file| {
485 writefiles.add(file.path, file.src);
486 }
487 const root_source_file = writefiles.getFileSource(update.files.items[0].path).?;
488
489 const artifact = if (case.is_test) b.addTest(.{
490 .root_source_file = root_source_file,
491 .name = case.name,
492 .target = case.target,
493 .optimize = case.optimize_mode,
494 }) else switch (case.output_mode) {
495 .Obj => b.addObject(.{
496 .root_source_file = root_source_file,
497 .name = case.name,
498 .target = case.target,
499 .optimize = case.optimize_mode,
500 }),
501 .Lib => b.addStaticLibrary(.{
502 .root_source_file = root_source_file,
503 .name = case.name,
504 .target = case.target,
505 .optimize = case.optimize_mode,
506 }),
507 .Exe => b.addExecutable(.{
508 .root_source_file = root_source_file,
509 .name = case.name,
510 .target = case.target,
511 .optimize = case.optimize_mode,
512 }),
513 };
514
515 if (case.link_libc) artifact.linkLibC();
516
517 switch (case.backend) {
518 .stage1 => continue,
519 .stage2 => {
520 artifact.use_llvm = false;
521 artifact.use_lld = false;
522 },
523 .llvm => {
524 artifact.use_llvm = true;
525 },
526 }
527
528 for (case.deps.items) |dep| {
529 artifact.addAnonymousModule(dep.name, .{
530 .source_file = writefiles.getFileSource(dep.path).?,
531 });
532 }
533
534 switch (update.case) {
535 .Compile => {
536 parent_step.dependOn(&artifact.step);
537 },
538 .CompareObjectFile => |expected_output| {
539 const check = b.addCheckFile(artifact.getOutputSource(), .{
540 .expected_exact = expected_output,
541 });
542
543 parent_step.dependOn(&check.step);
544 },
545 .Error => |expected_msgs| {
546 assert(expected_msgs.len != 0);
547 artifact.expect_errors = expected_msgs;
548 parent_step.dependOn(&artifact.step);
549 },
550 .Execution => |expected_stdout| {
551 const run = b.addRunArtifact(artifact);
552 run.skip_foreign_checks = true;
553 if (!case.is_test) {
554 run.expectStdOutEqual(expected_stdout);
555 }
556 parent_step.dependOn(&run.step);
557 },
558 .Header => @panic("TODO"),
559 }
560 }
561}
562
563/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig",
564/// "foo.1.zig", etc.) are contiguous and appear in numerical order.
565fn sortTestFilenames(filenames: [][]const u8) void {
566 const Context = struct {
567 pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool {
568 const a_parts = getTestFileNameParts(a);
569 const b_parts = getTestFileNameParts(b);
570
571 // Sort "<base_name>.X.<file_ext>" based on "<base_name>" and "<file_ext>" first
572 return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) {
573 .lt => true,
574 .gt => false,
575 .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) {
576 .lt => true,
577 .gt => false,
578 .eq => {
579 // a and b differ only in their ".X" part
580
581 // Sort "<base_name>.<file_ext>" before any "<base_name>.X.<file_ext>"
582 if (a_parts.test_index) |a_index| {
583 if (b_parts.test_index) |b_index| {
584 // Make sure that incremental tests appear in linear order
585 return a_index < b_index;
586 } else {
587 return false;
588 }
589 } else {
590 return b_parts.test_index != null;
591 }
592 },
593 },
594 };
595 }
596 };
597 std.sort.sort([]const u8, filenames, Context{}, Context.lessThan);
598}
599
600/// Iterates a set of filenames extracting batches that are either incremental
601/// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.).
602/// Assumes filenames are sorted.
603const TestIterator = struct {
604 start: usize = 0,
605 end: usize = 0,
606 filenames: []const []const u8,
607 /// reset on each call to `next`
608 index: usize = 0,
609
610 const Error = error{InvalidIncrementalTestIndex};
611
612 fn next(it: *TestIterator) Error!?[]const []const u8 {
613 try it.nextInner();
614 if (it.start == it.end) return null;
615 return it.filenames[it.start..it.end];
616 }
617
618 fn nextInner(it: *TestIterator) Error!void {
619 it.start = it.end;
620 if (it.end == it.filenames.len) return;
621 if (it.end + 1 == it.filenames.len) {
622 it.end += 1;
623 return;
624 }
625
626 const remaining = it.filenames[it.end..];
627 it.index = 0;
628 while (it.index < remaining.len - 1) : (it.index += 1) {
629 // First, check if this file is part of an incremental update sequence
630 // Split filename into "<base_name>.<index>.<file_ext>"
631 const prev_parts = getTestFileNameParts(remaining[it.index]);
632 const new_parts = getTestFileNameParts(remaining[it.index + 1]);
633
634 // If base_name and file_ext match, these files are in the same test sequence
635 // and the new one should be the incremented version of the previous test
636 if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and
637 std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext))
638 {
639 // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1
640 if (prev_parts.test_index == null)
641 return error.InvalidIncrementalTestIndex;
642 if (new_parts.test_index == null)
643 return error.InvalidIncrementalTestIndex;
644 if (new_parts.test_index.? != prev_parts.test_index.? + 1)
645 return error.InvalidIncrementalTestIndex;
646 } else {
647 // This is not the same test sequence, so the new file must be the first file
648 // in a new sequence ("*.0.zig") or an independent test file ("*.zig")
649 if (new_parts.test_index != null and new_parts.test_index.? != 0)
650 return error.InvalidIncrementalTestIndex;
651
652 it.end += it.index + 1;
653 break;
654 }
655 } else {
656 it.end += remaining.len;
657 }
658 }
659
660 /// In the event of an `error.InvalidIncrementalTestIndex`, this function can
661 /// be used to find the current filename that was being processed.
662 /// Asserts the iterator hasn't reached the end.
663 fn currentFilename(it: TestIterator) []const u8 {
664 assert(it.end != it.filenames.len);
665 const remaining = it.filenames[it.end..];
666 return remaining[it.index + 1];
667 }
668};
669
670/// For a filename in the format "<filename>.X.<ext>" or "<filename>.<ext>", returns
671/// "<filename>", "<ext>" and X parsed as a decimal number. If X is not present, or
672/// cannot be parsed as a decimal number, it is treated as part of <filename>
673fn getTestFileNameParts(name: []const u8) struct {
674 base_name: []const u8,
675 file_ext: []const u8,
676 test_index: ?usize,
677} {
678 const file_ext = std.fs.path.extension(name);
679 const trimmed = name[0 .. name.len - file_ext.len]; // Trim off ".<ext>"
680 const maybe_index = std.fs.path.extension(trimmed); // Extract ".X"
681
682 // Attempt to parse index
683 const index: ?usize = if (maybe_index.len > 0)
684 std.fmt.parseInt(usize, maybe_index[1..], 10) catch null
685 else
686 null;
687
688 // Adjust "<filename>" extent based on parsing success
689 const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0;
690 return .{
691 .base_name = name[0..base_name_end],
692 .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext,
693 .test_index = index,
694 };
695}
696
697const TestStrategy = enum {
698 /// Execute tests as independent compilations, unless they are explicitly
699 /// incremental ("foo.0.zig", "foo.1.zig", etc.)
700 independent,
701 /// Execute all tests as incremental updates to a single compilation. Explicitly
702 /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order
703 incremental,
704};
705
706/// Default config values for known test manifest key-value pairings.
707/// Currently handled defaults are:
708/// * backend
709/// * target
710/// * output_mode
711/// * is_test
712const TestManifestConfigDefaults = struct {
713 /// Asserts if the key doesn't exist - yep, it's an oversight alright.
714 fn get(@"type": TestManifest.Type, key: []const u8) []const u8 {
715 if (std.mem.eql(u8, key, "backend")) {
716 return "stage2";
717 } else if (std.mem.eql(u8, key, "target")) {
718 if (@"type" == .@"error") {
719 return "native";
720 }
721 comptime {
722 var defaults: []const u8 = "";
723 // TODO should we only return "mainstream" targets by default here?
724 // TODO we should also specify ABIs explicitly as the backends are
725 // getting more and more complete
726 // Linux
727 inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| {
728 defaults = defaults ++ arch ++ "-linux" ++ ",";
729 }
730 // macOS
731 inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| {
732 defaults = defaults ++ arch ++ "-macos" ++ ",";
733 }
734 // Windows
735 defaults = defaults ++ "x86_64-windows" ++ ",";
736 // Wasm
737 defaults = defaults ++ "wasm32-wasi";
738 return defaults;
739 }
740 } else if (std.mem.eql(u8, key, "output_mode")) {
741 return switch (@"type") {
742 .@"error" => "Obj",
743 .run => "Exe",
744 .compile => "Obj",
745 .cli => @panic("TODO test harness for CLI tests"),
746 };
747 } else if (std.mem.eql(u8, key, "is_test")) {
748 return "0";
749 } else if (std.mem.eql(u8, key, "link_libc")) {
750 return "0";
751 } else unreachable;
752 }
753};
754
755/// Manifest syntax example:
756/// (see https://github.com/ziglang/zig/issues/11288)
757///
758/// error
759/// backend=stage1,stage2
760/// output_mode=exe
761///
762/// :3:19: error: foo
763///
764/// run
765/// target=x86_64-linux,aarch64-macos
766///
767/// I am expected stdout! Hello!
768///
769/// cli
770///
771/// build test
772const TestManifest = struct {
773 type: Type,
774 config_map: std.StringHashMap([]const u8),
775 trailing_bytes: []const u8 = "",
776
777 const Type = enum {
778 @"error",
779 run,
780 cli,
781 compile,
782 };
783
784 const TrailingIterator = struct {
785 inner: std.mem.TokenIterator(u8),
786
787 fn next(self: *TrailingIterator) ?[]const u8 {
788 const next_inner = self.inner.next() orelse return null;
789 return std.mem.trim(u8, next_inner[2..], " \t");
790 }
791 };
792
793 fn ConfigValueIterator(comptime T: type) type {
794 return struct {
795 inner: std.mem.SplitIterator(u8),
796
797 fn next(self: *@This()) !?T {
798 const next_raw = self.inner.next() orelse return null;
799 const parseFn = getDefaultParser(T);
800 return try parseFn(next_raw);
801 }
802 };
803 }
804
805 fn parse(arena: Allocator, bytes: []const u8) !TestManifest {
806 // The manifest is the last contiguous block of comments in the file
807 // We scan for the beginning by searching backward for the first non-empty line that does not start with "//"
808 var start: ?usize = null;
809 var end: usize = bytes.len;
810 if (bytes.len > 0) {
811 var cursor: usize = bytes.len - 1;
812 while (true) {
813 // Move to beginning of line
814 while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1;
815
816 if (std.mem.startsWith(u8, bytes[cursor..], "//")) {
817 start = cursor; // Contiguous comment line, include in manifest
818 } else {
819 if (start != null) break; // Encountered non-comment line, end of manifest
820
821 // We ignore all-whitespace lines following the comment block, but anything else
822 // means that there is no manifest present.
823 if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) {
824 end = cursor;
825 } else break; // If it's not whitespace, there is no manifest
826 }
827
828 // Move to previous line
829 if (cursor != 0) cursor -= 1 else break;
830 }
831 }
832
833 const actual_start = start orelse return error.MissingTestManifest;
834 const manifest_bytes = bytes[actual_start..end];
835
836 var it = std.mem.tokenize(u8, manifest_bytes, "\r\n");
837
838 // First line is the test type
839 const tt: Type = blk: {
840 const line = it.next() orelse return error.MissingTestCaseType;
841 const raw = std.mem.trim(u8, line[2..], " \t");
842 if (std.mem.eql(u8, raw, "error")) {
843 break :blk .@"error";
844 } else if (std.mem.eql(u8, raw, "run")) {
845 break :blk .run;
846 } else if (std.mem.eql(u8, raw, "cli")) {
847 break :blk .cli;
848 } else if (std.mem.eql(u8, raw, "compile")) {
849 break :blk .compile;
850 } else {
851 std.log.warn("unknown test case type requested: {s}", .{raw});
852 return error.UnknownTestCaseType;
853 }
854 };
855
856 var manifest: TestManifest = .{
857 .type = tt,
858 .config_map = std.StringHashMap([]const u8).init(arena),
859 };
860
861 // Any subsequent line until a blank comment line is key=value(s) pair
862 while (it.next()) |line| {
863 const trimmed = std.mem.trim(u8, line[2..], " \t");
864 if (trimmed.len == 0) break;
865
866 // Parse key=value(s)
867 var kv_it = std.mem.split(u8, trimmed, "=");
868 const key = kv_it.first();
869 try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig);
870 }
871
872 // Finally, trailing is expected output
873 manifest.trailing_bytes = manifest_bytes[it.index..];
874
875 return manifest;
876 }
877
878 fn getConfigForKey(
879 self: TestManifest,
880 key: []const u8,
881 comptime T: type,
882 ) ConfigValueIterator(T) {
883 const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key);
884 return ConfigValueIterator(T){
885 .inner = std.mem.split(u8, bytes, ","),
886 };
887 }
888
889 fn getConfigForKeyAlloc(
890 self: TestManifest,
891 allocator: Allocator,
892 key: []const u8,
893 comptime T: type,
894 ) ![]const T {
895 var out = std.ArrayList(T).init(allocator);
896 defer out.deinit();
897 var it = self.getConfigForKey(key, T);
898 while (try it.next()) |item| {
899 try out.append(item);
900 }
901 return try out.toOwnedSlice();
902 }
903
904 fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T {
905 var it = self.getConfigForKey(key, T);
906 const res = (try it.next()) orelse unreachable;
907 assert((try it.next()) == null);
908 return res;
909 }
910
911 fn trailing(self: TestManifest) TrailingIterator {
912 return .{
913 .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"),
914 };
915 }
916
917 fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 {
918 var out = std.ArrayList([]const u8).init(allocator);
919 defer out.deinit();
920 var it = self.trailing();
921 while (it.next()) |line| {
922 try out.append(line);
923 }
924 return try out.toOwnedSlice();
925 }
926
927 fn ParseFn(comptime T: type) type {
928 return fn ([]const u8) anyerror!T;
929 }
930
931 fn getDefaultParser(comptime T: type) ParseFn(T) {
932 if (T == CrossTarget) return struct {
933 fn parse(str: []const u8) anyerror!T {
934 var opts = CrossTarget.ParseOptions{
935 .arch_os_abi = str,
936 };
937 return try CrossTarget.parse(opts);
938 }
939 }.parse;
940
941 switch (@typeInfo(T)) {
942 .Int => return struct {
943 fn parse(str: []const u8) anyerror!T {
944 return try std.fmt.parseInt(T, str, 0);
945 }
946 }.parse,
947 .Bool => return struct {
948 fn parse(str: []const u8) anyerror!T {
949 const as_int = try std.fmt.parseInt(u1, str, 0);
950 return as_int > 0;
951 }
952 }.parse,
953 .Enum => return struct {
954 fn parse(str: []const u8) anyerror!T {
955 return std.meta.stringToEnum(T, str) orelse {
956 std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str });
957 return error.UnknownEnumVariant;
958 };
959 }
960 }.parse,
961 .Struct => @compileError("no default parser for " ++ @typeName(T)),
962 else => @compileError("no default parser for " ++ @typeName(T)),
963 }
964 }
965};
966
967const Cases = @This();
968const builtin = @import("builtin");
969const std = @import("std");
970const assert = std.debug.assert;
971const Allocator = std.mem.Allocator;
972const CrossTarget = std.zig.CrossTarget;
973const Compilation = @import("../../src/Compilation.zig");
974const zig_h = @import("../../src/link.zig").File.C.zig_h;
975const introspect = @import("../../src/introspect.zig");
976const ThreadPool = std.Thread.Pool;
977const WaitGroup = std.Thread.WaitGroup;
978const build_options = @import("build_options");
979const Package = @import("../../src/Package.zig");
980
981pub const std_options = struct {
982 pub const log_level: std.log.Level = .err;
983};
984
985var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{
986 .stack_trace_frames = build_options.mem_leak_frames,
987}){};
988
989// TODO: instead of embedding the compiler in this process, spawn the compiler
990// as a sub-process and communicate the updates using the compiler protocol.
991pub fn main() !void {
992 const use_gpa = build_options.force_gpa or !builtin.link_libc;
993 const gpa = gpa: {
994 if (use_gpa) {
995 break :gpa general_purpose_allocator.allocator();
996 }
997 // We would prefer to use raw libc allocator here, but cannot
998 // use it if it won't support the alignment we need.
999 if (@alignOf(std.c.max_align_t) < @alignOf(i128)) {
1000 break :gpa std.heap.c_allocator;
1001 }
1002 break :gpa std.heap.raw_c_allocator;
1003 };
1004
1005 var single_threaded_arena = std.heap.ArenaAllocator.init(gpa);
1006 defer single_threaded_arena.deinit();
1007
1008 var thread_safe_arena: std.heap.ThreadSafeAllocator = .{
1009 .child_allocator = single_threaded_arena.allocator(),
1010 };
1011 const arena = thread_safe_arena.allocator();
1012
1013 const args = try std.process.argsAlloc(arena);
1014 const case_file_path = args[1];
1015 const zig_exe_path = args[2];
1016
1017 var filenames = std.ArrayList([]const u8).init(arena);
1018
1019 const case_dirname = std.fs.path.dirname(case_file_path).?;
1020 var iterable_dir = try std.fs.cwd().openIterableDir(case_dirname, .{});
1021 defer iterable_dir.close();
1022
1023 if (std.mem.endsWith(u8, case_file_path, ".0.zig")) {
1024 const stem = case_file_path[case_dirname.len + 1 .. case_file_path.len - "0.zig".len];
1025 var it = iterable_dir.iterate();
1026 while (try it.next()) |entry| {
1027 if (entry.kind != .File) continue;
1028 if (!std.mem.startsWith(u8, entry.name, stem)) continue;
1029 try filenames.append(try std.fs.path.join(arena, &.{ case_dirname, entry.name }));
1030 }
1031 } else {
1032 try filenames.append(case_file_path);
1033 }
1034
1035 if (filenames.items.len == 0) {
1036 std.debug.print("failed to find the input source file(s) from '{s}'\n", .{
1037 case_file_path,
1038 });
1039 std.process.exit(1);
1040 }
1041
1042 // Sort filenames, so that incremental tests are contiguous and in-order
1043 sortTestFilenames(filenames.items);
1044
1045 var ctx = Cases.init(gpa, arena);
1046
1047 var test_it = TestIterator{ .filenames = filenames.items };
1048 while (test_it.next()) |maybe_batch| {
1049 const batch = maybe_batch orelse break;
1050 const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent;
1051 var cases = std.ArrayList(usize).init(arena);
1052
1053 for (batch) |filename| {
1054 const max_file_size = 10 * 1024 * 1024;
1055 const src = try iterable_dir.dir.readFileAllocOptions(arena, filename, max_file_size, null, 1, 0);
1056
1057 // Parse the manifest
1058 var manifest = try TestManifest.parse(arena, src);
1059
1060 if (cases.items.len == 0) {
1061 const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend);
1062 const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget);
1063 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
1064 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);
1065
1066 // Cross-product to get all possible test combinations
1067 for (backends) |backend| {
1068 for (targets) |target| {
1069 const next = ctx.cases.items.len;
1070 try ctx.cases.append(.{
1071 .name = std.fs.path.stem(filename),
1072 .target = target,
1073 .backend = backend,
1074 .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator),
1075 .is_test = is_test,
1076 .output_mode = output_mode,
1077 .link_libc = backend == .llvm,
1078 .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
1079 });
1080 try cases.append(next);
1081 }
1082 }
1083 }
1084
1085 for (cases.items) |case_index| {
1086 const case = &ctx.cases.items[case_index];
1087 switch (manifest.type) {
1088 .compile => {
1089 case.addCompile(src);
1090 },
1091 .@"error" => {
1092 const errors = try manifest.trailingAlloc(arena);
1093 switch (strategy) {
1094 .independent => {
1095 case.addError(src, errors);
1096 },
1097 .incremental => {
1098 case.addErrorNamed("update", src, errors);
1099 },
1100 }
1101 },
1102 .run => {
1103 var output = std.ArrayList(u8).init(arena);
1104 var trailing_it = manifest.trailing();
1105 while (trailing_it.next()) |line| {
1106 try output.appendSlice(line);
1107 try output.append('\n');
1108 }
1109 if (output.items.len > 0) {
1110 try output.resize(output.items.len - 1);
1111 }
1112 case.addCompareOutput(src, try output.toOwnedSlice());
1113 },
1114 .cli => @panic("TODO cli tests"),
1115 }
1116 }
1117 }
1118 } else |err| {
1119 return err;
1120 }
1121
1122 return runCases(&ctx, zig_exe_path);
1123}
1124
1125fn runCases(self: *Cases, zig_exe_path: []const u8) !void {
1126 const host = try std.zig.system.NativeTargetInfo.detect(.{});
1127
1128 var progress = std.Progress{};
1129 const root_node = progress.start("compiler", self.cases.items.len);
1130 progress.terminal = null;
1131 defer root_node.end();
1132
1133 var zig_lib_directory = try introspect.findZigLibDir(self.gpa);
1134 defer zig_lib_directory.handle.close();
1135 defer self.gpa.free(zig_lib_directory.path.?);
1136
1137 var aux_thread_pool: ThreadPool = undefined;
1138 try aux_thread_pool.init(.{ .allocator = self.gpa });
1139 defer aux_thread_pool.deinit();
1140
1141 // Use the same global cache dir for all the tests, such that we for example don't have to
1142 // rebuild musl libc for every case (when LLVM backend is enabled).
1143 var global_tmp = std.testing.tmpDir(.{});
1144 defer global_tmp.cleanup();
1145
1146 var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{});
1147 defer cache_dir.close();
1148 const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path });
1149 defer self.gpa.free(tmp_dir_path);
1150
1151 const global_cache_directory: Compilation.Directory = .{
1152 .handle = cache_dir,
1153 .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }),
1154 };
1155 defer self.gpa.free(global_cache_directory.path.?);
1156
1157 {
1158 for (self.cases.items) |*case| {
1159 if (build_options.skip_non_native) {
1160 if (case.target.getCpuArch() != builtin.cpu.arch)
1161 continue;
1162 if (case.target.getObjectFormat() != builtin.object_format)
1163 continue;
1164 }
1165
1166 // Skip tests that require LLVM backend when it is not available
1167 if (!build_options.have_llvm and case.backend == .llvm)
1168 continue;
1169
1170 assert(case.backend != .stage1);
1171
1172 if (build_options.test_filter) |test_filter| {
1173 if (std.mem.indexOf(u8, case.name, test_filter) == null) continue;
1174 }
1175
1176 var prg_node = root_node.start(case.name, case.updates.items.len);
1177 prg_node.activate();
1178 defer prg_node.end();
1179
1180 try runOneCase(
1181 self.gpa,
1182 &prg_node,
1183 case.*,
1184 zig_lib_directory,
1185 zig_exe_path,
1186 &aux_thread_pool,
1187 global_cache_directory,
1188 host,
1189 );
1190 }
1191 }
1192}
1193
1194fn runOneCase(
1195 allocator: Allocator,
1196 root_node: *std.Progress.Node,
1197 case: Case,
1198 zig_lib_directory: Compilation.Directory,
1199 zig_exe_path: []const u8,
1200 thread_pool: *ThreadPool,
1201 global_cache_directory: Compilation.Directory,
1202 host: std.zig.system.NativeTargetInfo,
1203) !void {
1204 const tmp_src_path = "tmp.zig";
1205 const enable_rosetta = build_options.enable_rosetta;
1206 const enable_qemu = build_options.enable_qemu;
1207 const enable_wine = build_options.enable_wine;
1208 const enable_wasmtime = build_options.enable_wasmtime;
1209 const enable_darling = build_options.enable_darling;
1210 const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir;
1211
1212 const target_info = try std.zig.system.NativeTargetInfo.detect(case.target);
1213 const target = target_info.target;
1214
1215 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
1216 defer arena_allocator.deinit();
1217 const arena = arena_allocator.allocator();
1218
1219 var tmp = std.testing.tmpDir(.{});
1220 defer tmp.cleanup();
1221
1222 var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{});
1223 defer cache_dir.close();
1224
1225 const tmp_dir_path = try std.fs.path.join(
1226 arena,
1227 &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path },
1228 );
1229 const local_cache_path = try std.fs.path.join(
1230 arena,
1231 &[_][]const u8{ tmp_dir_path, "zig-cache" },
1232 );
1233
1234 const zig_cache_directory: Compilation.Directory = .{
1235 .handle = cache_dir,
1236 .path = local_cache_path,
1237 };
1238
1239 var main_pkg: Package = .{
1240 .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir },
1241 .root_src_path = tmp_src_path,
1242 };
1243 defer {
1244 var it = main_pkg.table.iterator();
1245 while (it.next()) |kv| {
1246 allocator.free(kv.key_ptr.*);
1247 kv.value_ptr.*.destroy(allocator);
1248 }
1249 main_pkg.table.deinit(allocator);
1250 }
1251
1252 for (case.deps.items) |dep| {
1253 var pkg = try Package.create(
1254 allocator,
1255 tmp_dir_path,
1256 dep.path,
1257 );
1258 errdefer pkg.destroy(allocator);
1259 try main_pkg.add(allocator, dep.name, pkg);
1260 }
1261
1262 const bin_name = try std.zig.binNameAlloc(arena, .{
1263 .root_name = "test_case",
1264 .target = target,
1265 .output_mode = case.output_mode,
1266 });
1267
1268 const emit_directory: Compilation.Directory = .{
1269 .path = tmp_dir_path,
1270 .handle = tmp.dir,
1271 };
1272 const emit_bin: Compilation.EmitLoc = .{
1273 .directory = emit_directory,
1274 .basename = bin_name,
1275 };
1276 const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{
1277 .directory = emit_directory,
1278 .basename = "test_case.h",
1279 } else null;
1280 const use_llvm: bool = switch (case.backend) {
1281 .llvm => true,
1282 else => false,
1283 };
1284 const comp = try Compilation.create(allocator, .{
1285 .local_cache_directory = zig_cache_directory,
1286 .global_cache_directory = global_cache_directory,
1287 .zig_lib_directory = zig_lib_directory,
1288 .thread_pool = thread_pool,
1289 .root_name = "test_case",
1290 .target = target,
1291 // TODO: support tests for object file building, and library builds
1292 // and linking. This will require a rework to support multi-file
1293 // tests.
1294 .output_mode = case.output_mode,
1295 .is_test = case.is_test,
1296 .optimize_mode = case.optimize_mode,
1297 .emit_bin = emit_bin,
1298 .emit_h = emit_h,
1299 .main_pkg = &main_pkg,
1300 .keep_source_files_loaded = true,
1301 .is_native_os = case.target.isNativeOs(),
1302 .is_native_abi = case.target.isNativeAbi(),
1303 .dynamic_linker = target_info.dynamic_linker.get(),
1304 .link_libc = case.link_libc,
1305 .use_llvm = use_llvm,
1306 .self_exe_path = zig_exe_path,
1307 // TODO instead of turning off color, pass in a std.Progress.Node
1308 .color = .off,
1309 .reference_trace = 0,
1310 // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in
1311 // until the auto-select mechanism deems them worthy
1312 .use_lld = switch (case.backend) {
1313 .stage2 => false,
1314 else => null,
1315 },
1316 });
1317 defer comp.destroy();
1318
1319 update: for (case.updates.items, 0..) |update, update_index| {
1320 var update_node = root_node.start(update.name, 3);
1321 update_node.activate();
1322 defer update_node.end();
1323
1324 var sync_node = update_node.start("write", 0);
1325 sync_node.activate();
1326 for (update.files.items) |file| {
1327 try tmp.dir.writeFile(file.path, file.src);
1328 }
1329 sync_node.end();
1330
1331 var module_node = update_node.start("parse/analysis/codegen", 0);
1332 module_node.activate();
1333 try comp.makeBinFileWritable();
1334 try comp.update(&module_node);
1335 module_node.end();
1336
1337 if (update.case != .Error) {
1338 var all_errors = try comp.getAllErrorsAlloc();
1339 defer all_errors.deinit(allocator);
1340 if (all_errors.errorMessageCount() > 0) {
1341 all_errors.renderToStdErr(.{
1342 .ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()),
1343 });
1344 // TODO print generated C code
1345 return error.UnexpectedCompileErrors;
1346 }
1347 }
1348
1349 switch (update.case) {
1350 .Header => |expected_output| {
1351 var file = try tmp.dir.openFile("test_case.h", .{ .mode = .read_only });
1352 defer file.close();
1353 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
1354
1355 try std.testing.expectEqualStrings(expected_output, out);
1356 },
1357 .CompareObjectFile => |expected_output| {
1358 var file = try tmp.dir.openFile(bin_name, .{ .mode = .read_only });
1359 defer file.close();
1360 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
1361
1362 try std.testing.expectEqualStrings(expected_output, out);
1363 },
1364 .Compile => {},
1365 .Error => |expected_errors| {
1366 var test_node = update_node.start("assert", 0);
1367 test_node.activate();
1368 defer test_node.end();
1369
1370 var error_bundle = try comp.getAllErrorsAlloc();
1371 defer error_bundle.deinit(allocator);
1372
1373 if (error_bundle.errorMessageCount() == 0) {
1374 return error.ExpectedCompilationErrors;
1375 }
1376
1377 var actual_stderr = std.ArrayList(u8).init(arena);
1378 try error_bundle.renderToWriter(.{
1379 .ttyconf = .no_color,
1380 .include_reference_trace = false,
1381 .include_source_line = false,
1382 }, actual_stderr.writer());
1383
1384 // Render the expected lines into a string that we can compare verbatim.
1385 var expected_generated = std.ArrayList(u8).init(arena);
1386
1387 var actual_line_it = std.mem.split(u8, actual_stderr.items, "\n");
1388 for (expected_errors) |expect_line| {
1389 const actual_line = actual_line_it.next() orelse {
1390 try expected_generated.appendSlice(expect_line);
1391 try expected_generated.append('\n');
1392 continue;
1393 };
1394 if (std.mem.endsWith(u8, actual_line, expect_line)) {
1395 try expected_generated.appendSlice(actual_line);
1396 try expected_generated.append('\n');
1397 continue;
1398 }
1399 if (std.mem.startsWith(u8, expect_line, ":?:?: ")) {
1400 if (std.mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) {
1401 try expected_generated.appendSlice(actual_line);
1402 try expected_generated.append('\n');
1403 continue;
1404 }
1405 }
1406 try expected_generated.appendSlice(expect_line);
1407 try expected_generated.append('\n');
1408 }
1409
1410 try std.testing.expectEqualStrings(expected_generated.items, actual_stderr.items);
1411 },
1412 .Execution => |expected_stdout| {
1413 if (!std.process.can_spawn) {
1414 std.debug.print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)});
1415 continue :update; // Pass test.
1416 }
1417
1418 update_node.setEstimatedTotalItems(4);
1419
1420 var argv = std.ArrayList([]const u8).init(allocator);
1421 defer argv.deinit();
1422
1423 var exec_result = x: {
1424 var exec_node = update_node.start("execute", 0);
1425 exec_node.activate();
1426 defer exec_node.end();
1427
1428 // We go out of our way here to use the unique temporary directory name in
1429 // the exe_path so that it makes its way into the cache hash, avoiding
1430 // cache collisions from multiple threads doing `zig run` at the same time
1431 // on the same test_case.c input filename.
1432 const ss = std.fs.path.sep_str;
1433 const exe_path = try std.fmt.allocPrint(
1434 arena,
1435 ".." ++ ss ++ "{s}" ++ ss ++ "{s}",
1436 .{ &tmp.sub_path, bin_name },
1437 );
1438 if (case.target.ofmt != null and case.target.ofmt.? == .c) {
1439 if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) {
1440 // We wouldn't be able to run the compiled C code.
1441 continue :update; // Pass test.
1442 }
1443 try argv.appendSlice(&[_][]const u8{
1444 zig_exe_path,
1445 "run",
1446 "-cflags",
1447 "-std=c99",
1448 "-pedantic",
1449 "-Werror",
1450 "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875
1451 "--",
1452 "-lc",
1453 exe_path,
1454 });
1455 if (zig_lib_directory.path) |p| {
1456 try argv.appendSlice(&.{ "-I", p });
1457 }
1458 } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) {
1459 .native => {
1460 if (case.backend == .stage2 and case.target.getCpuArch() == .arm) {
1461 // https://github.com/ziglang/zig/issues/13623
1462 continue :update; // Pass test.
1463 }
1464 try argv.append(exe_path);
1465 },
1466 .bad_dl, .bad_os_or_cpu => continue :update, // Pass test.
1467
1468 .rosetta => if (enable_rosetta) {
1469 try argv.append(exe_path);
1470 } else {
1471 continue :update; // Rosetta not available, pass test.
1472 },
1473
1474 .qemu => |qemu_bin_name| if (enable_qemu) {
1475 const need_cross_glibc = target.isGnuLibC() and case.link_libc;
1476 const glibc_dir_arg: ?[]const u8 = if (need_cross_glibc)
1477 glibc_runtimes_dir orelse continue :update // glibc dir not available; pass test
1478 else
1479 null;
1480 try argv.append(qemu_bin_name);
1481 if (glibc_dir_arg) |dir| {
1482 const linux_triple = try target.linuxTriple(arena);
1483 const full_dir = try std.fs.path.join(arena, &[_][]const u8{
1484 dir,
1485 linux_triple,
1486 });
1487
1488 try argv.append("-L");
1489 try argv.append(full_dir);
1490 }
1491 try argv.append(exe_path);
1492 } else {
1493 continue :update; // QEMU not available; pass test.
1494 },
1495
1496 .wine => |wine_bin_name| if (enable_wine) {
1497 try argv.append(wine_bin_name);
1498 try argv.append(exe_path);
1499 } else {
1500 continue :update; // Wine not available; pass test.
1501 },
1502
1503 .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
1504 try argv.append(wasmtime_bin_name);
1505 try argv.append("--dir=.");
1506 try argv.append(exe_path);
1507 } else {
1508 continue :update; // wasmtime not available; pass test.
1509 },
1510
1511 .darling => |darling_bin_name| if (enable_darling) {
1512 try argv.append(darling_bin_name);
1513 // Since we use relative to cwd here, we invoke darling with
1514 // "shell" subcommand.
1515 try argv.append("shell");
1516 try argv.append(exe_path);
1517 } else {
1518 continue :update; // Darling not available; pass test.
1519 },
1520 }
1521
1522 try comp.makeBinFileExecutable();
1523
1524 while (true) {
1525 break :x std.ChildProcess.exec(.{
1526 .allocator = allocator,
1527 .argv = argv.items,
1528 .cwd_dir = tmp.dir,
1529 .cwd = tmp_dir_path,
1530 }) catch |err| switch (err) {
1531 error.FileBusy => {
1532 // There is a fundamental design flaw in Unix systems with how
1533 // ETXTBSY interacts with fork+exec.
1534 // https://github.com/golang/go/issues/22315
1535 // https://bugs.openjdk.org/browse/JDK-8068370
1536 // Unfortunately, this could be a real error, but we can't
1537 // tell the difference here.
1538 continue;
1539 },
1540 else => {
1541 std.debug.print("\n{s}.{d} The following command failed with {s}:\n", .{
1542 case.name, update_index, @errorName(err),
1543 });
1544 dumpArgs(argv.items);
1545 return error.ChildProcessExecution;
1546 },
1547 };
1548 }
1549 };
1550 var test_node = update_node.start("test", 0);
1551 test_node.activate();
1552 defer test_node.end();
1553 defer allocator.free(exec_result.stdout);
1554 defer allocator.free(exec_result.stderr);
1555 switch (exec_result.term) {
1556 .Exited => |code| {
1557 if (code != 0) {
1558 std.debug.print("\n{s}\n{s}: execution exited with code {d}:\n", .{
1559 exec_result.stderr, case.name, code,
1560 });
1561 dumpArgs(argv.items);
1562 return error.ChildProcessExecution;
1563 }
1564 },
1565 else => {
1566 std.debug.print("\n{s}\n{s}: execution crashed:\n", .{
1567 exec_result.stderr, case.name,
1568 });
1569 dumpArgs(argv.items);
1570 return error.ChildProcessExecution;
1571 },
1572 }
1573 try std.testing.expectEqualStrings(expected_stdout, exec_result.stdout);
1574 // We allow stderr to have garbage in it because wasmtime prints a
1575 // warning about --invoke even though we don't pass it.
1576 //std.testing.expectEqualStrings("", exec_result.stderr);
1577 },
1578 }
1579 }
1580}
1581
1582fn dumpArgs(argv: []const []const u8) void {
1583 for (argv) |arg| {
1584 std.debug.print("{s} ", .{arg});
1585 }
1586 std.debug.print("\n", .{});
1587}
test/src/CompareOutput.zig created+174
...@@ -0,0 +1,174 @@
1//! This is the implementation of the test harness.
2//! For the actual test cases, see test/compare_output.zig.
3
4b: *std.Build,
5step: *std.Build.Step,
6test_index: usize,
7test_filter: ?[]const u8,
8optimize_modes: []const OptimizeMode,
9
10const Special = enum {
11 None,
12 Asm,
13 RuntimeSafety,
14};
15
16const TestCase = struct {
17 name: []const u8,
18 sources: ArrayList(SourceFile),
19 expected_output: []const u8,
20 link_libc: bool,
21 special: Special,
22 cli_args: []const []const u8,
23
24 const SourceFile = struct {
25 filename: []const u8,
26 source: []const u8,
27 };
28
29 pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void {
30 self.sources.append(SourceFile{
31 .filename = filename,
32 .source = source,
33 }) catch @panic("OOM");
34 }
35
36 pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void {
37 self.cli_args = args;
38 }
39};
40
41pub fn createExtra(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase {
42 var tc = TestCase{
43 .name = name,
44 .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator),
45 .expected_output = expected_output,
46 .link_libc = false,
47 .special = special,
48 .cli_args = &[_][]const u8{},
49 };
50 const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";
51 tc.addSourceFile(root_src_name, source);
52 return tc;
53}
54
55pub fn create(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) TestCase {
56 return createExtra(self, name, source, expected_output, Special.None);
57}
58
59pub fn addC(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void {
60 var tc = self.create(name, source, expected_output);
61 tc.link_libc = true;
62 self.addCase(tc);
63}
64
65pub fn add(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void {
66 const tc = self.create(name, source, expected_output);
67 self.addCase(tc);
68}
69
70pub fn addAsm(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void {
71 const tc = self.createExtra(name, source, expected_output, Special.Asm);
72 self.addCase(tc);
73}
74
75pub fn addRuntimeSafety(self: *CompareOutput, name: []const u8, source: []const u8) void {
76 const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety);
77 self.addCase(tc);
78}
79
80pub fn addCase(self: *CompareOutput, case: TestCase) void {
81 const b = self.b;
82
83 const write_src = b.addWriteFiles();
84 for (case.sources.items) |src_file| {
85 write_src.add(src_file.filename, src_file.source);
86 }
87
88 switch (case.special) {
89 Special.Asm => {
90 const annotated_case_name = fmt.allocPrint(self.b.allocator, "run assemble-and-link {s}", .{
91 case.name,
92 }) catch @panic("OOM");
93 if (self.test_filter) |filter| {
94 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
95 }
96
97 const exe = b.addExecutable(.{
98 .name = "test",
99 .target = .{},
100 .optimize = .Debug,
101 });
102 exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?);
103
104 const run = exe.run();
105 run.setName(annotated_case_name);
106 run.addArgs(case.cli_args);
107 run.expectStdOutEqual(case.expected_output);
108
109 self.step.dependOn(&run.step);
110 },
111 Special.None => {
112 for (self.optimize_modes) |optimize| {
113 const annotated_case_name = fmt.allocPrint(self.b.allocator, "run compare-output {s} ({s})", .{
114 case.name, @tagName(optimize),
115 }) catch @panic("OOM");
116 if (self.test_filter) |filter| {
117 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
118 }
119
120 const basename = case.sources.items[0].filename;
121 const exe = b.addExecutable(.{
122 .name = "test",
123 .root_source_file = write_src.getFileSource(basename).?,
124 .optimize = optimize,
125 .target = .{},
126 });
127 if (case.link_libc) {
128 exe.linkSystemLibrary("c");
129 }
130
131 const run = exe.run();
132 run.setName(annotated_case_name);
133 run.addArgs(case.cli_args);
134 run.expectStdOutEqual(case.expected_output);
135
136 self.step.dependOn(&run.step);
137 }
138 },
139 Special.RuntimeSafety => {
140 // TODO iterate over self.optimize_modes and test this in both
141 // debug and release safe mode
142 const annotated_case_name = fmt.allocPrint(self.b.allocator, "run safety {s}", .{case.name}) catch @panic("OOM");
143 if (self.test_filter) |filter| {
144 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
145 }
146
147 const basename = case.sources.items[0].filename;
148 const exe = b.addExecutable(.{
149 .name = "test",
150 .root_source_file = write_src.getFileSource(basename).?,
151 .target = .{},
152 .optimize = .Debug,
153 });
154 if (case.link_libc) {
155 exe.linkSystemLibrary("c");
156 }
157
158 const run = exe.run();
159 run.setName(annotated_case_name);
160 run.addArgs(case.cli_args);
161 run.expectExitCode(126);
162
163 self.step.dependOn(&run.step);
164 },
165 }
166}
167
168const CompareOutput = @This();
169const std = @import("std");
170const ArrayList = std.ArrayList;
171const fmt = std.fmt;
172const mem = std.mem;
173const fs = std.fs;
174const OptimizeMode = std.builtin.OptimizeMode;
test/src/StackTrace.zig created+107
...@@ -0,0 +1,107 @@
1b: *std.Build,
2step: *Step,
3test_index: usize,
4test_filter: ?[]const u8,
5optimize_modes: []const OptimizeMode,
6check_exe: *std.Build.CompileStep,
7
8const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8;
9
10pub fn addCase(self: *StackTrace, config: anytype) void {
11 if (@hasField(@TypeOf(config), "exclude")) {
12 if (config.exclude.exclude()) return;
13 }
14 if (@hasField(@TypeOf(config), "exclude_arch")) {
15 const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch;
16 for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return;
17 }
18 if (@hasField(@TypeOf(config), "exclude_os")) {
19 const exclude_os: []const std.Target.Os.Tag = &config.exclude_os;
20 for (exclude_os) |os| if (os == builtin.os.tag) return;
21 }
22 for (self.optimize_modes) |optimize_mode| {
23 switch (optimize_mode) {
24 .Debug => {
25 if (@hasField(@TypeOf(config), "Debug")) {
26 self.addExpect(config.name, config.source, optimize_mode, config.Debug);
27 }
28 },
29 .ReleaseSafe => {
30 if (@hasField(@TypeOf(config), "ReleaseSafe")) {
31 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe);
32 }
33 },
34 .ReleaseFast => {
35 if (@hasField(@TypeOf(config), "ReleaseFast")) {
36 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast);
37 }
38 },
39 .ReleaseSmall => {
40 if (@hasField(@TypeOf(config), "ReleaseSmall")) {
41 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall);
42 }
43 },
44 }
45 }
46}
47
48fn addExpect(
49 self: *StackTrace,
50 name: []const u8,
51 source: []const u8,
52 optimize_mode: OptimizeMode,
53 mode_config: anytype,
54) void {
55 if (@hasField(@TypeOf(mode_config), "exclude")) {
56 if (mode_config.exclude.exclude()) return;
57 }
58 if (@hasField(@TypeOf(mode_config), "exclude_arch")) {
59 const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch;
60 for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return;
61 }
62 if (@hasField(@TypeOf(mode_config), "exclude_os")) {
63 const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os;
64 for (exclude_os) |os| if (os == builtin.os.tag) return;
65 }
66
67 const b = self.b;
68 const annotated_case_name = fmt.allocPrint(b.allocator, "check {s} ({s})", .{
69 name, @tagName(optimize_mode),
70 }) catch @panic("OOM");
71 if (self.test_filter) |filter| {
72 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
73 }
74
75 const src_basename = "source.zig";
76 const write_src = b.addWriteFile(src_basename, source);
77 const exe = b.addExecutable(.{
78 .name = "test",
79 .root_source_file = write_src.getFileSource(src_basename).?,
80 .optimize = optimize_mode,
81 .target = .{},
82 });
83
84 const run = b.addRunArtifact(exe);
85 run.removeEnvironmentVariable("ZIG_DEBUG_COLOR");
86 run.setEnvironmentVariable("NO_COLOR", "1");
87 run.expectExitCode(1);
88 run.expectStdOutEqual("");
89
90 const check_run = b.addRunArtifact(self.check_exe);
91 check_run.setName(annotated_case_name);
92 check_run.addFileSourceArg(run.captureStdErr());
93 check_run.addArgs(&.{
94 @tagName(optimize_mode),
95 });
96 check_run.expectStdOutEqual(mode_config.expect);
97
98 self.step.dependOn(&check_run.step);
99}
100
101const StackTrace = @This();
102const std = @import("std");
103const builtin = @import("builtin");
104const Step = std.Build.Step;
105const OptimizeMode = std.builtin.OptimizeMode;
106const fmt = std.fmt;
107const mem = std.mem;
test/src/check-stack-trace.zig created+79
...@@ -0,0 +1,79 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const mem = std.mem;
4const fs = std.fs;
5
6pub fn main() !void {
7 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
8 defer arena_instance.deinit();
9 const arena = arena_instance.allocator();
10
11 const args = try std.process.argsAlloc(arena);
12
13 const input_path = args[1];
14 const optimize_mode_text = args[2];
15
16 const input_bytes = try std.fs.cwd().readFileAlloc(arena, input_path, 5 * 1024 * 1024);
17 const optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, optimize_mode_text).?;
18
19 var stderr = input_bytes;
20
21 // process result
22 // - keep only basename of source file path
23 // - replace address with symbolic string
24 // - replace function name with symbolic string when optimize_mode != .Debug
25 // - skip empty lines
26 const got: []const u8 = got_result: {
27 var buf = std.ArrayList(u8).init(arena);
28 defer buf.deinit();
29 if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1];
30 var it = mem.split(u8, stderr, "\n");
31 process_lines: while (it.next()) |line| {
32 if (line.len == 0) continue;
33
34 // offset search past `[drive]:` on windows
35 var pos: usize = if (builtin.os.tag == .windows) 2 else 0;
36 // locate delims/anchor
37 const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" };
38 var marks = [_]usize{0} ** delims.len;
39 for (delims, 0..) |delim, i| {
40 marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse {
41 // unexpected pattern: emit raw line and cont
42 try buf.appendSlice(line);
43 try buf.appendSlice("\n");
44 continue :process_lines;
45 };
46 pos = marks[i] + delim.len;
47 }
48 // locate source basename
49 pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse {
50 // unexpected pattern: emit raw line and cont
51 try buf.appendSlice(line);
52 try buf.appendSlice("\n");
53 continue :process_lines;
54 };
55 // end processing if source basename changes
56 if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break;
57 // emit substituted line
58 try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);
59 try buf.appendSlice(" [address]");
60 if (optimize_mode == .Debug) {
61 // On certain platforms (windows) or possibly depending on how we choose to link main
62 // the object file extension may be present so we simply strip any extension.
63 if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| {
64 try buf.appendSlice(line[marks[3] .. marks[4] + idot]);
65 try buf.appendSlice(line[marks[5]..]);
66 } else {
67 try buf.appendSlice(line[marks[3]..]);
68 }
69 } else {
70 try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]);
71 try buf.appendSlice("[function]");
72 }
73 try buf.appendSlice("\n");
74 }
75 break :got_result try buf.toOwnedSlice();
76 };
77
78 try std.io.getStdOut().writeAll(got);
79}
test/src/compare_output.zig deleted-177
...@@ -1,177 +0,0 @@
1// This is the implementation of the test harness.
2// For the actual test cases, see test/compare_output.zig.
3const std = @import("std");
4const ArrayList = std.ArrayList;
5const fmt = std.fmt;
6const mem = std.mem;
7const fs = std.fs;
8const OptimizeMode = std.builtin.OptimizeMode;
9
10pub const CompareOutputContext = struct {
11 b: *std.Build,
12 step: *std.Build.Step,
13 test_index: usize,
14 test_filter: ?[]const u8,
15 optimize_modes: []const OptimizeMode,
16
17 const Special = enum {
18 None,
19 Asm,
20 RuntimeSafety,
21 };
22
23 const TestCase = struct {
24 name: []const u8,
25 sources: ArrayList(SourceFile),
26 expected_output: []const u8,
27 link_libc: bool,
28 special: Special,
29 cli_args: []const []const u8,
30
31 const SourceFile = struct {
32 filename: []const u8,
33 source: []const u8,
34 };
35
36 pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void {
37 self.sources.append(SourceFile{
38 .filename = filename,
39 .source = source,
40 }) catch unreachable;
41 }
42
43 pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void {
44 self.cli_args = args;
45 }
46 };
47
48 pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase {
49 var tc = TestCase{
50 .name = name,
51 .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator),
52 .expected_output = expected_output,
53 .link_libc = false,
54 .special = special,
55 .cli_args = &[_][]const u8{},
56 };
57 const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";
58 tc.addSourceFile(root_src_name, source);
59 return tc;
60 }
61
62 pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase {
63 return createExtra(self, name, source, expected_output, Special.None);
64 }
65
66 pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void {
67 var tc = self.create(name, source, expected_output);
68 tc.link_libc = true;
69 self.addCase(tc);
70 }
71
72 pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void {
73 const tc = self.create(name, source, expected_output);
74 self.addCase(tc);
75 }
76
77 pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void {
78 const tc = self.createExtra(name, source, expected_output, Special.Asm);
79 self.addCase(tc);
80 }
81
82 pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void {
83 const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety);
84 self.addCase(tc);
85 }
86
87 pub fn addCase(self: *CompareOutputContext, case: TestCase) void {
88 const b = self.b;
89
90 const write_src = b.addWriteFiles();
91 for (case.sources.items) |src_file| {
92 write_src.add(src_file.filename, src_file.source);
93 }
94
95 switch (case.special) {
96 Special.Asm => {
97 const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {s}", .{
98 case.name,
99 }) catch unreachable;
100 if (self.test_filter) |filter| {
101 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
102 }
103
104 const exe = b.addExecutable(.{
105 .name = "test",
106 .target = .{},
107 .optimize = .Debug,
108 });
109 exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?);
110
111 const run = exe.run();
112 run.addArgs(case.cli_args);
113 run.expectStdErrEqual("");
114 run.expectStdOutEqual(case.expected_output);
115
116 self.step.dependOn(&run.step);
117 },
118 Special.None => {
119 for (self.optimize_modes) |optimize| {
120 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
121 "compare-output",
122 case.name,
123 @tagName(optimize),
124 }) catch unreachable;
125 if (self.test_filter) |filter| {
126 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
127 }
128
129 const basename = case.sources.items[0].filename;
130 const exe = b.addExecutable(.{
131 .name = "test",
132 .root_source_file = write_src.getFileSource(basename).?,
133 .optimize = optimize,
134 .target = .{},
135 });
136 if (case.link_libc) {
137 exe.linkSystemLibrary("c");
138 }
139
140 const run = exe.run();
141 run.addArgs(case.cli_args);
142 run.expectStdErrEqual("");
143 run.expectStdOutEqual(case.expected_output);
144
145 self.step.dependOn(&run.step);
146 }
147 },
148 Special.RuntimeSafety => {
149 // TODO iterate over self.optimize_modes and test this in both
150 // debug and release safe mode
151 const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable;
152 if (self.test_filter) |filter| {
153 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
154 }
155
156 const basename = case.sources.items[0].filename;
157 const exe = b.addExecutable(.{
158 .name = "test",
159 .root_source_file = write_src.getFileSource(basename).?,
160 .target = .{},
161 .optimize = .Debug,
162 });
163 if (case.link_libc) {
164 exe.linkSystemLibrary("c");
165 }
166
167 const run = exe.run();
168 run.addArgs(case.cli_args);
169 run.stderr_action = .ignore;
170 run.stdout_action = .ignore;
171 run.expected_term = .{ .Exited = 126 };
172
173 self.step.dependOn(&run.step);
174 },
175 }
176 }
177};
test/stage2/cbe.zig deleted-1015
...@@ -1,1015 +0,0 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4// These tests should work with all platforms, but we're using linux_x64 for
5// now for consistency. Will be expanded eventually.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
10
11pub fn addCases(ctx: *TestContext) !void {
12 {
13 var case = ctx.exeFromCompiledC("hello world with updates", .{});
14
15 // Regular old hello world
16 case.addCompareOutput(
17 \\extern fn puts(s: [*:0]const u8) c_int;
18 \\pub export fn main() c_int {
19 \\ _ = puts("hello world!");
20 \\ return 0;
21 \\}
22 , "hello world!" ++ std.cstr.line_sep);
23
24 // Now change the message only
25 case.addCompareOutput(
26 \\extern fn puts(s: [*:0]const u8) c_int;
27 \\pub export fn main() c_int {
28 \\ _ = puts("yo");
29 \\ return 0;
30 \\}
31 , "yo" ++ std.cstr.line_sep);
32
33 // Add an unused Decl
34 case.addCompareOutput(
35 \\extern fn puts(s: [*:0]const u8) c_int;
36 \\pub export fn main() c_int {
37 \\ _ = puts("yo!");
38 \\ return 0;
39 \\}
40 \\fn unused() void {}
41 , "yo!" ++ std.cstr.line_sep);
42
43 // Comptime return type and calling convention expected.
44 case.addError(
45 \\var x: i32 = 1234;
46 \\pub export fn main() x {
47 \\ return 0;
48 \\}
49 \\export fn foo() callconv(y) c_int {
50 \\ return 0;
51 \\}
52 \\var y: @import("std").builtin.CallingConvention = .C;
53 , &.{
54 ":2:22: error: expected type 'type', found 'i32'",
55 ":5:26: error: unable to resolve comptime value",
56 ":5:26: note: calling convention must be comptime-known",
57 });
58 }
59
60 {
61 var case = ctx.exeFromCompiledC("var args", .{});
62
63 case.addCompareOutput(
64 \\extern fn printf(format: [*:0]const u8, ...) c_int;
65 \\
66 \\pub export fn main() c_int {
67 \\ _ = printf("Hello, %s!\n", "world");
68 \\ return 0;
69 \\}
70 , "Hello, world!" ++ std.cstr.line_sep);
71 }
72
73 {
74 var case = ctx.exeFromCompiledC("@intToError", .{});
75
76 case.addCompareOutput(
77 \\pub export fn main() c_int {
78 \\ // comptime checks
79 \\ const a = error.A;
80 \\ const b = error.B;
81 \\ const c = @intToError(2);
82 \\ const d = @intToError(1);
83 \\ if (!(c == b)) unreachable;
84 \\ if (!(a == d)) unreachable;
85 \\ // runtime checks
86 \\ var x = error.A;
87 \\ var y = error.B;
88 \\ var z = @intToError(2);
89 \\ var f = @intToError(1);
90 \\ if (!(y == z)) unreachable;
91 \\ if (!(x == f)) unreachable;
92 \\ return 0;
93 \\}
94 , "");
95 case.addError(
96 \\pub export fn main() c_int {
97 \\ _ = @intToError(0);
98 \\ return 0;
99 \\}
100 , &.{":2:21: error: integer value '0' represents no error"});
101 case.addError(
102 \\pub export fn main() c_int {
103 \\ _ = @intToError(3);
104 \\ return 0;
105 \\}
106 , &.{":2:21: error: integer value '3' represents no error"});
107 }
108
109 {
110 var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
111
112 // Exit with 0
113 case.addCompareOutput(
114 \\fn exitGood() noreturn {
115 \\ asm volatile ("syscall"
116 \\ :
117 \\ : [number] "{rax}" (231),
118 \\ [arg1] "{rdi}" (0)
119 \\ );
120 \\ unreachable;
121 \\}
122 \\
123 \\pub export fn main() c_int {
124 \\ exitGood();
125 \\}
126 , "");
127
128 // Pass a usize parameter to exit
129 case.addCompareOutput(
130 \\pub export fn main() c_int {
131 \\ exit(0);
132 \\}
133 \\
134 \\fn exit(code: usize) noreturn {
135 \\ asm volatile ("syscall"
136 \\ :
137 \\ : [number] "{rax}" (231),
138 \\ [arg1] "{rdi}" (code)
139 \\ );
140 \\ unreachable;
141 \\}
142 , "");
143
144 // Change the parameter to u8
145 case.addCompareOutput(
146 \\pub export fn main() c_int {
147 \\ exit(0);
148 \\}
149 \\
150 \\fn exit(code: u8) noreturn {
151 \\ asm volatile ("syscall"
152 \\ :
153 \\ : [number] "{rax}" (231),
154 \\ [arg1] "{rdi}" (code)
155 \\ );
156 \\ unreachable;
157 \\}
158 , "");
159
160 // Do some arithmetic at the exit callsite
161 case.addCompareOutput(
162 \\pub export fn main() c_int {
163 \\ exitMath(1);
164 \\}
165 \\
166 \\fn exitMath(a: u8) noreturn {
167 \\ exit(0 + a - a);
168 \\}
169 \\
170 \\fn exit(code: u8) noreturn {
171 \\ asm volatile ("syscall"
172 \\ :
173 \\ : [number] "{rax}" (231),
174 \\ [arg1] "{rdi}" (code)
175 \\ );
176 \\ unreachable;
177 \\}
178 \\
179 , "");
180
181 // Invert the arithmetic
182 case.addCompareOutput(
183 \\pub export fn main() c_int {
184 \\ exitMath(1);
185 \\}
186 \\
187 \\fn exitMath(a: u8) noreturn {
188 \\ exit(a + 0 - a);
189 \\}
190 \\
191 \\fn exit(code: u8) noreturn {
192 \\ asm volatile ("syscall"
193 \\ :
194 \\ : [number] "{rax}" (231),
195 \\ [arg1] "{rdi}" (code)
196 \\ );
197 \\ unreachable;
198 \\}
199 \\
200 , "");
201 }
202
203 {
204 var case = ctx.exeFromCompiledC("alloc and retptr", .{});
205
206 case.addCompareOutput(
207 \\fn add(a: i32, b: i32) i32 {
208 \\ return a + b;
209 \\}
210 \\
211 \\fn addIndirect(a: i32, b: i32) i32 {
212 \\ return add(a, b);
213 \\}
214 \\
215 \\pub export fn main() c_int {
216 \\ return addIndirect(1, 2) - 3;
217 \\}
218 , "");
219 }
220
221 {
222 var case = ctx.exeFromCompiledC("inferred local const and var", .{});
223
224 case.addCompareOutput(
225 \\fn add(a: i32, b: i32) i32 {
226 \\ return a + b;
227 \\}
228 \\
229 \\pub export fn main() c_int {
230 \\ const x = add(1, 2);
231 \\ var y = add(3, 0);
232 \\ y -= x;
233 \\ return y;
234 \\}
235 , "");
236 }
237 {
238 var case = ctx.exeFromCompiledC("control flow", .{});
239
240 // Simple while loop
241 case.addCompareOutput(
242 \\pub export fn main() c_int {
243 \\ var a: c_int = 0;
244 \\ while (a < 5) : (a+=1) {}
245 \\ return a - 5;
246 \\}
247 , "");
248 case.addCompareOutput(
249 \\pub export fn main() c_int {
250 \\ var a = true;
251 \\ while (!a) {}
252 \\ return 0;
253 \\}
254 , "");
255
256 // If expression
257 case.addCompareOutput(
258 \\pub export fn main() c_int {
259 \\ var cond: c_int = 0;
260 \\ var a: c_int = @as(c_int, if (cond == 0)
261 \\ 2
262 \\ else
263 \\ 3) + 9;
264 \\ return a - 11;
265 \\}
266 , "");
267
268 // If expression with breakpoint that does not get hit
269 case.addCompareOutput(
270 \\pub export fn main() c_int {
271 \\ var x: i32 = 1;
272 \\ if (x != 1) @breakpoint();
273 \\ return 0;
274 \\}
275 , "");
276
277 // Switch expression
278 case.addCompareOutput(
279 \\pub export fn main() c_int {
280 \\ var cond: c_int = 0;
281 \\ var a: c_int = switch (cond) {
282 \\ 1 => 1,
283 \\ 2 => 2,
284 \\ 99...300, 12 => 3,
285 \\ 0 => 4,
286 \\ else => 5,
287 \\ };
288 \\ return a - 4;
289 \\}
290 , "");
291
292 // Switch expression missing else case.
293 case.addError(
294 \\pub export fn main() c_int {
295 \\ var cond: c_int = 0;
296 \\ const a: c_int = switch (cond) {
297 \\ 1 => 1,
298 \\ 2 => 2,
299 \\ 3 => 3,
300 \\ 4 => 4,
301 \\ };
302 \\ return a - 4;
303 \\}
304 , &.{":3:22: error: switch must handle all possibilities"});
305
306 // Switch expression, has an unreachable prong.
307 case.addCompareOutput(
308 \\pub export fn main() c_int {
309 \\ var cond: c_int = 0;
310 \\ const a: c_int = switch (cond) {
311 \\ 1 => 1,
312 \\ 2 => 2,
313 \\ 99...300, 12 => 3,
314 \\ 0 => 4,
315 \\ 13 => unreachable,
316 \\ else => 5,
317 \\ };
318 \\ return a - 4;
319 \\}
320 , "");
321
322 // Switch expression, has an unreachable prong and prongs write
323 // to result locations.
324 case.addCompareOutput(
325 \\pub export fn main() c_int {
326 \\ var cond: c_int = 0;
327 \\ var a: c_int = switch (cond) {
328 \\ 1 => 1,
329 \\ 2 => 2,
330 \\ 99...300, 12 => 3,
331 \\ 0 => 4,
332 \\ 13 => unreachable,
333 \\ else => 5,
334 \\ };
335 \\ return a - 4;
336 \\}
337 , "");
338
339 // Integer switch expression has duplicate case value.
340 case.addError(
341 \\pub export fn main() c_int {
342 \\ var cond: c_int = 0;
343 \\ const a: c_int = switch (cond) {
344 \\ 1 => 1,
345 \\ 2 => 2,
346 \\ 96, 11...13, 97 => 3,
347 \\ 0 => 4,
348 \\ 90, 12 => 100,
349 \\ else => 5,
350 \\ };
351 \\ return a - 4;
352 \\}
353 , &.{
354 ":8:13: error: duplicate switch value",
355 ":6:15: note: previous value here",
356 });
357
358 // Boolean switch expression has duplicate case value.
359 case.addError(
360 \\pub export fn main() c_int {
361 \\ var a: bool = false;
362 \\ const b: c_int = switch (a) {
363 \\ false => 1,
364 \\ true => 2,
365 \\ false => 3,
366 \\ };
367 \\ _ = b;
368 \\}
369 , &.{
370 ":6:9: error: duplicate switch value",
371 });
372
373 // Sparse (no range capable) switch expression has duplicate case value.
374 case.addError(
375 \\pub export fn main() c_int {
376 \\ const A: type = i32;
377 \\ const b: c_int = switch (A) {
378 \\ i32 => 1,
379 \\ bool => 2,
380 \\ f64, i32 => 3,
381 \\ else => 4,
382 \\ };
383 \\ _ = b;
384 \\}
385 , &.{
386 ":6:14: error: duplicate switch value",
387 ":4:9: note: previous value here",
388 });
389
390 // Ranges not allowed for some kinds of switches.
391 case.addError(
392 \\pub export fn main() c_int {
393 \\ const A: type = i32;
394 \\ const b: c_int = switch (A) {
395 \\ i32 => 1,
396 \\ bool => 2,
397 \\ f16...f64 => 3,
398 \\ else => 4,
399 \\ };
400 \\ _ = b;
401 \\}
402 , &.{
403 ":3:30: error: ranges not allowed when switching on type 'type'",
404 ":6:12: note: range here",
405 });
406
407 // Switch expression has unreachable else prong.
408 case.addError(
409 \\pub export fn main() c_int {
410 \\ var a: u2 = 0;
411 \\ const b: i32 = switch (a) {
412 \\ 0 => 10,
413 \\ 1 => 20,
414 \\ 2 => 30,
415 \\ 3 => 40,
416 \\ else => 50,
417 \\ };
418 \\ _ = b;
419 \\}
420 , &.{
421 ":8:14: error: unreachable else prong; all cases already handled",
422 });
423 }
424 //{
425 // var case = ctx.exeFromCompiledC("optionals", .{});
426
427 // // Simple while loop
428 // case.addCompareOutput(
429 // \\pub export fn main() c_int {
430 // \\ var count: c_int = 0;
431 // \\ var opt_ptr: ?*c_int = &count;
432 // \\ while (opt_ptr) |_| : (count += 1) {
433 // \\ if (count == 4) opt_ptr = null;
434 // \\ }
435 // \\ return count - 5;
436 // \\}
437 // , "");
438
439 // // Same with non pointer optionals
440 // case.addCompareOutput(
441 // \\pub export fn main() c_int {
442 // \\ var count: c_int = 0;
443 // \\ var opt_ptr: ?c_int = count;
444 // \\ while (opt_ptr) |_| : (count += 1) {
445 // \\ if (count == 4) opt_ptr = null;
446 // \\ }
447 // \\ return count - 5;
448 // \\}
449 // , "");
450 //}
451
452 {
453 var case = ctx.exeFromCompiledC("errors", .{});
454 case.addCompareOutput(
455 \\pub export fn main() c_int {
456 \\ var e1 = error.Foo;
457 \\ var e2 = error.Bar;
458 \\ assert(e1 != e2);
459 \\ assert(e1 == error.Foo);
460 \\ assert(e2 == error.Bar);
461 \\ return 0;
462 \\}
463 \\fn assert(b: bool) void {
464 \\ if (!b) unreachable;
465 \\}
466 , "");
467 case.addCompareOutput(
468 \\pub export fn main() c_int {
469 \\ var e: anyerror!c_int = 0;
470 \\ const i = e catch 69;
471 \\ return i;
472 \\}
473 , "");
474 case.addCompareOutput(
475 \\pub export fn main() c_int {
476 \\ var e: anyerror!c_int = error.Foo;
477 \\ const i = e catch 69;
478 \\ return 69 - i;
479 \\}
480 , "");
481 case.addCompareOutput(
482 \\const E = error{e};
483 \\const S = struct { x: u32 };
484 \\fn f() E!u32 {
485 \\ const x = (try @as(E!S, S{ .x = 1 })).x;
486 \\ return x;
487 \\}
488 \\pub export fn main() c_int {
489 \\ const x = f() catch @as(u32, 0);
490 \\ if (x != 1) unreachable;
491 \\ return 0;
492 \\}
493 , "");
494 }
495
496 {
497 var case = ctx.exeFromCompiledC("structs", .{});
498 case.addError(
499 \\const Point = struct { x: i32, y: i32 };
500 \\pub export fn main() c_int {
501 \\ var p: Point = .{
502 \\ .y = 24,
503 \\ .x = 12,
504 \\ .y = 24,
505 \\ };
506 \\ return p.y - p.x - p.x;
507 \\}
508 , &.{
509 ":6:10: error: duplicate field",
510 ":4:10: note: other field here",
511 });
512 case.addError(
513 \\const Point = struct { x: i32, y: i32 };
514 \\pub export fn main() c_int {
515 \\ var p: Point = .{
516 \\ .y = 24,
517 \\ };
518 \\ return p.y - p.x - p.x;
519 \\}
520 , &.{
521 ":3:21: error: missing struct field: x",
522 ":1:15: note: struct 'tmp.Point' declared here",
523 });
524 case.addError(
525 \\const Point = struct { x: i32, y: i32 };
526 \\pub export fn main() c_int {
527 \\ var p: Point = .{
528 \\ .x = 12,
529 \\ .y = 24,
530 \\ .z = 48,
531 \\ };
532 \\ return p.y - p.x - p.x;
533 \\}
534 , &.{
535 ":6:10: error: no field named 'z' in struct 'tmp.Point'",
536 ":1:15: note: struct declared here",
537 });
538 case.addCompareOutput(
539 \\const Point = struct { x: i32, y: i32 };
540 \\pub export fn main() c_int {
541 \\ var p: Point = .{
542 \\ .x = 12,
543 \\ .y = 24,
544 \\ };
545 \\ return p.y - p.x - p.x;
546 \\}
547 , "");
548 case.addCompareOutput(
549 \\const Point = struct { x: i32, y: i32, z: i32, a: i32, b: i32 };
550 \\pub export fn main() c_int {
551 \\ var p: Point = .{
552 \\ .x = 18,
553 \\ .y = 24,
554 \\ .z = 1,
555 \\ .a = 2,
556 \\ .b = 3,
557 \\ };
558 \\ return p.y - p.x - p.z - p.a - p.b;
559 \\}
560 , "");
561 }
562
563 {
564 var case = ctx.exeFromCompiledC("unions", .{});
565
566 case.addError(
567 \\const U = union {
568 \\ a: u32,
569 \\ b
570 \\};
571 , &.{
572 ":3:5: error: union field missing type",
573 });
574
575 case.addError(
576 \\const E = enum { a, b };
577 \\const U = union(E) {
578 \\ a: u32 = 1,
579 \\ b: f32 = 2,
580 \\};
581 , &.{
582 ":2:11: error: explicitly valued tagged union requires inferred enum tag type",
583 ":3:14: note: tag value specified here",
584 });
585
586 case.addError(
587 \\const U = union(enum) {
588 \\ a: u32 = 1,
589 \\ b: f32 = 2,
590 \\};
591 , &.{
592 ":1:11: error: explicitly valued tagged union missing integer tag type",
593 ":2:14: note: tag value specified here",
594 });
595 }
596
597 {
598 var case = ctx.exeFromCompiledC("enums", .{});
599
600 case.addError(
601 \\const E1 = packed enum { a, b, c };
602 \\const E2 = extern enum { a, b, c };
603 \\export fn foo() void {
604 \\ _ = E1.a;
605 \\}
606 \\export fn bar() void {
607 \\ _ = E2.a;
608 \\}
609 , &.{
610 ":1:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type",
611 ":2:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type",
612 });
613
614 // comptime and types are caught in AstGen.
615 case.addError(
616 \\const E1 = enum {
617 \\ a,
618 \\ comptime b,
619 \\ c,
620 \\};
621 \\const E2 = enum {
622 \\ a,
623 \\ b: i32,
624 \\ c,
625 \\};
626 \\export fn foo() void {
627 \\ _ = E1.a;
628 \\}
629 \\export fn bar() void {
630 \\ _ = E2.a;
631 \\}
632 , &.{
633 ":3:5: error: enum fields cannot be marked comptime",
634 ":8:8: error: enum fields do not have types",
635 ":6:12: note: consider 'union(enum)' here to make it a tagged union",
636 });
637
638 // @enumToInt, @intToEnum, enum literal coercion, field access syntax, comparison, switch
639 case.addCompareOutput(
640 \\const Number = enum { One, Two, Three };
641 \\
642 \\pub export fn main() c_int {
643 \\ var number1 = Number.One;
644 \\ var number2: Number = .Two;
645 \\ const number3 = @intToEnum(Number, 2);
646 \\ if (number1 == number2) return 1;
647 \\ if (number2 == number3) return 1;
648 \\ if (@enumToInt(number1) != 0) return 1;
649 \\ if (@enumToInt(number2) != 1) return 1;
650 \\ if (@enumToInt(number3) != 2) return 1;
651 \\ var x: Number = .Two;
652 \\ if (number2 != x) return 1;
653 \\ switch (x) {
654 \\ .One => return 1,
655 \\ .Two => return 0,
656 \\ number3 => return 2,
657 \\ }
658 \\}
659 , "");
660
661 // Specifying alignment is a parse error.
662 // This also tests going from a successful build to a parse error.
663 case.addError(
664 \\const E1 = enum {
665 \\ a,
666 \\ b align(4),
667 \\ c,
668 \\};
669 \\export fn foo() void {
670 \\ _ = E1.a;
671 \\}
672 , &.{
673 ":3:13: error: enum fields cannot be aligned",
674 });
675
676 // Redundant non-exhaustive enum mark.
677 // This also tests going from a parse error to an AstGen error.
678 case.addError(
679 \\const E1 = enum {
680 \\ a,
681 \\ _,
682 \\ b,
683 \\ c,
684 \\ _,
685 \\};
686 \\export fn foo() void {
687 \\ _ = E1.a;
688 \\}
689 , &.{
690 ":6:5: error: redundant non-exhaustive enum mark",
691 ":3:5: note: other mark here",
692 });
693
694 case.addError(
695 \\const E1 = enum {
696 \\ a,
697 \\ b,
698 \\ c,
699 \\ _ = 10,
700 \\};
701 \\export fn foo() void {
702 \\ _ = E1.a;
703 \\}
704 , &.{
705 ":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value",
706 });
707
708 case.addError(
709 \\const E1 = enum { a, b, _ };
710 \\export fn foo() void {
711 \\ _ = E1.a;
712 \\}
713 , &.{
714 ":1:12: error: non-exhaustive enum missing integer tag type",
715 ":1:25: note: marked non-exhaustive here",
716 });
717
718 case.addError(
719 \\const E1 = enum { a, b, c, b, d };
720 \\pub export fn main() c_int {
721 \\ _ = E1.a;
722 \\}
723 , &.{
724 ":1:28: error: duplicate enum field 'b'",
725 ":1:22: note: other field here",
726 });
727
728 case.addError(
729 \\pub export fn main() c_int {
730 \\ const a = true;
731 \\ _ = @enumToInt(a);
732 \\}
733 , &.{
734 ":3:20: error: expected enum or tagged union, found 'bool'",
735 });
736
737 case.addError(
738 \\pub export fn main() c_int {
739 \\ const a = 1;
740 \\ _ = @intToEnum(bool, a);
741 \\}
742 , &.{
743 ":3:20: error: expected enum, found 'bool'",
744 });
745
746 case.addError(
747 \\const E = enum { a, b, c };
748 \\pub export fn main() c_int {
749 \\ _ = @intToEnum(E, 3);
750 \\}
751 , &.{
752 ":3:9: error: enum 'tmp.E' has no tag with value '3'",
753 ":1:11: note: enum declared here",
754 });
755
756 case.addError(
757 \\const E = enum { a, b, c };
758 \\pub export fn main() c_int {
759 \\ var x: E = .a;
760 \\ switch (x) {
761 \\ .a => {},
762 \\ .c => {},
763 \\ }
764 \\}
765 , &.{
766 ":4:5: error: switch must handle all possibilities",
767 ":1:21: note: unhandled enumeration value: 'b'",
768 ":1:11: note: enum 'tmp.E' declared here",
769 });
770
771 case.addError(
772 \\const E = enum { a, b, c };
773 \\pub export fn main() c_int {
774 \\ var x: E = .a;
775 \\ switch (x) {
776 \\ .a => {},
777 \\ .b => {},
778 \\ .b => {},
779 \\ .c => {},
780 \\ }
781 \\}
782 , &.{
783 ":7:10: error: duplicate switch value",
784 ":6:10: note: previous value here",
785 });
786
787 case.addError(
788 \\const E = enum { a, b, c };
789 \\pub export fn main() c_int {
790 \\ var x: E = .a;
791 \\ switch (x) {
792 \\ .a => {},
793 \\ .b => {},
794 \\ .c => {},
795 \\ else => {},
796 \\ }
797 \\}
798 , &.{
799 ":8:14: error: unreachable else prong; all cases already handled",
800 });
801
802 case.addError(
803 \\const E = enum { a, b, c };
804 \\pub export fn main() c_int {
805 \\ var x: E = .a;
806 \\ switch (x) {
807 \\ .a => {},
808 \\ .b => {},
809 \\ _ => {},
810 \\ }
811 \\}
812 , &.{
813 ":4:5: error: '_' prong only allowed when switching on non-exhaustive enums",
814 ":7:11: note: '_' prong here",
815 });
816
817 case.addError(
818 \\const E = enum { a, b, c };
819 \\pub export fn main() c_int {
820 \\ _ = E.d;
821 \\}
822 , &.{
823 ":3:11: error: enum 'tmp.E' has no member named 'd'",
824 ":1:11: note: enum declared here",
825 });
826
827 case.addError(
828 \\const E = enum { a, b, c };
829 \\pub export fn main() c_int {
830 \\ var x: E = .d;
831 \\ _ = x;
832 \\}
833 , &.{
834 ":3:17: error: no field named 'd' in enum 'tmp.E'",
835 ":1:11: note: enum declared here",
836 });
837 }
838
839 {
840 var case = ctx.exeFromCompiledC("shift right + left", .{});
841 case.addCompareOutput(
842 \\pub export fn main() c_int {
843 \\ var i: u32 = 16;
844 \\ assert(i >> 1, 8);
845 \\ return 0;
846 \\}
847 \\fn assert(a: u32, b: u32) void {
848 \\ if (a != b) unreachable;
849 \\}
850 , "");
851
852 case.addCompareOutput(
853 \\pub export fn main() c_int {
854 \\ var i: u32 = 16;
855 \\ assert(i << 1, 32);
856 \\ return 0;
857 \\}
858 \\fn assert(a: u32, b: u32) void {
859 \\ if (a != b) unreachable;
860 \\}
861 , "");
862 }
863
864 {
865 var case = ctx.exeFromCompiledC("inferred error sets", .{});
866
867 case.addCompareOutput(
868 \\pub export fn main() c_int {
869 \\ if (foo()) |_| {
870 \\ @panic("test fail");
871 \\ } else |err| {
872 \\ if (err != error.ItBroke) {
873 \\ @panic("test fail");
874 \\ }
875 \\ }
876 \\ return 0;
877 \\}
878 \\fn foo() !void {
879 \\ return error.ItBroke;
880 \\}
881 , "");
882 }
883
884 {
885 // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)
886 var case = ctx.exeFromCompiledC("add/sub wrapping operations", .{});
887 case.addCompareOutput(
888 \\pub export fn main() c_int {
889 \\ // Addition
890 \\ if (!add_u3(1, 1, 2)) return 1;
891 \\ if (!add_u3(7, 1, 0)) return 1;
892 \\ if (!add_i3(1, 1, 2)) return 1;
893 \\ if (!add_i3(3, 2, -3)) return 1;
894 \\ if (!add_i3(-3, -2, 3)) return 1;
895 \\ if (!add_c_int(1, 1, 2)) return 1;
896 \\ // TODO enable these when stage2 supports std.math.maxInt
897 \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1;
898 \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1;
899 \\
900 \\ // Subtraction
901 \\ if (!sub_u3(2, 1, 1)) return 1;
902 \\ if (!sub_u3(0, 1, 7)) return 1;
903 \\ if (!sub_i3(2, 1, 1)) return 1;
904 \\ if (!sub_i3(3, -2, -3)) return 1;
905 \\ if (!sub_i3(-3, 2, 3)) return 1;
906 \\ if (!sub_c_int(2, 1, 1)) return 1;
907 \\ // TODO enable these when stage2 supports std.math.maxInt
908 \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1;
909 \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1;
910 \\
911 \\ return 0;
912 \\}
913 \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool {
914 \\ return expected == lhs +% rhs;
915 \\}
916 \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool {
917 \\ return expected == lhs +% rhs;
918 \\}
919 \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
920 \\ return expected == lhs +% rhs;
921 \\}
922 \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool {
923 \\ return expected == lhs -% rhs;
924 \\}
925 \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool {
926 \\ return expected == lhs -% rhs;
927 \\}
928 \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
929 \\ return expected == lhs -% rhs;
930 \\}
931 , "");
932 }
933
934 {
935 var case = ctx.exeFromCompiledC("@rem", linux_x64);
936 case.addCompareOutput(
937 \\fn assert(ok: bool) void {
938 \\ if (!ok) unreachable;
939 \\}
940 \\fn rem(lhs: i32, rhs: i32, expected: i32) bool {
941 \\ return @rem(lhs, rhs) == expected;
942 \\}
943 \\pub export fn main() c_int {
944 \\ assert(rem(-5, 3, -2));
945 \\ assert(rem(5, 3, 2));
946 \\ return 0;
947 \\}
948 , "");
949 }
950
951 ctx.h("simple header", linux_x64,
952 \\export fn start() void{}
953 ,
954 \\zig_extern void start(void);
955 \\
956 );
957 ctx.h("header with single param function", linux_x64,
958 \\export fn start(a: u8) void{
959 \\ _ = a;
960 \\}
961 ,
962 \\zig_extern void start(uint8_t const a0);
963 \\
964 );
965 ctx.h("header with multiple param function", linux_x64,
966 \\export fn start(a: u8, b: u8, c: u8) void{
967 \\ _ = a; _ = b; _ = c;
968 \\}
969 ,
970 \\zig_extern void start(uint8_t const a0, uint8_t const a1, uint8_t const a2);
971 \\
972 );
973 ctx.h("header with u32 param function", linux_x64,
974 \\export fn start(a: u32) void{ _ = a; }
975 ,
976 \\zig_extern void start(uint32_t const a0);
977 \\
978 );
979 ctx.h("header with usize param function", linux_x64,
980 \\export fn start(a: usize) void{ _ = a; }
981 ,
982 \\zig_extern void start(uintptr_t const a0);
983 \\
984 );
985 ctx.h("header with bool param function", linux_x64,
986 \\export fn start(a: bool) void{_ = a;}
987 ,
988 \\zig_extern void start(bool const a0);
989 \\
990 );
991 ctx.h("header with noreturn function", linux_x64,
992 \\export fn start() noreturn {
993 \\ unreachable;
994 \\}
995 ,
996 \\zig_extern zig_noreturn void start(void);
997 \\
998 );
999 ctx.h("header with multiple functions", linux_x64,
1000 \\export fn a() void{}
1001 \\export fn b() void{}
1002 \\export fn c() void{}
1003 ,
1004 \\zig_extern void a(void);
1005 \\zig_extern void b(void);
1006 \\zig_extern void c(void);
1007 \\
1008 );
1009 ctx.h("header with multiple includes", linux_x64,
1010 \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; }
1011 ,
1012 \\zig_extern void start(uint32_t const a0, uintptr_t const a1);
1013 \\
1014 );
1015}
test/stage2/nvptx.zig deleted-107
...@@ -1,107 +0,0 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4pub fn addCases(ctx: *TestContext) !void {
5 {
6 var case = addPtx(ctx, "nvptx: simple addition and subtraction");
7
8 case.compiles(
9 \\fn add(a: i32, b: i32) i32 {
10 \\ return a + b;
11 \\}
12 \\
13 \\pub export fn add_and_substract(a: i32, out: *i32) callconv(.PtxKernel) void {
14 \\ const x = add(a, 7);
15 \\ var y = add(2, 0);
16 \\ y -= x;
17 \\ out.* = y;
18 \\}
19 );
20 }
21
22 {
23 var case = addPtx(ctx, "nvptx: read special registers");
24
25 case.compiles(
26 \\fn threadIdX() u32 {
27 \\ return asm ("mov.u32 \t%[r], %tid.x;"
28 \\ : [r] "=r" (-> u32),
29 \\ );
30 \\}
31 \\
32 \\pub export fn special_reg(a: []const i32, out: []i32) callconv(.PtxKernel) void {
33 \\ const i = threadIdX();
34 \\ out[i] = a[i] + 7;
35 \\}
36 );
37 }
38
39 {
40 var case = addPtx(ctx, "nvptx: address spaces");
41
42 case.compiles(
43 \\var x: i32 addrspace(.global) = 0;
44 \\
45 \\pub export fn increment(out: *i32) callconv(.PtxKernel) void {
46 \\ x += 1;
47 \\ out.* = x;
48 \\}
49 );
50 }
51
52 {
53 var case = addPtx(ctx, "nvptx: reduce in shared mem");
54 case.compiles(
55 \\fn threadIdX() u32 {
56 \\ return asm ("mov.u32 \t%[r], %tid.x;"
57 \\ : [r] "=r" (-> u32),
58 \\ );
59 \\}
60 \\
61 \\ var _sdata: [1024]f32 addrspace(.shared) = undefined;
62 \\ pub export fn reduceSum(d_x: []const f32, out: *f32) callconv(.PtxKernel) void {
63 \\ var sdata = @addrSpaceCast(.generic, &_sdata);
64 \\ const tid: u32 = threadIdX();
65 \\ var sum = d_x[tid];
66 \\ sdata[tid] = sum;
67 \\ asm volatile ("bar.sync \t0;");
68 \\ var s: u32 = 512;
69 \\ while (s > 0) : (s = s >> 1) {
70 \\ if (tid < s) {
71 \\ sum += sdata[tid + s];
72 \\ sdata[tid] = sum;
73 \\ }
74 \\ asm volatile ("bar.sync \t0;");
75 \\ }
76 \\
77 \\ if (tid == 0) {
78 \\ out.* = sum;
79 \\ }
80 \\ }
81 );
82 }
83}
84
85const nvptx_target = std.zig.CrossTarget{
86 .cpu_arch = .nvptx64,
87 .os_tag = .cuda,
88};
89
90pub fn addPtx(
91 ctx: *TestContext,
92 name: []const u8,
93) *TestContext.Case {
94 ctx.cases.append(TestContext.Case{
95 .name = name,
96 .target = nvptx_target,
97 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
98 .output_mode = .Obj,
99 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
100 .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator),
101 .link_libc = false,
102 .backend = .llvm,
103 // Bug in Debug mode
104 .optimize_mode = .ReleaseSafe,
105 }) catch @panic("out of memory");
106 return &ctx.cases.items[ctx.cases.items.len - 1];
107}
test/standalone.zig+208-106
...@@ -1,116 +1,218 @@...@@ -1,116 +1,218 @@
1const std = @import("std");1pub const SimpleCase = struct {
2const builtin = @import("builtin");2 src_path: []const u8,
3const tests = @import("tests.zig");3 link_libc: bool = false,
4 all_modes: bool = false,
5 target: std.zig.CrossTarget = .{},
6 is_test: bool = false,
7 is_exe: bool = true,
8 /// Run only on this OS.
9 os_filter: ?std.Target.Os.Tag = null,
10};
411
5pub fn addCases(cases: *tests.StandaloneContext) void {12pub const BuildCase = struct {
6 cases.add("test/standalone/hello_world/hello.zig");13 build_root: []const u8,
7 cases.addC("test/standalone/hello_world/hello_libc.zig");14 import: type,
15};
816
9 cases.addBuildFile("test/standalone/options/build.zig", .{17pub const simple_cases = [_]SimpleCase{
10 .extra_argv = &.{18 .{
11 "-Dbool_true",19 .src_path = "test/standalone/hello_world/hello.zig",
12 "-Dbool_false=false",20 .all_modes = true,
13 "-Dint=1234",21 },
14 "-De=two",22 .{
15 "-Dstring=hello",23 .src_path = "test/standalone/hello_world/hello_libc.zig",
24 .link_libc = true,
25 .all_modes = true,
26 },
27 .{
28 .src_path = "test/standalone/cat/main.zig",
29 },
30 // https://github.com/ziglang/zig/issues/6025
31 //.{
32 // .src_path = "test/standalone/issue_9693/main.zig",
33 //},
34 .{
35 .src_path = "test/standalone/brace_expansion.zig",
36 .is_test = true,
37 },
38 .{
39 .src_path = "test/standalone/issue_7030.zig",
40 .target = .{
41 .cpu_arch = .wasm32,
42 .os_tag = .freestanding,
16 },43 },
17 });44 },
18
19 cases.add("test/standalone/cat/main.zig");
20 if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/6025
21 cases.add("test/standalone/issue_9693/main.zig");
22 }
23 cases.add("test/standalone/issue_12471/main.zig");
24 cases.add("test/standalone/guess_number/main.zig");
25 cases.add("test/standalone/main_return_error/error_u8.zig");
26 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
27 cases.add("test/standalone/noreturn_call/inline.zig");
28 cases.add("test/standalone/noreturn_call/as_arg.zig");
29 cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true });
30 cases.addBuildFile("test/standalone/issue_13970/build.zig", .{});
31 cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});
32 cases.addBuildFile("test/standalone/shared_library/build.zig", .{});
33 cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{});
34 cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{
35 .build_modes = true,
36 .cross_targets = true,
37 });
38 cases.addBuildFile("test/standalone/global_linkage/build.zig", .{});
39 cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{});
40 cases.addBuildFile("test/standalone/issue_339/build.zig", .{});
41 cases.addBuildFile("test/standalone/issue_8550/build.zig", .{});
42 cases.addBuildFile("test/standalone/issue_794/build.zig", .{});
43 cases.addBuildFile("test/standalone/issue_5825/build.zig", .{});
44 cases.addBuildFile("test/standalone/pkg_import/build.zig", .{});
45 cases.addBuildFile("test/standalone/use_alias/build.zig", .{});
46 cases.addBuildFile("test/standalone/brace_expansion/build.zig", .{});
47 if (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64) {
48 // https://github.com/ziglang/zig/issues/13685
49 cases.addBuildFile("test/standalone/empty_env/build.zig", .{});
50 }
51 cases.addBuildFile("test/standalone/issue_7030/build.zig", .{});
52 cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{});
53 if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194
54 cases.addBuildFile("test/standalone/issue_9812/build.zig", .{});
55 }
56 if (builtin.os.tag != .windows) {
57 // https://github.com/ziglang/zig/issues/12419
58 cases.addBuildFile("test/standalone/issue_11595/build.zig", .{});
59 }
60
61 if (builtin.os.tag != .wasi and
62 // https://github.com/ziglang/zig/issues/13550
63 (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and
64 // https://github.com/ziglang/zig/issues/13686
65 (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64))
66 {
67 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{});
68 }
6945
70 if (builtin.os.tag == .windows) {46 .{ .src_path = "test/standalone/issue_12471/main.zig" },
71 cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{});47 .{ .src_path = "test/standalone/guess_number/main.zig" },
72 }48 .{ .src_path = "test/standalone/main_return_error/error_u8.zig" },
49 .{ .src_path = "test/standalone/main_return_error/error_u8_non_zero.zig" },
50 .{ .src_path = "test/standalone/noreturn_call/inline.zig" },
51 .{ .src_path = "test/standalone/noreturn_call/as_arg.zig" },
7352
74 cases.addBuildFile("test/standalone/c_compiler/build.zig", .{53 .{
75 .build_modes = true,54 .src_path = "test/standalone/issue_9402/main.zig",
76 .cross_targets = true,55 .os_filter = .windows,
77 });56 .link_libc = true,
7857 },
79 if (builtin.os.tag == .windows) {
80 cases.addC("test/standalone/issue_9402/main.zig");
81 }
82 // Try to build and run a PIE executable.
83 if (builtin.os.tag == .linux) {
84 cases.addBuildFile("test/standalone/pie/build.zig", .{});
85 }
86 cases.addBuildFile("test/standalone/issue_12706/build.zig", .{});
87 if (std.os.have_sigpipe_support) {
88 cases.addBuildFile("test/standalone/sigpipe/build.zig", .{});
89 }
9058
91 // Ensure the development tools are buildable. Alphabetically sorted.59 // Ensure the development tools are buildable. Alphabetically sorted.
92 // No need to build `tools/spirv/grammar.zig`.60 // No need to build `tools/spirv/grammar.zig`.
93 cases.add("tools/extract-grammar.zig");61 .{ .src_path = "tools/extract-grammar.zig" },
94 cases.add("tools/gen_outline_atomics.zig");62 .{ .src_path = "tools/gen_outline_atomics.zig" },
95 cases.add("tools/gen_spirv_spec.zig");63 .{ .src_path = "tools/gen_spirv_spec.zig" },
96 cases.add("tools/gen_stubs.zig");64 .{ .src_path = "tools/gen_stubs.zig" },
97 cases.add("tools/generate_linux_syscalls.zig");65 .{ .src_path = "tools/generate_linux_syscalls.zig" },
98 cases.add("tools/process_headers.zig");66 .{ .src_path = "tools/process_headers.zig" },
99 cases.add("tools/update-license-headers.zig");67 .{ .src_path = "tools/update-license-headers.zig" },
100 cases.add("tools/update-linux-headers.zig");68 .{ .src_path = "tools/update-linux-headers.zig" },
101 cases.add("tools/update_clang_options.zig");69 .{ .src_path = "tools/update_clang_options.zig" },
102 cases.add("tools/update_cpu_features.zig");70 .{ .src_path = "tools/update_cpu_features.zig" },
103 cases.add("tools/update_glibc.zig");71 .{ .src_path = "tools/update_glibc.zig" },
104 cases.add("tools/update_spirv_features.zig");72 .{ .src_path = "tools/update_spirv_features.zig" },
73};
10574
106 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });75pub const build_cases = [_]BuildCase{
107 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});76 .{
108 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});77 .build_root = "test/standalone/test_runner_path",
109 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});78 .import = @import("standalone/test_runner_path/build.zig"),
79 },
80 .{
81 .build_root = "test/standalone/issue_13970",
82 .import = @import("standalone/issue_13970/build.zig"),
83 },
84 .{
85 .build_root = "test/standalone/main_pkg_path",
86 .import = @import("standalone/main_pkg_path/build.zig"),
87 },
88 .{
89 .build_root = "test/standalone/shared_library",
90 .import = @import("standalone/shared_library/build.zig"),
91 },
92 .{
93 .build_root = "test/standalone/mix_o_files",
94 .import = @import("standalone/mix_o_files/build.zig"),
95 },
96 .{
97 .build_root = "test/standalone/mix_c_files",
98 .import = @import("standalone/mix_c_files/build.zig"),
99 },
100 .{
101 .build_root = "test/standalone/global_linkage",
102 .import = @import("standalone/global_linkage/build.zig"),
103 },
104 .{
105 .build_root = "test/standalone/static_c_lib",
106 .import = @import("standalone/static_c_lib/build.zig"),
107 },
108 .{
109 .build_root = "test/standalone/issue_339",
110 .import = @import("standalone/issue_339/build.zig"),
111 },
112 .{
113 .build_root = "test/standalone/issue_8550",
114 .import = @import("standalone/issue_8550/build.zig"),
115 },
116 .{
117 .build_root = "test/standalone/issue_794",
118 .import = @import("standalone/issue_794/build.zig"),
119 },
120 .{
121 .build_root = "test/standalone/issue_5825",
122 .import = @import("standalone/issue_5825/build.zig"),
123 },
124 .{
125 .build_root = "test/standalone/pkg_import",
126 .import = @import("standalone/pkg_import/build.zig"),
127 },
128 .{
129 .build_root = "test/standalone/use_alias",
130 .import = @import("standalone/use_alias/build.zig"),
131 },
132 .{
133 .build_root = "test/standalone/install_raw_hex",
134 .import = @import("standalone/install_raw_hex/build.zig"),
135 },
136 // TODO take away EmitOption.emit_to option and make it give a FileSource
137 //.{
138 // .build_root = "test/standalone/emit_asm_and_bin",
139 // .import = @import("standalone/emit_asm_and_bin/build.zig"),
140 //},
141 // TODO take away EmitOption.emit_to option and make it give a FileSource
142 //.{
143 // .build_root = "test/standalone/issue_12588",
144 // .import = @import("standalone/issue_12588/build.zig"),
145 //},
146 .{
147 .build_root = "test/standalone/embed_generated_file",
148 .import = @import("standalone/embed_generated_file/build.zig"),
149 },
150 .{
151 .build_root = "test/standalone/extern",
152 .import = @import("standalone/extern/build.zig"),
153 },
154 .{
155 .build_root = "test/standalone/dep_diamond",
156 .import = @import("standalone/dep_diamond/build.zig"),
157 },
158 .{
159 .build_root = "test/standalone/dep_triangle",
160 .import = @import("standalone/dep_triangle/build.zig"),
161 },
162 .{
163 .build_root = "test/standalone/dep_recursive",
164 .import = @import("standalone/dep_recursive/build.zig"),
165 },
166 .{
167 .build_root = "test/standalone/dep_mutually_recursive",
168 .import = @import("standalone/dep_mutually_recursive/build.zig"),
169 },
170 .{
171 .build_root = "test/standalone/dep_shared_builtin",
172 .import = @import("standalone/dep_shared_builtin/build.zig"),
173 },
174 .{
175 .build_root = "test/standalone/empty_env",
176 .import = @import("standalone/empty_env/build.zig"),
177 },
178 .{
179 .build_root = "test/standalone/issue_9812",
180 .import = @import("standalone/issue_9812/build.zig"),
181 },
182 .{
183 .build_root = "test/standalone/issue_11595",
184 .import = @import("standalone/issue_11595/build.zig"),
185 },
186 .{
187 .build_root = "test/standalone/load_dynamic_library",
188 .import = @import("standalone/load_dynamic_library/build.zig"),
189 },
190 .{
191 .build_root = "test/standalone/windows_spawn",
192 .import = @import("standalone/windows_spawn/build.zig"),
193 },
194 .{
195 .build_root = "test/standalone/c_compiler",
196 .import = @import("standalone/c_compiler/build.zig"),
197 },
198 .{
199 .build_root = "test/standalone/pie",
200 .import = @import("standalone/pie/build.zig"),
201 },
202 .{
203 .build_root = "test/standalone/issue_12706",
204 .import = @import("standalone/issue_12706/build.zig"),
205 },
206 // TODO This test is disabled for doing naughty things in the build script.
207 // The logic needs to get moved to a child process instead of build.zig.
208 //.{
209 // .build_root = "test/standalone/sigpipe",
210 // .import = @import("standalone/sigpipe/build.zig"),
211 //},
212 .{
213 .build_root = "test/standalone/issue_13030",
214 .import = @import("standalone/issue_13030/build.zig"),
215 },
216};
110217
111 cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{});218const std = @import("std");
112 cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{});
113 cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{});
114 cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{});
115 cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{});
116}
test/standalone/brace_expansion.zig created+292
...@@ -0,0 +1,292 @@
1const std = @import("std");
2const io = std.io;
3const mem = std.mem;
4const debug = std.debug;
5const assert = debug.assert;
6const testing = std.testing;
7const ArrayList = std.ArrayList;
8const maxInt = std.math.maxInt;
9
10const Token = union(enum) {
11 Word: []const u8,
12 OpenBrace,
13 CloseBrace,
14 Comma,
15 Eof,
16};
17
18var gpa = std.heap.GeneralPurposeAllocator(.{}){};
19var global_allocator = gpa.allocator();
20
21fn tokenize(input: []const u8) !ArrayList(Token) {
22 const State = enum {
23 Start,
24 Word,
25 };
26
27 var token_list = ArrayList(Token).init(global_allocator);
28 errdefer token_list.deinit();
29 var tok_begin: usize = undefined;
30 var state = State.Start;
31
32 for (input, 0..) |b, i| {
33 switch (state) {
34 .Start => switch (b) {
35 'a'...'z', 'A'...'Z' => {
36 state = State.Word;
37 tok_begin = i;
38 },
39 '{' => try token_list.append(Token.OpenBrace),
40 '}' => try token_list.append(Token.CloseBrace),
41 ',' => try token_list.append(Token.Comma),
42 else => return error.InvalidInput,
43 },
44 .Word => switch (b) {
45 'a'...'z', 'A'...'Z' => {},
46 '{', '}', ',' => {
47 try token_list.append(Token{ .Word = input[tok_begin..i] });
48 switch (b) {
49 '{' => try token_list.append(Token.OpenBrace),
50 '}' => try token_list.append(Token.CloseBrace),
51 ',' => try token_list.append(Token.Comma),
52 else => unreachable,
53 }
54 state = State.Start;
55 },
56 else => return error.InvalidInput,
57 },
58 }
59 }
60 switch (state) {
61 State.Start => {},
62 State.Word => try token_list.append(Token{ .Word = input[tok_begin..] }),
63 }
64 try token_list.append(Token.Eof);
65 return token_list;
66}
67
68const Node = union(enum) {
69 Scalar: []const u8,
70 List: ArrayList(Node),
71 Combine: []Node,
72
73 fn deinit(self: Node) void {
74 switch (self) {
75 .Scalar => {},
76 .Combine => |pair| {
77 pair[0].deinit();
78 pair[1].deinit();
79 global_allocator.free(pair);
80 },
81 .List => |list| {
82 for (list.items) |item| {
83 item.deinit();
84 }
85 list.deinit();
86 },
87 }
88 }
89};
90
91const ParseError = error{
92 InvalidInput,
93 OutOfMemory,
94};
95
96fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node {
97 const first_token = tokens.items[token_index.*];
98 token_index.* += 1;
99
100 const result_node = switch (first_token) {
101 .Word => |word| Node{ .Scalar = word },
102 .OpenBrace => blk: {
103 var list = ArrayList(Node).init(global_allocator);
104 errdefer {
105 for (list.items) |node| node.deinit();
106 list.deinit();
107 }
108 while (true) {
109 try list.append(try parse(tokens, token_index));
110
111 const token = tokens.items[token_index.*];
112 token_index.* += 1;
113
114 switch (token) {
115 .CloseBrace => break,
116 .Comma => continue,
117 else => return error.InvalidInput,
118 }
119 }
120 break :blk Node{ .List = list };
121 },
122 else => return error.InvalidInput,
123 };
124
125 switch (tokens.items[token_index.*]) {
126 .Word, .OpenBrace => {
127 const pair = try global_allocator.alloc(Node, 2);
128 errdefer global_allocator.free(pair);
129 pair[0] = result_node;
130 pair[1] = try parse(tokens, token_index);
131 return Node{ .Combine = pair };
132 },
133 else => return result_node,
134 }
135}
136
137fn expandString(input: []const u8, output: *ArrayList(u8)) !void {
138 const tokens = try tokenize(input);
139 defer tokens.deinit();
140 if (tokens.items.len == 1) {
141 return output.resize(0);
142 }
143
144 var token_index: usize = 0;
145 const root = try parse(&tokens, &token_index);
146 defer root.deinit();
147 const last_token = tokens.items[token_index];
148 switch (last_token) {
149 Token.Eof => {},
150 else => return error.InvalidInput,
151 }
152
153 var result_list = ArrayList(ArrayList(u8)).init(global_allocator);
154 defer {
155 for (result_list.items) |*buf| buf.deinit();
156 result_list.deinit();
157 }
158
159 try expandNode(root, &result_list);
160
161 try output.resize(0);
162 for (result_list.items, 0..) |buf, i| {
163 if (i != 0) {
164 try output.append(' ');
165 }
166 try output.appendSlice(buf.items);
167 }
168}
169
170const ExpandNodeError = error{OutOfMemory};
171
172fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void {
173 assert(output.items.len == 0);
174 switch (node) {
175 .Scalar => |scalar| {
176 var list = ArrayList(u8).init(global_allocator);
177 errdefer list.deinit();
178 try list.appendSlice(scalar);
179 try output.append(list);
180 },
181 .Combine => |pair| {
182 const a_node = pair[0];
183 const b_node = pair[1];
184
185 var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator);
186 defer {
187 for (child_list_a.items) |*buf| buf.deinit();
188 child_list_a.deinit();
189 }
190 try expandNode(a_node, &child_list_a);
191
192 var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator);
193 defer {
194 for (child_list_b.items) |*buf| buf.deinit();
195 child_list_b.deinit();
196 }
197 try expandNode(b_node, &child_list_b);
198
199 for (child_list_a.items) |buf_a| {
200 for (child_list_b.items) |buf_b| {
201 var combined_buf = ArrayList(u8).init(global_allocator);
202 errdefer combined_buf.deinit();
203
204 try combined_buf.appendSlice(buf_a.items);
205 try combined_buf.appendSlice(buf_b.items);
206 try output.append(combined_buf);
207 }
208 }
209 },
210 .List => |list| {
211 for (list.items) |child_node| {
212 var child_list = ArrayList(ArrayList(u8)).init(global_allocator);
213 errdefer for (child_list.items) |*buf| buf.deinit();
214 defer child_list.deinit();
215
216 try expandNode(child_node, &child_list);
217
218 for (child_list.items) |buf| {
219 try output.append(buf);
220 }
221 }
222 },
223 }
224}
225
226pub fn main() !void {
227 defer _ = gpa.deinit();
228 const stdin_file = io.getStdIn();
229 const stdout_file = io.getStdOut();
230
231 const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize));
232 defer global_allocator.free(stdin);
233
234 var result_buf = ArrayList(u8).init(global_allocator);
235 defer result_buf.deinit();
236
237 try expandString(stdin, &result_buf);
238 try stdout_file.writeAll(result_buf.items);
239}
240
241test "invalid inputs" {
242 global_allocator = std.testing.allocator;
243
244 try expectError("}ABC", error.InvalidInput);
245 try expectError("{ABC", error.InvalidInput);
246 try expectError("}{", error.InvalidInput);
247 try expectError("{}", error.InvalidInput);
248 try expectError("A,B,C", error.InvalidInput);
249 try expectError("{A{B,C}", error.InvalidInput);
250 try expectError("{A,}", error.InvalidInput);
251
252 try expectError("\n", error.InvalidInput);
253}
254
255fn expectError(test_input: []const u8, expected_err: anyerror) !void {
256 var output_buf = ArrayList(u8).init(global_allocator);
257 defer output_buf.deinit();
258
259 try testing.expectError(expected_err, expandString(test_input, &output_buf));
260}
261
262test "valid inputs" {
263 global_allocator = std.testing.allocator;
264
265 try expectExpansion("{x,y,z}", "x y z");
266 try expectExpansion("{A,B}{x,y}", "Ax Ay Bx By");
267 try expectExpansion("{A,B{x,y}}", "A Bx By");
268
269 try expectExpansion("{ABC}", "ABC");
270 try expectExpansion("{A,B,C}", "A B C");
271 try expectExpansion("ABC", "ABC");
272
273 try expectExpansion("", "");
274 try expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh");
275 try expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh");
276 try expectExpansion("{A,B}a", "Aa Ba");
277 try expectExpansion("{C,{x,y}}", "C x y");
278 try expectExpansion("z{C,{x,y}}", "zC zx zy");
279 try expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg");
280 try expectExpansion("a{x,y}b", "axb ayb");
281 try expectExpansion("z{{a,b}}", "za zb");
282 try expectExpansion("a{b}", "ab");
283}
284
285fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void {
286 var result = ArrayList(u8).init(global_allocator);
287 defer result.deinit();
288
289 expandString(test_input, &result) catch unreachable;
290
291 try testing.expectEqualSlices(u8, expected_result, result.items);
292}
test/standalone/brace_expansion/build.zig deleted-11
...@@ -1,11 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
8
9 const test_step = b.step("test", "Test it");
10 test_step.dependOn(&main.step);
11}
test/standalone/brace_expansion/main.zig deleted-292
...@@ -1,292 +0,0 @@
1const std = @import("std");
2const io = std.io;
3const mem = std.mem;
4const debug = std.debug;
5const assert = debug.assert;
6const testing = std.testing;
7const ArrayList = std.ArrayList;
8const maxInt = std.math.maxInt;
9
10const Token = union(enum) {
11 Word: []const u8,
12 OpenBrace,
13 CloseBrace,
14 Comma,
15 Eof,
16};
17
18var gpa = std.heap.GeneralPurposeAllocator(.{}){};
19var global_allocator = gpa.allocator();
20
21fn tokenize(input: []const u8) !ArrayList(Token) {
22 const State = enum {
23 Start,
24 Word,
25 };
26
27 var token_list = ArrayList(Token).init(global_allocator);
28 errdefer token_list.deinit();
29 var tok_begin: usize = undefined;
30 var state = State.Start;
31
32 for (input, 0..) |b, i| {
33 switch (state) {
34 .Start => switch (b) {
35 'a'...'z', 'A'...'Z' => {
36 state = State.Word;
37 tok_begin = i;
38 },
39 '{' => try token_list.append(Token.OpenBrace),
40 '}' => try token_list.append(Token.CloseBrace),
41 ',' => try token_list.append(Token.Comma),
42 else => return error.InvalidInput,
43 },
44 .Word => switch (b) {
45 'a'...'z', 'A'...'Z' => {},
46 '{', '}', ',' => {
47 try token_list.append(Token{ .Word = input[tok_begin..i] });
48 switch (b) {
49 '{' => try token_list.append(Token.OpenBrace),
50 '}' => try token_list.append(Token.CloseBrace),
51 ',' => try token_list.append(Token.Comma),
52 else => unreachable,
53 }
54 state = State.Start;
55 },
56 else => return error.InvalidInput,
57 },
58 }
59 }
60 switch (state) {
61 State.Start => {},
62 State.Word => try token_list.append(Token{ .Word = input[tok_begin..] }),
63 }
64 try token_list.append(Token.Eof);
65 return token_list;
66}
67
68const Node = union(enum) {
69 Scalar: []const u8,
70 List: ArrayList(Node),
71 Combine: []Node,
72
73 fn deinit(self: Node) void {
74 switch (self) {
75 .Scalar => {},
76 .Combine => |pair| {
77 pair[0].deinit();
78 pair[1].deinit();
79 global_allocator.free(pair);
80 },
81 .List => |list| {
82 for (list.items) |item| {
83 item.deinit();
84 }
85 list.deinit();
86 },
87 }
88 }
89};
90
91const ParseError = error{
92 InvalidInput,
93 OutOfMemory,
94};
95
96fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node {
97 const first_token = tokens.items[token_index.*];
98 token_index.* += 1;
99
100 const result_node = switch (first_token) {
101 .Word => |word| Node{ .Scalar = word },
102 .OpenBrace => blk: {
103 var list = ArrayList(Node).init(global_allocator);
104 errdefer {
105 for (list.items) |node| node.deinit();
106 list.deinit();
107 }
108 while (true) {
109 try list.append(try parse(tokens, token_index));
110
111 const token = tokens.items[token_index.*];
112 token_index.* += 1;
113
114 switch (token) {
115 .CloseBrace => break,
116 .Comma => continue,
117 else => return error.InvalidInput,
118 }
119 }
120 break :blk Node{ .List = list };
121 },
122 else => return error.InvalidInput,
123 };
124
125 switch (tokens.items[token_index.*]) {
126 .Word, .OpenBrace => {
127 const pair = try global_allocator.alloc(Node, 2);
128 errdefer global_allocator.free(pair);
129 pair[0] = result_node;
130 pair[1] = try parse(tokens, token_index);
131 return Node{ .Combine = pair };
132 },
133 else => return result_node,
134 }
135}
136
137fn expandString(input: []const u8, output: *ArrayList(u8)) !void {
138 const tokens = try tokenize(input);
139 defer tokens.deinit();
140 if (tokens.items.len == 1) {
141 return output.resize(0);
142 }
143
144 var token_index: usize = 0;
145 const root = try parse(&tokens, &token_index);
146 defer root.deinit();
147 const last_token = tokens.items[token_index];
148 switch (last_token) {
149 Token.Eof => {},
150 else => return error.InvalidInput,
151 }
152
153 var result_list = ArrayList(ArrayList(u8)).init(global_allocator);
154 defer {
155 for (result_list.items) |*buf| buf.deinit();
156 result_list.deinit();
157 }
158
159 try expandNode(root, &result_list);
160
161 try output.resize(0);
162 for (result_list.items, 0..) |buf, i| {
163 if (i != 0) {
164 try output.append(' ');
165 }
166 try output.appendSlice(buf.items);
167 }
168}
169
170const ExpandNodeError = error{OutOfMemory};
171
172fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void {
173 assert(output.items.len == 0);
174 switch (node) {
175 .Scalar => |scalar| {
176 var list = ArrayList(u8).init(global_allocator);
177 errdefer list.deinit();
178 try list.appendSlice(scalar);
179 try output.append(list);
180 },
181 .Combine => |pair| {
182 const a_node = pair[0];
183 const b_node = pair[1];
184
185 var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator);
186 defer {
187 for (child_list_a.items) |*buf| buf.deinit();
188 child_list_a.deinit();
189 }
190 try expandNode(a_node, &child_list_a);
191
192 var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator);
193 defer {
194 for (child_list_b.items) |*buf| buf.deinit();
195 child_list_b.deinit();
196 }
197 try expandNode(b_node, &child_list_b);
198
199 for (child_list_a.items) |buf_a| {
200 for (child_list_b.items) |buf_b| {
201 var combined_buf = ArrayList(u8).init(global_allocator);
202 errdefer combined_buf.deinit();
203
204 try combined_buf.appendSlice(buf_a.items);
205 try combined_buf.appendSlice(buf_b.items);
206 try output.append(combined_buf);
207 }
208 }
209 },
210 .List => |list| {
211 for (list.items) |child_node| {
212 var child_list = ArrayList(ArrayList(u8)).init(global_allocator);
213 errdefer for (child_list.items) |*buf| buf.deinit();
214 defer child_list.deinit();
215
216 try expandNode(child_node, &child_list);
217
218 for (child_list.items) |buf| {
219 try output.append(buf);
220 }
221 }
222 },
223 }
224}
225
226pub fn main() !void {
227 defer _ = gpa.deinit();
228 const stdin_file = io.getStdIn();
229 const stdout_file = io.getStdOut();
230
231 const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize));
232 defer global_allocator.free(stdin);
233
234 var result_buf = ArrayList(u8).init(global_allocator);
235 defer result_buf.deinit();
236
237 try expandString(stdin.items, &result_buf);
238 try stdout_file.write(result_buf.items);
239}
240
241test "invalid inputs" {
242 global_allocator = std.testing.allocator;
243
244 try expectError("}ABC", error.InvalidInput);
245 try expectError("{ABC", error.InvalidInput);
246 try expectError("}{", error.InvalidInput);
247 try expectError("{}", error.InvalidInput);
248 try expectError("A,B,C", error.InvalidInput);
249 try expectError("{A{B,C}", error.InvalidInput);
250 try expectError("{A,}", error.InvalidInput);
251
252 try expectError("\n", error.InvalidInput);
253}
254
255fn expectError(test_input: []const u8, expected_err: anyerror) !void {
256 var output_buf = ArrayList(u8).init(global_allocator);
257 defer output_buf.deinit();
258
259 try testing.expectError(expected_err, expandString(test_input, &output_buf));
260}
261
262test "valid inputs" {
263 global_allocator = std.testing.allocator;
264
265 try expectExpansion("{x,y,z}", "x y z");
266 try expectExpansion("{A,B}{x,y}", "Ax Ay Bx By");
267 try expectExpansion("{A,B{x,y}}", "A Bx By");
268
269 try expectExpansion("{ABC}", "ABC");
270 try expectExpansion("{A,B,C}", "A B C");
271 try expectExpansion("ABC", "ABC");
272
273 try expectExpansion("", "");
274 try expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh");
275 try expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh");
276 try expectExpansion("{A,B}a", "Aa Ba");
277 try expectExpansion("{C,{x,y}}", "C x y");
278 try expectExpansion("z{C,{x,y}}", "zC zx zy");
279 try expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg");
280 try expectExpansion("a{x,y}b", "axb ayb");
281 try expectExpansion("z{{a,b}}", "za zb");
282 try expectExpansion("a{b}", "ab");
283}
284
285fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void {
286 var result = ArrayList(u8).init(global_allocator);
287 defer result.deinit();
288
289 expandString(test_input, &result) catch unreachable;
290
291 try testing.expectEqualSlices(u8, expected_result, result.items);
292}
test/standalone/c_compiler/build.zig+18-21
...@@ -1,27 +1,24 @@...@@ -1,27 +1,24 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;
43
5// TODO integrate this with the std.Build executor API4pub fn build(b: *std.Build) void {
6fn isRunnableTarget(t: CrossTarget) bool {5 const test_step = b.step("test", "Test it");
7 if (t.isNative()) return true;6 b.default_step = test_step;
87
9 return (t.getOsTag() == builtin.os.tag and8 add(b, test_step, .Debug);
10 t.getCpuArch() == builtin.cpu.arch);9 add(b, test_step, .ReleaseFast);
10 add(b, test_step, .ReleaseSmall);
11 add(b, test_step, .ReleaseSafe);
11}12}
1213
13pub fn build(b: *std.Build) void {14fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const optimize = b.standardOptimizeOption(.{});15 const target: std.zig.CrossTarget = .{};
15 const target = b.standardTargetOptions(.{});
16
17 const test_step = b.step("test", "Test the program");
1816
19 const exe_c = b.addExecutable(.{17 const exe_c = b.addExecutable(.{
20 .name = "test_c",18 .name = "test_c",
21 .optimize = optimize,19 .optimize = optimize,
22 .target = target,20 .target = target,
23 });21 });
24 b.default_step.dependOn(&exe_c.step);
25 exe_c.addCSourceFile("test.c", &[0][]const u8{});22 exe_c.addCSourceFile("test.c", &[0][]const u8{});
26 exe_c.linkLibC();23 exe_c.linkLibC();
2724
...@@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void {...@@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void {
47 else => {},44 else => {},
48 }45 }
4946
50 if (isRunnableTarget(target)) {47 const run_c_cmd = b.addRunArtifact(exe_c);
51 const run_c_cmd = exe_c.run();48 run_c_cmd.expectExitCode(0);
52 test_step.dependOn(&run_c_cmd.step);49 run_c_cmd.skip_foreign_checks = true;
53 const run_cpp_cmd = exe_cpp.run();50 test_step.dependOn(&run_c_cmd.step);
54 test_step.dependOn(&run_cpp_cmd.step);51
55 } else {52 const run_cpp_cmd = b.addRunArtifact(exe_cpp);
56 test_step.dependOn(&exe_c.step);53 run_cpp_cmd.expectExitCode(0);
57 test_step.dependOn(&exe_cpp.step);54 run_cpp_cmd.skip_foreign_checks = true;
58 }55 test_step.dependOn(&run_cpp_cmd.step);
59}56}
test/standalone/dep_diamond/build.zig+4-2
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const shared = b.createModule(.{9 const shared = b.createModule(.{
7 .source_file = .{ .path = "shared.zig" },10 .source_file = .{ .path = "shared.zig" },
...@@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void {...@@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void {
2326
24 const run = exe.run();27 const run = exe.run();
2528
26 const test_step = b.step("test", "Test it");
27 test_step.dependOn(&run.step);29 test_step.dependOn(&run.step);
28}30}
test/standalone/dep_mutually_recursive/build.zig+4-2
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const foo = b.createModule(.{9 const foo = b.createModule(.{
7 .source_file = .{ .path = "foo.zig" },10 .source_file = .{ .path = "foo.zig" },
...@@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void {...@@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void {
2124
22 const run = exe.run();25 const run = exe.run();
2326
24 const test_step = b.step("test", "Test it");
25 test_step.dependOn(&run.step);27 test_step.dependOn(&run.step);
26}28}
test/standalone/dep_recursive/build.zig+4-2
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const foo = b.createModule(.{9 const foo = b.createModule(.{
7 .source_file = .{ .path = "foo.zig" },10 .source_file = .{ .path = "foo.zig" },
...@@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void {...@@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void {
1720
18 const run = exe.run();21 const run = exe.run();
1922
20 const test_step = b.step("test", "Test it");
21 test_step.dependOn(&run.step);23 test_step.dependOn(&run.step);
22}24}
test/standalone/dep_shared_builtin/build.zig+4-2
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const exe = b.addExecutable(.{9 const exe = b.addExecutable(.{
7 .name = "test",10 .name = "test",
...@@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void {...@@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void {
1417
15 const run = exe.run();18 const run = exe.run();
1619
17 const test_step = b.step("test", "Test it");
18 test_step.dependOn(&run.step);20 test_step.dependOn(&run.step);
19}21}
test/standalone/dep_triangle/build.zig+4-2
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const shared = b.createModule(.{9 const shared = b.createModule(.{
7 .source_file = .{ .path = "shared.zig" },10 .source_file = .{ .path = "shared.zig" },
...@@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void {...@@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void {
2023
21 const run = exe.run();24 const run = exe.run();
2225
23 const test_step = b.step("test", "Test it");
24 test_step.dependOn(&run.step);26 test_step.dependOn(&run.step);
25}27}
test/standalone/embed_generated_file/build.zig+3-5
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});4 const test_step = b.step("test", "Test it");
5 const optimize = b.standardOptimizeOption(.{});5 b.default_step = test_step;
66
7 const bootloader = b.addExecutable(.{7 const bootloader = b.addExecutable(.{
8 .name = "bootloader",8 .name = "bootloader",
...@@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void {...@@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void {
1616
17 const exe = b.addTest(.{17 const exe = b.addTest(.{
18 .root_source_file = .{ .path = "main.zig" },18 .root_source_file = .{ .path = "main.zig" },
19 .target = target,19 .optimize = .Debug,
20 .optimize = optimize,
21 });20 });
22 exe.addAnonymousModule("bootloader.elf", .{21 exe.addAnonymousModule("bootloader.elf", .{
23 .source_file = bootloader.getOutputSource(),22 .source_file = bootloader.getOutputSource(),
24 });23 });
2524
26 const test_step = b.step("test", "Test the program");
27 test_step.dependOn(&exe.step);25 test_step.dependOn(&exe.step);
28}26}
test/standalone/emit_asm_and_bin/build.zig+4-2
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
4 const main = b.addTest(.{7 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },8 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),9 .optimize = b.standardOptimizeOption(.{}),
...@@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void {...@@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void {
8 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };11 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
9 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };12 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
1013
11 const test_step = b.step("test", "Run test");14 test_step.dependOn(&main.run().step);
12 test_step.dependOn(&main.step);
13}15}
test/standalone/empty_env/build.zig+13-3
...@@ -1,15 +1,25 @@...@@ -1,15 +1,25 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
23
3pub fn build(b: *std.Build) void {4pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test it");
6 b.default_step = test_step;
7
8 const optimize: std.builtin.OptimizeMode = .Debug;
9
10 if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) {
11 // https://github.com/ziglang/zig/issues/13685
12 return;
13 }
14
4 const main = b.addExecutable(.{15 const main = b.addExecutable(.{
5 .name = "main",16 .name = "main",
6 .root_source_file = .{ .path = "main.zig" },17 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),18 .optimize = optimize,
8 });19 });
920
10 const run = main.run();21 const run = b.addRunArtifact(main);
11 run.clearEnvironment();22 run.clearEnvironment();
1223
13 const test_step = b.step("test", "Test it");
14 test_step.dependOn(&run.step);24 test_step.dependOn(&run.step);
15}25}
test/standalone/extern/build.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize: std.builtin.OptimizeMode = .Debug;
5
6 const obj = b.addObject(.{
7 .name = "exports",
8 .root_source_file = .{ .path = "exports.zig" },
9 .target = .{},
10 .optimize = optimize,
11 });
12 const main = b.addTest(.{
13 .root_source_file = .{ .path = "main.zig" },
14 .optimize = optimize,
15 });
16 main.addObject(obj);
17
18 const test_step = b.step("test", "Test it");
19 test_step.dependOn(&main.step);
20}
test/standalone/extern/exports.zig created+12
...@@ -0,0 +1,12 @@
1var hidden: u32 = 0;
2export fn updateHidden(val: u32) void {
3 hidden = val;
4}
5export fn getHidden() u32 {
6 return hidden;
7}
8
9const T = extern struct { x: u32 };
10
11export var mut_val: f64 = 1.23;
12export const const_val: T = .{ .x = 42 };
test/standalone/extern/main.zig created+21
...@@ -0,0 +1,21 @@
1const assert = @import("std").debug.assert;
2
3const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
4const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
5
6const T = extern struct { x: u32 };
7
8test {
9 var mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
10 var const_val_ptr = @extern(*const T, .{ .name = "const_val" });
11
12 assert(getHidden() == 0);
13 updateHidden(123);
14 assert(getHidden() == 123);
15
16 assert(mut_val_ptr.* == 1.23);
17 mut_val_ptr.* = 10.0;
18 assert(mut_val_ptr.* == 10.0);
19
20 assert(const_val_ptr.x == 42);
21}
test/standalone/global_linkage/build.zig+8-5
...@@ -1,20 +1,24 @@...@@ -1,20 +1,24 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test the program");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target: std.zig.CrossTarget = .{};
59
6 const obj1 = b.addStaticLibrary(.{10 const obj1 = b.addStaticLibrary(.{
7 .name = "obj1",11 .name = "obj1",
8 .root_source_file = .{ .path = "obj1.zig" },12 .root_source_file = .{ .path = "obj1.zig" },
9 .optimize = optimize,13 .optimize = optimize,
10 .target = .{},14 .target = target,
11 });15 });
1216
13 const obj2 = b.addStaticLibrary(.{17 const obj2 = b.addStaticLibrary(.{
14 .name = "obj2",18 .name = "obj2",
15 .root_source_file = .{ .path = "obj2.zig" },19 .root_source_file = .{ .path = "obj2.zig" },
16 .optimize = optimize,20 .optimize = optimize,
17 .target = .{},21 .target = target,
18 });22 });
1923
20 const main = b.addTest(.{24 const main = b.addTest(.{
...@@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void {...@@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void {
24 main.linkLibrary(obj1);28 main.linkLibrary(obj1);
25 main.linkLibrary(obj2);29 main.linkLibrary(obj2);
2630
27 const test_step = b.step("test", "Test it");31 test_step.dependOn(&main.run().step);
28 test_step.dependOn(&main.step);
29}32}
test/standalone/install_raw_hex/build.zig+3-3
...@@ -3,8 +3,8 @@ const std = @import("std");...@@ -3,8 +3,8 @@ const std = @import("std");
3const CheckFileStep = std.Build.CheckFileStep;3const CheckFileStep = std.Build.CheckFileStep;
44
5pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test the program");6 const test_step = b.step("test", "Test it");
7 b.default_step.dependOn(test_step);7 b.default_step = test_step;
88
9 const target = .{9 const target = .{
10 .cpu_arch = .thumb,10 .cpu_arch = .thumb,
...@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {...@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
13 .abi = .gnueabihf,13 .abi = .gnueabihf,
14 };14 };
1515
16 const optimize = b.standardOptimizeOption(.{});16 const optimize: std.builtin.OptimizeMode = .Debug;
1717
18 const elf = b.addExecutable(.{18 const elf = b.addExecutable(.{
19 .name = "zig-nrf52-blink.elf",19 .name = "zig-nrf52-blink.elf",
test/standalone/issue_11595/build.zig+14-17
...@@ -1,18 +1,17 @@...@@ -1,18 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;
43
5// TODO integrate this with the std.Build executor API4pub fn build(b: *std.Build) void {
6fn isRunnableTarget(t: CrossTarget) bool {5 const test_step = b.step("test", "Test it");
7 if (t.isNative()) return true;6 b.default_step = test_step;
87
9 return (t.getOsTag() == builtin.os.tag and8 const optimize: std.builtin.OptimizeMode = .Debug;
10 t.getCpuArch() == builtin.cpu.arch);9 const target: std.zig.CrossTarget = .{};
11}
1210
13pub fn build(b: *std.Build) void {11 if (builtin.os.tag == .windows) {
14 const optimize = b.standardOptimizeOption(.{});12 // https://github.com/ziglang/zig/issues/12419
15 const target = b.standardTargetOptions(.{});13 return;
14 }
1615
17 const exe = b.addExecutable(.{16 const exe = b.addExecutable(.{
18 .name = "zigtest",17 .name = "zigtest",
...@@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void {...@@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void {
4443
45 b.default_step.dependOn(&exe.step);44 b.default_step.dependOn(&exe.step);
4645
47 const test_step = b.step("test", "Test the program");46 const run_cmd = b.addRunArtifact(exe);
48 if (isRunnableTarget(target)) {47 run_cmd.skip_foreign_checks = true;
49 const run_cmd = exe.run();48 run_cmd.expectExitCode(0);
50 test_step.dependOn(&run_cmd.step);49
51 } else {50 test_step.dependOn(&run_cmd.step);
52 test_step.dependOn(&exe.step);
53 }
54}51}
test/standalone/issue_12588/build.zig+5-3
...@@ -1,8 +1,11 @@...@@ -1,8 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 const target = b.standardTargetOptions(.{});5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target: std.zig.CrossTarget = .{};
69
7 const obj = b.addObject(.{10 const obj = b.addObject(.{
8 .name = "main",11 .name = "main",
...@@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void {...@@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void {
15 obj.emit_bin = .no_emit;18 obj.emit_bin = .no_emit;
16 b.default_step.dependOn(&obj.step);19 b.default_step.dependOn(&obj.step);
1720
18 const test_step = b.step("test", "Test the program");
19 test_step.dependOn(&obj.step);21 test_step.dependOn(&obj.step);
20}22}
test/standalone/issue_12706/build.zig+9-21
...@@ -2,17 +2,12 @@ const std = @import("std");...@@ -2,17 +2,12 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5// TODO integrate this with the std.Build executor API
6fn isRunnableTarget(t: CrossTarget) bool {
7 if (t.isNative()) return true;
8
9 return (t.getOsTag() == builtin.os.tag and
10 t.getCpuArch() == builtin.cpu.arch);
11}
12
13pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
15 const target = b.standardTargetOptions(.{});7 b.default_step = test_step;
8
9 const optimize: std.builtin.OptimizeMode = .Debug;
10 const target: std.zig.CrossTarget = .{};
1611
17 const exe = b.addExecutable(.{12 const exe = b.addExecutable(.{
18 .name = "main",13 .name = "main",
...@@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void {...@@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void {
20 .optimize = optimize,15 .optimize = optimize,
21 .target = target,16 .target = target,
22 });17 });
23 exe.install();
2418
25 const c_sources = [_][]const u8{19 const c_sources = [_][]const u8{
26 "test.c",20 "test.c",
27 };21 };
28
29 exe.addCSourceFiles(&c_sources, &.{});22 exe.addCSourceFiles(&c_sources, &.{});
30 exe.linkLibC();23 exe.linkLibC();
3124
32 b.default_step.dependOn(&exe.step);25 const run_cmd = b.addRunArtifact(exe);
3326 run_cmd.expectExitCode(0);
34 const test_step = b.step("test", "Test the program");27 run_cmd.skip_foreign_checks = true;
35 if (isRunnableTarget(target)) {28 test_step.dependOn(&run_cmd.step);
36 const run_cmd = exe.run();
37 test_step.dependOn(&run_cmd.step);
38 } else {
39 test_step.dependOn(&exe.step);
40 }
41}29}
test/standalone/issue_13030/build.zig+10-5
...@@ -3,17 +3,22 @@ const builtin = @import("builtin");...@@ -3,17 +3,22 @@ const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
6 const optimize = b.standardOptimizeOption(.{});6 const test_step = b.step("test", "Test it");
7 const target = b.standardTargetOptions(.{});7 b.default_step = test_step;
88
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
9 const obj = b.addObject(.{16 const obj = b.addObject(.{
10 .name = "main",17 .name = "main",
11 .root_source_file = .{ .path = "main.zig" },18 .root_source_file = .{ .path = "main.zig" },
12 .optimize = optimize,19 .optimize = optimize,
13 .target = target,20 .target = .{},
14 });21 });
15 b.default_step.dependOn(&obj.step);
1622
17 const test_step = b.step("test", "Test the program");
18 test_step.dependOn(&obj.step);23 test_step.dependOn(&obj.step);
19}24}
test/standalone/issue_13970/build.zig+6-4
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
4 const test1 = b.addTest(.{7 const test1 = b.addTest(.{
5 .root_source_file = .{ .path = "test_root/empty.zig" },8 .root_source_file = .{ .path = "test_root/empty.zig" },
6 });9 });
...@@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void {...@@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void {
14 test2.setTestRunner("src/main.zig");17 test2.setTestRunner("src/main.zig");
15 test3.setTestRunner("src/main.zig");18 test3.setTestRunner("src/main.zig");
1619
17 const test_step = b.step("test", "Test package path resolution of custom test runner");20 test_step.dependOn(&test1.run().step);
18 test_step.dependOn(&test1.step);21 test_step.dependOn(&test2.run().step);
19 test_step.dependOn(&test2.step);22 test_step.dependOn(&test3.run().step);
20 test_step.dependOn(&test3.step);
21}23}
test/standalone/issue_339/build.zig+8-3
...@@ -1,13 +1,18 @@...@@ -1,13 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target: std.zig.CrossTarget = .{};
9
4 const obj = b.addObject(.{10 const obj = b.addObject(.{
5 .name = "test",11 .name = "test",
6 .root_source_file = .{ .path = "test.zig" },12 .root_source_file = .{ .path = "test.zig" },
7 .target = b.standardTargetOptions(.{}),13 .target = target,
8 .optimize = b.standardOptimizeOption(.{}),14 .optimize = optimize,
9 });15 });
1016
11 const test_step = b.step("test", "Test the program");
12 test_step.dependOn(&obj.step);17 test_step.dependOn(&obj.step);
13}18}
test/standalone/issue_5825/build.zig+4-2
...@@ -1,12 +1,15 @@...@@ -1,12 +1,15 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
4 const target = .{7 const target = .{
5 .cpu_arch = .x86_64,8 .cpu_arch = .x86_64,
6 .os_tag = .windows,9 .os_tag = .windows,
7 .abi = .msvc,10 .abi = .msvc,
8 };11 };
9 const optimize = b.standardOptimizeOption(.{});12 const optimize: std.builtin.OptimizeMode = .Debug;
10 const obj = b.addObject(.{13 const obj = b.addObject(.{
11 .name = "issue_5825",14 .name = "issue_5825",
12 .root_source_file = .{ .path = "main.zig" },15 .root_source_file = .{ .path = "main.zig" },
...@@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void {...@@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void {
24 exe.linkSystemLibrary("ntdll");27 exe.linkSystemLibrary("ntdll");
25 exe.addObject(obj);28 exe.addObject(obj);
2629
27 const test_step = b.step("test", "Test the program");
28 test_step.dependOn(&exe.step);30 test_step.dependOn(&exe.step);
29}31}
test/standalone/issue_7030.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2
3pub const std_options = struct {
4 pub const logFn = log;
5};
6
7pub fn log(
8 comptime message_level: std.log.Level,
9 comptime scope: @Type(.EnumLiteral),
10 comptime format: []const u8,
11 args: anytype,
12) void {
13 _ = message_level;
14 _ = scope;
15 _ = format;
16 _ = args;
17}
18
19pub fn main() anyerror!void {
20 std.log.info("All your codebase are belong to us.", .{});
21}
test/standalone/issue_7030/build.zig deleted-17
...@@ -1,17 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const exe = b.addExecutable(.{
5 .name = "issue_7030",
6 .root_source_file = .{ .path = "main.zig" },
7 .target = .{
8 .cpu_arch = .wasm32,
9 .os_tag = .freestanding,
10 },
11 });
12 exe.install();
13 b.default_step.dependOn(&exe.step);
14
15 const test_step = b.step("test", "Test the program");
16 test_step.dependOn(&exe.step);
17}
test/standalone/issue_7030/main.zig deleted-21
...@@ -1,21 +0,0 @@
1const std = @import("std");
2
3pub const std_options = struct {
4 pub const logFn = log;
5};
6
7pub fn log(
8 comptime message_level: std.log.Level,
9 comptime scope: @Type(.EnumLiteral),
10 comptime format: []const u8,
11 args: anytype,
12) void {
13 _ = message_level;
14 _ = scope;
15 _ = format;
16 _ = args;
17}
18
19pub fn main() anyerror!void {
20 std.log.info("All your codebase are belong to us.", .{});
21}
test/standalone/issue_794/build.zig+3-3
...@@ -1,13 +1,13 @@...@@ -1,13 +1,13 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
4 const test_artifact = b.addTest(.{7 const test_artifact = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },8 .root_source_file = .{ .path = "main.zig" },
6 });9 });
7 test_artifact.addIncludePath("a_directory");10 test_artifact.addIncludePath("a_directory");
811
9 b.default_step.dependOn(&test_artifact.step);
10
11 const test_step = b.step("test", "Test the program");
12 test_step.dependOn(&test_artifact.step);12 test_step.dependOn(&test_artifact.step);
13}13}
test/standalone/issue_8550/build.zig+5-2
...@@ -1,6 +1,10 @@...@@ -1,6 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) !void {3pub fn build(b: *std.Build) !void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
4 const target = std.zig.CrossTarget{8 const target = std.zig.CrossTarget{
5 .os_tag = .freestanding,9 .os_tag = .freestanding,
6 .cpu_arch = .arm,10 .cpu_arch = .arm,
...@@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void {...@@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void {
8 .explicit = &std.Target.arm.cpu.arm1176jz_s,12 .explicit = &std.Target.arm.cpu.arm1176jz_s,
9 },13 },
10 };14 };
11 const optimize = b.standardOptimizeOption(.{});15
12 const kernel = b.addExecutable(.{16 const kernel = b.addExecutable(.{
13 .name = "kernel",17 .name = "kernel",
14 .root_source_file = .{ .path = "./main.zig" },18 .root_source_file = .{ .path = "./main.zig" },
...@@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void {...@@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void {
19 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });23 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });
20 kernel.install();24 kernel.install();
2125
22 const test_step = b.step("test", "Test it");
23 test_step.dependOn(&kernel.step);26 test_step.dependOn(&kernel.step);
24}27}
test/standalone/issue_9812/build.zig+5-2
...@@ -1,7 +1,11 @@...@@ -1,7 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) !void {3pub fn build(b: *std.Build) !void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8
5 const zip_add = b.addTest(.{9 const zip_add = b.addTest(.{
6 .root_source_file = .{ .path = "main.zig" },10 .root_source_file = .{ .path = "main.zig" },
7 .optimize = optimize,11 .optimize = optimize,
...@@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void {...@@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void {
13 zip_add.addIncludePath("vendor/kuba-zip");17 zip_add.addIncludePath("vendor/kuba-zip");
14 zip_add.linkLibC();18 zip_add.linkLibC();
1519
16 const test_step = b.step("test", "Test it");
17 test_step.dependOn(&zip_add.step);20 test_step.dependOn(&zip_add.step);
18}21}
test/standalone/load_dynamic_library/build.zig+16-4
...@@ -1,8 +1,19 @@...@@ -1,8 +1,19 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
23
3pub fn build(b: *std.Build) void {4pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});5 const test_step = b.step("test", "Test it");
5 const optimize = b.standardOptimizeOption(.{});6 b.default_step = test_step;
7
8 const optimize: std.builtin.OptimizeMode = .Debug;
9 const target: std.zig.CrossTarget = .{};
10
11 const ok = (builtin.os.tag != .wasi and
12 // https://github.com/ziglang/zig/issues/13550
13 (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and
14 // https://github.com/ziglang/zig/issues/13686
15 (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64));
16 if (!ok) return;
617
7 const lib = b.addSharedLibrary(.{18 const lib = b.addSharedLibrary(.{
8 .name = "add",19 .name = "add",
...@@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void {...@@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void {
19 .target = target,30 .target = target,
20 });31 });
2132
22 const run = main.run();33 const run = b.addRunArtifact(main);
23 run.addArtifactArg(lib);34 run.addArtifactArg(lib);
35 run.skip_foreign_checks = true;
36 run.expectExitCode(0);
2437
25 const test_step = b.step("test", "Test the program");
26 test_step.dependOn(&run.step);38 test_step.dependOn(&run.step);
27}39}
test/standalone/load_dynamic_library/main.zig+1-5
...@@ -11,11 +11,7 @@ pub fn main() !void {...@@ -11,11 +11,7 @@ pub fn main() !void {
11 var lib = try std.DynLib.open(dynlib_name);11 var lib = try std.DynLib.open(dynlib_name);
12 defer lib.close();12 defer lib.close();
1313
14 const Add = switch (@import("builtin").zig_backend) {14 const Add = *const fn (i32, i32) callconv(.C) i32;
15 .stage1 => fn (i32, i32) callconv(.C) i32,
16 else => *const fn (i32, i32) callconv(.C) i32,
17 };
18
19 const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound;15 const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound;
2016
21 const result = addFn(12, 34);17 const result = addFn(12, 34);
test/standalone/main_pkg_path/build.zig+4-2
...@@ -1,11 +1,13 @@...@@ -1,11 +1,13 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
4 const test_exe = b.addTest(.{7 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "a/test.zig" },8 .root_source_file = .{ .path = "a/test.zig" },
6 });9 });
7 test_exe.setMainPkgPath(".");10 test_exe.setMainPkgPath(".");
811
9 const test_step = b.step("test", "Test the program");12 test_step.dependOn(&test_exe.run().step);
10 test_step.dependOn(&test_exe.step);
11}13}
test/standalone/mix_c_files/build.zig+13-19
...@@ -1,34 +1,28 @@...@@ -1,34 +1,28 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;
42
5// TODO integrate this with the std.Build executor API3pub fn build(b: *std.Build) void {
6fn isRunnableTarget(t: CrossTarget) bool {4 const test_step = b.step("test", "Test it");
7 if (t.isNative()) return true;5 b.default_step = test_step;
86
9 return (t.getOsTag() == builtin.os.tag and7 add(b, test_step, .Debug);
10 t.getCpuArch() == builtin.cpu.arch);8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}11}
1212
13pub fn build(b: *std.Build) void {13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const optimize = b.standardOptimizeOption(.{});
15 const target = b.standardTargetOptions(.{});
16
17 const exe = b.addExecutable(.{14 const exe = b.addExecutable(.{
18 .name = "test",15 .name = "test",
19 .root_source_file = .{ .path = "main.zig" },16 .root_source_file = .{ .path = "main.zig" },
20 .optimize = optimize,17 .optimize = optimize,
21 .target = target,
22 });18 });
23 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});19 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});
24 exe.linkLibC();20 exe.linkLibC();
25 b.default_step.dependOn(&exe.step);21 b.default_step.dependOn(&exe.step);
2622
27 const test_step = b.step("test", "Test the program");23 const run_cmd = b.addRunArtifact(exe);
28 if (isRunnableTarget(target)) {24 run_cmd.skip_foreign_checks = true;
29 const run_cmd = exe.run();25 run_cmd.expectExitCode(0);
30 test_step.dependOn(&run_cmd.step);26
31 } else {27 test_step.dependOn(&run_cmd.step);
32 test_step.dependOn(&exe.step);
33 }
34}28}
test/standalone/mix_o_files/build.zig+7-3
...@@ -1,18 +1,23 @@...@@ -1,18 +1,23 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target: std.zig.CrossTarget = .{};
59
6 const obj = b.addObject(.{10 const obj = b.addObject(.{
7 .name = "base64",11 .name = "base64",
8 .root_source_file = .{ .path = "base64.zig" },12 .root_source_file = .{ .path = "base64.zig" },
9 .optimize = optimize,13 .optimize = optimize,
10 .target = .{},14 .target = target,
11 });15 });
1216
13 const exe = b.addExecutable(.{17 const exe = b.addExecutable(.{
14 .name = "test",18 .name = "test",
15 .optimize = optimize,19 .optimize = optimize,
20 .target = target,
16 });21 });
17 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});22 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
18 exe.addObject(obj);23 exe.addObject(obj);
...@@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void {...@@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void {
2227
23 const run_cmd = exe.run();28 const run_cmd = exe.run();
2429
25 const test_step = b.step("test", "Test the program");
26 test_step.dependOn(&run_cmd.step);30 test_step.dependOn(&run_cmd.step);
27}31}
test/standalone/options/build.zig+1-1
...@@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void {...@@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void {
20 options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?);20 options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?);
2121
22 const test_step = b.step("test", "Run unit tests");22 const test_step = b.step("test", "Run unit tests");
23 test_step.dependOn(&main.step);23 test_step.dependOn(&main.run().step);
24}24}
test/standalone/pie/build.zig+14-4
...@@ -1,14 +1,24 @@...@@ -1,14 +1,24 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target: std.zig.CrossTarget = .{
9 .os_tag = .linux,
10 .cpu_arch = .x86_64,
11 };
12
4 const main = b.addTest(.{13 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },14 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),15 .optimize = optimize,
16 .target = target,
7 });17 });
8 main.pie = true;18 main.pie = true;
919
10 const test_step = b.step("test", "Test the program");20 const run = main.run();
11 test_step.dependOn(&main.step);21 run.skip_foreign_checks = true;
1222
13 b.default_step.dependOn(test_step);23 test_step.dependOn(&run.step);
14}24}
test/standalone/pkg_import/build.zig+4-2
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const exe = b.addExecutable(.{9 const exe = b.addExecutable(.{
7 .name = "test",10 .name = "test",
...@@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void {...@@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void {
1215
13 const run = exe.run();16 const run = exe.run();
1417
15 const test_step = b.step("test", "Test it");
16 test_step.dependOn(&run.step);18 test_step.dependOn(&run.step);
17}19}
test/standalone/shared_library/build.zig+5-5
...@@ -1,8 +1,11 @@...@@ -1,8 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 const target = b.standardTargetOptions(.{});5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target: std.zig.CrossTarget = .{};
6 const lib = b.addSharedLibrary(.{9 const lib = b.addSharedLibrary(.{
7 .name = "mathtest",10 .name = "mathtest",
8 .root_source_file = .{ .path = "mathtest.zig" },11 .root_source_file = .{ .path = "mathtest.zig" },
...@@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void {...@@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void {
20 exe.linkLibrary(lib);23 exe.linkLibrary(lib);
21 exe.linkSystemLibrary("c");24 exe.linkSystemLibrary("c");
2225
23 b.default_step.dependOn(&exe.step);
24
25 const run_cmd = exe.run();26 const run_cmd = exe.run();
2627
27 const test_step = b.step("test", "Test the program");
28 test_step.dependOn(&run_cmd.step);28 test_step.dependOn(&run_cmd.step);
29}29}
test/standalone/sigpipe/build.zig+14-5
...@@ -2,7 +2,16 @@ const std = @import("std");...@@ -2,7 +2,16 @@ const std = @import("std");
2const os = std.os;2const os = std.os;
33
4pub fn build(b: *std.build.Builder) !void {4pub fn build(b: *std.build.Builder) !void {
5 const test_step = b.step("test", "Run the tests");5 const test_step = b.step("test", "Test it");
6 b.default_step = test_step;
7
8 // TODO signal handling code has no business being in a build script.
9 // this logic needs to move to a file called parent.zig which is
10 // added as an executable.
11
12 //if (!std.os.have_sigpipe_support) {
13 // return;
14 //}
615
7 // This test runs "breakpipe" as a child process and that process16 // This test runs "breakpipe" as a child process and that process
8 // depends on inheriting a SIGPIPE disposition of "default".17 // depends on inheriting a SIGPIPE disposition of "default".
...@@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void {...@@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void {
23 .root_source_file = .{ .path = "breakpipe.zig" },32 .root_source_file = .{ .path = "breakpipe.zig" },
24 });33 });
25 exe.addOptions("build_options", options);34 exe.addOptions("build_options", options);
26 const run = exe.run();35 const run = b.addRunArtifact(exe);
27 if (keep_sigpipe) {36 if (keep_sigpipe) {
28 run.expected_term = .{ .Signal = std.os.SIG.PIPE };37 run.addCheck(.{ .expect_term = .{ .Signal = std.os.SIG.PIPE } });
29 } else {38 } else {
30 run.stdout_action = .{ .expect_exact = "BrokenPipe\n" };39 run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" });
31 run.expected_term = .{ .Exited = 123 };40 run.addCheck(.{ .expect_term = .{ .Exited = 123 } });
32 }41 }
33 test_step.dependOn(&run.step);42 test_step.dependOn(&run.step);
34 }43 }
test/standalone/static_c_lib/build.zig+5-3
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
58
6 const foo = b.addStaticLibrary(.{9 const foo = b.addStaticLibrary(.{
7 .name = "foo",10 .name = "foo",
...@@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void {...@@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void {
18 test_exe.linkLibrary(foo);21 test_exe.linkLibrary(foo);
19 test_exe.addIncludePath(".");22 test_exe.addIncludePath(".");
2023
21 const test_step = b.step("test", "Test it");24 test_step.dependOn(&test_exe.run().step);
22 test_step.dependOn(&test_exe.step);
23}25}
test/standalone/test_runner_module_imports/build.zig+1-1
...@@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void {...@@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void {
15 t.addModule("module2", module2);15 t.addModule("module2", module2);
1616
17 const test_step = b.step("test", "Run unit tests");17 const test_step = b.step("test", "Run unit tests");
18 test_step.dependOn(&t.step);18 test_step.dependOn(&t.run().step);
19}19}
test/standalone/test_runner_path/build.zig+5-2
...@@ -1,14 +1,17 @@...@@ -1,14 +1,17 @@
1const std = @import("std");1const std = @import("std");
22
3pub const requires_stage2 = true;
4
3pub fn build(b: *std.Build) void {5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test the program");
7 b.default_step = test_step;
8
4 const test_exe = b.addTest(.{9 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "test.zig" },10 .root_source_file = .{ .path = "test.zig" },
6 .kind = .test_exe,
7 });11 });
8 test_exe.test_runner = "test_runner.zig";12 test_exe.test_runner = "test_runner.zig";
913
10 const test_run = test_exe.run();14 const test_run = test_exe.run();
1115
12 const test_step = b.step("test", "Test the program");
13 test_step.dependOn(&test_run.step);16 test_step.dependOn(&test_run.step);
14}17}
test/standalone/test_runner_path/test_runner.zig+4-36
...@@ -1,51 +1,19 @@...@@ -1,51 +1,19 @@
1const std = @import("std");1const std = @import("std");
2const io = std.io;
3const builtin = @import("builtin");2const builtin = @import("builtin");
43
5pub const io_mode: io.Mode = builtin.test_io_mode;
6
7pub fn main() void {4pub fn main() void {
8 const test_fn_list = builtin.test_functions;
9 var ok_count: usize = 0;5 var ok_count: usize = 0;
10 var skip_count: usize = 0;6 var skip_count: usize = 0;
11 var fail_count: usize = 0;7 var fail_count: usize = 0;
128
13 var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined;9 for (builtin.test_functions) |test_fn| {
14 // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly10 if (test_fn.func()) |_| {
15 // ignores the alignment of the slice.
16 async_frame_buffer = &[_]u8{};
17
18 for (test_fn_list) |test_fn| {
19 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
20 .evented => blk: {
21 if (async_frame_buffer.len < size) {
22 std.heap.page_allocator.free(async_frame_buffer);
23 async_frame_buffer = std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size) catch @panic("out of memory");
24 }
25 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);
26 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});
27 },
28 .blocking => {
29 skip_count += 1;
30 continue;
31 },
32 } else test_fn.func();
33 if (result) |_| {
34 ok_count += 1;11 ok_count += 1;
35 } else |err| switch (err) {12 } else |err| switch (err) {
36 error.SkipZigTest => {13 error.SkipZigTest => skip_count += 1,
37 skip_count += 1;14 else => fail_count += 1,
38 },
39 else => {
40 fail_count += 1;
41 },
42 }15 }
43 }16 }
44 if (ok_count == test_fn_list.len) {
45 std.debug.print("All {d} tests passed.\n", .{ok_count});
46 } else {
47 std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count });
48 }
49 if (ok_count != 1 or skip_count != 1 or fail_count != 1) {17 if (ok_count != 1 or skip_count != 1 or fail_count != 1) {
50 std.process.exit(1);18 std.process.exit(1);
51 }19 }
test/standalone/use_alias/build.zig+7-3
...@@ -1,12 +1,16 @@...@@ -1,12 +1,16 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8
4 const main = b.addTest(.{9 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },10 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),11 .optimize = optimize,
7 });12 });
8 main.addIncludePath(".");13 main.addIncludePath(".");
914
10 const test_step = b.step("test", "Test it");15 test_step.dependOn(&main.run().step);
11 test_step.dependOn(&main.step);
12}16}
test/standalone/windows_spawn/build.zig+13-3
...@@ -1,23 +1,33 @@...@@ -1,23 +1,33 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
23
3pub fn build(b: *std.Build) void {4pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});5 const test_step = b.step("test", "Test it");
6 b.default_step = test_step;
7
8 const optimize: std.builtin.OptimizeMode = .Debug;
9 const target: std.zig.CrossTarget = .{};
10
11 if (builtin.os.tag != .windows) return;
512
6 const hello = b.addExecutable(.{13 const hello = b.addExecutable(.{
7 .name = "hello",14 .name = "hello",
8 .root_source_file = .{ .path = "hello.zig" },15 .root_source_file = .{ .path = "hello.zig" },
9 .optimize = optimize,16 .optimize = optimize,
17 .target = target,
10 });18 });
1119
12 const main = b.addExecutable(.{20 const main = b.addExecutable(.{
13 .name = "main",21 .name = "main",
14 .root_source_file = .{ .path = "main.zig" },22 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,23 .optimize = optimize,
24 .target = target,
16 });25 });
1726
18 const run = main.run();27 const run = b.addRunArtifact(main);
19 run.addArtifactArg(hello);28 run.addArtifactArg(hello);
29 run.expectExitCode(0);
30 run.skip_foreign_checks = true;
2031
21 const test_step = b.step("test", "Test it");
22 test_step.dependOn(&run.step);32 test_step.dependOn(&run.step);
23}33}
test/tests.zig+449-745
...@@ -1,16 +1,9 @@...@@ -1,16 +1,9 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const debug = std.debug;3const assert = std.debug.assert;
4const CrossTarget = std.zig.CrossTarget;4const CrossTarget = std.zig.CrossTarget;
5const io = std.io;
6const fs = std.fs;
7const mem = std.mem;5const mem = std.mem;
8const fmt = std.fmt;
9const ArrayList = std.ArrayList;
10const OptimizeMode = std.builtin.OptimizeMode;6const OptimizeMode = std.builtin.OptimizeMode;
11const CompileStep = std.Build.CompileStep;
12const Allocator = mem.Allocator;
13const ExecError = std.Build.ExecError;
14const Step = std.Build.Step;7const Step = std.Build.Step;
158
16// Cases9// Cases
...@@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig");...@@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig");
20const assemble_and_link = @import("assemble_and_link.zig");13const assemble_and_link = @import("assemble_and_link.zig");
21const translate_c = @import("translate_c.zig");14const translate_c = @import("translate_c.zig");
22const run_translated_c = @import("run_translated_c.zig");15const run_translated_c = @import("run_translated_c.zig");
23const gen_h = @import("gen_h.zig");
24const link = @import("link.zig");16const link = @import("link.zig");
2517
26// Implementations18// Implementations
27pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext;19pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext;
28pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext;20pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext;
29pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext;21pub const CompareOutputContext = @import("src/CompareOutput.zig");
22pub const StackTracesContext = @import("src/StackTrace.zig");
3023
31const TestTarget = struct {24const TestTarget = struct {
32 target: CrossTarget = @as(CrossTarget, .{}),25 target: CrossTarget = @as(CrossTarget, .{}),
...@@ -460,10 +453,71 @@ const test_targets = blk: {...@@ -460,10 +453,71 @@ const test_targets = blk: {
460 };453 };
461};454};
462455
463const max_stdout_size = 1 * 1024 * 1024; // 1 MB456const c_abi_targets = [_]CrossTarget{
457 .{},
458 .{
459 .cpu_arch = .x86_64,
460 .os_tag = .linux,
461 .abi = .musl,
462 },
463 .{
464 .cpu_arch = .x86,
465 .os_tag = .linux,
466 .abi = .musl,
467 },
468 .{
469 .cpu_arch = .aarch64,
470 .os_tag = .linux,
471 .abi = .musl,
472 },
473 .{
474 .cpu_arch = .arm,
475 .os_tag = .linux,
476 .abi = .musleabihf,
477 },
478 .{
479 .cpu_arch = .mips,
480 .os_tag = .linux,
481 .abi = .musl,
482 },
483 .{
484 .cpu_arch = .riscv64,
485 .os_tag = .linux,
486 .abi = .musl,
487 },
488 .{
489 .cpu_arch = .wasm32,
490 .os_tag = .wasi,
491 .abi = .musl,
492 },
493 .{
494 .cpu_arch = .powerpc,
495 .os_tag = .linux,
496 .abi = .musl,
497 },
498 .{
499 .cpu_arch = .powerpc64le,
500 .os_tag = .linux,
501 .abi = .musl,
502 },
503 .{
504 .cpu_arch = .x86,
505 .os_tag = .windows,
506 .abi = .gnu,
507 },
508 .{
509 .cpu_arch = .x86_64,
510 .os_tag = .windows,
511 .abi = .gnu,
512 },
513};
464514
465pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {515pub fn addCompareOutputTests(
466 const cases = b.allocator.create(CompareOutputContext) catch unreachable;516 b: *std.Build,
517 test_filter: ?[]const u8,
518 optimize_modes: []const OptimizeMode,
519) *Step {
520 const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM");
467 cases.* = CompareOutputContext{521 cases.* = CompareOutputContext{
468 .b = b,522 .b = b,
469 .step = b.step("test-compare-output", "Run the compare output tests"),523 .step = b.step("test-compare-output", "Run the compare output tests"),
...@@ -477,14 +531,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m...@@ -477,14 +531,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m
477 return cases.step;531 return cases.step;
478}532}
479533
480pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {534pub fn addStackTraceTests(
481 const cases = b.allocator.create(StackTracesContext) catch unreachable;535 b: *std.Build,
482 cases.* = StackTracesContext{536 test_filter: ?[]const u8,
537 optimize_modes: []const OptimizeMode,
538) *Step {
539 const check_exe = b.addExecutable(.{
540 .name = "check-stack-trace",
541 .root_source_file = .{ .path = "test/src/check-stack-trace.zig" },
542 .target = .{},
543 .optimize = .Debug,
544 });
545
546 const cases = b.allocator.create(StackTracesContext) catch @panic("OOM");
547 cases.* = .{
483 .b = b,548 .b = b,
484 .step = b.step("test-stack-traces", "Run the stack trace tests"),549 .step = b.step("test-stack-traces", "Run the stack trace tests"),
485 .test_index = 0,550 .test_index = 0,
486 .test_filter = test_filter,551 .test_filter = test_filter,
487 .optimize_modes = optimize_modes,552 .optimize_modes = optimize_modes,
553 .check_exe = check_exe,
488 };554 };
489555
490 stack_traces.addCases(cases);556 stack_traces.addCases(cases);
...@@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode...@@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode
494560
495pub fn addStandaloneTests(561pub fn addStandaloneTests(
496 b: *std.Build,562 b: *std.Build,
497 test_filter: ?[]const u8,
498 optimize_modes: []const OptimizeMode,563 optimize_modes: []const OptimizeMode,
499 skip_non_native: bool,
500 enable_macos_sdk: bool,564 enable_macos_sdk: bool,
501 target: std.zig.CrossTarget,
502 omit_stage2: bool,565 omit_stage2: bool,
503 enable_darling: bool,
504 enable_qemu: bool,
505 enable_rosetta: bool,
506 enable_wasmtime: bool,
507 enable_wine: bool,
508 enable_symlinks_windows: bool,566 enable_symlinks_windows: bool,
509) *Step {567) *Step {
510 const cases = b.allocator.create(StandaloneContext) catch unreachable;568 const step = b.step("test-standalone", "Run the standalone tests");
511 cases.* = StandaloneContext{569 const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows;
512 .b = b,570
513 .step = b.step("test-standalone", "Run the standalone tests"),571 for (standalone.simple_cases) |case| {
514 .test_index = 0,572 for (optimize_modes) |optimize| {
515 .test_filter = test_filter,573 if (!case.all_modes and optimize != .Debug) continue;
516 .optimize_modes = optimize_modes,574 if (case.os_filter) |os_tag| {
517 .skip_non_native = skip_non_native,575 if (os_tag != builtin.os.tag) continue;
518 .enable_macos_sdk = enable_macos_sdk,576 }
519 .target = target,
520 .omit_stage2 = omit_stage2,
521 .enable_darling = enable_darling,
522 .enable_qemu = enable_qemu,
523 .enable_rosetta = enable_rosetta,
524 .enable_wasmtime = enable_wasmtime,
525 .enable_wine = enable_wine,
526 .enable_symlinks_windows = enable_symlinks_windows,
527 };
528577
529 standalone.addCases(cases);578 if (case.is_exe) {
579 const exe = b.addExecutable(.{
580 .name = std.fs.path.stem(case.src_path),
581 .root_source_file = .{ .path = case.src_path },
582 .optimize = optimize,
583 .target = case.target,
584 });
585 if (case.link_libc) exe.linkLibC();
530586
531 return cases.step;587 step.dependOn(&exe.step);
588 }
589
590 if (case.is_test) {
591 const exe = b.addTest(.{
592 .name = std.fs.path.stem(case.src_path),
593 .root_source_file = .{ .path = case.src_path },
594 .optimize = optimize,
595 .target = case.target,
596 });
597 if (case.link_libc) exe.linkLibC();
598
599 step.dependOn(&exe.run().step);
600 }
601 }
602 }
603
604 inline for (standalone.build_cases) |case| {
605 const requires_stage2 = @hasDecl(case.import, "requires_stage2") and
606 case.import.requires_stage2;
607 const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and
608 case.import.requires_symlinks;
609 const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and
610 case.import.requires_macos_sdk;
611 const bad =
612 (requires_stage2 and omit_stage2) or
613 (requires_symlinks and omit_symlinks) or
614 (requires_macos_sdk and !enable_macos_sdk);
615 if (!bad) {
616 const dep = b.anonymousDependency(case.build_root, case.import, .{});
617 const dep_step = dep.builder.default_step;
618 assert(mem.startsWith(u8, dep.builder.dep_prefix, "test."));
619 const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..];
620 dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name });
621 step.dependOn(dep_step);
622 }
623 }
624
625 return step;
532}626}
533627
534pub fn addLinkTests(628pub fn addLinkTests(
535 b: *std.Build,629 b: *std.Build,
536 test_filter: ?[]const u8,
537 optimize_modes: []const OptimizeMode,
538 enable_macos_sdk: bool,630 enable_macos_sdk: bool,
539 omit_stage2: bool,631 omit_stage2: bool,
540 enable_symlinks_windows: bool,632 enable_symlinks_windows: bool,
541) *Step {633) *Step {
542 const cases = b.allocator.create(StandaloneContext) catch unreachable;634 const step = b.step("test-link", "Run the linker tests");
543 cases.* = StandaloneContext{635 const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows;
544 .b = b,636
545 .step = b.step("test-link", "Run the linker tests"),637 inline for (link.cases) |case| {
546 .test_index = 0,638 const requires_stage2 = @hasDecl(case.import, "requires_stage2") and
547 .test_filter = test_filter,639 case.import.requires_stage2;
548 .optimize_modes = optimize_modes,640 const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and
549 .skip_non_native = true,641 case.import.requires_symlinks;
550 .enable_macos_sdk = enable_macos_sdk,642 const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and
551 .target = .{},643 case.import.requires_macos_sdk;
552 .omit_stage2 = omit_stage2,644 const bad =
553 .enable_symlinks_windows = enable_symlinks_windows,645 (requires_stage2 and omit_stage2) or
554 };646 (requires_symlinks and omit_symlinks) or
555 link.addCases(cases);647 (requires_macos_sdk and !enable_macos_sdk);
556 return cases.step;648 if (!bad) {
649 const dep = b.anonymousDependency(case.build_root, case.import, .{});
650 const dep_step = dep.builder.default_step;
651 assert(mem.startsWith(u8, dep.builder.dep_prefix, "test."));
652 const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..];
653 dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name });
654 step.dependOn(dep_step);
655 }
656 }
657
658 return step;
557}659}
558660
559pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {661pub fn addCliTests(b: *std.Build) *Step {
560 _ = test_filter;
561 _ = optimize_modes;
562 const step = b.step("test-cli", "Test the command line interface");662 const step = b.step("test-cli", "Test the command line interface");
663 const s = std.fs.path.sep_str;
664
665 {
666
667 // Test `zig init-lib`.
668 const tmp_path = b.makeTempPath();
669 const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" });
670 init_lib.cwd = tmp_path;
671 init_lib.setName("zig init-lib");
672 init_lib.expectStdOutEqual("");
673 init_lib.expectStdErrEqual("info: Created build.zig\n" ++
674 "info: Created src" ++ s ++ "main.zig\n" ++
675 "info: Next, try `zig build --help` or `zig build test`\n");
676
677 const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" });
678 run_test.cwd = tmp_path;
679 run_test.setName("zig build test");
680 run_test.expectStdOutEqual("");
681 run_test.step.dependOn(&init_lib.step);
682
683 const cleanup = b.addRemoveDirTree(tmp_path);
684 cleanup.step.dependOn(&run_test.step);
685
686 step.dependOn(&cleanup.step);
687 }
563688
564 const exe = b.addExecutable(.{689 {
565 .name = "test-cli",690 // Test `zig init-exe`.
566 .root_source_file = .{ .path = "test/cli.zig" },691 const tmp_path = b.makeTempPath();
567 .target = .{},692 const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" });
568 .optimize = .Debug,693 init_exe.cwd = tmp_path;
569 });694 init_exe.setName("zig init-exe");
570 const run_cmd = exe.run();695 init_exe.expectStdOutEqual("");
571 run_cmd.addArgs(&[_][]const u8{696 init_exe.expectStdErrEqual("info: Created build.zig\n" ++
572 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,697 "info: Created src" ++ s ++ "main.zig\n" ++
573 b.pathFromRoot(b.cache_root.path orelse "."),698 "info: Next, try `zig build --help` or `zig build run`\n");
574 });699
700 // Test missing output path.
701 const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe";
702 const ok_src_arg = "src" ++ s ++ "main.zig";
703 const expected = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n";
704 const run_bad = b.addSystemCommand(&.{ b.zig_exe, "build-exe", ok_src_arg, bad_out_arg });
705 run_bad.setName("zig build-exe error message for bad -femit-bin arg");
706 run_bad.expectExitCode(1);
707 run_bad.expectStdErrEqual(expected);
708 run_bad.expectStdOutEqual("");
709 run_bad.step.dependOn(&init_exe.step);
710
711 const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" });
712 run_test.cwd = tmp_path;
713 run_test.setName("zig build test");
714 run_test.expectStdOutEqual("");
715 run_test.step.dependOn(&init_exe.step);
716
717 const run_run = b.addSystemCommand(&.{ b.zig_exe, "build", "run" });
718 run_run.cwd = tmp_path;
719 run_run.setName("zig build run");
720 run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n");
721 run_run.expectStdErrEqual("All your codebase are belong to us.\n");
722 run_run.step.dependOn(&init_exe.step);
723
724 const cleanup = b.addRemoveDirTree(tmp_path);
725 cleanup.step.dependOn(&run_test.step);
726 cleanup.step.dependOn(&run_run.step);
727 cleanup.step.dependOn(&run_bad.step);
728
729 step.dependOn(&cleanup.step);
730 }
731
732 // Test Godbolt API
733 if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
734 const tmp_path = b.makeTempPath();
735
736 const writefile = b.addWriteFile("example.zig",
737 \\// Type your code here, or load an example.
738 \\export fn square(num: i32) i32 {
739 \\ return num * num;
740 \\}
741 \\extern fn zig_panic() noreturn;
742 \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn {
743 \\ _ = msg;
744 \\ _ = error_return_trace;
745 \\ zig_panic();
746 \\}
747 );
748
749 // This is intended to be the exact CLI usage used by godbolt.org.
750 const run = b.addSystemCommand(&.{
751 b.zig_exe, "build-obj",
752 "--cache-dir", tmp_path,
753 "--name", "example",
754 "-fno-emit-bin", "-fno-emit-h",
755 "-fstrip", "-OReleaseFast",
756 });
757 run.addFileSourceArg(writefile.getFileSource("example.zig").?);
758 const example_s = run.addPrefixedOutputFileArg("-femit-asm=", "example.s");
759
760 const checkfile = b.addCheckFile(example_s, .{
761 .expected_matches = &.{
762 "square:",
763 "mov\teax, edi",
764 "imul\teax, edi",
765 },
766 });
767 checkfile.setName("check godbolt.org CLI usage generating valid asm");
768
769 const cleanup = b.addRemoveDirTree(tmp_path);
770 cleanup.step.dependOn(&checkfile.step);
771
772 step.dependOn(&cleanup.step);
773 }
774
775 {
776 // Test `zig fmt`.
777 // This test must use a temporary directory rather than a cache
778 // directory because this test will be mutating the files. The cache
779 // system relies on cache directories being mutated only by their
780 // owners.
781 const tmp_path = b.makeTempPath();
782 const unformatted_code = " // no reason for indent";
783
784 var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled");
785 defer dir.close();
786 dir.writeFile("fmt1.zig", unformatted_code) catch @panic("unhandled");
787 dir.writeFile("fmt2.zig", unformatted_code) catch @panic("unhandled");
788
789 // Test zig fmt affecting only the appropriate files.
790 const run1 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "fmt1.zig" });
791 run1.setName("run zig fmt one file");
792 run1.cwd = tmp_path;
793 run1.has_side_effects = true;
794 // stdout should be file path + \n
795 run1.expectStdOutEqual("fmt1.zig\n");
796
797 // running it on the dir, only the new file should be changed
798 const run2 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." });
799 run2.setName("run zig fmt the directory");
800 run2.cwd = tmp_path;
801 run2.has_side_effects = true;
802 run2.expectStdOutEqual("." ++ s ++ "fmt2.zig\n");
803 run2.step.dependOn(&run1.step);
804
805 // both files have been formatted, nothing should change now
806 const run3 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." });
807 run3.setName("run zig fmt with nothing to do");
808 run3.cwd = tmp_path;
809 run3.has_side_effects = true;
810 run3.expectStdOutEqual("");
811 run3.step.dependOn(&run2.step);
812
813 const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
814 const fmt4_path = std.fs.path.join(b.allocator, &.{ tmp_path, "fmt4.zig" }) catch @panic("OOM");
815 const write4 = b.addWriteFiles();
816 write4.addBytesToSource(unformatted_code_utf16, fmt4_path);
817 write4.step.dependOn(&run3.step);
818
819 // Test `zig fmt` handling UTF-16 decoding.
820 const run4 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." });
821 run4.setName("run zig fmt convert UTF-16 to UTF-8");
822 run4.cwd = tmp_path;
823 run4.has_side_effects = true;
824 run4.expectStdOutEqual("." ++ s ++ "fmt4.zig\n");
825 run4.step.dependOn(&write4.step);
826
827 // TODO change this to an exact match
828 const check4 = b.addCheckFile(.{ .path = fmt4_path }, .{
829 .expected_matches = &.{
830 "// no reason",
831 },
832 });
833 check4.step.dependOn(&run4.step);
834
835 const cleanup = b.addRemoveDirTree(tmp_path);
836 cleanup.step.dependOn(&check4.step);
837
838 step.dependOn(&cleanup.step);
839 }
840
841 {
842 // TODO this should move to become a CLI test rather than standalone
843 // cases.addBuildFile("test/standalone/options/build.zig", .{
844 // .extra_argv = &.{
845 // "-Dbool_true",
846 // "-Dbool_false=false",
847 // "-Dint=1234",
848 // "-De=two",
849 // "-Dstring=hello",
850 // },
851 // });
852 }
575853
576 step.dependOn(&run_cmd.step);
577 return step;854 return step;
578}855}
579856
580pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {857pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
581 const cases = b.allocator.create(CompareOutputContext) catch unreachable;858 const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM");
582 cases.* = CompareOutputContext{859 cases.* = CompareOutputContext{
583 .b = b,860 .b = b,
584 .step = b.step("test-asm-link", "Run the assemble and link tests"),861 .step = b.step("test-asm-link", "Run the assemble and link tests"),
...@@ -593,7 +870,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize...@@ -593,7 +870,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize
593}870}
594871
595pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {872pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
596 const cases = b.allocator.create(TranslateCContext) catch unreachable;873 const cases = b.allocator.create(TranslateCContext) catch @panic("OOM");
597 cases.* = TranslateCContext{874 cases.* = TranslateCContext{
598 .b = b,875 .b = b,
599 .step = b.step("test-translate-c", "Run the C translation tests"),876 .step = b.step("test-translate-c", "Run the C translation tests"),
...@@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests(...@@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests(
611 test_filter: ?[]const u8,888 test_filter: ?[]const u8,
612 target: std.zig.CrossTarget,889 target: std.zig.CrossTarget,
613) *Step {890) *Step {
614 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;891 const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM");
615 cases.* = .{892 cases.* = .{
616 .b = b,893 .b = b,
617 .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"),894 .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"),
...@@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests(...@@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests(
625 return cases.step;902 return cases.step;
626}903}
627904
628pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {905const ModuleTestOptions = struct {
629 const cases = b.allocator.create(GenHContext) catch unreachable;
630 cases.* = GenHContext{
631 .b = b,
632 .step = b.step("test-gen-h", "Run the C header file generation tests"),
633 .test_index = 0,
634 .test_filter = test_filter,
635 };
636
637 gen_h.addCases(cases);
638
639 return cases.step;
640}
641
642pub fn addPkgTests(
643 b: *std.Build,
644 test_filter: ?[]const u8,906 test_filter: ?[]const u8,
645 root_src: []const u8,907 root_src: []const u8,
646 name: []const u8,908 name: []const u8,
...@@ -651,14 +913,17 @@ pub fn addPkgTests(...@@ -651,14 +913,17 @@ pub fn addPkgTests(
651 skip_libc: bool,913 skip_libc: bool,
652 skip_stage1: bool,914 skip_stage1: bool,
653 skip_stage2: bool,915 skip_stage2: bool,
654) *Step {916 max_rss: usize = 0,
655 const step = b.step(b.fmt("test-{s}", .{name}), desc);917};
918
919pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
920 const step = b.step(b.fmt("test-{s}", .{options.name}), options.desc);
656921
657 for (test_targets) |test_target| {922 for (test_targets) |test_target| {
658 if (skip_non_native and !test_target.target.isNative())923 if (options.skip_non_native and !test_target.target.isNative())
659 continue;924 continue;
660925
661 if (skip_libc and test_target.link_libc)926 if (options.skip_libc and test_target.link_libc)
662 continue;927 continue;
663928
664 if (test_target.link_libc and test_target.target.getOs().requiresLibC()) {929 if (test_target.link_libc and test_target.target.getOs().requiresLibC()) {
...@@ -666,7 +931,7 @@ pub fn addPkgTests(...@@ -666,7 +931,7 @@ pub fn addPkgTests(
666 continue;931 continue;
667 }932 }
668933
669 if (skip_single_threaded and test_target.single_threaded)934 if (options.skip_single_threaded and test_target.single_threaded)
670 continue;935 continue;
671936
672 if (test_target.disable_native and937 if (test_target.disable_native and
...@@ -677,12 +942,12 @@ pub fn addPkgTests(...@@ -677,12 +942,12 @@ pub fn addPkgTests(
677 }942 }
678943
679 if (test_target.backend) |backend| switch (backend) {944 if (test_target.backend) |backend| switch (backend) {
680 .stage1 => if (skip_stage1) continue,945 .stage1 => if (options.skip_stage1) continue,
681 .stage2_llvm => {},946 .stage2_llvm => {},
682 else => if (skip_stage2) continue,947 else => if (options.skip_stage2) continue,
683 };948 };
684949
685 const want_this_mode = for (optimize_modes) |m| {950 const want_this_mode = for (options.optimize_modes) |m| {
686 if (m == test_target.optimize_mode) break true;951 if (m == test_target.optimize_mode) break true;
687 } else false;952 } else false;
688 if (!want_this_mode) continue;953 if (!want_this_mode) continue;
...@@ -694,25 +959,24 @@ pub fn addPkgTests(...@@ -694,25 +959,24 @@ pub fn addPkgTests(
694 else959 else
695 "bare";960 "bare";
696961
697 const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable;962 const triple_prefix = test_target.target.zigTriple(b.allocator) catch @panic("OOM");
963
964 // wasm32-wasi builds need more RAM, idk why
965 const max_rss = if (test_target.target.getOs().tag == .wasi)
966 options.max_rss * 2
967 else
968 options.max_rss;
698969
699 const these_tests = b.addTest(.{970 const these_tests = b.addTest(.{
700 .root_source_file = .{ .path = root_src },971 .root_source_file = .{ .path = options.root_src },
701 .optimize = test_target.optimize_mode,972 .optimize = test_target.optimize_mode,
702 .target = test_target.target,973 .target = test_target.target,
974 .max_rss = max_rss,
703 });975 });
704 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";976 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
705 const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";977 const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";
706 these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{
707 name,
708 triple_prefix,
709 @tagName(test_target.optimize_mode),
710 libc_prefix,
711 single_threaded_txt,
712 backend_txt,
713 }));
714 these_tests.single_threaded = test_target.single_threaded;978 these_tests.single_threaded = test_target.single_threaded;
715 these_tests.setFilter(test_filter);979 these_tests.setFilter(options.test_filter);
716 if (test_target.link_libc) {980 if (test_target.link_libc) {
717 these_tests.linkSystemLibrary("c");981 these_tests.linkSystemLibrary("c");
718 }982 }
...@@ -736,654 +1000,94 @@ pub fn addPkgTests(...@@ -736,654 +1000,94 @@ pub fn addPkgTests(
736 },1000 },
737 };1001 };
7381002
739 step.dependOn(&these_tests.step);1003 const run = these_tests.run();
1004 run.skip_foreign_checks = true;
1005 run.setName(b.fmt("run test {s}-{s}-{s}-{s}-{s}-{s}", .{
1006 options.name,
1007 triple_prefix,
1008 @tagName(test_target.optimize_mode),
1009 libc_prefix,
1010 single_threaded_txt,
1011 backend_txt,
1012 }));
1013
1014 step.dependOn(&run.step);
740 }1015 }
741 return step;1016 return step;
742}1017}
7431018
744pub const StackTracesContext = struct {1019pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
745 b: *std.Build,1020 const step = b.step("test-c-abi", "Run the C ABI tests");
746 step: *Step,
747 test_index: usize,
748 test_filter: ?[]const u8,
749 optimize_modes: []const OptimizeMode,
750
751 const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8;
752
753 pub fn addCase(self: *StackTracesContext, config: anytype) void {
754 if (@hasField(@TypeOf(config), "exclude")) {
755 if (config.exclude.exclude()) return;
756 }
757 if (@hasField(@TypeOf(config), "exclude_arch")) {
758 const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch;
759 for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return;
760 }
761 if (@hasField(@TypeOf(config), "exclude_os")) {
762 const exclude_os: []const std.Target.Os.Tag = &config.exclude_os;
763 for (exclude_os) |os| if (os == builtin.os.tag) return;
764 }
765 for (self.optimize_modes) |optimize_mode| {
766 switch (optimize_mode) {
767 .Debug => {
768 if (@hasField(@TypeOf(config), "Debug")) {
769 self.addExpect(config.name, config.source, optimize_mode, config.Debug);
770 }
771 },
772 .ReleaseSafe => {
773 if (@hasField(@TypeOf(config), "ReleaseSafe")) {
774 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe);
775 }
776 },
777 .ReleaseFast => {
778 if (@hasField(@TypeOf(config), "ReleaseFast")) {
779 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast);
780 }
781 },
782 .ReleaseSmall => {
783 if (@hasField(@TypeOf(config), "ReleaseSmall")) {
784 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall);
785 }
786 },
787 }
788 }
789 }
790
791 fn addExpect(
792 self: *StackTracesContext,
793 name: []const u8,
794 source: []const u8,
795 optimize_mode: OptimizeMode,
796 mode_config: anytype,
797 ) void {
798 if (@hasField(@TypeOf(mode_config), "exclude")) {
799 if (mode_config.exclude.exclude()) return;
800 }
801 if (@hasField(@TypeOf(mode_config), "exclude_arch")) {
802 const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch;
803 for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return;
804 }
805 if (@hasField(@TypeOf(mode_config), "exclude_os")) {
806 const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os;
807 for (exclude_os) |os| if (os == builtin.os.tag) return;
808 }
809
810 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
811 "stack-trace",
812 name,
813 @tagName(optimize_mode),
814 }) catch unreachable;
815 if (self.test_filter) |filter| {
816 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
817 }
818
819 const b = self.b;
820 const src_basename = "source.zig";
821 const write_src = b.addWriteFile(src_basename, source);
822 const exe = b.addExecutable(.{
823 .name = "test",
824 .root_source_file = write_src.getFileSource(src_basename).?,
825 .optimize = optimize_mode,
826 .target = .{},
827 });
828
829 const run_and_compare = RunAndCompareStep.create(
830 self,
831 exe,
832 annotated_case_name,
833 optimize_mode,
834 mode_config.expect,
835 );
836
837 self.step.dependOn(&run_and_compare.step);
838 }
839
840 const RunAndCompareStep = struct {
841 pub const base_id = .custom;
842
843 step: Step,
844 context: *StackTracesContext,
845 exe: *CompileStep,
846 name: []const u8,
847 optimize_mode: OptimizeMode,
848 expect_output: []const u8,
849 test_index: usize,
850
851 pub fn create(
852 context: *StackTracesContext,
853 exe: *CompileStep,
854 name: []const u8,
855 optimize_mode: OptimizeMode,
856 expect_output: []const u8,
857 ) *RunAndCompareStep {
858 const allocator = context.b.allocator;
859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
860 ptr.* = RunAndCompareStep{
861 .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
862 .context = context,
863 .exe = exe,
864 .name = name,
865 .optimize_mode = optimize_mode,
866 .expect_output = expect_output,
867 .test_index = context.test_index,
868 };
869 ptr.step.dependOn(&exe.step);
870 context.test_index += 1;
871 return ptr;
872 }
873
874 fn make(step: *Step) !void {
875 const self = @fieldParentPtr(RunAndCompareStep, "step", step);
876 const b = self.context.b;
877
878 const full_exe_path = self.exe.getOutputSource().getPath(b);
879 var args = ArrayList([]const u8).init(b.allocator);
880 defer args.deinit();
881 args.append(full_exe_path) catch unreachable;
882
883 std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name });
884
885 if (!std.process.can_spawn) {
886 const cmd = try std.mem.join(b.allocator, " ", args.items);
887 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
888 b.allocator.free(cmd);
889 return ExecError.ExecNotSupported;
890 }
891
892 var child = std.ChildProcess.init(args.items, b.allocator);
893 child.stdin_behavior = .Ignore;
894 child.stdout_behavior = .Pipe;
895 child.stderr_behavior = .Pipe;
896 child.env_map = b.env_map;
897
898 if (b.verbose) {
899 printInvocation(args.items);
900 }
901 child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) });
902
903 const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
904 defer b.allocator.free(stdout);
905 const stderrFull = child.stderr.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
906 defer b.allocator.free(stderrFull);
907 var stderr = stderrFull;
908
909 const term = child.wait() catch |err| {
910 debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) });
911 };
912
913 switch (term) {
914 .Exited => |code| {
915 const expect_code: u32 = 1;
916 if (code != expect_code) {
917 std.debug.print("Process {s} exited with error code {d} but expected code {d}\n", .{
918 full_exe_path,
919 code,
920 expect_code,
921 });
922 printInvocation(args.items);
923 return error.TestFailed;
924 }
925 },
926 .Signal => |signum| {
927 std.debug.print("Process {s} terminated on signal {d}\n", .{ full_exe_path, signum });
928 printInvocation(args.items);
929 return error.TestFailed;
930 },
931 .Stopped => |signum| {
932 std.debug.print("Process {s} stopped on signal {d}\n", .{ full_exe_path, signum });
933 printInvocation(args.items);
934 return error.TestFailed;
935 },
936 .Unknown => |code| {
937 std.debug.print("Process {s} terminated unexpectedly with error code {d}\n", .{ full_exe_path, code });
938 printInvocation(args.items);
939 return error.TestFailed;
940 },
941 }
942
943 // process result
944 // - keep only basename of source file path
945 // - replace address with symbolic string
946 // - replace function name with symbolic string when optimize_mode != .Debug
947 // - skip empty lines
948 const got: []const u8 = got_result: {
949 var buf = ArrayList(u8).init(b.allocator);
950 defer buf.deinit();
951 if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1];
952 var it = mem.split(u8, stderr, "\n");
953 process_lines: while (it.next()) |line| {
954 if (line.len == 0) continue;
955
956 // offset search past `[drive]:` on windows
957 var pos: usize = if (builtin.os.tag == .windows) 2 else 0;
958 // locate delims/anchor
959 const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" };
960 var marks = [_]usize{0} ** delims.len;
961 for (delims, 0..) |delim, i| {
962 marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse {
963 // unexpected pattern: emit raw line and cont
964 try buf.appendSlice(line);
965 try buf.appendSlice("\n");
966 continue :process_lines;
967 };
968 pos = marks[i] + delim.len;
969 }
970 // locate source basename
971 pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse {
972 // unexpected pattern: emit raw line and cont
973 try buf.appendSlice(line);
974 try buf.appendSlice("\n");
975 continue :process_lines;
976 };
977 // end processing if source basename changes
978 if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break;
979 // emit substituted line
980 try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);
981 try buf.appendSlice(" [address]");
982 if (self.optimize_mode == .Debug) {
983 // On certain platforms (windows) or possibly depending on how we choose to link main
984 // the object file extension may be present so we simply strip any extension.
985 if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| {
986 try buf.appendSlice(line[marks[3] .. marks[4] + idot]);
987 try buf.appendSlice(line[marks[5]..]);
988 } else {
989 try buf.appendSlice(line[marks[3]..]);
990 }
991 } else {
992 try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]);
993 try buf.appendSlice("[function]");
994 }
995 try buf.appendSlice("\n");
996 }
997 break :got_result try buf.toOwnedSlice();
998 };
999
1000 if (!mem.eql(u8, self.expect_output, got)) {
1001 std.debug.print(
1002 \\
1003 \\========= Expected this output: =========
1004 \\{s}
1005 \\================================================
1006 \\{s}
1007 \\
1008 , .{ self.expect_output, got });
1009 return error.TestFailed;
1010 }
1011 std.debug.print("OK\n", .{});
1012 }
1013 };
1014};
1015
1016pub const StandaloneContext = struct {
1017 b: *std.Build,
1018 step: *Step,
1019 test_index: usize,
1020 test_filter: ?[]const u8,
1021 optimize_modes: []const OptimizeMode,
1022 skip_non_native: bool,
1023 enable_macos_sdk: bool,
1024 target: std.zig.CrossTarget,
1025 omit_stage2: bool,
1026 enable_darling: bool = false,
1027 enable_qemu: bool = false,
1028 enable_rosetta: bool = false,
1029 enable_wasmtime: bool = false,
1030 enable_wine: bool = false,
1031 enable_symlinks_windows: bool,
1032
1033 pub fn addC(self: *StandaloneContext, root_src: []const u8) void {
1034 self.addAllArgs(root_src, true);
1035 }
1036
1037 pub fn add(self: *StandaloneContext, root_src: []const u8) void {
1038 self.addAllArgs(root_src, false);
1039 }
1040
1041 pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8, features: struct {
1042 build_modes: bool = false,
1043 cross_targets: bool = false,
1044 requires_macos_sdk: bool = false,
1045 requires_stage2: bool = false,
1046 use_emulation: bool = false,
1047 requires_symlinks: bool = false,
1048 extra_argv: []const []const u8 = &.{},
1049 }) void {
1050 const b = self.b;
1051
1052 if (features.requires_macos_sdk and !self.enable_macos_sdk) return;
1053 if (features.requires_stage2 and self.omit_stage2) return;
1054 if (features.requires_symlinks and !self.enable_symlinks_windows and builtin.os.tag == .windows) return;
1055
1056 const annotated_case_name = b.fmt("build {s}", .{build_file});
1057 if (self.test_filter) |filter| {
1058 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
1059 }
1060
1061 var zig_args = ArrayList([]const u8).init(b.allocator);
1062 const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable;
1063 zig_args.append(rel_zig_exe) catch unreachable;
1064 zig_args.append("build") catch unreachable;
1065
1066 zig_args.append("--build-file") catch unreachable;
1067 zig_args.append(b.pathFromRoot(build_file)) catch unreachable;
1068
1069 zig_args.appendSlice(features.extra_argv) catch unreachable;
1070
1071 zig_args.append("test") catch unreachable;
1072
1073 if (b.verbose) {
1074 zig_args.append("--verbose") catch unreachable;
1075 }
1076
1077 if (features.cross_targets and !self.target.isNative()) {
1078 const target_triple = self.target.zigTriple(b.allocator) catch unreachable;
1079 const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable;
1080 zig_args.append(target_arg) catch unreachable;
1081 }
10821021
1083 if (features.use_emulation) {1022 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };
1084 if (self.enable_darling) {
1085 zig_args.append("-fdarling") catch unreachable;
1086 }
1087 if (self.enable_qemu) {
1088 zig_args.append("-fqemu") catch unreachable;
1089 }
1090 if (self.enable_rosetta) {
1091 zig_args.append("-frosetta") catch unreachable;
1092 }
1093 if (self.enable_wasmtime) {
1094 zig_args.append("-fwasmtime") catch unreachable;
1095 }
1096 if (self.enable_wine) {
1097 zig_args.append("-fwine") catch unreachable;
1098 }
1099 }
11001023
1101 const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug};1024 for (optimize_modes) |optimize_mode| {
1102 for (optimize_modes) |optimize_mode| {1025 if (optimize_mode != .Debug and skip_release) continue;
1103 const arg = switch (optimize_mode) {
1104 .Debug => "",
1105 .ReleaseFast => "-Doptimize=ReleaseFast",
1106 .ReleaseSafe => "-Doptimize=ReleaseSafe",
1107 .ReleaseSmall => "-Doptimize=ReleaseSmall",
1108 };
1109 const zig_args_base_len = zig_args.items.len;
1110 if (arg.len > 0)
1111 zig_args.append(arg) catch unreachable;
1112 defer zig_args.resize(zig_args_base_len) catch unreachable;
1113
1114 const run_cmd = b.addSystemCommand(zig_args.items);
1115 const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) });
1116 log_step.step.dependOn(&run_cmd.step);
1117
1118 self.step.dependOn(&log_step.step);
1119 }
1120 }
11211026
1122 pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void {1027 for (c_abi_targets) |c_abi_target| {
1123 const b = self.b;1028 if (skip_non_native and !c_abi_target.isNative()) continue;
11241029
1125 for (self.optimize_modes) |optimize| {1030 if (c_abi_target.isWindows() and c_abi_target.getCpuArch() == .aarch64) {
1126 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{1031 // https://github.com/ziglang/zig/issues/14908
1127 root_src,1032 continue;
1128 @tagName(optimize),
1129 }) catch unreachable;
1130 if (self.test_filter) |filter| {
1131 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
1132 }1033 }
11331034
1134 const exe = b.addExecutable(.{1035 const test_step = b.addTest(.{
1135 .name = "test",1036 .root_source_file = .{ .path = "test/c_abi/main.zig" },
1136 .root_source_file = .{ .path = root_src },1037 .optimize = optimize_mode,
1137 .optimize = optimize,1038 .target = c_abi_target,
1138 .target = .{},
1139 });1039 });
1140 if (link_libc) {1040 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {
1141 exe.linkSystemLibrary("c");1041 // TODO NativeTargetInfo insists on dynamically linking musl
1042 // for some reason?
1043 test_step.target_info.dynamic_linker.max_byte = null;
1142 }1044 }
11431045 test_step.linkLibC();
1144 const log_step = b.addLog("PASS {s}", .{annotated_case_name});1046 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});
1145 log_step.step.dependOn(&exe.step);1047
11461048 // test-c-abi should test both with LTO on and with LTO off. Only
1147 self.step.dependOn(&log_step.step);1049 // some combinations are passing currently:
1148 }1050 // https://github.com/ziglang/zig/issues/14908
1149 }1051 if (c_abi_target.isWindows()) {
1150};1052 test_step.want_lto = false;
1151
1152pub const GenHContext = struct {
1153 b: *std.Build,
1154 step: *Step,
1155 test_index: usize,
1156 test_filter: ?[]const u8,
1157
1158 const TestCase = struct {
1159 name: []const u8,
1160 sources: ArrayList(SourceFile),
1161 expected_lines: ArrayList([]const u8),
1162
1163 const SourceFile = struct {
1164 filename: []const u8,
1165 source: []const u8,
1166 };
1167
1168 pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void {
1169 self.sources.append(SourceFile{
1170 .filename = filename,
1171 .source = source,
1172 }) catch unreachable;
1173 }
1174
1175 pub fn addExpectedLine(self: *TestCase, text: []const u8) void {
1176 self.expected_lines.append(text) catch unreachable;
1177 }
1178 };
1179
1180 const GenHCmpOutputStep = struct {
1181 step: Step,
1182 context: *GenHContext,
1183 obj: *CompileStep,
1184 name: []const u8,
1185 test_index: usize,
1186 case: *const TestCase,
1187
1188 pub fn create(
1189 context: *GenHContext,
1190 obj: *CompileStep,
1191 name: []const u8,
1192 case: *const TestCase,
1193 ) *GenHCmpOutputStep {
1194 const allocator = context.b.allocator;
1195 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
1196 ptr.* = GenHCmpOutputStep{
1197 .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make),
1198 .context = context,
1199 .obj = obj,
1200 .name = name,
1201 .test_index = context.test_index,
1202 .case = case,
1203 };
1204 ptr.step.dependOn(&obj.step);
1205 context.test_index += 1;
1206 return ptr;
1207 }
1208
1209 fn make(step: *Step) !void {
1210 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
1211 const b = self.context.b;
1212
1213 std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name });
1214
1215 const full_h_path = self.obj.getOutputHPath();
1216 const actual_h = try io.readFileAlloc(b.allocator, full_h_path);
1217
1218 for (self.case.expected_lines.items) |expected_line| {
1219 if (mem.indexOf(u8, actual_h, expected_line) == null) {
1220 std.debug.print(
1221 \\
1222 \\========= Expected this output: ================
1223 \\{s}
1224 \\========= But found: ===========================
1225 \\{s}
1226 \\
1227 , .{ expected_line, actual_h });
1228 return error.TestFailed;
1229 }
1230 }1053 }
1231 std.debug.print("OK\n", .{});
1232 }
1233 };
1234
1235 pub fn create(
1236 self: *GenHContext,
1237 filename: []const u8,
1238 name: []const u8,
1239 source: []const u8,
1240 expected_lines: []const []const u8,
1241 ) *TestCase {
1242 const tc = self.b.allocator.create(TestCase) catch unreachable;
1243 tc.* = TestCase{
1244 .name = name,
1245 .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator),
1246 .expected_lines = ArrayList([]const u8).init(self.b.allocator),
1247 };
1248
1249 tc.addSourceFile(filename, source);
1250 var arg_i: usize = 0;
1251 while (arg_i < expected_lines.len) : (arg_i += 1) {
1252 tc.addExpectedLine(expected_lines[arg_i]);
1253 }
1254 return tc;
1255 }
1256
1257 pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: []const []const u8) void {
1258 const tc = self.create("test.zig", name, source, expected_lines);
1259 self.addCase(tc);
1260 }
12611054
1262 pub fn addCase(self: *GenHContext, case: *const TestCase) void {1055 const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM");
1263 const b = self.b;1056 test_step.setName(b.fmt("test-c-abi-{s}-{s} ", .{
1057 triple_prefix, @tagName(optimize_mode),
1058 }));
12641059
1265 const optimize_mode = std.builtin.OptimizeMode.Debug;1060 const run = test_step.run();
1266 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable;1061 run.skip_foreign_checks = true;
1267 if (self.test_filter) |filter| {1062 step.dependOn(&run.step);
1268 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
1269 }1063 }
1270
1271 const write_src = b.addWriteFiles();
1272 for (case.sources.items) |src_file| {
1273 write_src.add(src_file.filename, src_file.source);
1274 }
1275
1276 const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename);
1277 obj.setBuildMode(optimize_mode);
1278
1279 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);
1280
1281 self.step.dependOn(&cmp_h.step);
1282 }1064 }
1283};1065 return step;
1284
1285fn printInvocation(args: []const []const u8) void {
1286 for (args) |arg| {
1287 std.debug.print("{s} ", .{arg});
1288 }
1289 std.debug.print("\n", .{});
1290}1066}
12911067
1292const c_abi_targets = [_]CrossTarget{1068pub fn addCases(
1293 .{},1069 b: *std.Build,
1294 .{1070 parent_step: *Step,
1295 .cpu_arch = .x86_64,1071 opt_test_filter: ?[]const u8,
1296 .os_tag = .linux,1072 check_case_exe: *std.Build.CompileStep,
1297 .abi = .musl,1073) !void {
1298 },1074 const arena = b.allocator;
1299 .{1075 const gpa = b.allocator;
1300 .cpu_arch = .x86,1076
1301 .os_tag = .linux,1077 var cases = @import("src/Cases.zig").init(gpa, arena);
1302 .abi = .musl,1078
1303 },1079 var dir = try b.build_root.handle.openIterableDir("test/cases", .{});
1304 .{1080 defer dir.close();
1305 .cpu_arch = .aarch64,1081
1306 .os_tag = .linux,1082 cases.addFromDir(dir);
1307 .abi = .musl,1083 try @import("cases.zig").addCases(&cases);
1308 },1084
1309 .{1085 const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" });
1310 .cpu_arch = .arm,1086 cases.lowerToBuildSteps(
1311 .os_tag = .linux,1087 b,
1312 .abi = .musleabihf,1088 parent_step,
1313 },1089 opt_test_filter,
1314 .{1090 cases_dir_path,
1315 .cpu_arch = .mips,1091 check_case_exe,
1316 .os_tag = .linux,1092 );
1317 .abi = .musl,
1318 },
1319 .{
1320 .cpu_arch = .riscv64,
1321 .os_tag = .linux,
1322 .abi = .musl,
1323 },
1324 .{
1325 .cpu_arch = .wasm32,
1326 .os_tag = .wasi,
1327 .abi = .musl,
1328 },
1329 .{
1330 .cpu_arch = .powerpc,
1331 .os_tag = .linux,
1332 .abi = .musl,
1333 },
1334 .{
1335 .cpu_arch = .powerpc64le,
1336 .os_tag = .linux,
1337 .abi = .musl,
1338 },
1339 .{
1340 .cpu_arch = .x86,
1341 .os_tag = .windows,
1342 .abi = .gnu,
1343 },
1344 .{
1345 .cpu_arch = .x86_64,
1346 .os_tag = .windows,
1347 .abi = .gnu,
1348 },
1349};
1350
1351pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
1352 const step = b.step("test-c-abi", "Run the C ABI tests");
1353
1354 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };
1355
1356 for (optimize_modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |optimize_mode| for (c_abi_targets) |c_abi_target| {
1357 if (skip_non_native and !c_abi_target.isNative())
1358 continue;
1359
1360 const test_step = b.addTest(.{
1361 .root_source_file = .{ .path = "test/c_abi/main.zig" },
1362 .optimize = optimize_mode,
1363 .target = c_abi_target,
1364 });
1365 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {
1366 // TODO NativeTargetInfo insists on dynamically linking musl
1367 // for some reason?
1368 test_step.target_info.dynamic_linker.max_byte = null;
1369 }
1370 test_step.linkLibC();
1371 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});
1372
1373 if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) {
1374 // LTO currently incorrectly strips stdcall name-mangled functions
1375 // LLD crashes in LTO here when cross compiling for windows on linux
1376 test_step.want_lto = false;
1377 }
1378
1379 const triple_prefix = c_abi_target.zigTriple(b.allocator) catch unreachable;
1380 test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{
1381 "test-c-abi",
1382 triple_prefix,
1383 @tagName(optimize_mode),
1384 }));
1385
1386 step.dependOn(&test_step.step);
1387 };
1388 return step;
1389}1093}
test/translate_c.zig+8
...@@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3948 \\}3948 \\}
3949 });3949 });
3950 }3950 }
3951
3952 cases.add("extern array of unknown length",
3953 \\extern int foo[];
3954 , &[_][]const u8{
3955 \\const foo: [*c]c_int = @extern([*c]c_int, .{
3956 \\ .name = "foo",
3957 \\});
3958 });
3951}3959}
tools/lldb_pretty_printers.py+38-5
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1# pretty printing for the zig language, zig standard library, and zig stage 2 compiler.1# pretty printing for the zig language, zig standard library, and zig stage 2 compiler.
2# put commands in ~/.lldbinit to run them automatically when starting lldb2# put commands in ~/.lldbinit to run them automatically when starting lldb
3# `command script /path/to/stage2_lldb_pretty_printers.py` to import this file3# `command script import /path/to/zig/tools/lldb_pretty_printers.py` to import this file
4# `type category enable zig` to enable pretty printing for the zig language4# `type category enable zig` to enable pretty printing for the zig language
5# `type category enable zig.std` to enable pretty printing for the zig standard library5# `type category enable zig.std` to enable pretty printing for the zig standard library
6# `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler6# `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler
...@@ -201,7 +201,7 @@ class std_MultiArrayList_SynthProvider:...@@ -201,7 +201,7 @@ class std_MultiArrayList_SynthProvider:
201201
202 value_type = self.value.type202 value_type = self.value.type
203 for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):203 for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
204 ptr_self_type, ptr_child_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()204 ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
205 if ptr_self_type.GetPointeeType() == value_type: break205 if ptr_self_type.GetPointeeType() == value_type: break
206 else: return206 else: return
207207
...@@ -221,12 +221,44 @@ class std_MultiArrayList_SynthProvider:...@@ -221,12 +221,44 @@ class std_MultiArrayList_SynthProvider:
221 offset = 0221 offset = 0
222 data = lldb.SBData()222 data = lldb.SBData()
223 for field in self.entry_type.fields:223 for field in self.entry_type.fields:
224 ptr_field_type = field.type224 field_type = field.type.GetPointeeType()
225 field_size = ptr_field_type.GetPointeeType().size225 field_size = field_type.size
226 data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, ptr_field_type).address_of.data)226 data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, field_type).address_of.data)
227 offset += self.capacity * field_size227 offset += self.capacity * field_size
228 return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type)228 return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type)
229 except: return None229 except: return None
230class std_MultiArrayList_Slice_SynthProvider:
231 def __init__(self, value, _=None): self.value = value
232 def update(self):
233 try:
234 self.len = 0
235
236 value_type = self.value.type
237 for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
238 ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
239 if ptr_self_type.GetPointeeType() == value_type: break
240 else: return
241
242 self.fields = {member.name: index for index, member in enumerate(ptr_field_type.GetPointeeType().enum_members)}
243 self.entry_type = ptr_entry_type.GetPointeeType()
244 self.ptrs = self.value.GetChildMemberWithName('ptrs')
245 self.len = self.value.GetChildMemberWithName('len').unsigned
246 self.capacity = self.value.GetChildMemberWithName('capacity').unsigned
247 except: pass
248 def has_children(self): return True
249 def num_children(self): return self.len
250 def get_child_index(self, name):
251 try: return int(name.removeprefix('[').removesuffix(']'))
252 except: return -1
253 def get_child_at_index(self, index):
254 try:
255 if index < 0 or index >= self.len: return None
256 data = lldb.SBData()
257 for field in self.entry_type.fields:
258 field_type = field.type.GetPointeeType()
259 data.Append(self.ptrs.child[self.fields[field.name.removesuffix('_ptr')]].CreateChildAtOffset(field.name, index * field_type.size, field_type).address_of.data)
260 return self.ptrs.CreateValueFromData('[%d]' % index, data, self.entry_type)
261 except: return None
230262
231class std_HashMapUnmanaged_SynthProvider:263class std_HashMapUnmanaged_SynthProvider:
232 def __init__(self, value, _=None): self.value = value264 def __init__(self, value, _=None): self.value = value
...@@ -556,6 +588,7 @@ def __lldb_init_module(debugger, _=None):...@@ -556,6 +588,7 @@ def __lldb_init_module(debugger, _=None):
556 add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')588 add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')
557 add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')589 add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')
558 add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')590 add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
591 add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)\\.Slice$', identifier='std_MultiArrayList_Slice', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
559 add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)592 add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)
560 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)593 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)
561 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)594 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)