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
296296 "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"
297297 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
298298 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
299 "${CMAKE_SOURCE_DIR}/lib/std/os/darwin.zig"
300299 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
301300 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig"
302301 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
303302 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
304303 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig"
305304 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
306 "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig"
307305 "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig"
308306 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig"
309307 "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig"
......@@ -507,7 +505,9 @@ set(ZIG_STAGE2_SOURCES
507505 "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig"
508506 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig"
509507 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig"
508 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Pool.zig"
510509 "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/Thread/WaitGroup.zig"
511511 "${CMAKE_SOURCE_DIR}/lib/std/time.zig"
512512 "${CMAKE_SOURCE_DIR}/lib/std/treap.zig"
513513 "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig"
......@@ -517,6 +517,7 @@ set(ZIG_STAGE2_SOURCES
517517 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
518518 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
519519 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
520 "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
520521 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
521522 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
522523 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
......@@ -531,9 +532,7 @@ set(ZIG_STAGE2_SOURCES
531532 "${CMAKE_SOURCE_DIR}/src/Package.zig"
532533 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
533534 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
534 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
535535 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
536 "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"
537536 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
538537 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"
539538 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"
......@@ -560,9 +559,12 @@ set(ZIG_STAGE2_SOURCES
560559 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"
561560 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig"
562561 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig"
562 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig"
563563 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig"
564 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig"
565564 "${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"
566568 "${CMAKE_SOURCE_DIR}/src/clang.zig"
567569 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
568570 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
......@@ -827,6 +829,12 @@ else()
827829 set(ZIG_STATIC_ARG "")
828830endif()
829831
832if(CMAKE_POSITION_INDEPENDENT_CODE)
833 set(ZIG_PIE_ARG="-Dpie")
834else()
835 set(ZIG_PIE_ARG="")
836endif()
837
830838set(ZIG_BUILD_ARGS
831839 --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"
832840 "-Dconfig_h=${ZIG_CONFIG_H_OUT}"
......@@ -835,6 +843,7 @@ set(ZIG_BUILD_ARGS
835843 ${ZIG_STATIC_ARG}
836844 ${ZIG_NO_LIB_ARG}
837845 ${ZIG_SINGLE_THREADED_ARG}
846 ${ZIG_PIE_ARG}
838847 "-Dtarget=${ZIG_TARGET_TRIPLE}"
839848 "-Dcpu=${ZIG_TARGET_MCPU}"
840849 "-Dversion-string=${RESOLVED_ZIG_VERSION}"
build.zig+114-108
......@@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void {
3131 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
3333 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
3540 const docgen_exe = b.addExecutable(.{
3641 .name = "docgen",
......@@ -40,28 +45,35 @@ pub fn build(b: *std.Build) !void {
4045 });
4146 docgen_exe.single_threaded = single_threaded;
4247
43 const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"});
44 const docgen_cmd = docgen_exe.run();
45 docgen_cmd.addArgs(&[_][]const u8{
46 "--zig",
47 b.zig_exe,
48 "doc" ++ fs.path.sep_str ++ "langref.html.in",
49 langref_out_path,
50 });
51 docgen_cmd.step.dependOn(&docgen_exe.step);
48 const docgen_cmd = b.addRunArtifact(docgen_exe);
49 docgen_cmd.addArgs(&.{ "--zig", b.zig_exe });
50 if (b.zig_lib_dir) |p| {
51 docgen_cmd.addArgs(&.{ "--zig-lib-dir", p });
52 }
53 docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" });
54 const langref_file = docgen_cmd.addOutputFileArg("langref.html");
55 const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
56 if (!skip_install_lib_files) {
57 b.getInstallStep().dependOn(&install_langref.step);
58 }
5259
5360 const docs_step = b.step("docs", "Build documentation");
5461 docs_step.dependOn(&docgen_cmd.step);
5562
56 const test_cases = b.addTest(.{
57 .root_source_file = .{ .path = "src/test.zig" },
63 // This is for legacy reasons, to be removed after our CI scripts are upgraded to use
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" },
5872 .optimize = optimize,
5973 });
60 test_cases.main_pkg_path = ".";
61 test_cases.stack_size = stack_size;
62 test_cases.single_threaded = single_threaded;
63
64 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
74 check_case_exe.main_pkg_path = ".";
75 check_case_exe.stack_size = stack_size;
76 check_case_exe.single_threaded = single_threaded;
6577
6678 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
6779 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 {
7486 const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false;
7587 const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
7688 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
8390 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 {
157164 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
158165 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
159166 const strip = b.option(bool, "strip", "Omit debug information");
167 const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
160168 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
162170 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 {
167175
168176 const exe = addCompilerStep(b, optimize, target);
169177 exe.strip = strip;
178 exe.pie = pie;
170179 exe.sanitize_thread = sanitize_thread;
171180 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
172181 exe.install();
......@@ -178,13 +187,12 @@ pub fn build(b: *std.Build) !void {
178187 test_step.dependOn(&exe.step);
179188 }
180189
181 b.default_step.dependOn(&exe.step);
182190 exe.single_threaded = single_threaded;
183191
184192 if (target.isWindows() and target.getAbi() == .gnu) {
185193 // LTO is currently broken on mingw, this can be removed when it's fixed.
186194 exe.want_lto = false;
187 test_cases.want_lto = false;
195 check_case_exe.want_lto = false;
188196 }
189197
190198 const exe_options = b.addOptions();
......@@ -199,11 +207,11 @@ pub fn build(b: *std.Build) !void {
199207 exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
200208 exe_options.addOption(bool, "force_gpa", force_gpa);
201209 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
204212 if (link_libc) {
205213 exe.linkLibC();
206 test_cases.linkLibC();
214 check_case_exe.linkLibC();
207215 }
208216
209217 const is_debug = optimize == .Debug;
......@@ -289,14 +297,14 @@ pub fn build(b: *std.Build) !void {
289297 }
290298
291299 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);
293301 } else {
294302 // Here we are -Denable-llvm but no cmake integration.
295303 try addStaticLlvmOptionsToExe(exe);
296 try addStaticLlvmOptionsToExe(test_cases);
304 try addStaticLlvmOptionsToExe(check_case_exe);
297305 }
298306 if (target.isWindows()) {
299 inline for (.{ exe, test_cases }) |artifact| {
307 inline for (.{ exe, check_case_exe }) |artifact| {
300308 artifact.linkSystemLibrary("version");
301309 artifact.linkSystemLibrary("uuid");
302310 artifact.linkSystemLibrary("ole32");
......@@ -341,8 +349,9 @@ pub fn build(b: *std.Build) !void {
341349 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
342350
343351 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);
346355 test_cases_options.addOption(bool, "enable_logging", enable_logging);
347356 test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
348357 test_cases_options.addOption(bool, "skip_non_native", skip_non_native);
......@@ -366,12 +375,6 @@ pub fn build(b: *std.Build) !void {
366375 test_cases_options.addOption(std.SemanticVersion, "semver", semver);
367376 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
375378 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
376379 var chosen_mode_index: usize = 0;
377380 if (!skip_debug) {
......@@ -392,96 +395,101 @@ pub fn build(b: *std.Build) !void {
392395 }
393396 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 works
396 test_step.dependOn(&fmt_build_zig.step);
397 const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works");
398 fmt_step.dependOn(&fmt_build_zig.step);
399
400 test_step.dependOn(tests.addPkgTests(
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 ));
398 const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" };
399 const fmt_exclude_paths = &.{"test/cases"};
400 const do_fmt = b.addFmt(.{
401 .paths = fmt_include_paths,
402 .exclude_paths = fmt_exclude_paths,
403 });
413404
414 test_step.dependOn(tests.addPkgTests(
415 b,
416 test_filter,
417 "lib/compiler_rt.zig",
418 "compiler-rt",
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 ));
405 b.step("test-fmt", "Check source files having conforming formatting").dependOn(&b.addFmt(.{
406 .paths = fmt_include_paths,
407 .exclude_paths = fmt_exclude_paths,
408 .check = true,
409 }).step);
427410
428 test_step.dependOn(tests.addPkgTests(
429 b,
430 test_filter,
431 "lib/c.zig",
432 "universal-libc",
433 "Run the universal libc tests",
434 optimization_modes,
435 true, // skip_single_threaded
436 skip_non_native,
437 true, // skip_libc
438 skip_stage1,
439 skip_stage2_tests or true, // TODO get these all passing
440 ));
411 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
412 try tests.addCases(b, test_cases_step, test_filter, check_case_exe);
413 if (!skip_stage2_tests) test_step.dependOn(test_cases_step);
414
415 test_step.dependOn(tests.addModuleTests(b, .{
416 .test_filter = test_filter,
417 .root_src = "test/behavior.zig",
418 .name = "behavior",
419 .desc = "Run the behavior tests",
420 .optimize_modes = optimization_modes,
421 .skip_single_threaded = skip_single_threaded,
422 .skip_non_native = skip_non_native,
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
442455 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
443456 test_step.dependOn(tests.addStandaloneTests(
444457 b,
445 test_filter,
446458 optimization_modes,
447 skip_non_native,
448459 enable_macos_sdk,
449 target,
450460 skip_stage2_tests,
451 b.enable_darling,
452 b.enable_qemu,
453 b.enable_rosetta,
454 b.enable_wasmtime,
455 b.enable_wine,
456461 enable_symlinks_windows,
457462 ));
458463 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));
460465 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));
462467 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
463468 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
464469 if (!skip_run_translated_c) {
465470 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
466471 }
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(
471 b,
472 test_filter,
473 "lib/std/std.zig",
474 "std",
475 "Run the standard library tests",
476 optimization_modes,
477 skip_single_threaded,
478 skip_non_native,
479 skip_libc,
480 skip_stage1,
481 true, // TODO get these all passing
482 ));
473 test_step.dependOn(tests.addModuleTests(b, .{
474 .test_filter = test_filter,
475 .root_src = "lib/std/std.zig",
476 .name = "std",
477 .desc = "Run the standard library tests",
478 .optimize_modes = optimization_modes,
479 .skip_single_threaded = skip_single_threaded,
480 .skip_non_native = skip_non_native,
481 .skip_libc = skip_libc,
482 .skip_stage1 = skip_stage1,
483 .skip_stage2 = true, // TODO get all these passing
484 // I observed a value of 3398275072 on my M1, and multiplied by 1.1 to
485 // get this amount:
486 .max_rss = 3738102579,
487 }));
483488
484489 try addWasiUpdateStep(b, version);
490
491 b.step("fmt", "Modify source files in place to have conforming formatting")
492 .dependOn(&do_fmt.step);
485493}
486494
487495fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
......@@ -510,6 +518,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
510518 exe_options.addOption(bool, "enable_tracy_callstack", false);
511519 exe_options.addOption(bool, "enable_tracy_allocation", false);
512520 exe_options.addOption(bool, "value_tracing", false);
521 exe_options.addOption(bool, "omit_pkg_fetching_code", true);
513522
514523 const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" });
515524 run_opt.addArtifactArg(exe);
......@@ -681,10 +690,7 @@ fn addCxxKnownPath(
681690) !void {
682691 if (!std.process.can_spawn)
683692 return error.RequiredLibraryNotFound;
684 const path_padded = try b.exec(&[_][]const u8{
685 ctx.cxx_compiler,
686 b.fmt("-print-file-name={s}", .{objname}),
687 });
693 const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) });
688694 var tokenizer = mem.tokenize(u8, path_padded, "\r\n");
689695 const path_unpadded = tokenizer.next().?;
690696 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 \
6767 --zig-lib-dir "$(pwd)/../lib"
6868
6969# 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
7272# Produce the experimental std lib documentation.
7373stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
......@@ -98,3 +98,9 @@ unset CXX
9898ninja install
9999
100100stage3/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 \
6767 --zig-lib-dir "$(pwd)/../lib"
6868
6969# 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
7272# Produce the experimental std lib documentation.
7373stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
......@@ -98,3 +98,9 @@ unset CXX
9898ninja install
9999
100100stage3/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 \
6666 --zig-lib-dir "$(pwd)/../lib"
6767
6868# 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
7171# Produce the experimental std lib documentation.
7272stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
......@@ -97,3 +97,9 @@ unset CXX
9797ninja install
9898
9999stage3/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 \
6767 --zig-lib-dir "$(pwd)/../lib"
6868
6969# 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
7272# Produce the experimental std lib documentation.
7373stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
......@@ -114,3 +114,9 @@ unset CXX
114114ninja install
115115
116116stage3/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 =
2828 \\
2929;
3030
31fn errorf(comptime format: []const u8, args: anytype) noreturn {
31fn fatal(comptime format: []const u8, args: anytype) noreturn {
3232 const stderr = io.getStdErr().writer();
3333
34 stderr.print("error: " ++ format, args) catch {};
34 stderr.print("error: " ++ format ++ "\n", args) catch {};
3535 process.exit(1);
3636}
3737
......@@ -45,6 +45,7 @@ pub fn main() !void {
4545 if (!args_it.skip()) @panic("expected self arg");
4646
4747 var zig_exe: []const u8 = "zig";
48 var opt_zig_lib_dir: ?[]const u8 = null;
4849 var do_code_tests = true;
4950 var files = [_][]const u8{ "", "" };
5051
......@@ -59,24 +60,29 @@ pub fn main() !void {
5960 if (args_it.next()) |param| {
6061 zig_exe = param;
6162 } 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", .{});
6370 }
6471 } else if (mem.eql(u8, arg, "--skip-code-tests")) {
6572 do_code_tests = false;
6673 } else {
67 errorf("unrecognized option: '{s}'\n", .{arg});
74 fatal("unrecognized option: '{s}'", .{arg});
6875 }
6976 } else {
7077 if (i > 1) {
71 errorf("too many arguments\n", .{});
78 fatal("too many arguments", .{});
7279 }
7380 files[i] = arg;
7481 i += 1;
7582 }
7683 }
7784 if (i < 2) {
78 errorf("not enough arguments\n", .{});
79 process.exit(1);
85 fatal("not enough arguments", .{});
8086 }
8187
8288 var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only });
......@@ -95,7 +101,7 @@ pub fn main() !void {
95101 try fs.cwd().makePath(tmp_dir_name);
96102 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);
99105 try buffered_writer.flush();
100106}
101107
......@@ -1268,9 +1274,10 @@ fn genHtml(
12681274 toc: *Toc,
12691275 out: anytype,
12701276 zig_exe: []const u8,
1277 opt_zig_lib_dir: ?[]const u8,
12711278 do_code_tests: bool,
12721279) !void {
1273 var progress = Progress{};
1280 var progress = Progress{ .dont_print_on_dumb = true };
12741281 const root_node = progress.start("Generating docgen examples", toc.nodes.len);
12751282 defer root_node.end();
12761283
......@@ -1278,7 +1285,7 @@ fn genHtml(
12781285 try env_map.put("ZIG_DEBUG_COLOR", "1");
12791286
12801287 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
12831290 for (toc.nodes) |node| {
12841291 defer root_node.completeOne();
......@@ -1370,6 +1377,9 @@ fn genHtml(
13701377 "--color", "on",
13711378 "--enable-cache", tmp_source_file_name,
13721379 });
1380 if (opt_zig_lib_dir) |zig_lib_dir| {
1381 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1382 }
13731383
13741384 try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext});
13751385
......@@ -1512,8 +1522,12 @@ fn genHtml(
15121522 defer test_args.deinit();
15131523
15141524 try test_args.appendSlice(&[_][]const u8{
1515 zig_exe, "test", tmp_source_file_name,
1525 zig_exe, "test",
1526 tmp_source_file_name,
15161527 });
1528 if (opt_zig_lib_dir) |zig_lib_dir| {
1529 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1530 }
15171531 try shell_out.print("$ zig test {s}.zig ", .{code.name});
15181532
15191533 switch (code.mode) {
......@@ -1564,12 +1578,13 @@ fn genHtml(
15641578 defer test_args.deinit();
15651579
15661580 try test_args.appendSlice(&[_][]const u8{
1567 zig_exe,
1568 "test",
1569 "--color",
1570 "on",
1581 zig_exe, "test",
1582 "--color", "on",
15711583 tmp_source_file_name,
15721584 });
1585 if (opt_zig_lib_dir) |zig_lib_dir| {
1586 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1587 }
15731588 try shell_out.print("$ zig test {s}.zig ", .{code.name});
15741589
15751590 switch (code.mode) {
......@@ -1624,8 +1639,12 @@ fn genHtml(
16241639 defer test_args.deinit();
16251640
16261641 try test_args.appendSlice(&[_][]const u8{
1627 zig_exe, "test", tmp_source_file_name,
1642 zig_exe, "test",
1643 tmp_source_file_name,
16281644 });
1645 if (opt_zig_lib_dir) |zig_lib_dir| {
1646 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1647 }
16291648 var mode_arg: []const u8 = "";
16301649 switch (code.mode) {
16311650 .Debug => {},
......@@ -1684,17 +1703,17 @@ fn genHtml(
16841703 defer build_args.deinit();
16851704
16861705 try build_args.appendSlice(&[_][]const u8{
1687 zig_exe,
1688 "build-obj",
1706 zig_exe, "build-obj",
1707 "--color", "on",
1708 "--name", code.name,
16891709 tmp_source_file_name,
1690 "--color",
1691 "on",
1692 "--name",
1693 code.name,
16941710 try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{
16951711 tmp_dir_name, fs.path.sep, name_plus_obj_ext,
16961712 }),
16971713 });
1714 if (opt_zig_lib_dir) |zig_lib_dir| {
1715 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1716 }
16981717
16991718 try shell_out.print("$ zig build-obj {s}.zig ", .{code.name});
17001719
......@@ -1758,13 +1777,15 @@ fn genHtml(
17581777 defer test_args.deinit();
17591778
17601779 try test_args.appendSlice(&[_][]const u8{
1761 zig_exe,
1762 "build-lib",
1780 zig_exe, "build-lib",
17631781 tmp_source_file_name,
17641782 try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{
17651783 tmp_dir_name, fs.path.sep_str, bin_basename,
17661784 }),
17671785 });
1786 if (opt_zig_lib_dir) |zig_lib_dir| {
1787 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1788 }
17681789 try shell_out.print("$ zig build-lib {s}.zig ", .{code.name});
17691790
17701791 switch (code.mode) {
......@@ -1829,9 +1850,23 @@ fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8
18291850 return result;
18301851}
18311852
1832fn getBuiltinCode(allocator: Allocator, env_map: *process.EnvMap, zig_exe: []const u8) ![]const u8 {
1833 const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" });
1834 return result.stdout;
1853fn getBuiltinCode(
1854 allocator: Allocator,
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 }
18351870}
18361871
18371872fn dumpArgs(args: []const []const u8) void {
doc/langref.html.in+28-12
......@@ -7457,20 +7457,20 @@ pub const STDOUT_FILENO = 1;
74577457
74587458pub fn syscall1(number: usize, arg1: usize) usize {
74597459 return asm volatile ("syscall"
7460 : [ret] "={rax}" (-> usize)
7460 : [ret] "={rax}" (-> usize),
74617461 : [number] "{rax}" (number),
7462 [arg1] "{rdi}" (arg1)
7462 [arg1] "{rdi}" (arg1),
74637463 : "rcx", "r11"
74647464 );
74657465}
74667466
74677467pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
74687468 return asm volatile ("syscall"
7469 : [ret] "={rax}" (-> usize)
7469 : [ret] "={rax}" (-> usize),
74707470 : [number] "{rax}" (number),
74717471 [arg1] "{rdi}" (arg1),
74727472 [arg2] "{rsi}" (arg2),
7473 [arg3] "{rdx}" (arg3)
7473 [arg3] "{rdx}" (arg3),
74747474 : "rcx", "r11"
74757475 );
74767476}
......@@ -7519,14 +7519,14 @@ pub fn syscall1(number: usize, arg1: usize) usize {
75197519 // type is the result type of the inline assembly expression.
75207520 // If it is a value binding, then `%[ret]` syntax would be used
75217521 // to refer to the register bound to the value.
7522 (-> usize)
7522 (-> usize),
75237523 // Next is the list of inputs.
75247524 // The constraint for these inputs means, "when the assembly code is
75257525 // executed, $rax shall have the value of `number` and $rdi shall have
75267526 // the value of `arg1`". Any number of input parameters is allowed,
75277527 // including none.
75287528 : [number] "{rax}" (number),
7529 [arg1] "{rdi}" (arg1)
7529 [arg1] "{rdi}" (arg1),
75307530 // Next is the list of clobbers. These declare a set of registers whose
75317531 // values will not be preserved by the execution of this assembly code.
75327532 // These do not include output or input registers. The special clobber
......@@ -7818,12 +7818,14 @@ comptime {
78187818 <p>
78197819 This function inserts a platform-specific debug trap instruction which causes
78207820 debuggers to break there.
7821 Unlike for {#syntax#}@trap(){#endsyntax#}, execution may continue after this point if the program is resumed.
78217822 </p>
78227823 <p>
78237824 This function is only valid within function scope.
78247825 </p>
7825
7826 {#see_also|@trap#}
78267827 {#header_close#}
7828
78277829 {#header_open|@mulAdd#}
78287830 <pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre>
78297831 <p>
......@@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {
93939395 </p>
93949396 {#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
93969411 {#header_open|@truncate#}
93979412 <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
93989413 <p>
......@@ -9565,9 +9580,10 @@ pub fn build(b: *std.Build) void {
95659580 This causes these options to be available:
95669581 </p>
95679582 <dl>
9568 <dt><kbd>-Drelease-safe=[bool]</kbd></dt><dd>Optimizations on and safety on</dd>
9569 <dt><kbd>-Drelease-fast=[bool]</kbd></dt><dd>Optimizations on and safety off</dd>
9570 <dt><kbd>-Drelease-small=[bool]</kbd></dt><dd>Size optimizations on and safety off</dd>
9583 <dt><kbd>-Doptimize=Debug</kbd></dt><dd>Optimizations off and safety on (default)</dd>
9584 <dt><kbd>-Doptimize=ReleaseSafe</kbd></dt><dd>Optimizations on and safety on</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>
95719587 </dl>
95729588 {#header_open|Debug#}
95739589 {#shell_samp#}$ zig build-exe example.zig{#end_shell_samp#}
......@@ -9788,8 +9804,8 @@ pub fn main() !void {
97889804 {#header_close#}
97899805 {#header_open|Builtin Overflow Functions#}
97909806 <p>
9791 These builtins return a {#syntax#}bool{#endsyntax#} of whether or not overflow
9792 occurred, as well as returning the overflowed bits:
9807 These builtins return a tuple containing whether there was an overflow
9808 (as a {#syntax#}u1{#endsyntax#}) and the possibly overflowed bits of the operation:
97939809 </p>
97949810 <ul>
97959811 <li>{#link|@addWithOverflow#}</li>
lib/build_runner.zig+702-53
......@@ -1,12 +1,14 @@
11const root = @import("@build");
22const std = @import("std");
33const builtin = @import("builtin");
4const assert = std.debug.assert;
45const io = std.io;
56const fmt = std.fmt;
67const mem = std.mem;
78const process = std.process;
89const ArrayList = std.ArrayList;
910const File = std.fs.File;
11const Step = std.Build.Step;
1012
1113pub const dependencies = @import("@dependencies");
1214
......@@ -14,12 +16,15 @@ pub fn main() !void {
1416 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
1517 // one shot program. We don't need to waste time freeing memory and finding places to squish
1618 // bytes into. So we free everything all at once at the very end.
17 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
18 defer arena.deinit();
19 var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
20 defer single_threaded_arena.deinit();
1921
20 const allocator = arena.allocator();
21 var args = try process.argsAlloc(allocator);
22 defer process.argsFree(allocator, args);
22 var thread_safe_arena: std.heap.ThreadSafeAllocator = .{
23 .child_allocator = single_threaded_arena.allocator(),
24 };
25 const arena = thread_safe_arena.allocator();
26
27 var args = try process.argsAlloc(arena);
2328
2429 // skip my own exe name
2530 var arg_idx: usize = 1;
......@@ -59,18 +64,17 @@ pub fn main() !void {
5964 };
6065
6166 var cache: std.Build.Cache = .{
62 .gpa = allocator,
67 .gpa = arena,
6368 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
6469 };
6570 cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
6671 cache.addPrefix(build_root_directory);
6772 cache.addPrefix(local_cache_directory);
6873 cache.addPrefix(global_cache_directory);
69
70 //cache.hash.addBytes(builtin.zig_version);
74 cache.hash.addBytes(builtin.zig_version_string);
7175
7276 const builder = try std.Build.create(
73 allocator,
77 arena,
7478 zig_exe,
7579 build_root_directory,
7680 local_cache_directory,
......@@ -80,35 +84,34 @@ pub fn main() !void {
8084 );
8185 defer builder.destroy();
8286
83 var targets = ArrayList([]const u8).init(allocator);
84 var debug_log_scopes = ArrayList([]const u8).init(allocator);
85
86 const stderr_stream = io.getStdErr().writer();
87 const stdout_stream = io.getStdOut().writer();
87 var targets = ArrayList([]const u8).init(arena);
88 var debug_log_scopes = ArrayList([]const u8).init(arena);
89 var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena };
8890
8991 var install_prefix: ?[]const u8 = null;
9092 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 variable
93 // if it exists, default the color setting to .off
94 // explicit --color arguments will still override this setting.
95 builder.color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto;
97 const stderr_stream = io.getStdErr().writer();
98 const stdout_stream = io.getStdOut().writer();
9699
97100 while (nextArg(args, &arg_idx)) |arg| {
98101 if (mem.startsWith(u8, arg, "-D")) {
99102 const option_contents = arg[2..];
100103 if (option_contents.len == 0) {
101104 std.debug.print("Expected option name after '-D'\n\n", .{});
102 return usageAndErr(builder, false, stderr_stream);
105 usageAndErr(builder, false, stderr_stream);
103106 }
104107 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
105108 const option_name = option_contents[0..name_end];
106109 const option_value = option_contents[name_end + 1 ..];
107110 if (try builder.addUserInputOption(option_name, option_value))
108 return usageAndErr(builder, false, stderr_stream);
111 usageAndErr(builder, false, stderr_stream);
109112 } else {
110113 if (try builder.addUserInputFlag(option_contents))
111 return usageAndErr(builder, false, stderr_stream);
114 usageAndErr(builder, false, stderr_stream);
112115 }
113116 } else if (mem.startsWith(u8, arg, "-")) {
114117 if (mem.eql(u8, arg, "--verbose")) {
......@@ -118,69 +121,83 @@ pub fn main() !void {
118121 } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) {
119122 install_prefix = nextArg(args, &arg_idx) orelse {
120123 std.debug.print("Expected argument after {s}\n\n", .{arg});
121 return usageAndErr(builder, false, stderr_stream);
124 usageAndErr(builder, false, stderr_stream);
122125 };
123126 } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
124127 return steps(builder, false, stdout_stream);
125128 } else if (mem.eql(u8, arg, "--prefix-lib-dir")) {
126129 dir_list.lib_dir = nextArg(args, &arg_idx) orelse {
127130 std.debug.print("Expected argument after {s}\n\n", .{arg});
128 return usageAndErr(builder, false, stderr_stream);
131 usageAndErr(builder, false, stderr_stream);
129132 };
130133 } else if (mem.eql(u8, arg, "--prefix-exe-dir")) {
131134 dir_list.exe_dir = nextArg(args, &arg_idx) orelse {
132135 std.debug.print("Expected argument after {s}\n\n", .{arg});
133 return usageAndErr(builder, false, stderr_stream);
136 usageAndErr(builder, false, stderr_stream);
134137 };
135138 } else if (mem.eql(u8, arg, "--prefix-include-dir")) {
136139 dir_list.include_dir = nextArg(args, &arg_idx) orelse {
137140 std.debug.print("Expected argument after {s}\n\n", .{arg});
138 return usageAndErr(builder, false, stderr_stream);
141 usageAndErr(builder, false, stderr_stream);
139142 };
140143 } else if (mem.eql(u8, arg, "--sysroot")) {
141144 const sysroot = nextArg(args, &arg_idx) orelse {
142145 std.debug.print("Expected argument after --sysroot\n\n", .{});
143 return usageAndErr(builder, false, stderr_stream);
146 usageAndErr(builder, false, stderr_stream);
144147 };
145148 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 };
146161 } else if (mem.eql(u8, arg, "--search-prefix")) {
147162 const search_prefix = nextArg(args, &arg_idx) orelse {
148163 std.debug.print("Expected argument after --search-prefix\n\n", .{});
149 return usageAndErr(builder, false, stderr_stream);
164 usageAndErr(builder, false, stderr_stream);
150165 };
151166 builder.addSearchPrefix(search_prefix);
152167 } else if (mem.eql(u8, arg, "--libc")) {
153168 const libc_file = nextArg(args, &arg_idx) orelse {
154169 std.debug.print("Expected argument after --libc\n\n", .{});
155 return usageAndErr(builder, false, stderr_stream);
170 usageAndErr(builder, false, stderr_stream);
156171 };
157172 builder.libc_file = libc_file;
158173 } else if (mem.eql(u8, arg, "--color")) {
159174 const next_arg = nextArg(args, &arg_idx) orelse {
160175 std.debug.print("expected [auto|on|off] after --color", .{});
161 return usageAndErr(builder, false, stderr_stream);
176 usageAndErr(builder, false, stderr_stream);
162177 };
163 builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse {
178 color = std.meta.stringToEnum(Color, next_arg) orelse {
164179 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);
166181 };
167182 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
168183 builder.zig_lib_dir = nextArg(args, &arg_idx) orelse {
169184 std.debug.print("Expected argument after --zig-lib-dir\n\n", .{});
170 return usageAndErr(builder, false, stderr_stream);
185 usageAndErr(builder, false, stderr_stream);
171186 };
172187 } else if (mem.eql(u8, arg, "--debug-log")) {
173188 const next_arg = nextArg(args, &arg_idx) orelse {
174189 std.debug.print("Expected argument after {s}\n\n", .{arg});
175 return usageAndErr(builder, false, stderr_stream);
190 usageAndErr(builder, false, stderr_stream);
176191 };
177192 try debug_log_scopes.append(next_arg);
193 } else if (mem.eql(u8, arg, "--debug-pkg-config")) {
194 builder.debug_pkg_config = true;
178195 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
179196 builder.debug_compile_errors = true;
180197 } else if (mem.eql(u8, arg, "--glibc-runtimes")) {
181198 builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse {
182199 std.debug.print("Expected argument after --glibc-runtimes\n\n", .{});
183 return usageAndErr(builder, false, stderr_stream);
200 usageAndErr(builder, false, stderr_stream);
184201 };
185202 } else if (mem.eql(u8, arg, "--verbose-link")) {
186203 builder.verbose_link = true;
......@@ -194,8 +211,6 @@ pub fn main() !void {
194211 builder.verbose_cc = true;
195212 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
196213 builder.verbose_llvm_cpu_features = true;
197 } else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
198 builder.prominent_compile_errors = true;
199214 } else if (mem.eql(u8, arg, "-fwine")) {
200215 builder.enable_wine = true;
201216 } else if (mem.eql(u8, arg, "-fno-wine")) {
......@@ -216,6 +231,10 @@ pub fn main() !void {
216231 builder.enable_darling = true;
217232 } else if (mem.eql(u8, arg, "-fno-darling")) {
218233 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;
219238 } else if (mem.eql(u8, arg, "-freference-trace")) {
220239 builder.reference_trace = 256;
221240 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
......@@ -226,39 +245,639 @@ pub fn main() !void {
226245 };
227246 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
228247 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;
229261 } else if (mem.eql(u8, arg, "--")) {
230262 builder.args = argsRest(args, arg_idx);
231263 break;
232264 } else {
233265 std.debug.print("Unrecognized argument: {s}\n\n", .{arg});
234 return usageAndErr(builder, false, stderr_stream);
266 usageAndErr(builder, false, stderr_stream);
235267 }
236268 } else {
237269 try targets.append(arg);
238270 }
239271 }
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
241284 builder.debug_log_scopes = debug_log_scopes.items;
242285 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
245292 if (builder.validateUserInputDidItFail())
246 return usageAndErr(builder, true, stderr_stream);
293 usageAndErr(builder, true, stderr_stream);
247294
248 builder.make(targets.items) catch |err| {
249 switch (err) {
250 error.InvalidStepName => {
251 return usageAndErr(builder, true, stderr_stream);
295 var run: Run = .{
296 .max_rss = max_rss,
297 .max_rss_is_default = false,
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 }
252463 },
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,
260464 }
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 }
262881}
263882
264883fn 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
269888 }
270889
271890 const allocator = builder.allocator;
272 for (builder.top_level_steps.items) |top_level_step| {
891 for (builder.top_level_steps.values()) |top_level_step| {
273892 const name = if (&top_level_step.step == builder.default_step)
274893 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})
275894 else
......@@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
327946 \\ --verbose Print commands before executing them
328947 \\ --color [auto|off|on] Enable or disable colored error messages
329948 \\ --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)
330953 \\
331954 \\Project-Specific Options:
332955 \\
......@@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
364987 \\ --zig-lib-dir [arg] Override path to Zig lib directory
365988 \\ --build-runner [file] Override path to build runner
366989 \\ --debug-log [scope] Enable debugging the compiler
990 \\ --debug-pkg-config Fail if unknown pkg-config flags encountered
367991 \\ --verbose-link Enable compiler debug output for linking
368992 \\ --verbose-air Enable compiler debug output for Zig AIR
369993 \\ --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
374998 );
375999}
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 {
3781002 usage(builder, already_ran_build, out_stream) catch {};
3791003 process.exit(1);
3801004}
......@@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 {
3891013 if (idx >= args.len) return null;
3901014 return args[idx..];
3911015}
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 {
7979 }
8080 break;
8181 }
82 var carry: u64 = 0;
82 var carry: i64 = 0;
8383 i = 0;
8484 while (i <= n) : (i += 1) {
8585 const p = qhat * limb(&vn, i);
8686 const t = limb(&un, i + j) - carry - @truncate(u32, p);
87 limb_set(&un, i + j, @truncate(u32, t));
88 carry = @intCast(u64, p >> 32) - @intCast(u64, t >> 32);
87 limb_set(&un, i + j, @truncate(u32, @bitCast(u64, t)));
88 carry = @intCast(i64, p >> 32) - @intCast(i64, t >> 32);
8989 }
90 const t = limb(&un, j + n + 1) - carry;
91 limb_set(&un, j + n + 1, @truncate(u32, t));
90 const t = limb(&un, j + n + 1) -% carry;
91 limb_set(&un, j + n + 1, @truncate(u32, @bitCast(u64, t)));
9292 if (q) |q_| limb_set(q_, j, @truncate(u32, qhat));
9393 if (t < 0) {
9494 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 {
9999 limb_set(&un, i + j, @truncate(u32, t2));
100100 carry2 = t2 >> 32;
101101 }
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));
103103 }
104104 if (j == 0) break;
105105 }
lib/docs/main.js-4
......@@ -1187,10 +1187,6 @@ const NAV_MODES = {
11871187 payloadHtml += "panic";
11881188 break;
11891189 }
1190 case "set_cold": {
1191 payloadHtml += "setCold";
1192 break;
1193 }
11941190 case "set_runtime_safety": {
11951191 payloadHtml += "setRuntimeSafety";
11961192 break;
lib/std/Build.zig+243-245
......@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");
3232pub const CheckFileStep = @import("Build/CheckFileStep.zig");
3333pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
3434pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
35pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
3635pub const FmtStep = @import("Build/FmtStep.zig");
3736pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
3837pub const InstallDirStep = @import("Build/InstallDirStep.zig");
3938pub const InstallFileStep = @import("Build/InstallFileStep.zig");
4039pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");
4140pub const CompileStep = @import("Build/CompileStep.zig");
42pub const LogStep = @import("Build/LogStep.zig");
4341pub const OptionsStep = @import("Build/OptionsStep.zig");
4442pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
4543pub const RunStep = @import("Build/RunStep.zig");
......@@ -59,15 +57,12 @@ verbose_air: bool,
5957verbose_llvm_ir: bool,
6058verbose_cimport: bool,
6159verbose_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,
6560reference_trace: ?u32 = null,
6661invalid_user_input: bool,
6762zig_exe: []const u8,
6863default_step: *Step,
6964env_map: *EnvMap,
70top_level_steps: ArrayList(*TopLevelStep),
65top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep),
7166install_prefix: []const u8,
7267dest_dir: ?[]const u8,
7368lib_dir: []const u8,
......@@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
9085args: ?[][]const u8 = null,
9186debug_log_scopes: []const []const u8 = &.{},
9287debug_compile_errors: bool = false,
88debug_pkg_config: bool = false,
9389
9490/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
9591enable_darling: bool = false,
......@@ -198,7 +194,7 @@ pub fn create(
198194 env_map.* = try process.getEnvMap(allocator);
199195
200196 const self = try allocator.create(Build);
201 self.* = Build{
197 self.* = .{
202198 .zig_exe = zig_exe,
203199 .build_root = build_root,
204200 .cache_root = cache_root,
......@@ -211,13 +207,12 @@ pub fn create(
211207 .verbose_llvm_ir = false,
212208 .verbose_cimport = false,
213209 .verbose_llvm_cpu_features = false,
214 .prominent_compile_errors = false,
215210 .invalid_user_input = false,
216211 .allocator = allocator,
217212 .user_input_options = UserInputOptionsMap.init(allocator),
218213 .available_options_map = AvailableOptionsMap.init(allocator),
219214 .available_options_list = ArrayList(AvailableOption).init(allocator),
220 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
215 .top_level_steps = .{},
221216 .default_step = undefined,
222217 .env_map = env_map,
223218 .search_prefixes = ArrayList([]const u8).init(allocator),
......@@ -227,12 +222,21 @@ pub fn create(
227222 .h_dir = undefined,
228223 .dest_dir = env_map.get("DESTDIR"),
229224 .installed_files = ArrayList(InstalledFile).init(allocator),
230 .install_tls = TopLevelStep{
231 .step = Step.initNoOp(.top_level, "install", allocator),
225 .install_tls = .{
226 .step = Step.init(.{
227 .id = .top_level,
228 .name = "install",
229 .owner = self,
230 }),
232231 .description = "Copy build artifacts to prefix path",
233232 },
234 .uninstall_tls = TopLevelStep{
235 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
233 .uninstall_tls = .{
234 .step = Step.init(.{
235 .id = .top_level,
236 .name = "uninstall",
237 .owner = self,
238 .makeFn = makeUninstall,
239 }),
236240 .description = "Remove build artifacts from prefix path",
237241 },
238242 .zig_lib_dir = null,
......@@ -241,8 +245,8 @@ pub fn create(
241245 .host = host,
242246 .modules = std.StringArrayHashMap(*Module).init(allocator),
243247 };
244 try self.top_level_steps.append(&self.install_tls);
245 try self.top_level_steps.append(&self.uninstall_tls);
248 try self.top_level_steps.put(allocator, self.install_tls.step.name, &self.install_tls);
249 try self.top_level_steps.put(allocator, self.uninstall_tls.step.name, &self.uninstall_tls);
246250 self.default_step = &self.install_tls.step;
247251 return self;
248252}
......@@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
264268 child.* = .{
265269 .allocator = allocator,
266270 .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 }),
268276 .description = "Copy build artifacts to prefix path",
269277 },
270278 .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 }),
272285 .description = "Remove build artifacts from prefix path",
273286 },
274287 .user_input_options = UserInputOptionsMap.init(allocator),
......@@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
281294 .verbose_llvm_ir = parent.verbose_llvm_ir,
282295 .verbose_cimport = parent.verbose_cimport,
283296 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
284 .prominent_compile_errors = parent.prominent_compile_errors,
285 .color = parent.color,
286297 .reference_trace = parent.reference_trace,
287298 .invalid_user_input = false,
288299 .zig_exe = parent.zig_exe,
289300 .default_step = undefined,
290301 .env_map = parent.env_map,
291 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
302 .top_level_steps = .{},
292303 .install_prefix = undefined,
293304 .dest_dir = parent.dest_dir,
294305 .lib_dir = parent.lib_dir,
......@@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
306317 .zig_lib_dir = parent.zig_lib_dir,
307318 .debug_log_scopes = parent.debug_log_scopes,
308319 .debug_compile_errors = parent.debug_compile_errors,
320 .debug_pkg_config = parent.debug_pkg_config,
309321 .enable_darling = parent.enable_darling,
310322 .enable_qemu = parent.enable_qemu,
311323 .enable_rosetta = parent.enable_rosetta,
......@@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
316328 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
317329 .modules = std.StringArrayHashMap(*Module).init(allocator),
318330 };
319 try child.top_level_steps.append(&child.install_tls);
320 try child.top_level_steps.append(&child.uninstall_tls);
331 try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls);
332 try child.top_level_steps.put(allocator, child.uninstall_tls.step.name, &child.uninstall_tls);
321333 child.default_step = &child.install_tls.step;
322334 return child;
323335}
......@@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void {
372384 },
373385 }
374386 }
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;
376391 // Random bytes to make unique. Refresh this with new random bytes when
377392 // implementation is modified in a non-backwards-compatible way.
378 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
379 hash.update(b.dep_prefix);
393 hash.add(@as(u32, 0xd8cb0055));
394 hash.addBytes(b.dep_prefix);
380395 // TODO additionally update the hash with `args`.
381
382 var digest: [16]u8 = undefined;
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 });
396 const digest = hash.final();
397 const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest });
389398 b.resolveInstallPrefix(install_prefix, .{});
390399}
391400
392pub fn destroy(self: *Build) void {
393 self.env_map.deinit();
394 self.top_level_steps.deinit();
395 self.allocator.destroy(self);
401pub fn destroy(b: *Build) void {
402 b.env_map.deinit();
403 b.top_level_steps.deinit(b.allocator);
404 b.allocator.destroy(b);
396405}
397406
398407/// 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 {
441450 target: CrossTarget = .{},
442451 optimize: std.builtin.Mode = .Debug,
443452 linkage: ?CompileStep.Linkage = null,
453 max_rss: usize = 0,
444454};
445455
446456pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
......@@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
452462 .optimize = options.optimize,
453463 .kind = .exe,
454464 .linkage = options.linkage,
465 .max_rss = options.max_rss,
455466 });
456467}
457468
......@@ -460,6 +471,7 @@ pub const ObjectOptions = struct {
460471 root_source_file: ?FileSource = null,
461472 target: CrossTarget,
462473 optimize: std.builtin.Mode,
474 max_rss: usize = 0,
463475};
464476
465477pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
......@@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
469481 .target = options.target,
470482 .optimize = options.optimize,
471483 .kind = .obj,
484 .max_rss = options.max_rss,
472485 });
473486}
474487
......@@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct {
478491 version: ?std.builtin.Version = null,
479492 target: CrossTarget,
480493 optimize: std.builtin.Mode,
494 max_rss: usize = 0,
481495};
482496
483497pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
......@@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
489503 .version = options.version,
490504 .target = options.target,
491505 .optimize = options.optimize,
506 .max_rss = options.max_rss,
492507 });
493508}
494509
......@@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct {
498513 target: CrossTarget,
499514 optimize: std.builtin.Mode,
500515 version: ?std.builtin.Version = null,
516 max_rss: usize = 0,
501517};
502518
503519pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
......@@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
509525 .version = options.version,
510526 .target = options.target,
511527 .optimize = options.optimize,
528 .max_rss = options.max_rss,
512529 });
513530}
514531
515532pub const TestOptions = struct {
516533 name: []const u8 = "test",
517 kind: CompileStep.Kind = .@"test",
518534 root_source_file: FileSource,
519535 target: CrossTarget = .{},
520536 optimize: std.builtin.Mode = .Debug,
521537 version: ?std.builtin.Version = null,
538 max_rss: usize = 0,
522539};
523540
524541pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
525542 return CompileStep.create(b, .{
526543 .name = options.name,
527 .kind = options.kind,
544 .kind = .@"test",
528545 .root_source_file = options.root_source_file,
529546 .target = options.target,
530547 .optimize = options.optimize,
548 .max_rss = options.max_rss,
531549 });
532550}
533551
......@@ -536,6 +554,7 @@ pub const AssemblyOptions = struct {
536554 source_file: FileSource,
537555 target: CrossTarget,
538556 optimize: std.builtin.Mode,
557 max_rss: usize = 0,
539558};
540559
541560pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
......@@ -545,22 +564,19 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
545564 .root_source_file = null,
546565 .target = options.target,
547566 .optimize = options.optimize,
567 .max_rss = options.max_rss,
548568 });
549569 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
550570 return obj_step;
551571}
552572
553pub const AddModuleOptions = struct {
554 name: []const u8,
555 source_file: FileSource,
556 dependencies: []const ModuleDependency = &.{},
557};
558
559pub fn addModule(b: *Build, options: AddModuleOptions) void {
560 b.modules.put(b.dupe(options.name), b.createModule(.{
561 .source_file = options.source_file,
562 .dependencies = options.dependencies,
563 })) catch @panic("OOM");
573/// This function creates a module and adds it to the package's module set, making
574/// it available to other packages which depend on this one.
575/// `createModule` can be used instead to create a private module.
576pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module {
577 const module = b.createModule(options);
578 b.modules.put(b.dupe(name), module) catch @panic("OOM");
579 return module;
564580}
565581
566582pub const ModuleDependency = struct {
......@@ -573,8 +589,9 @@ pub const CreateModuleOptions = struct {
573589 dependencies: []const ModuleDependency = &.{},
574590};
575591
576/// Prefer to use `addModule` which will make the module available to other
577/// packages which depend on this package.
592/// This function creates a private module, to be used by the current package,
593/// but not exposed to other packages depending on this one.
594/// `addModule` can be used instead to create a public module.
578595pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {
579596 const module = b.allocator.create(Module) catch @panic("OOM");
580597 module.* = .{
......@@ -608,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
608625/// Creates a `RunStep` with an executable built with `addExecutable`.
609626/// Add command line arguments with methods of `RunStep`.
610627pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
611 assert(exe.kind == .exe or exe.kind == .test_exe);
612
613628 // It doesn't have to be native. We catch that if you actually try to run it.
614629 // Consider that this is declarative; the run step may not be run unless a user
615630 // 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}));
617632 run_step.addArtifactArg(exe);
618633
619 if (exe.kind == .test_exe) {
620 run_step.addArg(b.zig_exe);
634 if (exe.kind == .@"test") {
635 run_step.stdio = .zig_test;
636 run_step.addArgs(&.{"--listen=-"});
621637 }
622638
623639 if (exe.vcpkg_bin_path) |path| {
......@@ -637,7 +653,11 @@ pub fn addConfigHeader(
637653 options: ConfigHeaderStep.Options,
638654 values: anytype,
639655) *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);
641661 config_header_step.addValues(values);
642662 return config_header_step;
643663}
......@@ -674,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ
674694 return write_file_step;
675695}
676696
677pub fn addWriteFiles(self: *Build) *WriteFileStep {
678 const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");
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;
697pub fn addWriteFiles(b: *Build) *WriteFileStep {
698 return WriteFileStep.create(b);
688699}
689700
690701pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
......@@ -693,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
693704 return remove_dir_step;
694705}
695706
696pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
697 return FmtStep.create(self, paths);
707pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep {
708 return FmtStep.create(b, options);
698709}
699710
700711pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
701712 return TranslateCStep.create(self, options);
702713}
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
722715pub fn getInstallStep(self: *Build) *Step {
723716 return &self.install_tls.step;
724717}
......@@ -727,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step {
727720 return &self.uninstall_tls.step;
728721}
729722
730fn makeUninstall(uninstall_step: *Step) anyerror!void {
723fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void {
724 _ = prog_node;
731725 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
732726 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
733727
......@@ -742,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void {
742736 // TODO remove empty directories
743737}
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
776739pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
777740 const name = self.dupe(name_raw);
778741 const description = self.dupe(description_raw);
......@@ -909,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
909872
910873pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
911874 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
912 step_info.* = TopLevelStep{
913 .step = Step.initNoOp(.top_level, name, self.allocator),
875 step_info.* = .{
876 .step = Step.init(.{
877 .id = .top_level,
878 .name = name,
879 .owner = self,
880 }),
914881 .description = self.dupe(description),
915882 };
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");
917884 return &step_info.step;
918885}
919886
......@@ -1181,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool {
11811148 return self.invalid_user_input;
11821149}
11831150
1184pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1185 return self.spawnChildEnvMap(null, self.env_map, argv);
1186}
1187
1188fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1189 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1151fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
1152 var buf = ArrayList(u8).init(ally);
1153 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
11901154 for (argv) |arg| {
1191 std.debug.print("{s} ", .{arg});
1155 try buf.writer().print("{s} ", .{arg});
11921156 }
1193 std.debug.print("\n", .{});
1157 return buf.toOwnedSlice();
11941158}
11951159
1196pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1197 if (self.verbose) {
1198 printCmd(cwd, argv);
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 }
1160fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
1161 const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM");
1162 std.debug.print("{s}\n", .{text});
12281163}
12291164
12301165pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
......@@ -1283,12 +1218,7 @@ pub fn addInstallFileWithDir(
12831218 install_dir: InstallDir,
12841219 dest_rel_path: []const u8,
12851220) *InstallFileStep {
1286 if (dest_rel_path.len == 0) {
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;
1221 return InstallFileStep.create(self, source.dupe(self), install_dir, dest_rel_path);
12921222}
12931223
12941224pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
......@@ -1297,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst
12971227 return install_step;
12981228}
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
13001238pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
13011239 const file = InstalledFile{
13021240 .dir = dir,
......@@ -1305,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u
13051243 self.installed_files.append(file.dupe(self)) catch @panic("OOM");
13061244}
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
13201246pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
13211247 if (self.verbose) {
13221248 log.info("truncate {s}", .{dest_path});
......@@ -1400,7 +1326,7 @@ pub fn execAllowFail(
14001326) ExecError![]u8 {
14011327 assert(argv.len != 0);
14021328
1403 if (!std.process.can_spawn)
1329 if (!process.can_spawn)
14041330 return error.ExecNotSupported;
14051331
14061332 const max_output_size = 400 * 1024;
......@@ -1433,59 +1359,27 @@ pub fn execAllowFail(
14331359 }
14341360}
14351361
1436pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1437 assert(argv.len != 0);
1438
1439 if (self.verbose) {
1440 printCmd(null, argv);
1441 }
1442
1443 if (!std.process.can_spawn) {
1444 if (src_step) |s| log.err("{s}...", .{s.name});
1445 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1446 printCmd(null, argv);
1447 std.os.abort();
1362/// This is a helper function to be called from build.zig scripts, *not* from
1363/// inside step make() functions. If any errors occur, it fails the build with
1364/// a helpful message.
1365pub fn exec(b: *Build, argv: []const []const u8) []u8 {
1366 if (!process.can_spawn) {
1367 std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{
1368 try allocPrintCmd(b.allocator, null, argv),
1369 });
1370 process.exit(1);
14481371 }
14491372
14501373 var code: u8 = undefined;
1451 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1452 error.ExecNotSupported => {
1453 if (src_step) |s| log.err("{s}...", .{s.name});
1454 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1455 printCmd(null, argv);
1456 std.os.abort();
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,
1374 return b.execAllowFail(argv, &code, .Inherit) catch |err| {
1375 const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");
1376 std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{
1377 @errorName(err), printed_cmd,
1378 });
1379 process.exit(1);
14821380 };
14831381}
14841382
1485pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1486 return self.execFromStep(argv, null);
1487}
1488
14891383pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
14901384 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
14911385}
......@@ -1550,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
15501444
15511445 const full_path = b.pathFromRoot("build.zig.zon");
15521446 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);
15541467}
15551468
1556fn dependencyInner(
1469pub fn dependencyInner(
15571470 b: *Build,
15581471 name: []const u8,
15591472 build_root_string: []const u8,
......@@ -1566,7 +1479,7 @@ fn dependencyInner(
15661479 std.debug.print("unable to open '{s}': {s}\n", .{
15671480 build_root_string, @errorName(err),
15681481 });
1569 std.process.exit(1);
1482 process.exit(1);
15701483 },
15711484 };
15721485 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
......@@ -1610,7 +1523,7 @@ pub const GeneratedFile = struct {
16101523
16111524 pub fn getPath(self: GeneratedFile) []const u8 {
16121525 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}'?",
16141527 .{self.step.name},
16151528 );
16161529 }
......@@ -1650,12 +1563,23 @@ pub const FileSource = union(enum) {
16501563 }
16511564
16521565 /// 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 {
1654 const path = switch (self) {
1655 .path => |p| builder.pathFromRoot(p),
1656 .generated => |gen| gen.getPath(),
1657 };
1658 return path;
1566 pub fn getPath(self: FileSource, src_builder: *Build) []const u8 {
1567 return getPath2(self, src_builder, null);
1568 }
1569
1570 /// Should only be called during make(), returns a path relative to the build root or absolute.
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 }
16591583 }
16601584
16611585 /// Duplicates the file source for a given builder.
......@@ -1667,6 +1591,54 @@ pub const FileSource = union(enum) {
16671591 }
16681592};
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
16701642/// Allocates a new string for assigning a value to a named macro.
16711643/// If the value is omitted, it is set to 1.
16721644/// `name` and `value` need not live longer than the function call.
......@@ -1706,9 +1678,7 @@ pub const InstallDir = union(enum) {
17061678 /// Duplicates the install directory including the path if set to custom.
17071679 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
17081680 if (self == .custom) {
1709 // Written with this temporary to avoid RLS problems
1710 const duped_path = builder.dupe(self.custom);
1711 return .{ .custom = duped_path };
1681 return .{ .custom = builder.dupe(self.custom) };
17121682 } else {
17131683 return self;
17141684 }
......@@ -1756,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
17561726 }
17571727}
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
17591759test {
17601760 _ = CheckFileStep;
17611761 _ = CheckObjectStep;
1762 _ = EmulatableRunStep;
17631762 _ = FmtStep;
17641763 _ = InstallArtifactStep;
17651764 _ = InstallDirStep;
17661765 _ = InstallFileStep;
17671766 _ = ObjCopyStep;
17681767 _ = CompileStep;
1769 _ = LogStep;
17701768 _ = OptionsStep;
17711769 _ = RemoveDirStep;
17721770 _ = RunStep;
lib/std/Build/Cache.zig+74-61
......@@ -7,27 +7,27 @@ pub const Directory = struct {
77 /// directly, but it is needed when passing the directory to a child process.
88 /// `null` means cwd.
99 path: ?[]const u8,
10 handle: std.fs.Dir,
10 handle: fs.Dir,
1111
1212 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
1313 if (self.path) |p| {
1414 // 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);
1616 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 });
1818 } else {
19 return std.fs.path.join(allocator, paths);
19 return fs.path.join(allocator, paths);
2020 }
2121 }
2222
2323 pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {
2424 if (self.path) |p| {
2525 // 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);
2727 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 });
2929 } else {
30 return std.fs.path.joinZ(allocator, paths);
30 return fs.path.joinZ(allocator, paths);
3131 }
3232 }
3333
......@@ -39,6 +39,20 @@ pub const Directory = struct {
3939 if (self.path) |p| gpa.free(p);
4040 self.* = undefined;
4141 }
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 }
4256};
4357
4458gpa: Allocator,
......@@ -243,10 +257,10 @@ pub const HashHelper = struct {
243257 hh.hasher.final(&bin_digest);
244258
245259 var out_digest: [hex_digest_len]u8 = undefined;
246 _ = std.fmt.bufPrint(
260 _ = fmt.bufPrint(
247261 &out_digest,
248262 "{s}",
249 .{std.fmt.fmtSliceHexLower(&bin_digest)},
263 .{fmt.fmtSliceHexLower(&bin_digest)},
250264 ) catch unreachable;
251265 return out_digest;
252266 }
......@@ -365,10 +379,10 @@ pub const Manifest = struct {
365379 var bin_digest: BinDigest = undefined;
366380 self.hash.hasher.final(&bin_digest);
367381
368 _ = std.fmt.bufPrint(
382 _ = fmt.bufPrint(
369383 &self.hex_digest,
370384 "{s}",
371 .{std.fmt.fmtSliceHexLower(&bin_digest)},
385 .{fmt.fmtSliceHexLower(&bin_digest)},
372386 ) catch unreachable;
373387
374388 self.hash.hasher = hasher_init;
......@@ -408,7 +422,11 @@ pub const Manifest = struct {
408422 self.have_exclusive_lock = true;
409423 return false; // cache miss; exclusive lock already held
410424 } 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,
412430 else => |e| return e,
413431 }
414432 },
......@@ -425,7 +443,10 @@ pub const Manifest = struct {
425443 self.manifest_file = manifest_file;
426444 self.have_exclusive_lock = true;
427445 } 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 => {
429450 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{
430451 .lock = .Shared,
431452 });
......@@ -469,7 +490,7 @@ pub const Manifest = struct {
469490 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
470491 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
471492 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;
473494 const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat;
474495 if (prefix >= self.cache.prefixes_len) return error.InvalidFormat;
475496
......@@ -806,10 +827,10 @@ pub const Manifest = struct {
806827 self.hash.hasher.final(&bin_digest);
807828
808829 var out_digest: [hex_digest_len]u8 = undefined;
809 _ = std.fmt.bufPrint(
830 _ = fmt.bufPrint(
810831 &out_digest,
811832 "{s}",
812 .{std.fmt.fmtSliceHexLower(&bin_digest)},
833 .{fmt.fmtSliceHexLower(&bin_digest)},
813834 ) catch unreachable;
814835
815836 return out_digest;
......@@ -831,10 +852,10 @@ pub const Manifest = struct {
831852 var encoded_digest: [hex_digest_len]u8 = undefined;
832853
833854 for (self.files.items) |file| {
834 _ = std.fmt.bufPrint(
855 _ = fmt.bufPrint(
835856 &encoded_digest,
836857 "{s}",
837 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},
858 .{fmt.fmtSliceHexLower(&file.bin_digest)},
838859 ) catch unreachable;
839860 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{
840861 file.stat.size,
......@@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
955976}
956977
957978// Create/Write a file, close it, then grab its stat.mtime timestamp.
958fn testGetCurrentFileTimestamp() !i128 {
979fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 {
959980 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, .{
962983 .read = true,
963984 .truncate = true,
964985 });
965986 defer {
966987 file.close();
967 fs.cwd().deleteFile(test_out_file) catch {};
988 dir.deleteFile(test_out_file) catch {};
968989 }
969990
970991 return (try file.stat()).mtime;
......@@ -976,16 +997,17 @@ test "cache file and then recall it" {
976997 return error.SkipZigTest;
977998 }
978999
979 const cwd = fs.cwd();
1000 var tmp = testing.tmpDir(.{});
1001 defer tmp.cleanup();
9801002
9811003 const temp_file = "test.txt";
9821004 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
9861008 // Wait for file timestamps to tick
987 const initial_time = try testGetCurrentFileTimestamp();
988 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1009 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1010 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
9891011 std.time.sleep(1);
9901012 }
9911013
......@@ -995,9 +1017,9 @@ test "cache file and then recall it" {
9951017 {
9961018 var cache = Cache{
9971019 .gpa = testing.allocator,
998 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1020 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
9991021 };
1000 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1022 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
10011023 defer cache.manifest_dir.close();
10021024
10031025 {
......@@ -1033,9 +1055,6 @@ test "cache file and then recall it" {
10331055
10341056 try testing.expectEqual(digest1, digest2);
10351057 }
1036
1037 try cwd.deleteTree(temp_manifest_dir);
1038 try cwd.deleteFile(temp_file);
10391058}
10401059
10411060test "check that changing a file makes cache fail" {
......@@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" {
10431062 // https://github.com/ziglang/zig/issues/5437
10441063 return error.SkipZigTest;
10451064 }
1046 const cwd = fs.cwd();
1065 var tmp = testing.tmpDir(.{});
1066 defer tmp.cleanup();
10471067
10481068 const temp_file = "cache_hash_change_file_test.txt";
10491069 const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
10501070 const original_temp_file_contents = "Hello, world!\n";
10511071 const updated_temp_file_contents = "Hello, world; but updated!\n";
10521072
1053 try cwd.deleteTree(temp_manifest_dir);
1054 try cwd.deleteTree(temp_file);
1055
1056 try cwd.writeFile(temp_file, original_temp_file_contents);
1073 try tmp.dir.writeFile(temp_file, original_temp_file_contents);
10571074
10581075 // Wait for file timestamps to tick
1059 const initial_time = try testGetCurrentFileTimestamp();
1060 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1076 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1077 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
10611078 std.time.sleep(1);
10621079 }
10631080
......@@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" {
10671084 {
10681085 var cache = Cache{
10691086 .gpa = testing.allocator,
1070 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1087 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
10711088 };
1072 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1089 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
10731090 defer cache.manifest_dir.close();
10741091
10751092 {
......@@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" {
10891106 try ch.writeManifest();
10901107 }
10911108
1092 try cwd.writeFile(temp_file, updated_temp_file_contents);
1109 try tmp.dir.writeFile(temp_file, updated_temp_file_contents);
10931110
10941111 {
10951112 var ch = cache.obtain();
......@@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" {
11111128
11121129 try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
11131130 }
1114
1115 try cwd.deleteTree(temp_manifest_dir);
1116 try cwd.deleteTree(temp_file);
11171131}
11181132
11191133test "no file inputs" {
......@@ -1121,18 +1135,20 @@ test "no file inputs" {
11211135 // https://github.com/ziglang/zig/issues/5437
11221136 return error.SkipZigTest;
11231137 }
1124 const cwd = fs.cwd();
1138
1139 var tmp = testing.tmpDir(.{});
1140 defer tmp.cleanup();
1141
11251142 const temp_manifest_dir = "no_file_inputs_manifest_dir";
1126 defer cwd.deleteTree(temp_manifest_dir) catch {};
11271143
11281144 var digest1: [hex_digest_len]u8 = undefined;
11291145 var digest2: [hex_digest_len]u8 = undefined;
11301146
11311147 var cache = Cache{
11321148 .gpa = testing.allocator,
1133 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1149 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
11341150 };
1135 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1151 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
11361152 defer cache.manifest_dir.close();
11371153
11381154 {
......@@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" {
11671183 // https://github.com/ziglang/zig/issues/5437
11681184 return error.SkipZigTest;
11691185 }
1170 const cwd = fs.cwd();
1186 var tmp = testing.tmpDir(.{});
1187 defer tmp.cleanup();
11711188
11721189 const temp_file1 = "cache_hash_post_file_test1.txt";
11731190 const temp_file2 = "cache_hash_post_file_test2.txt";
11741191 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
11751192
1176 try cwd.writeFile(temp_file1, "Hello, world!\n");
1177 try cwd.writeFile(temp_file2, "Hello world the second!\n");
1193 try tmp.dir.writeFile(temp_file1, "Hello, world!\n");
1194 try tmp.dir.writeFile(temp_file2, "Hello world the second!\n");
11781195
11791196 // Wait for file timestamps to tick
1180 const initial_time = try testGetCurrentFileTimestamp();
1181 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1197 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1198 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
11821199 std.time.sleep(1);
11831200 }
11841201
......@@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" {
11891206 {
11901207 var cache = Cache{
11911208 .gpa = testing.allocator,
1192 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1209 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
11931210 };
1194 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1211 cache.addPrefix(.{ .path = null, .handle = tmp.dir });
11951212 defer cache.manifest_dir.close();
11961213
11971214 {
......@@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" {
12241241 try testing.expect(mem.eql(u8, &digest1, &digest2));
12251242
12261243 // 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
12291246 // Wait for file timestamps to tick
1230 const initial_time2 = try testGetCurrentFileTimestamp();
1231 while ((try testGetCurrentFileTimestamp()) == initial_time2) {
1247 const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir);
1248 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) {
12321249 std.time.sleep(1);
12331250 }
12341251
......@@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" {
12511268
12521269 try testing.expect(!mem.eql(u8, &digest1, &digest3));
12531270 }
1254
1255 try cwd.deleteTree(temp_manifest_dir);
1256 try cwd.deleteFile(temp_file1);
1257 try cwd.deleteFile(temp_file2);
12581271}
lib/std/Build/CheckFileStep.zig+62-25
......@@ -1,51 +1,88 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const mem = std.mem;
5
6const CheckFileStep = @This();
7
8pub const base_id = .check_file;
1//! Fail the build step if a file does not match certain checks.
2//! TODO: make this more flexible, supporting more kinds of checks.
3//! TODO: generalize the code in std.testing.expectEqualStrings and make this
4//! CheckFileStep produce those helpful diagnostics when there is not a match.
95
106step: Step,
11builder: *std.Build,
127expected_matches: []const []const u8,
8expected_exact: ?[]const u8,
139source: std.Build.FileSource,
1410max_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
1619pub fn create(
17 builder: *std.Build,
20 owner: *std.Build,
1821 source: std.Build.FileSource,
19 expected_matches: []const []const u8,
22 options: Options,
2023) *CheckFileStep {
21 const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");
22 self.* = CheckFileStep{
23 .builder = builder,
24 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
25 .source = source.dupe(builder),
26 .expected_matches = builder.dupeStrings(expected_matches),
24 const self = owner.allocator.create(CheckFileStep) catch @panic("OOM");
25 self.* = .{
26 .step = Step.init(.{
27 .id = .check_file,
28 .name = "CheckFile",
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,
2735 };
2836 self.source.addStepDependencies(&self.step);
2937 return self;
3038}
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;
3347 const self = @fieldParentPtr(CheckFileStep, "step", step);
3448
35 const src_path = self.source.getPath(self.builder);
36 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
49 const src_path = self.source.getPath(b);
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
3856 for (self.expected_matches) |expected_match| {
3957 if (mem.indexOf(u8, contents, expected_match) == null) {
40 std.debug.print(
58 return step.fail(
4159 \\
42 \\========= Expected to find: ===================
60 \\========= expected to find: ===================
4361 \\{s}
44 \\========= But file does not contain it: =======
62 \\========= but file does not contain it: =======
4563 \\{s}
46 \\
64 \\===============================================
4765 , .{ 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 });
4980 }
5081 }
5182}
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();
1010
1111const Allocator = mem.Allocator;
1212const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
1413
1514pub const base_id = .check_object;
1615
1716step: Step,
18builder: *std.Build,
1917source: std.Build.FileSource,
2018max_bytes: usize = 20 * 1024 * 1024,
2119checks: std.ArrayList(Check),
2220dump_symtab: bool = false,
2321obj_format: std.Target.ObjectFormat,
2422
25pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
26 const gpa = builder.allocator;
23pub fn create(
24 owner: *std.Build,
25 source: std.Build.FileSource,
26 obj_format: std.Target.ObjectFormat,
27) *CheckObjectStep {
28 const gpa = owner.allocator;
2729 const self = gpa.create(CheckObjectStep) catch @panic("OOM");
2830 self.* = .{
29 .builder = builder,
30 .step = Step.init(.check_file, "CheckObject", gpa, make),
31 .source = source.dupe(builder),
31 .step = Step.init(.{
32 .id = .check_file,
33 .name = "CheckObject",
34 .owner = owner,
35 .makeFn = make,
36 }),
37 .source = source.dupe(owner),
3238 .checks = std.ArrayList(Check).init(gpa),
3339 .obj_format = obj_format,
3440 };
......@@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std
3844
3945/// Runs and (optionally) compares the output of a binary.
4046/// 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 {
4251 const dependencies_len = self.step.dependencies.items.len;
4352 assert(dependencies_len > 0);
4453 const exe_step = self.step.dependencies.items[dependencies_len - 1];
4554 const exe = exe_step.cast(std.Build.CompileStep).?;
46 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
47 emulatable_step.step.dependOn(&self.step);
48 return emulatable_step;
55 const run = self.step.owner.addRunArtifact(exe);
56 run.skip_foreign_checks = true;
57 run.step.dependOn(&self.step);
58 return run;
4959}
5060
5161/// There two types of actions currently suported:
......@@ -123,7 +133,8 @@ const Action = struct {
123133 /// Will return true if the `phrase` is correctly parsed into an RPN program and
124134 /// its reduced, computed value compares using `op` with the expected value, either
125135 /// 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;
127138 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
128139 var values = std.ArrayList(u64).init(gpa);
129140
......@@ -140,11 +151,11 @@ const Action = struct {
140151 } else {
141152 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142153 break :blk global_vars.get(next) orelse {
143 std.debug.print(
154 try step.addError(
144155 \\
145 \\========= Variable was not extracted: ===========
156 \\========= variable was not extracted: ===========
146157 \\{s}
147 \\
158 \\=================================================
148159 , .{next});
149160 return error.UnknownVariable;
150161 };
......@@ -176,11 +187,11 @@ const Action = struct {
176187
177188 const exp_value = switch (act.expected.?.value) {
178189 .variable => |name| global_vars.get(name) orelse {
179 std.debug.print(
190 try step.addError(
180191 \\
181 \\========= Variable was not extracted: ===========
192 \\========= variable was not extracted: ===========
182193 \\{s}
183 \\
194 \\=================================================
184195 , .{name});
185196 return error.UnknownVariable;
186197 },
......@@ -249,7 +260,7 @@ const Check = struct {
249260
250261/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
251262pub 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);
253264 new_check.match(phrase);
254265 self.checks.append(new_check) catch @panic("OOM");
255266}
......@@ -291,34 +302,34 @@ pub fn checkComputeCompare(
291302 program: []const u8,
292303 expected: ComputeCompareExpected,
293304) void {
294 var new_check = Check.create(self.builder);
305 var new_check = Check.create(self.step.owner);
295306 new_check.computeCmp(program, expected);
296307 self.checks.append(new_check) catch @panic("OOM");
297308}
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;
300314 const self = @fieldParentPtr(CheckObjectStep, "step", step);
301315
302 const gpa = self.builder.allocator;
303 const src_path = self.source.getPath(self.builder);
304 const contents = try fs.cwd().readFileAllocOptions(
316 const src_path = self.source.getPath(b);
317 const contents = fs.cwd().readFileAllocOptions(
305318 gpa,
306319 src_path,
307320 self.max_bytes,
308321 null,
309322 @alignOf(u64),
310323 null,
311 );
324 ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) });
312325
313326 const output = switch (self.obj_format) {
314 .macho => try MachODumper.parseAndDump(contents, .{
315 .gpa = gpa,
327 .macho => try MachODumper.parseAndDump(step, contents, .{
316328 .dump_symtab = self.dump_symtab,
317329 }),
318330 .elf => @panic("TODO elf parser"),
319331 .coff => @panic("TODO coff parser"),
320 .wasm => try WasmDumper.parseAndDump(contents, .{
321 .gpa = gpa,
332 .wasm => try WasmDumper.parseAndDump(step, contents, .{
322333 .dump_symtab = self.dump_symtab,
323334 }),
324335 else => unreachable,
......@@ -334,54 +345,50 @@ fn make(step: *Step) !void {
334345 while (it.next()) |line| {
335346 if (try act.match(line, &vars)) break;
336347 } else {
337 std.debug.print(
348 return step.fail(
338349 \\
339 \\========= Expected to find: ==========================
350 \\========= expected to find: ==========================
340351 \\{s}
341 \\========= But parsed file does not contain it: =======
352 \\========= but parsed file does not contain it: =======
342353 \\{s}
343 \\
354 \\======================================================
344355 , .{ act.phrase, output });
345 return error.TestFailed;
346356 }
347357 },
348358 .not_present => {
349359 while (it.next()) |line| {
350360 if (try act.match(line, &vars)) {
351 std.debug.print(
361 return step.fail(
352362 \\
353 \\========= Expected not to find: ===================
363 \\========= expected not to find: ===================
354364 \\{s}
355 \\========= But parsed file does contain it: ========
365 \\========= but parsed file does contain it: ========
356366 \\{s}
357 \\
367 \\===================================================
358368 , .{ act.phrase, output });
359 return error.TestFailed;
360369 }
361370 }
362371 },
363372 .compute_cmp => {
364 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
373 const res = act.computeCmp(step, vars) catch |err| switch (err) {
365374 error.UnknownVariable => {
366 std.debug.print(
367 \\========= From parsed file: =====================
375 return step.fail(
376 \\========= from parsed file: =====================
368377 \\{s}
369 \\
378 \\=================================================
370379 , .{output});
371 return error.TestFailed;
372380 },
373381 else => |e| return e,
374382 };
375383 if (!res) {
376 std.debug.print(
384 return step.fail(
377385 \\
378 \\========= Comparison failed for action: ===========
386 \\========= comparison failed for action: ===========
379387 \\{s} {}
380 \\========= From parsed file: =======================
388 \\========= from parsed file: =======================
381389 \\{s}
382 \\
390 \\===================================================
383391 , .{ act.phrase, act.expected.?, output });
384 return error.TestFailed;
385392 }
386393 },
387394 }
......@@ -390,7 +397,6 @@ fn make(step: *Step) !void {
390397}
391398
392399const Opts = struct {
393 gpa: ?Allocator = null,
394400 dump_symtab: bool = false,
395401};
396402
......@@ -398,8 +404,8 @@ const MachODumper = struct {
398404 const LoadCommandIterator = macho.LoadCommandIterator;
399405 const symtab_label = "symtab";
400406
401 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
402 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
407 fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
408 const gpa = step.owner.allocator;
403409 var stream = std.io.fixedBufferStream(bytes);
404410 const reader = stream.reader();
405411
......@@ -681,8 +687,8 @@ const MachODumper = struct {
681687const WasmDumper = struct {
682688 const symtab_label = "symbols";
683689
684 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
685 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
690 fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 {
691 const gpa = step.owner.allocator;
686692 if (opts.dump_symtab) {
687693 @panic("TODO: Implement symbol table parsing and dumping");
688694 }
......@@ -703,20 +709,24 @@ const WasmDumper = struct {
703709 const writer = output.writer();
704710
705711 while (reader.readByte()) |current_byte| {
706 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
707 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
708 return err;
712 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch {
713 return step.fail("Found invalid section id '{d}'", .{current_byte});
709714 };
710715
711716 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);
713718 fbs.pos += section_length;
714719 } else |_| {} // reached end of stream
715720
716721 return output.toOwnedSlice();
717722 }
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 {
720730 var fbs = std.io.fixedBufferStream(data);
721731 const reader = fbs.reader();
722732
......@@ -739,7 +749,7 @@ const WasmDumper = struct {
739749 => {
740750 const entries = try std.leb.readULEB128(u32, reader);
741751 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);
743753 },
744754 .custom => {
745755 const name_length = try std.leb.readULEB128(u32, reader);
......@@ -748,7 +758,7 @@ const WasmDumper = struct {
748758 try writer.print("\nname {s}\n", .{name});
749759
750760 if (mem.eql(u8, name, "name")) {
751 try parseDumpNames(reader, writer, data);
761 try parseDumpNames(step, reader, writer, data);
752762 } else if (mem.eql(u8, name, "producers")) {
753763 try parseDumpProducers(reader, writer, data);
754764 } else if (mem.eql(u8, name, "target_features")) {
......@@ -764,7 +774,7 @@ const WasmDumper = struct {
764774 }
765775 }
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 {
768778 var fbs = std.io.fixedBufferStream(data);
769779 const reader = fbs.reader();
770780
......@@ -774,19 +784,18 @@ const WasmDumper = struct {
774784 while (i < entries) : (i += 1) {
775785 const func_type = try reader.readByte();
776786 if (func_type != std.wasm.function_type) {
777 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
778 return error.UnexpectedByte;
787 return step.fail("expected function type, found byte '{d}'", .{func_type});
779788 }
780789 const params = try std.leb.readULEB128(u32, reader);
781790 try writer.print("params {d}\n", .{params});
782791 var index: u32 = 0;
783792 while (index < params) : (index += 1) {
784 try parseDumpType(std.wasm.Valtype, reader, writer);
793 try parseDumpType(step, std.wasm.Valtype, reader, writer);
785794 } else index = 0;
786795 const returns = try std.leb.readULEB128(u32, reader);
787796 try writer.print("returns {d}\n", .{returns});
788797 while (index < returns) : (index += 1) {
789 try parseDumpType(std.wasm.Valtype, reader, writer);
798 try parseDumpType(step, std.wasm.Valtype, reader, writer);
790799 }
791800 }
792801 },
......@@ -800,9 +809,8 @@ const WasmDumper = struct {
800809 const name = data[fbs.pos..][0..name_len];
801810 fbs.pos += name_len;
802811
803 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
804 std.debug.print("Invalid import kind\n", .{});
805 return err;
812 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch {
813 return step.fail("invalid import kind", .{});
806814 };
807815
808816 try writer.print(
......@@ -819,11 +827,11 @@ const WasmDumper = struct {
819827 try parseDumpLimits(reader, writer);
820828 },
821829 .global => {
822 try parseDumpType(std.wasm.Valtype, reader, writer);
830 try parseDumpType(step, std.wasm.Valtype, reader, writer);
823831 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
824832 },
825833 .table => {
826 try parseDumpType(std.wasm.RefType, reader, writer);
834 try parseDumpType(step, std.wasm.RefType, reader, writer);
827835 try parseDumpLimits(reader, writer);
828836 },
829837 }
......@@ -838,7 +846,7 @@ const WasmDumper = struct {
838846 .table => {
839847 var i: u32 = 0;
840848 while (i < entries) : (i += 1) {
841 try parseDumpType(std.wasm.RefType, reader, writer);
849 try parseDumpType(step, std.wasm.RefType, reader, writer);
842850 try parseDumpLimits(reader, writer);
843851 }
844852 },
......@@ -851,9 +859,9 @@ const WasmDumper = struct {
851859 .global => {
852860 var i: u32 = 0;
853861 while (i < entries) : (i += 1) {
854 try parseDumpType(std.wasm.Valtype, reader, writer);
862 try parseDumpType(step, std.wasm.Valtype, reader, writer);
855863 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
856 try parseDumpInit(reader, writer);
864 try parseDumpInit(step, reader, writer);
857865 }
858866 },
859867 .@"export" => {
......@@ -863,9 +871,8 @@ const WasmDumper = struct {
863871 const name = data[fbs.pos..][0..name_len];
864872 fbs.pos += name_len;
865873 const kind_byte = try std.leb.readULEB128(u8, reader);
866 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
867 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
868 return err;
874 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch {
875 return step.fail("invalid export kind value '{d}'", .{kind_byte});
869876 };
870877 const index = try std.leb.readULEB128(u32, reader);
871878 try writer.print(
......@@ -880,7 +887,7 @@ const WasmDumper = struct {
880887 var i: u32 = 0;
881888 while (i < entries) : (i += 1) {
882889 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
885892 const function_indexes = try std.leb.readULEB128(u32, reader);
886893 var function_index: u32 = 0;
......@@ -896,7 +903,7 @@ const WasmDumper = struct {
896903 while (i < entries) : (i += 1) {
897904 const index = try std.leb.readULEB128(u32, reader);
898905 try writer.print("memory index 0x{x}\n", .{index});
899 try parseDumpInit(reader, writer);
906 try parseDumpInit(step, reader, writer);
900907 const size = try std.leb.readULEB128(u32, reader);
901908 try writer.print("size {d}\n", .{size});
902909 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
......@@ -906,11 +913,10 @@ const WasmDumper = struct {
906913 }
907914 }
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 {
910917 const type_byte = try reader.readByte();
911 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
912 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
913 return err;
918 const valtype = std.meta.intToEnum(WasmType, type_byte) catch {
919 return step.fail("Invalid wasm type value '{d}'", .{type_byte});
914920 };
915921 try writer.print("type {s}\n", .{@tagName(valtype)});
916922 }
......@@ -925,11 +931,10 @@ const WasmDumper = struct {
925931 }
926932 }
927933
928 fn parseDumpInit(reader: anytype, writer: anytype) !void {
934 fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void {
929935 const byte = try std.leb.readULEB128(u8, reader);
930 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
931 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
932 return err;
936 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch {
937 return step.fail("invalid wasm opcode '{d}'", .{byte});
933938 };
934939 switch (opcode) {
935940 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
......@@ -941,14 +946,13 @@ const WasmDumper = struct {
941946 }
942947 const end_opcode = try std.leb.readULEB128(u8, reader);
943948 if (end_opcode != std.wasm.opcode(.end)) {
944 std.debug.print("expected 'end' opcode in init expression\n", .{});
945 return error.MissingEndOpcode;
949 return step.fail("expected 'end' opcode in init expression", .{});
946950 }
947951 }
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 {
950954 while (reader.context.pos < data.len) {
951 try parseDumpType(std.wasm.NameSubsection, reader, writer);
955 try parseDumpType(step, std.wasm.NameSubsection, reader, writer);
952956 const size = try std.leb.readULEB128(u32, reader);
953957 const entries = try std.leb.readULEB128(u32, reader);
954958 try writer.print(
lib/std/Build/CompileStep.zig+443-378
......@@ -1,7 +1,6 @@
11const builtin = @import("builtin");
22const std = @import("../std.zig");
33const mem = std.mem;
4const log = std.log;
54const fs = std.fs;
65const assert = std.debug.assert;
76const panic = std.debug.panic;
......@@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir;
2221const InstallArtifactStep = std.Build.InstallArtifactStep;
2322const GeneratedFile = std.Build.GeneratedFile;
2423const ObjCopyStep = std.Build.ObjCopyStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
2624const CheckObjectStep = std.Build.CheckObjectStep;
2725const RunStep = std.Build.RunStep;
2826const OptionsStep = std.Build.OptionsStep;
......@@ -32,7 +30,6 @@ const CompileStep = @This();
3230pub const base_id: Step.Id = .compile;
3331
3432step: Step,
35builder: *std.Build,
3633name: []const u8,
3734target: CrossTarget,
3835target_info: NativeTargetInfo,
......@@ -49,9 +46,9 @@ strip: ?bool,
4946unwind_tables: ?bool,
5047// keep in sync with src/link.zig:CompressDebugSections
5148compress_debug_sections: enum { none, zlib } = .none,
52lib_paths: ArrayList([]const u8),
53rpaths: ArrayList([]const u8),
54framework_dirs: ArrayList([]const u8),
49lib_paths: ArrayList(FileSource),
50rpaths: ArrayList(FileSource),
51framework_dirs: ArrayList(FileSource),
5552frameworks: StringHashMap(FrameworkLinkInfo),
5653verbose_link: bool,
5754verbose_cc: bool,
......@@ -86,7 +83,6 @@ c_std: std.Build.CStd,
8683zig_lib_dir: ?[]const u8,
8784main_pkg_path: ?[]const u8,
8885exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,
9086filter: ?[]const u8,
9187test_evented_io: bool = false,
9288test_runner: ?[]const u8,
......@@ -210,10 +206,17 @@ want_lto: ?bool = null,
210206use_llvm: ?bool = null,
211207use_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
213215output_path_source: GeneratedFile,
214216output_lib_path_source: GeneratedFile,
215217output_h_path_source: GeneratedFile,
216218output_pdb_path_source: GeneratedFile,
219output_dirname_source: GeneratedFile,
217220
218221pub const CSourceFiles = struct {
219222 files: []const []const u8,
......@@ -277,6 +280,7 @@ pub const Options = struct {
277280 kind: Kind,
278281 linkage: ?Linkage = null,
279282 version: ?std.builtin.Version = null,
283 max_rss: usize = 0,
280284};
281285
282286pub const Kind = enum {
......@@ -284,7 +288,6 @@ pub const Kind = enum {
284288 lib,
285289 obj,
286290 @"test",
287 test_exe,
288291};
289292
290293pub const Linkage = enum { dynamic, static };
......@@ -305,18 +308,35 @@ pub const EmitOption = union(enum) {
305308 }
306309};
307310
308pub fn create(builder: *std.Build, options: Options) *CompileStep {
309 const name = builder.dupe(options.name);
310 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
311pub fn create(owner: *std.Build, options: Options) *CompileStep {
312 const name = owner.dupe(options.name);
313 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null;
311314 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
312315 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
313316 }
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");
316337 self.* = CompileStep{
317338 .strip = null,
318339 .unwind_tables = null,
319 .builder = builder,
320340 .verbose_link = false,
321341 .verbose_cc = false,
322342 .optimize = options.optimize,
......@@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
325345 .kind = options.kind,
326346 .root_src = root_src,
327347 .name = name,
328 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
329 .step = Step.init(base_id, name, builder.allocator, make),
348 .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator),
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 }),
330356 .version = options.version,
331357 .out_filename = undefined,
332 .out_h_filename = builder.fmt("{s}.h", .{name}),
358 .out_h_filename = owner.fmt("{s}.h", .{name}),
333359 .out_lib_filename = undefined,
334 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
360 .out_pdb_filename = owner.fmt("{s}.pdb", .{name}),
335361 .major_only_filename = null,
336362 .name_only_filename = null,
337 .modules = std.StringArrayHashMap(*Module).init(builder.allocator),
338 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
339 .link_objects = ArrayList(LinkObject).init(builder.allocator),
340 .c_macros = ArrayList([]const u8).init(builder.allocator),
341 .lib_paths = ArrayList([]const u8).init(builder.allocator),
342 .rpaths = ArrayList([]const u8).init(builder.allocator),
343 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
344 .installed_headers = ArrayList(*Step).init(builder.allocator),
363 .modules = std.StringArrayHashMap(*Module).init(owner.allocator),
364 .include_dirs = ArrayList(IncludeDir).init(owner.allocator),
365 .link_objects = ArrayList(LinkObject).init(owner.allocator),
366 .c_macros = ArrayList([]const u8).init(owner.allocator),
367 .lib_paths = ArrayList(FileSource).init(owner.allocator),
368 .rpaths = ArrayList(FileSource).init(owner.allocator),
369 .framework_dirs = ArrayList(FileSource).init(owner.allocator),
370 .installed_headers = ArrayList(*Step).init(owner.allocator),
345371 .object_src = undefined,
346372 .c_std = std.Build.CStd.C99,
347373 .zig_lib_dir = null,
348374 .main_pkg_path = null,
349375 .exec_cmd_args = null,
350 .name_prefix = "",
351376 .filter = null,
352377 .test_runner = null,
353378 .disable_stack_probing = false,
......@@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
363388 .output_lib_path_source = GeneratedFile{ .step = &self.step },
364389 .output_h_path_source = GeneratedFile{ .step = &self.step },
365390 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
391 .output_dirname_source = GeneratedFile{ .step = &self.step },
366392
367393 .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"),
368394 };
......@@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
372398}
373399
374400fn computeOutFileNames(self: *CompileStep) void {
401 const b = self.step.owner;
375402 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, .{
378405 .root_name = self.name,
379406 .target = target,
380407 .output_mode = switch (self.kind) {
381408 .lib => .Lib,
382409 .obj => .Obj,
383 .exe, .@"test", .test_exe => .Exe,
410 .exe, .@"test" => .Exe,
384411 },
385412 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
386413 .dynamic => .Dynamic,
......@@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void {
394421 self.out_lib_filename = self.out_filename;
395422 } else if (self.version) |version| {
396423 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", .{
398425 self.name,
399426 version.major,
400427 });
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});
402429 self.out_lib_filename = self.out_filename;
403430 } 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});
405432 } else {
406 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
407 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
433 self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major });
434 self.name_only_filename = b.fmt("lib{s}.so", .{self.name});
408435 self.out_lib_filename = self.out_filename;
409436 }
410437 } else {
411438 if (target.isDarwin()) {
412439 self.out_lib_filename = self.out_filename;
413440 } 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});
415442 } else {
416443 self.out_lib_filename = self.out_filename;
417444 }
418445 }
419446 if (self.output_dir != null) {
420 self.output_lib_path_source.path = self.builder.pathJoin(
447 self.output_lib_path_source.path = b.pathJoin(
421448 &.{ self.output_dir.?, self.out_lib_filename },
422449 );
423450 }
......@@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void {
425452}
426453
427454pub 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);
429457}
430458
431459pub fn install(self: *CompileStep) void {
432 self.builder.installArtifact(self);
460 const b = self.step.owner;
461 b.installArtifact(self);
433462}
434463
435pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
436 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
437 a.builder.getInstallStep().dependOn(&install_file.step);
438 a.installed_headers.append(&install_file.step) catch @panic("OOM");
464pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
465 const b = cs.step.owner;
466 const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);
467 b.getInstallStep().dependOn(&install_file.step);
468 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
439469}
440470
441471pub const InstallConfigHeaderOptions = struct {
......@@ -449,13 +479,14 @@ pub fn installConfigHeader(
449479 options: InstallConfigHeaderOptions,
450480) void {
451481 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(
453484 .{ .generated = &config_header.output_file },
454485 options.install_dir,
455486 dest_rel_path,
456487 );
457488 install_file.step.dependOn(&config_header.step);
458 cs.builder.getInstallStep().dependOn(&install_file.step);
489 b.getInstallStep().dependOn(&install_file.step);
459490 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
460491}
461492
......@@ -472,91 +503,83 @@ pub fn installHeadersDirectory(
472503}
473504
474505pub fn installHeadersDirectoryOptions(
475 a: *CompileStep,
506 cs: *CompileStep,
476507 options: std.Build.InstallDirStep.Options,
477508) void {
478 const install_dir = a.builder.addInstallDirectory(options);
479 a.builder.getInstallStep().dependOn(&install_dir.step);
480 a.installed_headers.append(&install_dir.step) catch @panic("OOM");
509 const b = cs.step.owner;
510 const install_dir = b.addInstallDirectory(options);
511 b.getInstallStep().dependOn(&install_dir.step);
512 cs.installed_headers.append(&install_dir.step) catch @panic("OOM");
481513}
482514
483pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
515pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void {
484516 assert(l.kind == .lib);
485 const install_step = a.builder.getInstallStep();
517 const b = cs.step.owner;
518 const install_step = b.getInstallStep();
486519 // Copy each element from installed_headers, modifying the builder
487520 // to be the new parent's builder.
488521 for (l.installed_headers.items) |step| {
489522 const step_copy = switch (step.id) {
490523 inline .install_file, .install_dir => |id| blk: {
491524 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");
493526 ptr.* = step.cast(T).?.*;
494 ptr.override_source_builder = ptr.builder;
495 ptr.builder = a.builder;
527 ptr.dest_builder = b;
496528 break :blk &ptr.step;
497529 },
498530 else => unreachable,
499531 };
500 a.installed_headers.append(step_copy) catch @panic("OOM");
532 cs.installed_headers.append(step_copy) catch @panic("OOM");
501533 install_step.dependOn(step_copy);
502534 }
503 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
535 cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
504536}
505537
506538pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep {
539 const b = cs.step.owner;
507540 var copy = options;
508541 if (copy.basename == null) {
509542 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) });
511544 } else {
512545 copy.basename = cs.name;
513546 }
514547 }
515 return cs.builder.addObjCopy(cs.getOutputSource(), copy);
548 return b.addObjCopy(cs.getOutputSource(), copy);
516549}
517550
518551/// Deprecated: use `std.Build.addRunArtifact`
519552/// This function will run in the context of the package that created the executable,
520553/// which is undesirable when running an executable provided by a dependency package.
521pub fn run(exe: *CompileStep) *RunStep {
522 return exe.builder.addRunArtifact(exe);
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;
554pub fn run(cs: *CompileStep) *RunStep {
555 return cs.step.owner.addRunArtifact(cs);
537556}
538557
539pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
540 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
558pub fn checkObject(self: *CompileStep) *CheckObjectStep {
559 return CheckObjectStep.create(self.step.owner, self.getOutputSource(), self.target_info.target.ofmt);
541560}
542561
543562pub 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);
545565 source.addStepDependencies(&self.step);
546566}
547567
548568pub 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");
550571}
551572
552573pub 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), .{
554576 .needed = true,
555577 }) catch @panic("OOM");
556578}
557579
558580pub 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), .{
560583 .weak = true,
561584 }) catch @panic("OOM");
562585}
......@@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool {
595618 if (!self.target.isWindows() and !self.target.isUefi()) return false;
596619 if (self.target.getObjectFormat() == .c) return false;
597620 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";
599622}
600623
601624pub fn linkLibC(self: *CompileStep) void {
......@@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void {
609632/// If the value is omitted, it is set to 1.
610633/// `name` and `value` need not live longer than the function call.
611634pub 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);
613637 self.c_macros.append(macro) catch @panic("OOM");
614638}
615639
616640/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
617641pub 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");
619644}
620645
621646/// This one has no integration with anything, it just puts -lname on the command line.
622647/// Prefer to use `linkSystemLibrary` instead.
623648pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
649 const b = self.step.owner;
624650 self.link_objects.append(.{
625651 .system_lib = .{
626 .name = self.builder.dupe(name),
652 .name = b.dupe(name),
627653 .needed = false,
628654 .weak = false,
629655 .use_pkg_config = .no,
......@@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
634660/// This one has no integration with anything, it just puts -needed-lname on the command line.
635661/// Prefer to use `linkSystemLibraryNeeded` instead.
636662pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
663 const b = self.step.owner;
637664 self.link_objects.append(.{
638665 .system_lib = .{
639 .name = self.builder.dupe(name),
666 .name = b.dupe(name),
640667 .needed = true,
641668 .weak = false,
642669 .use_pkg_config = .no,
......@@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
647674/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
648675/// command line. Prefer to use `linkSystemLibraryWeak` instead.
649676pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
677 const b = self.step.owner;
650678 self.link_objects.append(.{
651679 .system_lib = .{
652 .name = self.builder.dupe(name),
680 .name = b.dupe(name),
653681 .needed = false,
654682 .weak = true,
655683 .use_pkg_config = .no,
......@@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
660688/// This links against a system library, exclusively using pkg-config to find the library.
661689/// Prefer to use `linkSystemLibrary` instead.
662690pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
691 const b = self.step.owner;
663692 self.link_objects.append(.{
664693 .system_lib = .{
665 .name = self.builder.dupe(lib_name),
694 .name = b.dupe(lib_name),
666695 .needed = false,
667696 .weak = false,
668697 .use_pkg_config = .force,
......@@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8)
673702/// This links against a system library, exclusively using pkg-config to find the library.
674703/// Prefer to use `linkSystemLibraryNeeded` instead.
675704pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
705 const b = self.step.owner;
676706 self.link_objects.append(.{
677707 .system_lib = .{
678 .name = self.builder.dupe(lib_name),
708 .name = b.dupe(lib_name),
679709 .needed = true,
680710 .weak = false,
681711 .use_pkg_config = .force,
......@@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons
685715
686716/// Run pkg-config for the given library name and parse the output, returning the arguments
687717/// 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;
689720 const pkg_name = match: {
690721 // First we have to map the library name to pkg config name. Unfortunately,
691722 // there are several examples where this is not straightforward:
692723 // -lSDL2 -> pkg-config sdl2
693724 // -lgdk-3 -> pkg-config gdk-3.0
694725 // -latk-1.0 -> pkg-config atk
695 const pkgs = try getPkgConfigList(self.builder);
726 const pkgs = try getPkgConfigList(b);
696727
697728 // Exact match means instant winner.
698729 for (pkgs) |pkg| {
......@@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
732763 };
733764
734765 var code: u8 = undefined;
735 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
766 const stdout = if (b.execAllowFail(&[_][]const u8{
736767 "pkg-config",
737768 pkg_name,
738769 "--cflags",
......@@ -742,11 +773,10 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
742773 error.ExecNotSupported => return error.PkgConfigFailed,
743774 error.ExitCodeFailure => return error.PkgConfigFailed,
744775 error.FileNotFound => return error.PkgConfigNotInstalled,
745 error.ChildExecFailed => return error.PkgConfigFailed,
746776 else => return err,
747777 };
748778
749 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
779 var zig_args = ArrayList([]const u8).init(b.allocator);
750780 defer zig_args.deinit();
751781
752782 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
771801 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
772802 } else if (mem.startsWith(u8, tok, "-D")) {
773803 try zig_args.append(tok);
774 } else if (self.builder.verbose) {
775 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
804 } else if (b.debug_pkg_config) {
805 return self.step.fail("unknown pkg-config flag '{s}'", .{tok});
776806 }
777807 }
778808
......@@ -795,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
795825 needed: bool = false,
796826 weak: bool = false,
797827}) void {
828 const b = self.step.owner;
798829 if (isLibCLibrary(name)) {
799830 self.linkLibC();
800831 return;
......@@ -806,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
806837
807838 self.link_objects.append(.{
808839 .system_lib = .{
809 .name = self.builder.dupe(name),
840 .name = b.dupe(name),
810841 .needed = opts.needed,
811842 .weak = opts.weak,
812843 .use_pkg_config = .yes,
......@@ -814,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
814845 }) catch @panic("OOM");
815846}
816847
817pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
818 assert(self.kind == .@"test" or self.kind == .test_exe);
819 self.name_prefix = self.builder.dupe(text);
848pub fn setName(self: *CompileStep, text: []const u8) void {
849 const b = self.step.owner;
850 assert(self.kind == .@"test");
851 self.name = b.dupe(text);
820852}
821853
822854pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
823 assert(self.kind == .@"test" or self.kind == .test_exe);
824 self.filter = if (text) |t| self.builder.dupe(t) else null;
855 const b = self.step.owner;
856 assert(self.kind == .@"test");
857 self.filter = if (text) |t| b.dupe(t) else null;
825858}
826859
827860pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
828 assert(self.kind == .@"test" or self.kind == .test_exe);
829 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
861 const b = self.step.owner;
862 assert(self.kind == .@"test");
863 self.test_runner = if (path) |p| b.dupePath(p) else null;
830864}
831865
832866/// Handy when you have many C/C++ source files and want them all to have the same flags.
833867pub 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);
837 const flags_copy = self.builder.dupeStrings(flags);
871 const files_copy = b.dupeStrings(files);
872 const flags_copy = b.dupeStrings(flags);
838873
839874 c_source_files.* = .{
840875 .files = files_copy,
......@@ -851,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con
851886}
852887
853888pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
854 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");
855 c_source_file.* = source.dupe(self.builder);
889 const b = self.step.owner;
890 const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM");
891 c_source_file.* = source.dupe(b);
856892 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
857893 source.source.addStepDependencies(&self.step);
858894}
......@@ -866,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {
866902}
867903
868904pub 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);
870907}
871908
872909pub 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);
874912}
875913
876914pub 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;
878917}
879918
880919/// Returns the generated executable, library or object file.
881920/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
882921pub 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 };
884927}
885928
886929/// Returns the generated import library. This function can only be called for libraries.
887930pub fn getOutputLibSource(self: *CompileStep) FileSource {
888931 assert(self.kind == .lib);
889 return FileSource{ .generated = &self.output_lib_path_source };
932 return .{ .generated = &self.output_lib_path_source };
890933}
891934
892935/// Returns the generated header file.
893936/// This function can only be called for libraries or object files which have `emit_h` set.
894937pub 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");
896939 assert(self.emit_h);
897 return FileSource{ .generated = &self.output_h_path_source };
940 return .{ .generated = &self.output_h_path_source };
898941}
899942
900943/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
901944pub fn getOutputPdbSource(self: *CompileStep) FileSource {
902945 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
903946 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 };
905948}
906949
907950pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
951 const b = self.step.owner;
908952 self.link_objects.append(.{
909 .assembly_file = .{ .path = self.builder.dupe(path) },
953 .assembly_file = .{ .path = b.dupe(path) },
910954 }) catch @panic("OOM");
911955}
912956
913957pub 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);
915960 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
916961 source_duped.addStepDependencies(&self.step);
917962}
......@@ -921,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
921966}
922967
923968pub 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");
925971 source.addStepDependencies(&self.step);
926972}
927973
......@@ -936,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath");
936982pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
937983
938984pub 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");
940987}
941988
942989pub 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");
944992}
945993
946994pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
......@@ -949,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi
949997}
950998
951999pub 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);
9531007}
9541008
9551009pub 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);
9571017}
9581018
9591019pub 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);
9611027}
9621028
9631029/// Adds a module to be used with `@import` and exposing it in the current
9641030/// package's module table using `name`.
9651031pub 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);
9691036 defer done.deinit();
9701037 cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");
9711038}
......@@ -973,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
9731040/// Adds a module to be used with `@import` without exposing it in the current
9741041/// package's module table.
9751042pub 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);
9771045 return addModule(cs, name, module);
9781046}
9791047
......@@ -993,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM
9931061/// If Vcpkg was found on the system, it will be added to include and lib
9941062/// paths for the specified target.
9951063pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
1064 const b = self.step.owner;
9961065 // Ideally in the Unattempted case we would call the function recursively
9971066 // after findVcpkgRoot and have only one switch statement, but the compiler
9981067 // cannot resolve the error set.
999 switch (self.builder.vcpkg_root) {
1068 switch (b.vcpkg_root) {
10001069 .unattempted => {
1001 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1070 b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root|
10021071 VcpkgRoot{ .found = root }
10031072 else
10041073 .not_found;
......@@ -1007,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
10071076 .found => {},
10081077 }
10091078
1010 switch (self.builder.vcpkg_root) {
1079 switch (b.vcpkg_root) {
10111080 .unattempted => unreachable,
10121081 .not_found => return error.VcpkgNotFound,
10131082 .found => |root| {
1014 const allocator = self.builder.allocator;
1083 const allocator = b.allocator;
10151084 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" });
10191088 errdefer allocator.free(include_path);
10201089 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
10211090
1022 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1023 try self.lib_paths.append(lib_path);
1091 const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" });
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" });
10261095 },
10271096 }
10281097}
10291098
10301099pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1100 const b = self.step.owner;
10311101 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");
10331103 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;
10351105 }
10361106 self.exec_cmd_args = duped_args;
10371107}
......@@ -1040,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
10401110 self.step.dependOn(&other.step);
10411111 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
10421112 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 }
10431117}
10441118
10451119fn appendModuleArgs(
10461120 cs: *CompileStep,
10471121 zig_args: *ArrayList([]const u8),
10481122) error{OutOfMemory}!void {
1123 const b = cs.step.owner;
10491124 // First, traverse the whole dependency graph and give every module a unique name, ideally one
10501125 // named after what it's called somewhere in the graph. It will help here to have both a mapping
10511126 // 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);
1053 var names = std.StringHashMap(void).init(cs.builder.allocator);
1127 var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator);
1128 var names = std.StringHashMap(void).init(b.allocator);
10541129
10551130 var to_name = std.ArrayList(struct {
10561131 name: []const u8,
10571132 mod: *Module,
1058 }).init(cs.builder.allocator);
1133 }).init(b.allocator);
10591134 {
10601135 var it = cs.modules.iterator();
10611136 while (it.next()) |kv| {
......@@ -1076,7 +1151,7 @@ fn appendModuleArgs(
10761151 if (mod_names.contains(dep.mod)) continue;
10771152
10781153 // 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);
10801155 // First, try just the exposed dependency name
10811156 std.mem.copy(u8, buf, dep.name);
10821157 var name = buf[0..dep.name.len];
......@@ -1113,15 +1188,15 @@ fn appendModuleArgs(
11131188 const mod = kv.key_ptr.*;
11141189 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);
11171192 const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));
11181193 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 }));
11201195 }
11211196 }
11221197
11231198 // 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);
11251200 if (deps_str.len > 0) {
11261201 try zig_args.append("--deps");
11271202 try zig_args.append(deps_str);
......@@ -1151,43 +1226,36 @@ fn constructDepString(
11511226 }
11521227}
11531228
1154fn make(step: *Step) !void {
1229fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1230 const b = step.owner;
11551231 const self = @fieldParentPtr(CompileStep, "step", step);
1156 const builder = self.builder;
11571232
11581233 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});
1160 return error.NeedAnObject;
1234 return step.fail("the linker needs one or more objects to link", .{});
11611235 }
11621236
1163 var zig_args = ArrayList([]const u8).init(builder.allocator);
1237 var zig_args = ArrayList([]const u8).init(b.allocator);
11641238 defer zig_args.deinit();
11651239
1166 try zig_args.append(builder.zig_exe);
1240 try zig_args.append(b.zig_exe);
11671241
11681242 const cmd = switch (self.kind) {
11691243 .lib => "build-lib",
11701244 .exe => "build-exe",
11711245 .obj => "build-obj",
11721246 .@"test" => "test",
1173 .test_exe => "test",
11741247 };
11751248 try zig_args.append(cmd);
11761249
1177 if (builder.color != .auto) {
1178 try zig_args.append("--color");
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}));
1250 if (b.reference_trace) |some| {
1251 try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some}));
11841252 }
11851253
11861254 try addFlag(&zig_args, "LLVM", self.use_llvm);
11871255 try addFlag(&zig_args, "LLD", self.use_lld);
11881256
11891257 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)}));
11911259 }
11921260
11931261 if (self.entry_symbol_name) |entry| {
......@@ -1197,18 +1265,18 @@ fn make(step: *Step) !void {
11971265
11981266 if (self.stack_size) |stack_size| {
11991267 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}));
12011269 }
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
12051273 // We will add link objects from transitive dependencies, but we want to keep
12061274 // all link objects in the same order provided.
12071275 // This array is used to keep self.link_objects immutable.
12081276 var transitive_deps: TransitiveDeps = .{
1209 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1210 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1211 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1277 .link_objects = ArrayList(LinkObject).init(b.allocator),
1278 .seen_system_libs = StringHashMap(void).init(b.allocator),
1279 .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator),
12121280 .is_linking_libcpp = self.is_linking_libcpp,
12131281 .is_linking_libc = self.is_linking_libc,
12141282 .frameworks = &self.frameworks,
......@@ -1221,14 +1289,13 @@ fn make(step: *Step) !void {
12211289
12221290 for (transitive_deps.link_objects.items) |link_object| {
12231291 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
12261294 .other_step => |other| switch (other.kind) {
12271295 .exe => @panic("Cannot link with an executable build artifact"),
1228 .test_exe => @panic("Cannot link with an executable build artifact"),
12291296 .@"test" => @panic("Cannot link with a test"),
12301297 .obj => {
1231 try zig_args.append(other.getOutputSource().getPath(builder));
1298 try zig_args.append(other.getOutputSource().getPath(b));
12321299 },
12331300 .lib => l: {
12341301 if (self.isStaticLibrary() and other.isStaticLibrary()) {
......@@ -1236,7 +1303,7 @@ fn make(step: *Step) !void {
12361303 break :l;
12371304 }
12381305
1239 const full_path_lib = other.getOutputLibSource().getPath(builder);
1306 const full_path_lib = other.getOutputLibSource().getPath(b);
12401307 try zig_args.append(full_path_lib);
12411308
12421309 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
......@@ -1251,14 +1318,11 @@ fn make(step: *Step) !void {
12511318 .system_lib => |system_lib| {
12521319 const prefix: []const u8 = prefix: {
12531320 if (system_lib.needed) break :prefix "-needed-l";
1254 if (system_lib.weak) {
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 }
1321 if (system_lib.weak) break :prefix "-weak-l";
12581322 break :prefix "-l";
12591323 };
12601324 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 })),
12621326 .yes, .force => {
12631327 if (self.runPkgConfig(system_lib.name)) |args| {
12641328 try zig_args.appendSlice(args);
......@@ -1272,7 +1336,7 @@ fn make(step: *Step) !void {
12721336 .yes => {
12731337 // pkg-config failed, so fall back to linking the library
12741338 // by name directly.
1275 try zig_args.append(builder.fmt("{s}{s}", .{
1339 try zig_args.append(b.fmt("{s}{s}", .{
12761340 prefix,
12771341 system_lib.name,
12781342 }));
......@@ -1295,7 +1359,7 @@ fn make(step: *Step) !void {
12951359 try zig_args.append("--");
12961360 prev_has_extra_flags = false;
12971361 }
1298 try zig_args.append(asm_file.getPath(builder));
1362 try zig_args.append(asm_file.getPath(b));
12991363 },
13001364
13011365 .c_source_file => |c_source_file| {
......@@ -1312,7 +1376,7 @@ fn make(step: *Step) !void {
13121376 }
13131377 try zig_args.append("--");
13141378 }
1315 try zig_args.append(c_source_file.source.getPath(builder));
1379 try zig_args.append(c_source_file.source.getPath(b));
13161380 },
13171381
13181382 .c_source_files => |c_source_files| {
......@@ -1330,7 +1394,7 @@ fn make(step: *Step) !void {
13301394 try zig_args.append("--");
13311395 }
13321396 for (c_source_files.files) |file| {
1333 try zig_args.append(builder.pathFromRoot(file));
1397 try zig_args.append(b.pathFromRoot(file));
13341398 }
13351399 },
13361400 }
......@@ -1346,7 +1410,7 @@ fn make(step: *Step) !void {
13461410
13471411 if (self.image_base) |image_base| {
13481412 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}));
13501414 }
13511415
13521416 if (self.filter) |filter| {
......@@ -1358,39 +1422,34 @@ fn make(step: *Step) !void {
13581422 try zig_args.append("--test-evented-io");
13591423 }
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
13661425 if (self.test_runner) |test_runner| {
13671426 try zig_args.append("--test-runner");
1368 try zig_args.append(builder.pathFromRoot(test_runner));
1427 try zig_args.append(b.pathFromRoot(test_runner));
13691428 }
13701429
1371 for (builder.debug_log_scopes) |log_scope| {
1430 for (b.debug_log_scopes) |log_scope| {
13721431 try zig_args.append("--debug-log");
13731432 try zig_args.append(log_scope);
13741433 }
13751434
1376 if (builder.debug_compile_errors) {
1435 if (b.debug_compile_errors) {
13771436 try zig_args.append("--debug-compile-errors");
13781437 }
13791438
1380 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");
1381 if (builder.verbose_air) try zig_args.append("--verbose-air");
1382 if (builder.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");
1384 if (builder.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");
1439 if (b.verbose_cimport) try zig_args.append("--verbose-cimport");
1440 if (b.verbose_air) try zig_args.append("--verbose-air");
1441 if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1442 if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1443 if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
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);
1388 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1389 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1390 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1391 if (self.emit_implib.getArg(builder, "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);
1393 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1446 if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg);
1447 if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg);
1448 if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg);
1449 if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg);
1450 if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg);
1451 if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1452 if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg);
13941453
13951454 if (self.emit_h) try zig_args.append("-femit-h");
13961455
......@@ -1431,31 +1490,31 @@ fn make(step: *Step) !void {
14311490 }
14321491 if (self.link_z_common_page_size) |size| {
14331492 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}));
14351494 }
14361495 if (self.link_z_max_page_size) |size| {
14371496 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}));
14391498 }
14401499
14411500 if (self.libc_file) |libc_file| {
14421501 try zig_args.append("--libc");
1443 try zig_args.append(libc_file.getPath(builder));
1444 } else if (builder.libc_file) |libc_file| {
1502 try zig_args.append(libc_file.getPath(b));
1503 } else if (b.libc_file) |libc_file| {
14451504 try zig_args.append("--libc");
14461505 try zig_args.append(libc_file);
14471506 }
14481507
14491508 switch (self.optimize) {
14501509 .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)})),
14521511 }
14531512
14541513 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
14571516 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
14601519 try zig_args.append("--name");
14611520 try zig_args.append(self.name);
......@@ -1467,11 +1526,11 @@ fn make(step: *Step) !void {
14671526 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
14681527 if (self.version) |version| {
14691528 try zig_args.append("--version");
1470 try zig_args.append(builder.fmt("{}", .{version}));
1529 try zig_args.append(b.fmt("{}", .{version}));
14711530 }
14721531
14731532 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}", .{
14751534 self.target.libPrefix(),
14761535 self.name,
14771536 self.target.dynamicLibSuffix(),
......@@ -1485,7 +1544,7 @@ fn make(step: *Step) !void {
14851544 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
14861545 }
14871546 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});
14891548 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
14901549 }
14911550 if (self.search_strategy) |strat| switch (strat) {
......@@ -1493,7 +1552,7 @@ fn make(step: *Step) !void {
14931552 .dylibs_first => try zig_args.append("-search_dylibs_first"),
14941553 };
14951554 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});
14971556 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
14981557 }
14991558 if (self.headerpad_max_install_names) {
......@@ -1541,16 +1600,16 @@ fn make(step: *Step) !void {
15411600 try zig_args.append("--export-table");
15421601 }
15431602 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}));
15451604 }
15461605 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}));
15481607 }
15491608 if (self.shared_memory) {
15501609 try zig_args.append("--shared-memory");
15511610 }
15521611 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}));
15541613 }
15551614
15561615 if (self.code_model != .default) {
......@@ -1558,16 +1617,16 @@ fn make(step: *Step) !void {
15581617 try zig_args.append(@tagName(self.code_model));
15591618 }
15601619 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)}));
15621621 }
15631622 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}));
15651624 }
15661625
15671626 if (!self.target.isNative()) {
15681627 try zig_args.appendSlice(&.{
1569 "-target", try self.target.zigTriple(builder.allocator),
1570 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
1628 "-target", try self.target.zigTriple(b.allocator),
1629 "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()),
15711630 });
15721631
15731632 if (self.target.dynamic_linker.get()) |dynamic_linker| {
......@@ -1578,12 +1637,12 @@ fn make(step: *Step) !void {
15781637
15791638 if (self.linker_script) |linker_script| {
15801639 try zig_args.append("--script");
1581 try zig_args.append(linker_script.getPath(builder));
1640 try zig_args.append(linker_script.getPath(b));
15821641 }
15831642
15841643 if (self.version_script) |version_script| {
15851644 try zig_args.append("--version-script");
1586 try zig_args.append(builder.pathFromRoot(version_script));
1645 try zig_args.append(b.pathFromRoot(version_script));
15871646 }
15881647
15891648 if (self.kind == .@"test") {
......@@ -1596,83 +1655,7 @@ fn make(step: *Step) !void {
15961655 try zig_args.append("--test-cmd-bin");
15971656 }
15981657 }
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 }
16731658 }
1674 } else if (self.kind == .test_exe) {
1675 try zig_args.append("--test-no-exec");
16761659 }
16771660
16781661 try self.appendModuleArgs(&zig_args);
......@@ -1681,18 +1664,18 @@ fn make(step: *Step) !void {
16811664 switch (include_dir) {
16821665 .raw_path => |include_path| {
16831666 try zig_args.append("-I");
1684 try zig_args.append(builder.pathFromRoot(include_path));
1667 try zig_args.append(b.pathFromRoot(include_path));
16851668 },
16861669 .raw_path_system => |include_path| {
1687 if (builder.sysroot != null) {
1670 if (b.sysroot != null) {
16881671 try zig_args.append("-iwithsysroot");
16891672 } else {
16901673 try zig_args.append("-isystem");
16911674 }
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: {
16961679 // We need to check for disk designator and strip it out from dir path so
16971680 // that zig/clang can concat resolved_include_path with sysroot.
16981681 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
......@@ -1708,17 +1691,14 @@ fn make(step: *Step) !void {
17081691 },
17091692 .other_step => |other| {
17101693 if (other.emit_h) {
1711 const h_path = other.getOutputHSource().getPath(builder);
1694 const h_path = other.getOutputHSource().getPath(b);
17121695 try zig_args.append("-isystem");
17131696 try zig_args.append(fs.path.dirname(h_path).?);
17141697 }
17151698 if (other.installed_headers.items.len > 0) {
1716 for (other.installed_headers.items) |install_step| {
1717 try install_step.make();
1718 }
17191699 try zig_args.append("-I");
1720 try zig_args.append(builder.pathJoin(&.{
1721 other.builder.install_prefix, "include",
1700 try zig_args.append(b.pathJoin(&.{
1701 other.step.owner.install_prefix, "include",
17221702 }));
17231703 }
17241704 },
......@@ -1730,33 +1710,35 @@ fn make(step: *Step) !void {
17301710 }
17311711 }
17321712
1733 for (self.lib_paths.items) |lib_path| {
1734 try zig_args.append("-L");
1735 try zig_args.append(lib_path);
1713 for (self.c_macros.items) |c_macro| {
1714 try zig_args.append("-D");
1715 try zig_args.append(c_macro);
17361716 }
17371717
1738 for (self.rpaths.items) |rpath| {
1739 try zig_args.append("-rpath");
1740 try zig_args.append(rpath);
1718 try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len);
1719 for (self.lib_paths.items) |lib_path| {
1720 zig_args.appendAssumeCapacity("-L");
1721 zig_args.appendAssumeCapacity(lib_path.getPath2(b, step));
17411722 }
17421723
1743 for (self.c_macros.items) |c_macro| {
1744 try zig_args.append("-D");
1745 try zig_args.append(c_macro);
1724 try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len);
1725 for (self.rpaths.items) |rpath| {
1726 zig_args.appendAssumeCapacity("-rpath");
1727 zig_args.appendAssumeCapacity(rpath.getPath2(b, step));
17461728 }
17471729
1748 if (self.target.isDarwin()) {
1749 for (self.framework_dirs.items) |dir| {
1750 if (builder.sysroot != null) {
1751 try zig_args.append("-iframeworkwithsysroot");
1752 } else {
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);
1730 for (self.framework_dirs.items) |directory_source| {
1731 if (b.sysroot != null) {
1732 try zig_args.append("-iframeworkwithsysroot");
1733 } else {
1734 try zig_args.append("-iframework");
17581735 }
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 {
17601742 var it = self.frameworks.iterator();
17611743 while (it.next()) |entry| {
17621744 const name = entry.key_ptr.*;
......@@ -1770,29 +1752,45 @@ fn make(step: *Step) !void {
17701752 }
17711753 try zig_args.append(name);
17721754 }
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 }
17811755 }
17821756
1783 if (builder.sysroot) |sysroot| {
1757 if (b.sysroot) |sysroot| {
17841758 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
17851759 }
17861760
1787 for (builder.search_prefixes.items) |search_prefix| {
1788 try zig_args.append("-L");
1789 try zig_args.append(builder.pathJoin(&.{
1790 search_prefix, "lib",
1791 }));
1792 try zig_args.append("-I");
1793 try zig_args.append(builder.pathJoin(&.{
1794 search_prefix, "include",
1795 }));
1761 for (b.search_prefixes.items) |search_prefix| {
1762 var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| {
1763 return step.fail("unable to open prefix directory '{s}': {s}", .{
1764 search_prefix, @errorName(err),
1765 });
1766 };
1767 defer prefix_dir.close();
1768
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 }
17961794 }
17971795
17981796 try addFlag(&zig_args, "valgrind", self.valgrind_support);
......@@ -1801,15 +1799,15 @@ fn make(step: *Step) !void {
18011799
18021800 if (self.zig_lib_dir) |dir| {
18031801 try zig_args.append("--zig-lib-dir");
1804 try zig_args.append(builder.pathFromRoot(dir));
1805 } else if (builder.zig_lib_dir) |dir| {
1802 try zig_args.append(b.pathFromRoot(dir));
1803 } else if (b.zig_lib_dir) |dir| {
18061804 try zig_args.append("--zig-lib-dir");
18071805 try zig_args.append(dir);
18081806 }
18091807
18101808 if (self.main_pkg_path) |dir| {
18111809 try zig_args.append("--main-pkg-path");
1812 try zig_args.append(builder.pathFromRoot(dir));
1810 try zig_args.append(b.pathFromRoot(dir));
18131811 }
18141812
18151813 try addFlag(&zig_args, "PIC", self.force_pic);
......@@ -1831,6 +1829,7 @@ fn make(step: *Step) !void {
18311829 }
18321830
18331831 try zig_args.append("--enable-cache");
1832 try zig_args.append("--listen=-");
18341833
18351834 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
18361835 // 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 {
18411840 args_length += arg.len + 1; // +1 to account for null terminator
18421841 }
18431842 if (args_length >= 30 * 1024) {
1844 try builder.cache_root.handle.makePath("args");
1843 try b.cache_root.handle.makePath("args");
18451844
18461845 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);
18481847 arg_blk: for (args_to_escape) |arg| {
18491848 for (arg, 0..) |c, arg_idx| {
18501849 if (c == '\\' or c == '"') {
18511850 // 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);
18531852 const writer = escaped.writer();
18541853 try writer.writeAll(arg[0..arg_idx]);
18551854 for (arg[arg_idx..]) |to_escape| {
......@@ -1865,8 +1864,8 @@ fn make(step: *Step) !void {
18651864
18661865 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
18671866 // other zig build commands running in parallel.
1868 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1869 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1867 const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items);
1868 const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
18701869
18711870 var args_hash: [Sha256.digest_length]u8 = undefined;
18721871 Sha256.hash(args, &args_hash, .{});
......@@ -1878,28 +1877,35 @@ fn make(step: *Step) !void {
18781877 );
18791878
18801879 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, &.{
18841883 "@",
1885 try builder.cache_root.join(builder.allocator, &.{args_file}),
1884 try b.cache_root.join(b.allocator, &.{args_file}),
18861885 });
18871886
18881887 zig_args.shrinkRetainingCapacity(2);
18891888 try zig_args.append(resolved_args_file);
18901889 }
18911890
1892 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1893 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1891 const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
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
18951901 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, .{});
18971903 defer src_dir.close();
18981904
18991905 // 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, .{});
19031909 defer dest_dir.close();
19041910
19051911 var it = src_dir.iterate();
......@@ -1923,25 +1929,34 @@ fn make(step: *Step) !void {
19231929
19241930 // Update generated files
19251931 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(
19271935 &.{ self.output_dir.?, self.out_filename },
19281936 );
19291937
19301938 if (self.emit_h) {
1931 self.output_h_path_source.path = builder.pathJoin(
1939 self.output_h_path_source.path = b.pathJoin(
19321940 &.{ self.output_dir.?, self.out_h_filename },
19331941 );
19341942 }
19351943
19361944 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(
19381946 &.{ self.output_dir.?, self.out_pdb_filename },
19391947 );
19401948 }
19411949 }
19421950
1943 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1944 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1951 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and
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 );
19451960 }
19461961}
19471962
......@@ -1983,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
19831998}
19841999
19852000pub fn doAtomicSymLinks(
1986 allocator: Allocator,
2001 step: *Step,
19872002 output_path: []const u8,
19882003 filename_major_only: []const u8,
19892004 filename_name_only: []const u8,
19902005) !void {
2006 const arena = step.owner.allocator;
19912007 const out_dir = fs.path.dirname(output_path) orelse ".";
19922008 const out_basename = fs.path.basename(output_path);
19932009 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1994 const major_only_path = try fs.path.join(
1995 allocator,
1996 &[_][]const u8{ out_dir, filename_major_only },
1997 );
1998 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1999 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
2000 return err;
2010 const major_only_path = try fs.path.join(arena, &.{ out_dir, filename_major_only });
2011 fs.atomicSymLink(arena, out_basename, major_only_path) catch |err| {
2012 return step.fail("unable to symlink {s} -> {s}: {s}", .{
2013 major_only_path, out_basename, @errorName(err),
2014 });
20012015 };
20022016 // sym link for libfoo.so to libfoo.so.1
2003 const name_only_path = try fs.path.join(
2004 allocator,
2005 &[_][]const u8{ out_dir, filename_name_only },
2006 );
2007 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
2008 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
2009 return err;
2017 const name_only_path = try fs.path.join(arena, &.{ out_dir, filename_name_only });
2018 fs.atomicSymLink(arena, filename_major_only, name_only_path) catch |err| {
2019 return step.fail("Unable to symlink {s} -> {s}: {s}", .{
2020 name_only_path, filename_major_only, @errorName(err),
2021 });
20102022 };
20112023}
20122024
......@@ -2042,7 +2054,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
20422054 error.FileNotFound => error.PkgConfigNotInstalled,
20432055 error.InvalidName => error.PkgConfigNotInstalled,
20442056 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2045 error.ChildExecFailed => error.PkgConfigFailed,
20462057 else => return err,
20472058 };
20482059 self.pkg_config_pkg_list = result;
......@@ -2119,3 +2130,57 @@ const TransitiveDeps = struct {
21192130 }
21202131 }
21212132};
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 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.Build.Step;
4
5pub const base_id: Step.Id = .config_header;
6
71pub const Style = union(enum) {
82 /// The configure format supported by autotools. It uses `#undef foo` to
93 /// mark lines that can be substituted with different values.
......@@ -34,7 +28,6 @@ pub const Value = union(enum) {
3428};
3529
3630step: Step,
37builder: *std.Build,
3831values: std.StringArrayHashMap(Value),
3932output_file: std.Build.GeneratedFile,
4033
......@@ -42,43 +35,57 @@ style: Style,
4235max_bytes: usize,
4336include_path: []const u8,
4437
38pub const base_id: Step.Id = .config_header;
39
4540pub const Options = struct {
4641 style: Style = .blank,
4742 max_bytes: usize = 2 * 1024 * 1024,
4843 include_path: ?[]const u8 = null,
44 first_ret_addr: ?usize = null,
4945};
5046
51pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
52 const self = builder.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),
47pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep {
48 const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM");
6249
63 .max_bytes = options.max_bytes,
64 .include_path = "config.h",
65 .output_file = .{ .step = &self.step },
66 };
50 var include_path: []const u8 = "config.h";
6751
6852 if (options.style.getFileSource()) |s| switch (s) {
6953 .path => |p| {
7054 const basename = std.fs.path.basename(p);
7155 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];
7357 }
7458 },
7559 else => {},
7660 };
7761
78 if (options.include_path) |include_path| {
79 self.include_path = include_path;
62 if (options.include_path) |p| {
63 include_path = p;
8064 }
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
8289 return self;
8390}
8491
......@@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
146153 }
147154}
148155
149fn make(step: *Step) !void {
156fn make(step: *Step, prog_node: *std.Progress.Node) !void {
157 _ = prog_node;
158 const b = step.owner;
150159 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
151 const gpa = self.builder.allocator;
152
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.
160 const gpa = b.allocator;
161 const arena = b.allocator;
156162
157 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
158 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
163 var man = b.cache.obtain();
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);
165166 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
166167 // random bytes when ConfigHeaderStep implementation is modified in a
167168 // non-backwards-compatible way.
168 var hash = Hasher.init("PGuDTpidxyMqnkGM");
169 man.hash.add(@as(u32, 0xdef08d23));
169170
170171 var output = std.ArrayList(u8).init(gpa);
171172 defer output.deinit();
......@@ -177,15 +178,15 @@ fn make(step: *Step) !void {
177178 switch (self.style) {
178179 .autoconf => |file_source| {
179180 try output.appendSlice(c_generated_line);
180 const src_path = file_source.getPath(self.builder);
181 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
182 try render_autoconf(contents, &output, self.values, src_path);
181 const src_path = file_source.getPath(b);
182 const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes);
183 try render_autoconf(step, contents, &output, self.values, src_path);
183184 },
184185 .cmake => |file_source| {
185186 try output.appendSlice(c_generated_line);
186 const src_path = file_source.getPath(self.builder);
187 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
188 try render_cmake(contents, &output, self.values, src_path);
187 const src_path = file_source.getPath(b);
188 const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes);
189 try render_cmake(step, contents, &output, self.values, src_path);
189190 },
190191 .blank => {
191192 try output.appendSlice(c_generated_line);
......@@ -197,43 +198,44 @@ fn make(step: *Step) !void {
197198 },
198199 }
199200
200 hash.update(output.items);
201 man.hash.addBytes(output.items);
201202
202 var digest: [16]u8 = undefined;
203 hash.final(&digest);
204 var hash_basename: [digest.len * 2]u8 = undefined;
205 _ = std.fmt.bufPrint(
206 &hash_basename,
207 "{s}",
208 .{std.fmt.fmtSliceHexLower(&digest)},
209 ) catch unreachable;
203 if (try step.cacheHit(&man)) {
204 const digest = man.final();
205 self.output_file.path = try b.cache_root.join(arena, &.{
206 "o", &digest, self.include_path,
207 });
208 return;
209 }
210210
211 const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename });
211 const digest = man.final();
212212
213213 // If output_path has directory parts, deal with them. Example:
214214 // output_dir is zig-cache/o/HASH
215215 // output_path is libavutil/avconfig.h
216216 // We want to open directory zig-cache/o/HASH/libavutil/
217217 // 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|
219 try std.fs.path.join(gpa, &.{ output_dir, d })
220 else
221 output_dir;
218 const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, self.include_path });
219 const sub_path_dirname = std.fs.path.dirname(sub_path).?;
222220
223 var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
224 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
225 return err;
221 b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
222 return step.fail("unable to make path '{}{s}': {s}", .{
223 b.cache_root, sub_path_dirname, @errorName(err),
224 });
226225 };
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, &.{
232 output_dir, self.include_path,
233 });
233 self.output_file.path = try b.cache_root.join(arena, &.{sub_path});
234 try man.writeManifest();
234235}
235236
236237fn render_autoconf(
238 step: *Step,
237239 contents: []const u8,
238240 output: *std.ArrayList(u8),
239241 values: std.StringArrayHashMap(Value),
......@@ -260,7 +262,7 @@ fn render_autoconf(
260262 }
261263 const name = it.rest();
262264 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}'", .{
264266 src_path, line_index + 1, name,
265267 });
266268 any_errors = true;
......@@ -270,15 +272,17 @@ fn render_autoconf(
270272 }
271273
272274 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;
274277 }
275278
276279 if (any_errors) {
277 return error.HeaderConfigFailed;
280 return error.MakeFailed;
278281 }
279282}
280283
281284fn render_cmake(
285 step: *Step,
282286 contents: []const u8,
283287 output: *std.ArrayList(u8),
284288 values: std.StringArrayHashMap(Value),
......@@ -304,14 +308,14 @@ fn render_cmake(
304308 continue;
305309 }
306310 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", .{
308312 src_path, line_index + 1,
309313 });
310314 any_errors = true;
311315 continue;
312316 };
313317 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}'", .{
315319 src_path, line_index + 1, name,
316320 });
317321 any_errors = true;
......@@ -321,7 +325,8 @@ fn render_cmake(
321325 }
322326
323327 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;
325330 }
326331
327332 if (any_errors) {
......@@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !
426431 },
427432 }
428433}
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 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FmtStep = @This();
1//! This step has two modes:
2//! * Modify mode: directly modify source files, formatting them in place.
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
510pub const base_id = .fmt;
611
7step: Step,
8builder: *std.Build,
9argv: [][]const u8,
10
11pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {
12 const self = builder.allocator.create(FmtStep) catch @panic("OOM");
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 };
12pub const Options = struct {
13 paths: []const []const u8 = &.{},
14 exclude_paths: []const []const u8 = &.{},
15 /// If true, fails the build step when any non-conforming files are encountered.
16 check: bool = false,
17};
1918
20 self.argv[0] = builder.zig_exe;
21 self.argv[1] = "fmt";
22 for (paths, 0..) |path, i| {
23 self.argv[2 + i] = builder.pathFromRoot(path);
24 }
19pub fn create(owner: *std.Build, options: Options) *FmtStep {
20 const self = owner.allocator.create(FmtStep) catch @panic("OOM");
21 const name = if (options.check) "zig fmt --check" else "zig fmt";
22 self.* = .{
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 };
2533 return self;
2634}
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;
2947 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);
3269}
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;
33const CompileStep = std.Build.CompileStep;
44const InstallDir = std.Build.InstallDir;
55const InstallArtifactStep = @This();
6const fs = std.fs;
67
78pub const base_id = .install_artifact;
89
910step: Step,
10builder: *std.Build,
11dest_builder: *std.Build,
1112artifact: *CompileStep,
1213dest_dir: InstallDir,
1314pdb_dir: ?InstallDir,
1415h_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 {
1721 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");
2024 self.* = InstallArtifactStep{
21 .builder = builder,
22 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
25 .step = Step.init(.{
26 .id = base_id,
27 .name = owner.fmt("install {s}", .{artifact.name}),
28 .owner = owner,
29 .makeFn = make,
30 }),
31 .dest_builder = owner,
2332 .artifact = artifact,
2433 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
2534 .obj => @panic("Cannot install a .obj build artifact."),
26 .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."),
27 .exe, .test_exe => InstallDir{ .bin = {} },
35 .exe, .@"test" => InstallDir{ .bin = {} },
2836 .lib => InstallDir{ .lib = {} },
2937 },
3038 .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") {
3240 break :blk InstallDir{ .bin = {} };
3341 } else {
3442 break :blk InstallDir{ .lib = {} };
3543 }
3644 } else null,
3745 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
46 .dest_sub_path = null,
3847 };
3948 self.step.dependOn(&artifact.step);
4049 artifact.install_step = self;
4150
42 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
51 owner.pushInstalledFile(self.dest_dir, artifact.out_filename);
4352 if (self.artifact.isDynamicLibrary()) {
4453 if (artifact.major_only_filename) |name| {
45 builder.pushInstalledFile(.lib, name);
54 owner.pushInstalledFile(.lib, name);
4655 }
4756 if (artifact.name_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
57 owner.pushInstalledFile(.lib, name);
4958 }
5059 if (self.artifact.target.isWindows()) {
51 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
60 owner.pushInstalledFile(.lib, artifact.out_lib_filename);
5261 }
5362 }
5463 if (self.pdb_dir) |pdb_dir| {
55 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
64 owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
5665 }
5766 if (self.h_dir) |h_dir| {
58 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
67 owner.pushInstalledFile(h_dir, artifact.out_h_filename);
5968 }
6069 return self;
6170}
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;
6475 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);
68 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
69 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
70 try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
78 const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename;
79 const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path);
80 const cwd = fs.cwd();
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.?);
7199 }
72 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
73 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
74 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
100 if (self.artifact.isDynamicLibrary() and
101 self.artifact.target.isWindows() and
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;
75112 }
76113 if (self.pdb_dir) |pdb_dir| {
77 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
78 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
114 const full_src_path = self.artifact.getOutputPdbSource().getPath(src_builder);
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;
79122 }
80123 if (self.h_dir) |h_dir| {
81 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
82 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
124 const full_src_path = self.artifact.getOutputHSource().getPath(src_builder);
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;
83132 }
84133 self.artifact.installed_path = full_dest_path;
134 step.result_cached = all_cached;
85135}
lib/std/Build/InstallDirStep.zig+43-26
......@@ -4,14 +4,12 @@ const fs = std.fs;
44const Step = std.Build.Step;
55const InstallDir = std.Build.InstallDir;
66const InstallDirStep = @This();
7const log = std.log;
87
98step: Step,
10builder: *std.Build,
119options: Options,
1210/// This is used by the build system when a file being installed comes from one
1311/// package but is being installed by another.
14override_source_builder: ?*std.Build = null,
12dest_builder: *std.Build,
1513
1614pub const base_id = .install_dir;
1715
......@@ -40,31 +38,35 @@ pub const Options = struct {
4038 }
4139};
4240
43pub fn init(
44 builder: *std.Build,
45 options: Options,
46) InstallDirStep {
47 builder.pushInstalledFile(options.install_dir, options.install_subdir);
48 return InstallDirStep{
49 .builder = builder,
50 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
51 .options = options.dupe(builder),
41pub fn init(owner: *std.Build, options: Options) InstallDirStep {
42 owner.pushInstalledFile(options.install_dir, options.install_subdir);
43 return .{
44 .step = Step.init(.{
45 .id = .install_dir,
46 .name = owner.fmt("install {s}/", .{options.source_dir}),
47 .owner = owner,
48 .makeFn = make,
49 }),
50 .options = options.dupe(owner),
51 .dest_builder = owner,
5252 };
5353}
5454
55fn make(step: *Step) !void {
55fn make(step: *Step, prog_node: *std.Progress.Node) !void {
56 _ = prog_node;
5657 const self = @fieldParentPtr(InstallDirStep, "step", step);
57 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
58 const src_builder = self.override_source_builder orelse self.builder;
59 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
60 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
61 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
62 full_src_dir, @errorName(err),
58 const dest_builder = self.dest_builder;
59 const arena = dest_builder.allocator;
60 const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
61 const src_builder = self.step.owner;
62 var src_dir = src_builder.build_root.handle.openIterableDir(self.options.source_dir, .{}) catch |err| {
63 return step.fail("unable to open source directory '{}{s}': {s}", .{
64 src_builder.build_root, self.options.source_dir, @errorName(err),
6365 });
64 return error.StepFailed;
6566 };
6667 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;
6870 next_entry: while (try it.next()) |entry| {
6971 for (self.options.exclude_extensions) |ext| {
7072 if (mem.endsWith(u8, entry.path, ext)) {
......@@ -72,22 +74,37 @@ fn make(step: *Step) !void {
7274 }
7375 }
7476
75 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
76 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
77 // relative to src build root
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
7882 switch (entry.kind) {
79 .Directory => try fs.cwd().makePath(dest_path),
83 .Directory => try cwd.makePath(dest_path),
8084 .File => {
8185 for (self.options.blank_extensions) |ext| {
8286 if (mem.endsWith(u8, entry.path, ext)) {
83 try self.builder.truncateFile(dest_path);
87 try dest_builder.truncateFile(dest_path);
8488 continue :next_entry;
8589 }
8690 }
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;
89104 },
90105 else => continue,
91106 }
92107 }
108
109 step.result_cached = all_cached;
93110}
lib/std/Build/InstallFileStep.zig+34-17
......@@ -3,38 +3,55 @@ const Step = std.Build.Step;
33const FileSource = std.Build.FileSource;
44const InstallDir = std.Build.InstallDir;
55const InstallFileStep = @This();
6const assert = std.debug.assert;
67
78pub const base_id = .install_file;
89
910step: Step,
10builder: *std.Build,
1111source: FileSource,
1212dir: InstallDir,
1313dest_rel_path: []const u8,
1414/// This is used by the build system when a file being installed comes from one
1515/// package but is being installed by another.
16override_source_builder: ?*std.Build = null,
16dest_builder: *std.Build,
1717
18pub fn init(
19 builder: *std.Build,
18pub fn create(
19 owner: *std.Build,
2020 source: FileSource,
2121 dir: InstallDir,
2222 dest_rel_path: []const u8,
23) InstallFileStep {
24 builder.pushInstalledFile(dir, dest_rel_path);
25 return InstallFileStep{
26 .builder = builder,
27 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
28 .source = source.dupe(builder),
29 .dir = dir.dupe(builder),
30 .dest_rel_path = builder.dupePath(dest_rel_path),
23) *InstallFileStep {
24 assert(dest_rel_path.len != 0);
25 owner.pushInstalledFile(dir, dest_rel_path);
26 const self = owner.allocator.create(InstallFileStep) catch @panic("OOM");
27 self.* = .{
28 .step = Step.init(.{
29 .id = base_id,
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,
3138 };
39 source.addStepDependencies(&self.step);
40 return self;
3241}
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;
3546 const self = @fieldParentPtr(InstallFileStep, "step", step);
36 const src_builder = self.override_source_builder orelse self.builder;
37 const full_src_path = self.source.getPath(src_builder);
38 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
39 try self.builder.updateFile(full_src_path, full_dest_path);
47 const dest_builder = self.dest_builder;
48 const full_src_path = self.source.getPath2(src_builder, step);
49 const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_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;
4057}
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 {
2121};
2222
2323step: Step,
24builder: *std.Build,
2524file_source: std.Build.FileSource,
2625basename: []const u8,
2726output_file: std.Build.GeneratedFile,
......@@ -38,19 +37,18 @@ pub const Options = struct {
3837};
3938
4039pub fn create(
41 builder: *std.Build,
40 owner: *std.Build,
4241 file_source: std.Build.FileSource,
4342 options: Options,
4443) *ObjCopyStep {
45 const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM");
44 const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM");
4645 self.* = ObjCopyStep{
47 .step = Step.init(
48 base_id,
49 builder.fmt("objcopy {s}", .{file_source.getDisplayName()}),
50 builder.allocator,
51 make,
52 ),
53 .builder = builder,
46 .step = Step.init(.{
47 .id = base_id,
48 .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}),
49 .owner = owner,
50 .makeFn = make,
51 }),
5452 .file_source = file_source,
5553 .basename = options.basename orelse file_source.getDisplayName(),
5654 .output_file = std.Build.GeneratedFile{ .step = &self.step },
......@@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {
6765 return .{ .generated = &self.output_file };
6866}
6967
70fn make(step: *Step) !void {
68fn make(step: *Step, prog_node: *std.Progress.Node) !void {
69 const b = step.owner;
7170 const self = @fieldParentPtr(ObjCopyStep, "step", step);
72 const b = self.builder;
7371
7472 var man = b.cache.obtain();
7573 defer man.deinit();
......@@ -84,7 +82,7 @@ fn make(step: *Step) !void {
8482 man.hash.addOptional(self.pad_to);
8583 man.hash.addOptional(self.format);
8684
87 if (man.hit() catch |err| failWithCacheError(man, err)) {
85 if (try step.cacheHit(&man)) {
8886 // Cache hit, skip subprocess execution.
8987 const digest = man.final();
9088 self.output_file.path = try b.cache_root.join(b.allocator, &.{
......@@ -97,8 +95,7 @@ fn make(step: *Step) !void {
9795 const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename });
9896 const cache_path = "o" ++ fs.path.sep_str ++ digest;
9997 b.cache_root.handle.makePath(cache_path) catch |err| {
100 std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });
101 return err;
98 return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) });
10299 };
103100
104101 var argv = std.ArrayList([]const u8).init(b.allocator);
......@@ -116,23 +113,10 @@ fn make(step: *Step) !void {
116113 };
117114
118115 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
121120 self.output_file.path = full_dest_path;
122121 try man.writeManifest();
123122}
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;
1212
1313step: Step,
1414generated_file: GeneratedFile,
15builder: *std.Build,
1615
1716contents: std.ArrayList(u8),
1817artifact_args: std.ArrayList(OptionArtifactArg),
1918file_source_args: std.ArrayList(OptionFileSourceArg),
2019
21pub fn create(builder: *std.Build) *OptionsStep {
22 const self = builder.allocator.create(OptionsStep) catch @panic("OOM");
20pub fn create(owner: *std.Build) *OptionsStep {
21 const self = owner.allocator.create(OptionsStep) catch @panic("OOM");
2322 self.* = .{
24 .builder = builder,
25 .step = Step.init(.options, "options", builder.allocator, make),
23 .step = Step.init(.{
24 .id = base_id,
25 .name = "options",
26 .owner = owner,
27 .makeFn = make,
28 }),
2629 .generated_file = undefined,
27 .contents = std.ArrayList(u8).init(builder.allocator),
28 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
29 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
30 .contents = std.ArrayList(u8).init(owner.allocator),
31 .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator),
32 .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator),
3033 };
3134 self.generated_file = .{ .step = &self.step };
3235
......@@ -192,7 +195,7 @@ pub fn addOptionFileSource(
192195) void {
193196 self.file_source_args.append(.{
194197 .name = name,
195 .source = source.dupe(self.builder),
198 .source = source.dupe(self.step.owner),
196199 }) catch @panic("OOM");
197200 source.addStepDependencies(&self.step);
198201}
......@@ -200,12 +203,12 @@ pub fn addOptionFileSource(
200203/// The value is the path in the cache dir.
201204/// Adds a dependency automatically.
202205pub 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");
204207 self.step.dependOn(&artifact.step);
205208}
206209
207210pub fn createModule(self: *OptionsStep) *std.Build.Module {
208 return self.builder.createModule(.{
211 return self.step.owner.createModule(.{
209212 .source_file = self.getSource(),
210213 .dependencies = &.{},
211214 });
......@@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource {
215218 return .{ .generated = &self.generated_file };
216219}
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;
219226 const self = @fieldParentPtr(OptionsStep, "step", step);
220227
221228 for (self.artifact_args.items) |item| {
222229 self.addOption(
223230 []const u8,
224231 item.name,
225 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
232 b.pathFromRoot(item.artifact.getOutputSource().getPath(b)),
226233 );
227234 }
228235
......@@ -230,20 +237,18 @@ fn make(step: *Step) !void {
230237 self.addOption(
231238 []const u8,
232239 item.name,
233 item.source.getPath(self.builder),
240 item.source.getPath(b),
234241 );
235242 }
236243
237 var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{});
244 var options_dir = try b.cache_root.handle.makeOpenPath("options", .{});
238245 defer options_dir.close();
239246
240247 const basename = self.hashContentsToFileName();
241248
242249 try options_dir.writeFile(&basename, self.contents.items);
243250
244 self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{
245 "options", &basename,
246 });
251 self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename });
247252}
248253
249254fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
lib/std/Build/RemoveDirStep.zig+24-11
......@@ -1,5 +1,4 @@
11const std = @import("../std.zig");
2const log = std.log;
32const fs = std.fs;
43const Step = std.Build.Step;
54const RemoveDirStep = @This();
......@@ -7,23 +6,37 @@ const RemoveDirStep = @This();
76pub const base_id = .remove_dir;
87
98step: Step,
10builder: *std.Build,
119dir_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 {
1412 return RemoveDirStep{
15 .builder = builder,
16 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
17 .dir_path = builder.dupePath(dir_path),
13 .step = Step.init(.{
14 .id = .remove_dir,
15 .name = owner.fmt("RemoveDir {s}", .{dir_path}),
16 .owner = owner,
17 .makeFn = make,
18 }),
19 .dir_path = owner.dupePath(dir_path),
1820 };
1921}
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;
2229 const self = @fieldParentPtr(RemoveDirStep, "step", step);
2330
24 const full_path = self.builder.pathFromRoot(self.dir_path);
25 fs.cwd().deleteTree(full_path) catch |err| {
26 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
27 return err;
31 b.build_root.handle.deleteTree(self.dir_path) catch |err| {
32 if (b.build_root.path) |base| {
33 return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{
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 }
2841 };
2942}
lib/std/Build/RunStep.zig+975-271
......@@ -10,76 +10,136 @@ const ArrayList = std.ArrayList;
1010const EnvMap = process.EnvMap;
1111const Allocator = mem.Allocator;
1212const ExecError = std.Build.ExecError;
13
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
13const assert = std.debug.assert;
1514
1615const RunStep = @This();
1716
1817pub const base_id: Step.Id = .run;
1918
2019step: Step,
21builder: *std.Build,
2220
2321/// See also addArg and addArgs to modifying this directly
2422argv: ArrayList(Arg),
2523
2624/// 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.
2727cwd: ?[]const u8,
2828
2929/// Override this field to modify the environment, or use setEnvironmentVariable
3030env_map: ?*EnvMap,
3131
32stdout_action: StdIoAction = .inherit,
33stderr_action: StdIoAction = .inherit,
34
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,
36
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
38expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },
39
40/// Print the command before running it
41print: bool,
42/// Controls whether execution is skipped if the output file is up-to-date.
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,
32/// Configures whether the RunStep is considered to have side-effects, and also
33/// whether the RunStep will inherit stdio streams, forwarding them to the
34/// parent process, in which case will require a global lock to prevent other
35/// steps from interfering with stdio while the subprocess associated with this
36/// RunStep is running.
37/// If the RunStep is determined to not have side-effects, then execution will
38/// be skipped if all output files are up-to-date and input files are
39/// unchanged.
40stdio: StdIo = .infer_from_args,
41/// This field must be `null` if stdio is `inherit`.
42stdin: ?[]const u8 = null,
4643
4744/// Additional file paths relative to build.zig that, when modified, indicate
4845/// 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.
4948extra_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.
5286 inherit,
53 ignore,
54 expect_exact: []const u8,
55 expect_matches: []const []const u8,
87 /// Causes the RunStep to be considered to *not* have side-effects. The
88 /// process will be re-executed if any of the input dependencies are
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 };
56106};
57107
58108pub const Arg = union(enum) {
59109 artifact: *CompileStep,
60110 file_source: std.Build.FileSource,
111 directory_source: std.Build.FileSource,
61112 bytes: []u8,
62 output: Output,
113 output: *Output,
114};
63115
64 pub const Output = struct {
65 generated_file: *std.Build.GeneratedFile,
66 basename: []const u8,
67 };
116pub const Output = struct {
117 generated_file: std.Build.GeneratedFile,
118 prefix: []const u8,
119 basename: []const u8,
68120};
69121
70pub fn create(builder: *std.Build, name: []const u8) *RunStep {
71 const self = builder.allocator.create(RunStep) catch @panic("OOM");
72 self.* = RunStep{
73 .builder = builder,
74 .step = Step.init(base_id, name, builder.allocator, make),
75 .argv = ArrayList(Arg).init(builder.allocator),
122pub fn create(owner: *std.Build, name: []const u8) *RunStep {
123 const self = owner.allocator.create(RunStep) catch @panic("OOM");
124 self.* = .{
125 .step = Step.init(.{
126 .id = base_id,
127 .name = name,
128 .owner = owner,
129 .makeFn = make,
130 }),
131 .argv = ArrayList(Arg).init(owner.allocator),
76132 .cwd = null,
77133 .env_map = null,
78 .print = builder.verbose,
79134 };
80135 return self;
81136}
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
83143pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
84144 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
85145 self.step.dependOn(&artifact.step);
......@@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
89149/// run, and returns a FileSource which can be used as inputs to other APIs
90150/// throughout the build system.
91151pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
92 const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
93 generated_file.* = .{ .step = &rs.step };
94 rs.argv.append(.{ .output = .{
95 .generated_file = generated_file,
96 .basename = rs.builder.dupe(basename),
97 } }) catch @panic("OOM");
152 return addPrefixedOutputFileArg(rs, "", basename);
153}
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 };
100175}
101176
102177pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
103 self.argv.append(Arg{
104 .file_source = file_source.dupe(self.builder),
178 self.argv.append(.{
179 .file_source = file_source.dupe(self.step.owner),
105180 }) catch @panic("OOM");
106181 file_source.addStepDependencies(&self.step);
107182}
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
109191pub 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");
111193}
112194
113195pub fn addArgs(self: *RunStep, args: []const []const u8) void {
......@@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void {
117199}
118200
119201pub fn clearEnvironment(self: *RunStep) void {
120 const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");
121 new_env_map.* = EnvMap.init(self.builder.allocator);
202 const b = self.step.owner;
203 const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM");
204 new_env_map.* = EnvMap.init(b.allocator);
122205 self.env_map = new_env_map;
123206}
124207
125208pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
126 addPathDirInternal(&self.step, self.builder, search_path);
127}
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);
209 const b = self.step.owner;
210 const env_map = getEnvMapInternal(self);
132211
133212 const key = "PATH";
134213 var prev_path = env_map.get(key);
135214
136215 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 });
138217 env_map.put(key, new_path) catch @panic("OOM");
139218 } 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");
141220 }
142221}
143222
144223pub fn getEnvMap(self: *RunStep) *EnvMap {
145 return getEnvMapInternal(&self.step, self.builder.allocator);
224 return getEnvMapInternal(self);
146225}
147226
148fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
149 const maybe_env_map = switch (step.id) {
150 .run => step.cast(RunStep).?.env_map,
151 .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
152 else => unreachable,
153 };
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 }
227fn getEnvMapInternal(self: *RunStep) *EnvMap {
228 const arena = self.step.owner.allocator;
229 return self.env_map orelse {
230 const env_map = arena.create(EnvMap) catch @panic("OOM");
231 env_map.* = process.getEnvMap(arena) catch @panic("unhandled error");
232 self.env_map = env_map;
162233 return env_map;
163234 };
164235}
165236
166237pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
238 const b = self.step.owner;
167239 const env_map = self.getEnvMap();
168 env_map.put(
169 self.builder.dupe(key),
170 self.builder.dupe(value),
171 ) catch @panic("unhandled error");
240 env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error");
241}
242
243pub fn removeEnvironmentVariable(self: *RunStep, key: []const u8) void {
244 self.getEnvMap().remove(key);
172245}
173246
247/// Adds a check for exact stderr match. Does not add any other checks.
174248pub 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);
176251}
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.
178255pub 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 }
180261}
181262
182fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
183 return switch (action) {
184 .ignore => .Ignore,
185 .inherit => .Inherit,
186 .expect_exact, .expect_matches => .Pipe,
263pub fn expectExitCode(self: *RunStep, code: u8) void {
264 const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } };
265 self.addCheck(new_check);
266}
267
268pub fn hasTermCheck(self: RunStep) bool {
269 for (self.stdio.check.items) |check| switch (check) {
270 .expect_term => return true,
271 else => continue,
187272 };
273 return false;
188274}
189275
190fn needOutputCheck(self: RunStep) bool {
191 switch (self.condition) {
192 .always => return false,
193 .output_outdated => {},
276pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void {
277 switch (self.stdio) {
278 .infer_from_args => {
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"),
194284 }
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;
197331 for (self.argv.items) |arg| switch (arg) {
198332 .output => return true,
199333 else => continue,
200334 };
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 };
202363 return false;
203364}
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;
206369 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);
210373 var output_placeholders = ArrayList(struct {
211374 index: usize,
212 output: Arg.Output,
213 }).init(self.builder.allocator);
375 output: *Output,
376 }).init(arena);
214377
215 var man = self.builder.cache.obtain();
378 var man = b.cache.obtain();
216379 defer man.deinit();
217380
218381 for (self.argv.items) |arg| {
......@@ -222,23 +385,29 @@ fn make(step: *Step) !void {
222385 man.hash.addBytes(bytes);
223386 },
224387 .file_source => |file| {
225 const file_path = file.getPath(self.builder);
388 const file_path = file.getPath(b);
226389 try argv_list.append(file_path);
227390 _ = try man.addFile(file_path, null);
228391 },
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 },
229397 .artifact => |artifact| {
230398 if (artifact.target.isWindows()) {
231399 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
232400 self.addPathForDynLibs(artifact);
233401 }
234402 const file_path = artifact.installed_path orelse
235 artifact.getOutputSource().getPath(self.builder);
403 artifact.getOutputSource().getPath(b);
236404
237405 try argv_list.append(file_path);
238406
239407 _ = try man.addFile(file_path, null);
240408 },
241409 .output => |output| {
410 man.hash.addBytes(output.prefix);
242411 man.hash.addBytes(output.basename);
243412 // Add a placeholder into the argument list because we need the
244413 // manifest hash to be updated with all arguments before the
......@@ -252,60 +421,77 @@ fn make(step: *Step) !void {
252421 }
253422 }
254423
255 if (need_output_check) {
256 for (self.extra_file_dependencies) |file_path| {
257 _ = try man.addFile(self.builder.pathFromRoot(file_path), null);
258 }
424 if (self.captured_stdout) |output| {
425 man.hash.addBytes(output.basename);
426 }
259427
260 if (man.hit() catch |err| failWithCacheError(man, err)) {
261 // cache hit, skip running command
262 const digest = man.final();
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 }
428 if (self.captured_stderr) |output| {
429 man.hash.addBytes(output.basename);
430 }
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();
274446 for (output_placeholders.items) |placeholder| {
275 const output_path = try self.builder.cache_root.join(
276 self.builder.allocator,
277 &.{ "o", &digest, placeholder.output.basename },
278 );
279 const output_dir = fs.path.dirname(output_path).?;
280 fs.cwd().makePath(output_dir) catch |err| {
281 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
282 return err;
283 };
447 placeholder.output.generated_file.path = try b.cache_root.join(arena, &.{
448 "o", &digest, placeholder.output.basename,
449 });
450 }
451
452 if (self.captured_stdout) |output| {
453 output.generated_file.path = try b.cache_root.join(arena, &.{
454 "o", &digest, output.basename,
455 });
456 }
284457
285 placeholder.output.generated_file.path = output_path;
286 argv_list.items[placeholder.index] = output_path;
458 if (self.captured_stderr) |output| {
459 output.generated_file.path = try b.cache_root.join(arena, &.{
460 "o", &digest, output.basename,
461 });
287462 }
463
464 step.result_cached = true;
465 return;
288466 }
289467
290 try runCommand(
291 argv_list.items,
292 self.builder,
293 self.expected_term,
294 self.stdout_action,
295 self.stderr_action,
296 self.stdin_behavior,
297 self.env_map,
298 self.cwd,
299 self.print,
300 );
301
302 if (need_output_check) {
303 try man.writeManifest();
468 const digest = man.final();
469
470 for (output_placeholders.items) |placeholder| {
471 const output_components = .{ "o", &digest, placeholder.output.basename };
472 const output_sub_path = try fs.path.join(arena, &output_components);
473 const output_sub_dir_path = fs.path.dirname(output_sub_path).?;
474 b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
475 return step.fail("unable to make path '{}{s}': {s}", .{
476 b.cache_root, output_sub_dir_path, @errorName(err),
477 });
478 };
479 const output_path = try b.cache_root.join(arena, &output_components);
480 placeholder.output.generated_file.path = output_path;
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;
304486 }
487
488 try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node);
489
490 try step.writeManifest(&man);
305491}
306492
307493fn formatTerm(
308 term: ?std.ChildProcess.Term,
494 term: ?std.process.Child.Term,
309495 comptime fmt: []const u8,
310496 options: std.fmt.FormatOptions,
311497 writer: anytype,
......@@ -321,11 +507,11 @@ fn formatTerm(
321507 try writer.writeAll("exited with any code");
322508 }
323509}
324fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) {
510fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) {
325511 return .{ .data = term };
326512}
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 {
329515 return if (expected) |e| switch (e) {
330516 .Exited => |expected_code| switch (actual) {
331517 .Exited => |actual_code| expected_code == actual_code,
......@@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)
349535 };
350536}
351537
352pub fn runCommand(
538fn runCommand(
539 self: *RunStep,
353540 argv: []const []const u8,
354 builder: *std.Build,
355 expected_term: ?std.ChildProcess.Term,
356 stdout_action: StdIoAction,
357 stderr_action: StdIoAction,
358 stdin_behavior: std.ChildProcess.StdIo,
359 env_map: ?*EnvMap,
360 maybe_cwd: ?[]const u8,
361 print: bool,
541 has_side_effects: bool,
542 digest: ?*const [std.Build.Cache.hex_digest_len]u8,
543 prog_node: *std.Progress.Node,
362544) !void {
363 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path;
364
365 if (!std.process.can_spawn) {
366 const cmd = try std.mem.join(builder.allocator, " ", argv);
367 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
368 @tagName(builtin.os.tag), cmd,
369 });
370 builder.allocator.free(cmd);
371 return ExecError.ExecNotSupported;
372 }
545 const step = &self.step;
546 const b = step.owner;
547 const arena = b.allocator;
548
549 try step.handleChildProcUnsupported(self.cwd, argv);
550 try Step.handleVerbose2(step.owner, self.cwd, self.env_map, argv);
551
552 const allow_skip = switch (self.stdio) {
553 .check, .zig_test => self.skip_foreign_checks,
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);
375 child.cwd = cwd;
376 child.env_map = env_map orelse builder.env_map;
671 if (exe.target.isWindows()) {
672 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
673 self.addPathForDynLibs(exe);
674 }
377675
378 child.stdin_behavior = stdin_behavior;
379 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
380 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
676 try Step.handleVerbose2(step.owner, self.cwd, self.env_map, interp_argv.items);
381677
382 if (print)
383 printCmd(cwd, argv);
678 break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| {
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| {
386 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
387 return err;
685 return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
388686 };
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;
393 defer if (stdout) |s| builder.allocator.free(s);
692 // Capture stdout and stderr to GeneratedFile objects.
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) {
396 .expect_exact, .expect_matches => {
397 stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
732 const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items;
733
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);
398836 },
399 .inherit, .ignore => {},
400837 }
838}
401839
402 var stderr: ?[]const u8 = null;
403 defer if (stderr) |s| builder.allocator.free(s);
840const ChildProcResult = struct {
841 term: std.process.Child.Term,
842 elapsed_ns: u64,
843 peak_rss: usize,
404844
405 switch (stderr_action) {
406 .expect_exact, .expect_matches => {
407 stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
408 },
409 .inherit, .ignore => {},
845 stdio: StdIoResult,
846};
847
848fn spawnChildAndCollect(
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;
410863 }
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| {
413 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
414 return err;
867 child.stdin_behavior = switch (self.stdio) {
868 .infer_from_args => if (has_side_effects) .Inherit else .Close,
869 .inherit => .Inherit,
870 .check => .Close,
871 .zig_test => .Pipe,
415872 };
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)) {
418 if (builder.prominent_compile_errors) {
419 std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) });
420 } else {
421 std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) });
422 printCmd(cwd, argv);
892 try child.spawn();
893 var timer = try std.time.Timer.start();
894
895 const result = if (self.stdio == .zig_test)
896 evalZigTest(self, &child, prog_node)
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;
423959 }
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);
4251032 }
4261033
427 switch (stderr_action) {
428 .inherit, .ignore => {},
429 .expect_exact => |expected_bytes| {
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 },
1034 if (stderr.readableLength() > 0) {
1035 const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n");
1036 if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg);
4571037 }
4581038
459 switch (stdout_action) {
460 .inherit, .ignore => {},
461 .expect_exact => |expected_bytes| {
462 if (!mem.eql(u8, expected_bytes, stdout.?)) {
463 std.debug.print(
464 \\
465 \\========= Expected this stdout: =========
466 \\{s}
467 \\========= But found: ====================
468 \\{s}
469 \\
470 , .{ expected_bytes, stdout.? });
471 printCmd(cwd, argv);
472 return error.TestFailed;
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 }
1039 // Send EOF to stdin.
1040 child.stdin.?.close();
1041 child.stdin = null;
1042
1043 return .{
1044 .stdout = &.{},
1045 .stderr = &.{},
1046 .stdout_null = true,
1047 .stderr_null = true,
1048 .test_results = .{
1049 .test_count = test_count,
1050 .fail_count = fail_count,
1051 .skip_count = skip_count,
1052 .leak_count = leak_count,
4881053 },
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);
4891086 }
4901087}
4911088
492fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
493 const i = man.failed_file_index orelse failWithSimpleError(err);
494 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
495 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
496 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
497 std.process.exit(1);
1089fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
1090 const header: std.zig.Client.Message.Header = .{
1091 .tag = tag,
1092 .bytes_len = 0,
1093 };
1094 try file.writeAll(std.mem.asBytes(&header));
4981095}
4991096
500fn failWithSimpleError(err: anyerror) noreturn {
501 std.debug.print("{s}\n", .{@errorName(err)});
502 std.process.exit(1);
1097fn sendRunTestMessage(file: std.fs.File, index: u32) !void {
1098 const header: std.zig.Client.Message.Header = .{
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);
5031104}
5041105
505fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
506 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
507 for (argv) |arg| {
508 std.debug.print("{s} ", .{arg});
1106fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult {
1107 const arena = self.step.owner.allocator;
1108
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;
5091115 }
510 std.debug.print("\n", .{});
511}
5121116
513fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
514 addPathForDynLibsInternal(&self.step, self.builder, artifact);
1117 // These are not optionals, as a workaround for
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 };
5151170}
5161171
517/// This should only be used for internal usage, this is called automatically
518/// for the user.
519pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void {
1172fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
1173 const b = self.step.owner;
5201174 for (artifact.link_objects.items) |link_object| {
5211175 switch (link_object) {
5221176 .other_step => |other| {
5231177 if (other.target.isWindows() and other.isDynamicLibrary()) {
524 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
525 addPathForDynLibsInternal(step, builder, other);
1178 addPathDir(self, fs.path.dirname(other.getOutputSource().getPath(b)).?);
1179 addPathForDynLibs(self, other);
5261180 }
5271181 },
5281182 else => {},
5291183 }
5301184 }
5311185}
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 @@
11id: Id,
22name: []const u8,
3makeFn: *const fn (self: *Step) anyerror!void,
3owner: *Build,
4makeFn: MakeFn,
5
46dependencies: std.ArrayList(*Step),
5loop_flag: bool,
6done_flag: bool,
7/// This field is empty during execution of the user's build script, and
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
876pub const Id = enum {
977 top_level,
......@@ -17,7 +85,6 @@ pub const Id = enum {
1785 translate_c,
1886 write_file,
1987 run,
20 emulatable_run,
2188 check_file,
2289 check_object,
2390 config_header,
......@@ -38,7 +105,6 @@ pub const Id = enum {
38105 .translate_c => Build.TranslateCStep,
39106 .write_file => Build.WriteFileStep,
40107 .run => Build.RunStep,
41 .emulatable_run => Build.EmulatableRunStep,
42108 .check_file => Build.CheckFileStep,
43109 .check_object => Build.CheckObjectStep,
44110 .config_header => Build.ConfigHeaderStep,
......@@ -49,39 +115,99 @@ pub const Id = enum {
49115 }
50116};
51117
52pub fn init(
118pub const Options = struct {
53119 id: Id,
54120 name: []const u8,
55 allocator: Allocator,
56 makeFn: *const fn (self: *Step) anyerror!void,
57) Step {
58 return Step{
59 .id = id,
60 .name = allocator.dupe(u8, name) catch @panic("OOM"),
61 .makeFn = makeFn,
62 .dependencies = std.ArrayList(*Step).init(allocator),
63 .loop_flag = false,
64 .done_flag = false,
121 owner: *Build,
122 makeFn: MakeFn = makeNoOp,
123 first_ret_addr: ?usize = null,
124 max_rss: usize = 0,
125};
126
127pub fn init(options: Options) Step {
128 const arena = options.owner.allocator;
129
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,
65135 };
66}
136 std.debug.captureStackTrace(first_ret_addr, &stack_trace);
67137
68pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
69 return init(id, name, allocator, makeNoOp);
138 return .{
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 };
70155}
71156
72pub fn make(self: *Step) !void {
73 if (self.done_flag) return;
157/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
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);
76 self.done_flag = true;
176 if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) {
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 }
77183}
78184
79185pub fn dependOn(self: *Step, other: *Step) void {
80186 self.dependencies.append(other) catch @panic("OOM");
81187}
82188
83fn makeNoOp(self: *Step) anyerror!void {
84 _ = self;
189pub fn getStackTrace(s: *Step) std.builtin.StackTrace {
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;
85211}
86212
87213pub fn cast(step: *Step, comptime T: type) ?*T {
......@@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
91217 return null;
92218}
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
94242const Step = @This();
95243const std = @import("../std.zig");
96244const Build = std.Build;
97245const 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();
1111pub const base_id = .translate_c;
1212
1313step: Step,
14builder: *std.Build,
1514source: std.Build.FileSource,
1615include_dirs: std.ArrayList([]const u8),
1716c_macros: std.ArrayList([]const u8),
......@@ -26,15 +25,19 @@ pub const Options = struct {
2625 optimize: std.builtin.OptimizeMode,
2726};
2827
29pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
30 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");
31 const source = options.source_file.dupe(builder);
28pub fn create(owner: *std.Build, options: Options) *TranslateCStep {
29 const self = owner.allocator.create(TranslateCStep) catch @panic("OOM");
30 const source = options.source_file.dupe(owner);
3231 self.* = TranslateCStep{
33 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
34 .builder = builder,
32 .step = Step.init(.{
33 .id = .translate_c,
34 .name = "translate-c",
35 .owner = owner,
36 .makeFn = make,
37 }),
3538 .source = source,
36 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
37 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
39 .include_dirs = std.ArrayList([]const u8).init(owner.allocator),
40 .c_macros = std.ArrayList([]const u8).init(owner.allocator),
3841 .out_basename = undefined,
3942 .target = options.target,
4043 .optimize = options.optimize,
......@@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct {
5457
5558/// Creates a step to build an executable from the translated source.
5659pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
57 return self.builder.addExecutable(.{
60 return self.step.owner.addExecutable(.{
5861 .root_source_file = .{ .generated = &self.output_file },
5962 .name = options.name orelse "translated_c",
6063 .version = options.version,
......@@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp
6568}
6669
6770pub 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");
6972}
7073
7174pub 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 );
7380}
7481
7582/// If the value is omitted, it is set to 1.
7683/// `name` and `value` need not live longer than the function call.
7784pub 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);
7986 self.c_macros.append(macro) catch @panic("OOM");
8087}
8188
8289/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
8390pub 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");
8592}
8693
87fn make(step: *Step) !void {
94fn make(step: *Step, prog_node: *std.Progress.Node) !void {
95 const b = step.owner;
8896 const self = @fieldParentPtr(TranslateCStep, "step", step);
8997
90 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
91 try argv_list.append(self.builder.zig_exe);
98 var argv_list = std.ArrayList([]const u8).init(b.allocator);
99 try argv_list.append(b.zig_exe);
92100 try argv_list.append("translate-c");
93101 try argv_list.append("-lc");
94102
95103 try argv_list.append("--enable-cache");
104 try argv_list.append("--listen=-");
96105
97106 if (!self.target.isNative()) {
98107 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));
100109 }
101110
102111 switch (self.optimize) {
103112 .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)})),
105114 }
106115
107116 for (self.include_dirs.items) |include_dir| {
......@@ -114,16 +123,15 @@ fn make(step: *Step) !void {
114123 try argv_list.append(c_macro);
115124 }
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);
120 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
128 const output_path = try step.evalZigProcess(argv_list.items, prog_node);
121129
122130 self.out_basename = fs.path.basename(output_path);
123131 const output_dir = fs.path.dirname(output_path).?;
124132
125133 self.output_file.path = try fs.path.join(
126 self.builder.allocator,
134 b.allocator,
127135 &[_][]const u8{ output_dir, self.out_basename },
128136 );
129137}
lib/std/Build/WriteFileStep.zig+145-67
......@@ -10,11 +10,11 @@
1010//! control.
1111
1212step: Step,
13builder: *std.Build,
1413/// The elements here are pointers because we need stable pointers for the
1514/// GeneratedFile field.
1615files: std.ArrayListUnmanaged(*File),
1716output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
17generated_directory: std.Build.GeneratedFile,
1818
1919pub const base_id = .write_file;
2020
......@@ -34,24 +34,34 @@ pub const Contents = union(enum) {
3434 copy: std.Build.FileSource,
3535};
3636
37pub fn init(builder: *std.Build) WriteFileStep {
38 return .{
39 .builder = builder,
40 .step = Step.init(.write_file, "writefile", builder.allocator, make),
37pub fn create(owner: *std.Build) *WriteFileStep {
38 const wf = owner.allocator.create(WriteFileStep) catch @panic("OOM");
39 wf.* = .{
40 .step = Step.init(.{
41 .id = .write_file,
42 .name = "WriteFile",
43 .owner = owner,
44 .makeFn = make,
45 }),
4146 .files = .{},
4247 .output_source_files = .{},
48 .generated_directory = .{ .step = &wf.step },
4349 };
50 return wf;
4451}
4552
4653pub 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;
4856 const file = gpa.create(File) catch @panic("OOM");
4957 file.* = .{
5058 .generated_file = .{ .step = &wf.step },
51 .sub_path = wf.builder.dupePath(sub_path),
52 .contents = .{ .bytes = wf.builder.dupe(bytes) },
59 .sub_path = b.dupePath(sub_path),
60 .contents = .{ .bytes = b.dupe(bytes) },
5361 };
5462 wf.files.append(gpa, file) catch @panic("OOM");
63
64 wf.maybeUpdateName();
5565}
5666
5767/// 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 {
6272/// required sub-path exists.
6373/// This is the option expected to be used most commonly with `addCopyFile`.
6474pub 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;
6677 const file = gpa.create(File) catch @panic("OOM");
6778 file.* = .{
6879 .generated_file = .{ .step = &wf.step },
69 .sub_path = wf.builder.dupePath(sub_path),
80 .sub_path = b.dupePath(sub_path),
7081 .contents = .{ .copy = source },
7182 };
7283 wf.files.append(gpa, file) catch @panic("OOM");
84
85 wf.maybeUpdateName();
86 source.addStepDependencies(&wf.step);
7387}
7488
7589/// A path relative to the package root.
......@@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [
7993/// those changes to version control.
8094/// A file added this way is not available with `getFileSource`.
8195pub 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, .{
8398 .contents = .{ .copy = source },
8499 .sub_path = sub_path,
85100 }) 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");
86116}
87117
88118/// 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
95125 return null;
96126}
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;
99146 const wf = @fieldParentPtr(WriteFileStep, "step", step);
100147
101148 // Writing to source files is kind of an extra capability of this
102149 // WriteFileStep - arguably it should be a different step. But anyway here
103150 // it is, it happens unconditionally and does not interact with the other
104151 // files here.
152 var any_miss = false;
105153 for (wf.output_source_files.items) |output_source_file| {
106 const basename = fs.path.basename(output_source_file.sub_path);
107154 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
108 var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{});
109 defer dir.close();
110 try writeFile(wf, dir, output_source_file.contents, basename);
111 } else {
112 try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename);
155 b.build_root.handle.makePath(dirname) catch |err| {
156 return step.fail("unable to make path '{}{s}': {s}", .{
157 b.build_root, dirname, @errorName(err),
158 });
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 },
113185 }
114186 }
115187
......@@ -120,7 +192,7 @@ fn make(step: *Step) !void {
120192 // If, for example, a hard-coded path was used as the location to put WriteFileStep
121193 // 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();
124196 defer man.deinit();
125197
126198 // Random bytes to make WriteFileStep unique. Refresh this with
......@@ -135,76 +207,82 @@ fn make(step: *Step) !void {
135207 man.hash.addBytes(bytes);
136208 },
137209 .copy => |file_source| {
138 _ = try man.addFile(file_source.getPath(wf.builder), null);
210 _ = try man.addFile(file_source.getPath(b), null);
139211 },
140212 }
141213 }
142214
143 if (man.hit() catch |err| failWithCacheError(man, err)) {
144 // Cache hit, skip writing file data.
215 if (try step.cacheHit(&man)) {
145216 const digest = man.final();
146217 for (wf.files.items) |file| {
147 file.generated_file.path = try wf.builder.cache_root.join(
148 wf.builder.allocator,
149 &.{ "o", &digest, file.sub_path },
150 );
218 file.generated_file.path = try b.cache_root.join(b.allocator, &.{
219 "o", &digest, file.sub_path,
220 });
151221 }
222 wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest });
152223 return;
153224 }
154225
155226 const digest = man.final();
156227 const cache_path = "o" ++ fs.path.sep_str ++ digest;
157228
158 var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
159 std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });
160 return err;
229 wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest });
230
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 });
161235 };
162236 defer cache_dir.close();
163237
164238 for (wf.files.items) |file| {
165 const basename = fs.path.basename(file.sub_path);
166239 if (fs.path.dirname(file.sub_path)) |dirname| {
167 var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{});
168 defer dir.close();
169 try writeFile(wf, dir, file.contents, basename);
170 } else {
171 try writeFile(wf, cache_dir, file.contents, basename);
240 cache_dir.makePath(dirname) catch |err| {
241 return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{
242 b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err),
243 });
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 },
172278 }
173279
174 file.generated_file.path = try wf.builder.cache_root.join(
175 wf.builder.allocator,
176 &.{ cache_path, file.sub_path },
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 },
280 file.generated_file.path = try b.cache_root.join(b.allocator, &.{
281 cache_path, file.sub_path,
282 });
192283 }
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 {
206 std.debug.print("{s}\n", .{@errorName(err)});
207 std.process.exit(1);
285 try step.writeManifest(&man);
208286}
209287
210288const std = @import("../std.zig");
lib/std/Progress.zig+72-26
......@@ -126,6 +126,21 @@ pub const Node = struct {
126126 }
127127 }
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
129144 /// Thread-safe. 0 means unknown.
130145 pub fn setEstimatedTotalItems(self: *Node, count: usize) void {
131146 @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic);
......@@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void {
174189 if (self.timer) |*timer| {
175190 if (!self.update_mutex.tryLock()) return;
176191 defer self.update_mutex.unlock();
177 const now = timer.read();
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();
192 maybeRefreshWithHeldLock(self, timer);
184193 }
185194}
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
187206/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.
188207pub fn refresh(self: *Progress) void {
189208 if (!self.update_mutex.tryLock()) return;
......@@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void {
192211 return self.refreshWithHeldLock();
193212}
194213
195fn refreshWithHeldLock(self: *Progress) void {
196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
197 if (is_dumb and self.dont_print_on_dumb) return;
198
199 const file = self.terminal orelse return;
200
201 var end: usize = 0;
202 if (self.columns_written > 0) {
214fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void {
215 const file = p.terminal orelse return;
216 var end = end_ptr.*;
217 if (p.columns_written > 0) {
203218 // restore the cursor position by moving the cursor
204219 // `columns_written` cells to the left, then clear the rest of the
205220 // line
206 if (self.supports_ansi_escape_codes) {
207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
221 if (p.supports_ansi_escape_codes) {
222 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len;
223 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
209224 } else if (builtin.os.tag == .windows) winapi: {
210 std.debug.assert(self.is_windows_terminal);
225 std.debug.assert(p.is_windows_terminal);
211226
212227 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
213228 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
214229 // stop trying to write to this file
215 self.terminal = null;
230 p.terminal = null;
216231 break :winapi;
217232 }
218233
219234 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),
221236 .Y = info.dwCursorPosition.Y,
222237 };
223238
......@@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void {
235250 &written,
236251 ) != windows.TRUE) {
237252 // stop trying to write to this file
238 self.terminal = null;
253 p.terminal = null;
239254 break :winapi;
240255 }
241256 if (windows.kernel32.FillConsoleOutputCharacterW(
......@@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void {
246261 &written,
247262 ) != windows.TRUE) {
248263 // stop trying to write to this file
249 self.terminal = null;
264 p.terminal = null;
250265 break :winapi;
251266 }
252267 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {
253268 // stop trying to write to this file
254 self.terminal = null;
269 p.terminal = null;
255270 break :winapi;
256271 }
257272 } else {
258273 // 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';
260275 end += 1;
261276 }
262277
263 self.columns_written = 0;
278 p.columns_written = 0;
264279 }
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
266292 if (!self.done) {
267293 var need_ellipse = false;
......@@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
318344 self.columns_written = 0;
319345}
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
321367fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
322368 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
323369 const amt = written.len;
lib/std/Thread.zig+5
......@@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig");
1616pub const Semaphore = @import("Thread/Semaphore.zig");
1717pub const Condition = @import("Thread/Condition.zig");
1818pub const RwLock = @import("Thread/RwLock.zig");
19pub const Pool = @import("Thread/Pool.zig");
20pub const WaitGroup = @import("Thread/WaitGroup.zig");
1921
2022pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc;
2123const is_gnu = target.abi.isGnu();
......@@ -945,6 +947,7 @@ const LinuxThreadImpl = struct {
945947
946948 // map all memory needed without read/write permissions
947949 // to avoid committing the whole region right away
950 // anonymous mapping ensures file descriptor limits are not exceeded
948951 const mapped = os.mmap(
949952 null,
950953 map_bytes,
......@@ -956,6 +959,8 @@ const LinuxThreadImpl = struct {
956959 error.MemoryMappingNotSupported => unreachable,
957960 error.AccessDenied => unreachable,
958961 error.PermissionDenied => unreachable,
962 error.ProcessFdQuotaExceeded => unreachable,
963 error.SystemFdQuotaExceeded => unreachable,
959964 else => |e| return e,
960965 };
961966 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,
1616
1717/// Applies URI encoding and replaces all reserved characters with their respective %XX code.
1818pub 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 {
1931 var outsize: usize = 0;
2032 for (input) |c| {
21 outsize += if (isUnreserved(c)) @as(usize, 1) else 3;
33 outsize += if (keepUnescaped(c)) @as(usize, 1) else 3;
2234 }
2335 var output = try allocator.alloc(u8, outsize);
2436 var outptr: usize = 0;
2537
2638 for (input) |c| {
27 if (isUnreserved(c)) {
39 if (keepUnescaped(c)) {
2840 output[outptr] = c;
2941 outptr += 1;
3042 } else {
......@@ -94,13 +106,14 @@ pub fn unescapeString(allocator: std.mem.Allocator, input: []const u8) error{Out
94106
95107pub 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.
98111/// The return value will contain unescaped strings pointing into the
99112/// 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 {
101114 var reader = SliceReader{ .slice = text };
102115 var uri = Uri{
103 .scheme = reader.readWhile(isSchemeChar),
116 .scheme = "",
104117 .user = null,
105118 .password = null,
106119 .host = null,
......@@ -110,14 +123,6 @@ pub fn parse(text: []const u8) ParseError!Uri {
110123 .fragment = null,
111124 };
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
121126 if (reader.peekPrefix("//")) { // authority part
122127 std.debug.assert(reader.get().? == '/');
123128 std.debug.assert(reader.get().? == '/');
......@@ -179,6 +184,76 @@ pub fn parse(text: []const u8) ParseError!Uri {
179184 return uri;
180185}
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
182257const SliceReader = struct {
183258 const Self = @This();
184259
......@@ -284,6 +359,14 @@ fn isPathSeparator(c: u8) bool {
284359 };
285360}
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
287370fn isQuerySeparator(c: u8) bool {
288371 return switch (c) {
289372 '#' => true,
lib/std/array_list.zig+26-7
......@@ -141,11 +141,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
141141 return cloned;
142142 }
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.
145146 /// This operation is O(N).
146147 /// Invalidates pointers if additional memory is needed.
147148 pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void {
148149 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);
149159 self.items.len += 1;
150160
151161 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
609619 return cloned;
610620 }
611621
612 /// Insert `item` at index `n`. Moves `list[n .. list.len]`
613 /// to higher indices to make room.
622 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
623 /// If `n` is equal to the length of the list this operation is equivalent to append.
614624 /// This operation is O(N).
615625 /// Invalidates pointers if additional memory is needed.
616626 pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void {
617627 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);
618637 self.items.len += 1;
619638
620639 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" {
13091328 var list = ArrayList(i32).init(a);
13101329 defer list.deinit();
13111330
1312 try list.append(1);
1331 try list.insert(0, 1);
13131332 try list.append(2);
1314 try list.append(3);
1333 try list.insert(2, 3);
13151334 try list.insert(0, 5);
13161335 try testing.expect(list.items[0] == 5);
13171336 try testing.expect(list.items[1] == 1);
......@@ -1322,9 +1341,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" {
13221341 var list = ArrayListUnmanaged(i32){};
13231342 defer list.deinit(a);
13241343
1325 try list.append(a, 1);
1344 try list.insert(a, 0, 1);
13261345 try list.append(a, 2);
1327 try list.append(a, 3);
1346 try list.insert(a, 2, 3);
13281347 try list.insert(a, 0, 5);
13291348 try testing.expect(list.items[0] == 5);
13301349 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
153153pub extern "c" fn unlink(path: [*:0]const u8) c_int;
154154pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int;
155155pub 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;
157158pub extern "c" fn fork() c_int;
158159pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;
159160pub 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;
88
99pub const aarch64 = @import("darwin/aarch64.zig");
1010pub const x86_64 = @import("darwin/x86_64.zig");
11pub const cssm = @import("darwin/cssm.zig");
1112
1213const arch_bits = switch (native_arch) {
1314 .aarch64 => @import("darwin/aarch64.zig"),
......@@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE {
21792180 return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
21802181}
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
21822191/// Kernel return values
21832192pub const KernE = enum(u32) {
21842193 SUCCESS = 0,
......@@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31;
30853094pub const caddr_t = ?[*]u8;
30863095
30873096pub 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;
1717const TailQueue = std.TailQueue;
1818const maxInt = std.math.maxInt;
1919const assert = std.debug.assert;
20const is_darwin = builtin.target.isDarwin();
2021
2122pub const ChildProcess = struct {
2223 pub const Id = switch (builtin.os.tag) {
2324 .windows => windows.HANDLE,
25 .wasi => void,
2426 else => os.pid_t,
2527 };
2628
......@@ -70,6 +72,43 @@ pub const ChildProcess = struct {
7072 /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.
7173 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
73112 pub const Arg0Expand = os.Arg0Expand;
74113
75114 pub const SpawnError = error{
......@@ -90,8 +129,7 @@ pub const ChildProcess = struct {
90129 os.SetIdError ||
91130 os.ChangeCurDirError ||
92131 windows.CreateProcessError ||
93 windows.WaitForSingleObjectError ||
94 os.posix_spawn.Error;
132 windows.WaitForSingleObjectError;
95133
96134 pub const Term = union(enum) {
97135 Exited: u8,
......@@ -143,10 +181,6 @@ pub const ChildProcess = struct {
143181 @compileError("the target operating system cannot spawn processes");
144182 }
145183
146 if (comptime builtin.target.isDarwin()) {
147 return self.spawnMacos();
148 }
149
150184 if (builtin.os.tag == .windows) {
151185 return self.spawnWindows();
152186 } else {
......@@ -337,10 +371,16 @@ pub const ChildProcess = struct {
337371 }
338372
339373 fn waitUnwrapped(self: *ChildProcess) !void {
340 const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())
341 try os.posix_spawn.waitpid(self.id, 0)
342 else
343 os.waitpid(self.id, 0);
374 const res: os.WaitPidResult = res: {
375 if (builtin.os.tag == .linux and self.request_resource_usage_statistics) {
376 var ru: std.os.rusage = undefined;
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 };
344384 const status = res.status;
345385 self.cleanupStreams();
346386 self.handleWaitResult(status);
......@@ -416,121 +456,6 @@ pub const ChildProcess = struct {
416456 Term{ .Unknown = status };
417457 }
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
534459 fn spawnPosix(self: *ChildProcess) SpawnError!void {
535460 const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
536461 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 @@
1const root = @import("root");
2
13/// Authenticated Encryption with Associated Data
24pub const aead = struct {
35 pub const aegis = struct {
......@@ -66,6 +68,11 @@ pub const dh = struct {
6668 pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
6769};
6870
71/// Key Encapsulation Mechanisms.
72pub const kem = struct {
73 pub const kyber_d00 = @import("crypto/kyber_d00.zig");
74};
75
6976/// Elliptic-curve arithmetic.
7077pub const ecc = struct {
7178 pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
......@@ -183,6 +190,27 @@ pub const errors = @import("crypto/errors.zig");
183190pub const tls = @import("crypto/tls.zig");
184191pub 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
186214test {
187215 _ = aead.aegis.Aegis128L;
188216 _ = aead.aegis.Aegis256;
......@@ -217,6 +245,8 @@ test {
217245
218246 _ = dh.X25519;
219247
248 _ = kem.kyber_d00;
249
220250 _ = ecc.Curve25519;
221251 _ = ecc.Edwards25519;
222252 _ = ecc.P256;
lib/std/crypto/25519/field.zig+8-1
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const crypto = std.crypto;
34const readIntLittle = std.mem.readIntLittle;
45const writeIntLittle = std.mem.writeIntLittle;
......@@ -6,6 +7,12 @@ const writeIntLittle = std.mem.writeIntLittle;
67const NonCanonicalError = crypto.errors.NonCanonicalError;
78const 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
916pub const Fe = struct {
1017 limbs: [5]u64,
1118
......@@ -264,7 +271,7 @@ pub const Fe = struct {
264271 }
265272
266273 /// 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 {
268275 var ax: [5]u128 = undefined;
269276 var bx: [5]u128 = undefined;
270277 var a19: [5]u128 = undefined;
lib/std/crypto/aes/armcrypto.zig+8-16
......@@ -32,62 +32,54 @@ pub const Block = struct {
3232 /// Encrypt a block with a round key.
3333 pub inline fn encrypt(block: Block, round_key: Block) Block {
3434 return Block{
35 .repr = asm (
35 .repr = (asm (
3636 \\ mov %[out].16b, %[in].16b
3737 \\ aese %[out].16b, %[zero].16b
3838 \\ aesmc %[out].16b, %[out].16b
39 \\ eor %[out].16b, %[out].16b, %[rk].16b
4039 : [out] "=&x" (-> BlockVec),
4140 : [in] "x" (block.repr),
42 [rk] "x" (round_key.repr),
4341 [zero] "x" (zero),
44 ),
42 )) ^ round_key.repr,
4543 };
4644 }
4745
4846 /// Encrypt a block with the last round key.
4947 pub inline fn encryptLast(block: Block, round_key: Block) Block {
5048 return Block{
51 .repr = asm (
49 .repr = (asm (
5250 \\ mov %[out].16b, %[in].16b
5351 \\ aese %[out].16b, %[zero].16b
54 \\ eor %[out].16b, %[out].16b, %[rk].16b
5552 : [out] "=&x" (-> BlockVec),
5653 : [in] "x" (block.repr),
57 [rk] "x" (round_key.repr),
5854 [zero] "x" (zero),
59 ),
55 )) ^ round_key.repr,
6056 };
6157 }
6258
6359 /// Decrypt a block with a round key.
6460 pub inline fn decrypt(block: Block, inv_round_key: Block) Block {
6561 return Block{
66 .repr = asm (
62 .repr = (asm (
6763 \\ mov %[out].16b, %[in].16b
6864 \\ aesd %[out].16b, %[zero].16b
6965 \\ aesimc %[out].16b, %[out].16b
70 \\ eor %[out].16b, %[out].16b, %[rk].16b
7166 : [out] "=&x" (-> BlockVec),
7267 : [in] "x" (block.repr),
73 [rk] "x" (inv_round_key.repr),
7468 [zero] "x" (zero),
75 ),
69 )) ^ inv_round_key.repr,
7670 };
7771 }
7872
7973 /// Decrypt a block with the last round key.
8074 pub inline fn decryptLast(block: Block, inv_round_key: Block) Block {
8175 return Block{
82 .repr = asm (
76 .repr = (asm (
8377 \\ mov %[out].16b, %[in].16b
8478 \\ aesd %[out].16b, %[zero].16b
85 \\ eor %[out].16b, %[out].16b, %[rk].16b
8679 : [out] "=&x" (-> BlockVec),
8780 : [in] "x" (block.repr),
88 [rk] "x" (inv_round_key.repr),
8981 [zero] "x" (zero),
90 ),
82 )) ^ inv_round_key.repr,
9183 };
9284 }
9385
lib/std/crypto/aes/soft.zig+302-63
......@@ -1,11 +1,11 @@
1// Based on Go stdlib implementation
2
31const std = @import("../../std.zig");
42const math = std.math;
53const mem = std.mem;
64
75const BlockVec = [4]u32;
86
7const side_channels_mitigations = std.options.side_channels_mitigations;
8
99/// A single AES block.
1010pub const Block = struct {
1111 pub const block_length: usize = 16;
......@@ -15,20 +15,20 @@ pub const Block = struct {
1515
1616 /// Convert a byte sequence into an internal representation.
1717 pub inline fn fromBytes(bytes: *const [16]u8) Block {
18 const s0 = mem.readIntBig(u32, bytes[0..4]);
19 const s1 = mem.readIntBig(u32, bytes[4..8]);
20 const s2 = mem.readIntBig(u32, bytes[8..12]);
21 const s3 = mem.readIntBig(u32, bytes[12..16]);
18 const s0 = mem.readIntLittle(u32, bytes[0..4]);
19 const s1 = mem.readIntLittle(u32, bytes[4..8]);
20 const s2 = mem.readIntLittle(u32, bytes[8..12]);
21 const s3 = mem.readIntLittle(u32, bytes[12..16]);
2222 return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };
2323 }
2424
2525 /// Convert the internal representation of a block into a byte sequence.
2626 pub inline fn toBytes(block: Block) [16]u8 {
2727 var bytes: [16]u8 = undefined;
28 mem.writeIntBig(u32, bytes[0..4], block.repr[0]);
29 mem.writeIntBig(u32, bytes[4..8], block.repr[1]);
30 mem.writeIntBig(u32, bytes[8..12], block.repr[2]);
31 mem.writeIntBig(u32, bytes[12..16], block.repr[3]);
28 mem.writeIntLittle(u32, bytes[0..4], block.repr[0]);
29 mem.writeIntLittle(u32, bytes[4..8], block.repr[1]);
30 mem.writeIntLittle(u32, bytes[8..12], block.repr[2]);
31 mem.writeIntLittle(u32, bytes[12..16], block.repr[3]);
3232 return bytes;
3333 }
3434
......@@ -50,32 +50,93 @@ pub const Block = struct {
5050 const s2 = block.repr[2];
5151 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)];
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)];
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)];
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)];
53 var x: [4]u32 = undefined;
54 x = table_lookup(&table_encrypt, @truncate(u8, s0), @truncate(u8, s1 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 24));
55 var t0 = x[0] ^ x[1] ^ x[2] ^ x[3];
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
58113 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
59114 }
60115
61116 /// Encrypt a block with the last round key.
62117 pub inline fn encryptLast(block: Block, round_key: Block) Block {
63 const t0 = block.repr[0];
64 const t1 = block.repr[1];
65 const t2 = block.repr[2];
66 const t3 = block.repr[3];
118 const s0 = block.repr[0];
119 const s1 = block.repr[1];
120 const s2 = block.repr[2];
121 const s3 = block.repr[3];
67122
68123 // 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]);
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]);
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]);
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]);
73 s0 ^= round_key.repr[0];
74 s1 ^= round_key.repr[1];
75 s2 ^= round_key.repr[2];
76 s3 ^= round_key.repr[3];
124 var x: [4]u8 = undefined;
125 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s3 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s0));
126 var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
127 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s0 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s1));
128 var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
129 x = sbox_lookup(&sbox_encrypt, @truncate(u8, s1 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s2));
130 var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[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 } };
79140 }
80141
81142 /// Decrypt a block with a round key.
......@@ -85,32 +146,93 @@ pub const Block = struct {
85146 const s2 = block.repr[2];
86147 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)];
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)];
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)];
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)];
149 var x: [4]u32 = undefined;
150 x = table_lookup(&table_decrypt, @truncate(u8, s0), @truncate(u8, s3 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 24));
151 var t0 = x[0] ^ x[1] ^ x[2] ^ x[3];
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
93209 return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
94210 }
95211
96212 /// Decrypt a block with the last round key.
97213 pub inline fn decryptLast(block: Block, round_key: Block) Block {
98 const t0 = block.repr[0];
99 const t1 = block.repr[1];
100 const t2 = block.repr[2];
101 const t3 = block.repr[3];
214 const s0 = block.repr[0];
215 const s1 = block.repr[1];
216 const s2 = block.repr[2];
217 const s3 = block.repr[3];
102218
103219 // 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]);
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]);
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]);
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]);
108 s0 ^= round_key.repr[0];
109 s1 ^= round_key.repr[1];
110 s2 ^= round_key.repr[2];
111 s3 ^= round_key.repr[3];
220 var x: [4]u8 = undefined;
221 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s1 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s0));
222 var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
223 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s2 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s1));
224 var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]);
225 x = sbox_lookup(&sbox_decrypt, @truncate(u8, s3 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s2));
226 var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[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 } };
114236 }
115237
116238 /// Apply the bitwise XOR operation to the content of two blocks.
......@@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type {
226348 const subw = struct {
227349 // Apply sbox_encrypt to each byte in w.
228350 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]);
230353 }
231354 }.func;
232355
......@@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type {
244367 }
245368 round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t;
246369 }
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 }
247374 return Self{ .round_keys = round_keys };
248375 }
249376
......@@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type {
257384 const ei = total_words - i - 4;
258385 comptime var j: usize = 0;
259386 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];
261388 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];
263392 }
264 inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x;
393 inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = rk;
265394 }
266395 }
267396 return Self{ .round_keys = inv_round_keys };
......@@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type {
293422 const round_keys = ctx.key_schedule.round_keys;
294423 var t = Block.fromBytes(src).xorBlocks(round_keys[0]);
295424 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 }
297436 t = t.encrypt(round_keys[i]);
298437 }
299438 t = t.encryptLast(round_keys[rounds]);
......@@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type {
305444 const round_keys = ctx.key_schedule.round_keys;
306445 var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]);
307446 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 }
309458 t = t.encrypt(round_keys[i]);
310459 }
311460 t = t.encryptLast(round_keys[rounds]);
......@@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type {
359508 const inv_round_keys = ctx.key_schedule.round_keys;
360509 var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]);
361510 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 }
363522 t = t.decrypt(inv_round_keys[i]);
364523 }
365524 t = t.decryptLast(inv_round_keys[rounds]);
......@@ -428,10 +587,11 @@ const powx = init: {
428587 break :init array;
429588};
430589
431const sbox_encrypt align(64) = generateSbox(false);
432const sbox_decrypt align(64) = generateSbox(true);
433const table_encrypt align(64) = generateTable(false);
434const table_decrypt align(64) = generateTable(true);
590const sbox_encrypt align(64) = generateSbox(false); // S-box for encryption
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
592const sbox_decrypt align(64) = generateSbox(true); // S-box for decryption
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
436596// Generate S-box substitution values.
437597fn generateSbox(invert: bool) [256]u8 {
......@@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 {
472632 var table: [4][256]u32 = undefined;
473633
474634 for (generateSbox(invert), 0..) |value, index| {
475 table[0][index] = mul(value, if (invert) 0xb else 0x3);
476 table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 8);
477 table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 16);
478 table[0][index] |= math.shl(u32, mul(value, if (invert) 0xe else 0x2), 24);
479
480 table[1][index] = math.rotr(u32, table[0][index], 8);
481 table[2][index] = math.rotr(u32, table[0][index], 16);
482 table[3][index] = math.rotr(u32, table[0][index], 24);
635 table[0][index] = math.shl(u32, mul(value, if (invert) 0xb else 0x3), 24);
636 table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 16);
637 table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 8);
638 table[0][index] |= mul(value, if (invert) 0xe else 0x2);
639
640 table[1][index] = math.rotl(u32, table[0][index], 8);
641 table[2][index] = math.rotl(u32, table[0][index], 16);
642 table[3][index] = math.rotl(u32, table[0][index], 24);
483643 }
484644
485645 return table;
......@@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 {
506666
507667 return @truncate(u8, s);
508668}
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(
138138}
139139
140140fn blake2bLong(out: []u8, in: []const u8) void {
141 var b2 = Blake2b512.init(.{ .expected_out_bits = math.min(512, out.len * 8) });
142
143 var buffer: [Blake2b512.digest_length]u8 = undefined;
144 mem.writeIntLittle(u32, buffer[0..4], @intCast(u32, out.len));
145 b2.update(buffer[0..4]);
146 b2.update(in);
147 b2.final(&buffer);
148
149 if (out.len <= Blake2b512.digest_length) {
150 mem.copy(u8, out, buffer[0..out.len]);
141 const H = Blake2b512;
142 var outlen_bytes: [4]u8 = undefined;
143 mem.writeIntLittle(u32, &outlen_bytes, @intCast(u32, out.len));
144
145 var out_buf: [H.digest_length]u8 = undefined;
146
147 if (out.len <= H.digest_length) {
148 var h = H.init(.{ .expected_out_bits = out.len * 8 });
149 h.update(&outlen_bytes);
150 h.update(in);
151 h.final(&out_buf);
152 mem.copy(u8, out, out_buf[0..out.len]);
151153 return;
152154 }
153155
154 b2 = Blake2b512.init(.{});
155 mem.copy(u8, out, buffer[0..32]);
156 var out_slice = out[32..];
157 while (out_slice.len > Blake2b512.digest_length) : ({
158 out_slice = out_slice[32..];
159 b2 = Blake2b512.init(.{});
160 }) {
161 b2.update(&buffer);
162 b2.final(&buffer);
163 mem.copy(u8, out_slice, buffer[0..32]);
164 }
165
166 var r = Blake2b512.digest_length;
167 if (out.len % Blake2b512.digest_length > 0) {
168 r = ((out.len + 31) / 32) - 2;
169 b2 = Blake2b512.init(.{ .expected_out_bits = r * 8 });
156 var h = H.init(.{});
157 h.update(&outlen_bytes);
158 h.update(in);
159 h.final(&out_buf);
160 var out_slice = out;
161 mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]);
162 out_slice = out_slice[H.digest_length / 2 ..];
163
164 var in_buf: [H.digest_length]u8 = undefined;
165 while (out_slice.len > H.digest_length) {
166 mem.copy(u8, &in_buf, &out_buf);
167 H.hash(&in_buf, &out_buf, .{});
168 mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]);
169 out_slice = out_slice[H.digest_length / 2 ..];
170170 }
171
172 b2.update(&buffer);
173 b2.final(&buffer);
174 mem.copy(u8, out_slice, buffer[0..r]);
171 mem.copy(u8, &in_buf, &out_buf);
172 H.hash(&in_buf, &out_buf, .{ .expected_out_bits = out_slice.len * 8 });
173 mem.copy(u8, out_slice, out_buf[0..out_slice.len]);
175174}
176175
177176fn initBlocks(
lib/std/crypto/benchmark.zig+89
......@@ -27,6 +27,8 @@ const hashes = [_]Crypto{
2727 Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
2828 Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },
2929 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" },
3032 Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
3133 Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
3234 Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
......@@ -201,6 +203,72 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime
201203 return throughput;
202204}
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
204272const aeads = [_]Crypto{
205273 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
206274 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
......@@ -483,4 +551,25 @@ pub fn main() !void {
483551 try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });
484552 }
485553 }
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 }
486575}
lib/std/crypto/blake3.zig+1-1
......@@ -200,7 +200,7 @@ const CompressGeneric = struct {
200200 }
201201};
202202
203const compress = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c)
203const compress = if (builtin.cpu.arch == .x86_64)
204204 CompressVectorized.compress
205205else
206206 CompressGeneric.compress;
lib/std/crypto/gimli.zig+1-1
......@@ -152,7 +152,7 @@ pub const State = struct {
152152 self.endianSwap();
153153 }
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: {
156156 break :impl permute_vectorized;
157157 } else if (builtin.mode == .ReleaseSmall) impl: {
158158 break :impl permute_small;
lib/std/crypto/keccak_p.zig+3-3
......@@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type {
175175 /// Apply a (possibly) reduced-round permutation to the state.
176176 pub fn permuteR(self: *Self, comptime rounds: u5) void {
177177 var i = RC.len - rounds;
178 while (i < rounds - rounds % 3) : (i += 3) {
178 while (i < RC.len - RC.len % 3) : (i += 3) {
179179 self.round(RC[i]);
180180 self.round(RC[i + 1]);
181181 self.round(RC[i + 2]);
182182 }
183 while (i < rounds) : (i += 1) {
183 while (i < RC.len) : (i += 1) {
184184 self.round(RC[i]);
185185 }
186186 }
......@@ -231,7 +231,7 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti
231231 bytes = bytes[rate..];
232232 }
233233 if (bytes.len > 0) {
234 self.st.addBytes(bytes[0..]);
234 mem.copy(u8, &self.buf, bytes);
235235 self.offset = bytes.len;
236236 }
237237 }
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");
1818pub const Shake128 = Shake(128);
1919pub 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
2135/// A generic Keccak hash function.
2236pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {
2337 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
7690
7791/// The SHAKE extendable output hash function.
7892pub 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 {
79103 const f = 1600;
80 const rounds = 24;
81 const State = KeccakState(f, security_level * 2, 0x1f, rounds);
104 const State = KeccakState(f, security_level * 2, delim, rounds);
82105
83106 return struct {
84107 const Self = @This();
......@@ -348,3 +371,23 @@ test "SHAKE-256 single" {
348371 Shake256.hash("hello123", &out, .{});
349372 try htest.assertEqual("ade612ba265f92de4a37", &out);
350373}
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 {
8888 }
8989};
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
91139/// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which
92140/// must conform to `StreamInterface`.
93141///
94142/// `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 {
96144 const host_len = @intCast(u16, host.len);
97145
98146 var random_buffer: [128]u8 = undefined;
lib/std/debug.zig+33
......@@ -635,6 +635,7 @@ pub const TTY = struct {
635635 pub const Color = enum {
636636 Red,
637637 Green,
638 Yellow,
638639 Cyan,
639640 White,
640641 Dim,
......@@ -659,6 +660,7 @@ pub const TTY = struct {
659660 const color_string = switch (color) {
660661 .Red => "\x1b[31;1m",
661662 .Green => "\x1b[32;1m",
663 .Yellow => "\x1b[33;1m",
662664 .Cyan => "\x1b[36;1m",
663665 .White => "\x1b[37;1m",
664666 .Bold => "\x1b[1m",
......@@ -671,6 +673,7 @@ pub const TTY = struct {
671673 const attributes = switch (color) {
672674 .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,
673675 .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
676 .Yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
674677 .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
675678 .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
676679 .Dim => windows.FOREGROUND_INTENSITY,
......@@ -682,6 +685,36 @@ pub const TTY = struct {
682685 },
683686 };
684687 }
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 }
685718 };
686719};
687720
lib/std/fifo.zig+27
......@@ -164,6 +164,17 @@ pub fn LinearFifo(
164164 return self.readableSliceMut(offset);
165165 }
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
167178 /// Discard first `count` items in the fifo
168179 pub fn discard(self: *Self, count: usize) void {
169180 assert(count <= self.count);
......@@ -383,6 +394,22 @@ pub fn LinearFifo(
383394 self.discard(try dest_writer.write(self.readableSlice(0)));
384395 }
385396 }
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 }
386413 };
387414}
388415
lib/std/fmt.zig+22
......@@ -2555,6 +2555,21 @@ test "bytes.hex" {
25552555 try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)});
25562556}
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
25582573/// Decodes the sequence of bytes represented by the specified string of
25592574/// hexadecimal characters.
25602575/// Returns a slice of the output buffer containing the decoded bytes.
......@@ -2575,6 +2590,13 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {
25752590 return out[0 .. in_i / 2];
25762591}
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
25782600test "hexToBytes" {
25792601 var buf: [32]u8 = undefined;
25802602 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" {
119119}
120120
121121test "fmt.parseFloat hex.f32" {
122 try testing.expectError(error.InvalidCharacter, parseFloat(f32, "0x"));
122123 try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0);
123124 try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5);
124125 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
107107 tryParseDigits(MantissaT, stream, &mantissa, info.base);
108108 var int_end = stream.offsetTrue();
109109 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
111113 // handle dot with the following digits
112114 var exponent: i64 = 0;
lib/std/fs/file.zig+33-5
......@@ -1048,12 +1048,27 @@ pub const File = struct {
10481048 /// Returns the number of bytes read. If the number read is smaller than the total bytes
10491049 /// from all the buffers, it means the file reached the end. Reaching the end of a file
10501050 /// is not an error condition.
1051 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1052 /// order to handle partial reads from the underlying OS layer.
1053 /// See https://github.com/ziglang/zig/issues/7699
1051 ///
1052 /// The `iovecs` parameter is mutable because:
1053 /// * 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
10541061 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
10551062 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
10571072 var i: usize = 0;
10581073 var off: usize = 0;
10591074 while (true) {
......@@ -1181,13 +1196,26 @@ pub const File = struct {
11811196 }
11821197 }
11831198
1184 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1185 /// order to handle partial writes from the underlying OS layer.
1199 /// The `iovecs` parameter is mutable because:
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.
11861206 /// See https://github.com/ziglang/zig/issues/7699
11871207 /// See equivalent function: `std.net.Stream.writevAll`.
11881208 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
11891209 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
11911219 var i: usize = 0;
11921220 while (true) {
11931221 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" {
11241124test "open file with exclusive nonblocking lock twice (absolute paths)" {
11251125 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 defer allocator.free(cwd);
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);
1130 var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1131 _ = fs.base64_encoder.encode(&random_b64, &random_bytes);
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 });
11381152 file1.close();
11391153 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"
1919pub const WasmAllocator = @import("heap/WasmAllocator.zig");
2020pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");
2121pub const PageAllocator = @import("heap/PageAllocator.zig");
22pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig");
2223
2324const memory_pool = @import("heap/memory_pool.zig");
2425pub 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) {
248248
249249pub const TransferEncoding = enum {
250250 chunked,
251 // compression is intentionally omitted here, as std.http.Client stores it as content-encoding
252};
253
254pub const ContentEncoding = enum {
251255 compress,
252256 deflate,
253257 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,
254269};
255270
256271const std = @import("std.zig");
lib/std/http/Client.zig+251-785
......@@ -13,6 +13,12 @@ const Uri = std.Uri;
1313const Allocator = std.mem.Allocator;
1414const 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
1622/// Used for tcpConnectToHost and storing HTTP headers when an externally
1723/// managed buffer is not provided.
1824allocator: Allocator,
......@@ -21,853 +27,256 @@ ca_bundle: std.crypto.Certificate.Bundle = .{},
2127/// it will first rescan the system for root certificates.
2228next_https_rescan_certs: bool = true,
2329
24pub const Connection = struct {
25 stream: net.Stream,
26 /// undefined unless protocol is tls.
27 tls_client: std.crypto.tls.Client,
28 protocol: Protocol,
30connection_pool: ConnectionPool = .{},
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 {
33 switch (conn.protocol) {
34 .plain => return conn.stream.read(buffer),
35 .tls => return conn.tls_client.read(conn.stream, buffer),
39 const Queue = std.TailQueue(Connection);
40 pub const Node = Queue.Node;
41
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;
3662 }
37 }
3863
39 pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize {
40 switch (conn.protocol) {
41 .plain => return conn.stream.readAtLeast(buffer, len),
42 .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len),
43 }
64 return null;
4465 }
4566
46 pub fn writeAll(conn: *Connection, buffer: []const u8) !void {
47 switch (conn.protocol) {
48 .plain => return conn.stream.writeAll(buffer),
49 .tls => return conn.tls_client.writeAll(conn.stream, buffer),
50 }
67 /// Acquires an existing connection from the connection pool. This function is not threadsafe.
68 pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void {
69 pool.free.remove(node);
70 pool.free_len -= 1;
71
72 pool.used.append(node);
5173 }
5274
53 pub fn write(conn: *Connection, buffer: []const u8) !usize {
54 switch (conn.protocol) {
55 .plain => return conn.stream.write(buffer),
56 .tls => return conn.tls_client.write(conn.stream, buffer),
57 }
75 /// Acquires an existing connection from the connection pool. This function is threadsafe.
76 pub fn acquire(pool: *ConnectionPool, node: *Node) void {
77 pool.mutex.lock();
78 defer pool.mutex.unlock();
79
80 return pool.acquireUnsafe(node);
5881 }
59};
6082
61/// TODO: emit error.UnexpectedEndOfStream or something like that when the read
62/// data does not match the content length. This is necessary since HTTPS disables
63/// close_notify protection on underlying TLS streams.
64pub const Request = struct {
65 client: *Client,
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 }
83 /// Tries to release a connection back to the connection pool. This function is threadsafe.
84 /// If the connection is marked as closing, it will be closed instead.
85 pub fn release(pool: *ConnectionPool, client: *Client, node: *Node) void {
86 pool.mutex.lock();
87 defer pool.mutex.unlock();
20688
207 pub fn initStatic(buf: []u8) Response {
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 }
89 pool.used.remove(node);
21790
218 /// Returns how many bytes are part of HTTP headers. Always less than or
219 /// equal to bytes.len. If the amount returned is less than bytes.len, it
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 }
91 if (node.data.closing) {
92 node.data.close(client);
39893
399 pub fn findChunkedLen(r: *Response, bytes: []const u8) usize {
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 }
94 return client.allocator.destroy(node);
44295 }
44396
444 fn parseInt3(nnn: @Vector(3, u8)) u10 {
445 const zero: @Vector(3, u8) = .{ '0', '0', '0' };
446 const mmm: @Vector(3, u10) = .{ 100, 10, 1 };
447 return @reduce(.Add, @as(@Vector(3, u10), nnn -% zero) *% mmm);
448 }
97 if (pool.free_len + 1 >= pool.free_size) {
98 const popped = pool.free.popFirst() orelse unreachable;
44999
450 test parseInt3 {
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 }
100 popped.data.close(client);
456101
457 test "find headers end basic" {
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"));
102 return client.allocator.destroy(popped);
463103 }
464104
465 test "find headers end vectorized" {
466 var buffer: [1]u8 = undefined;
467 var r = Response.initStatic(&buffer);
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 }
105 pool.free.append(node);
106 pool.free_len += 1;
107 }
476108
477 test "find headers end bug" {
478 var buffer: [1]u8 = undefined;
479 var r = Response.initStatic(&buffer);
480 const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
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 };
109 /// Adds a newly created node to the pool of used connections. This function is threadsafe.
110 pub fn addUsed(pool: *ConnectionPool, node: *Node) void {
111 pool.mutex.lock();
112 defer pool.mutex.unlock();
500113
501 pub const Headers = struct {
502 version: http.Version = .@"HTTP/1.1",
503 method: http.Method = .GET,
504 };
114 pool.used.append(node);
115 }
505116
506 pub const Options = struct {
507 max_redirects: u32 = 3,
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 };
117 pub fn deinit(pool: *ConnectionPool, client: *Client) void {
118 pool.mutex.lock();
523119
524 /// May be skipped if header strategy is buffer.
525 pub fn deinit(req: *Request) void {
526 if (req.response.header_bytes_owned) {
527 req.response.header_bytes.deinit(req.client.allocator);
120 var next = pool.free.first;
121 while (next) |node| {
122 defer client.allocator.destroy(node);
123 next = node.next;
124
125 node.data.close(client);
528126 }
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;
530137 }
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 {
535 return .{ .context = req };
157 pub fn read(conn: *Connection, buffer: []u8) !usize {
158 switch (conn.protocol) {
159 .plain => return conn.stream.read(buffer),
160 .tls => return conn.tls_client.read(conn.stream, buffer),
161 }
536162 }
537163
538 pub fn readAll(req: *Request, buffer: []u8) !usize {
539 return readAtLeast(req, buffer, buffer.len);
164 pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize {
165 switch (conn.protocol) {
166 .plain => return conn.stream.readAtLeast(buffer, len),
167 .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len),
168 }
540169 }
541170
542171 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,
572172 TlsConnectionTruncated,
573173 TlsRecordOverflow,
574174 TlsDecodeError,
575175 TlsAlert,
576176 TlsBadRecordMac,
177 Overflow,
577178 TlsBadLength,
578179 TlsIllegalParameter,
579180 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,
661181 };
662182
663 pub fn read(req: *Request, buffer: []u8) ReadError!usize {
664 return readAtLeast(req, buffer, 1);
183 pub const Reader = std.io.Reader(*Connection, ReadError, read);
184
185 pub fn reader(conn: *Connection) Reader {
186 return Reader{ .context = conn };
665187 }
666188
667 pub fn readAtLeast(req: *Request, buffer: []u8, len: usize) !usize {
668 assert(len <= buffer.len);
669 var index: usize = 0;
670 while (index < len) {
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;
189 pub fn writeAll(conn: *Connection, buffer: []const u8) !void {
190 switch (conn.protocol) {
191 .plain => return conn.stream.writeAll(buffer),
192 .tls => return conn.tls_client.writeAll(conn.stream, buffer),
675193 }
676 return index;
677194 }
678195
679 /// This one can return 0 without meaning EOF.
680 /// TODO change to readvAdvanced
681 pub fn readAdvanced(req: *Request, buffer: []u8) !usize {
682 var in = buffer[0..try req.connection.read(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 }
196 pub fn write(conn: *Connection, buffer: []const u8) !usize {
197 switch (conn.protocol) {
198 .plain => return conn.stream.write(buffer),
199 .tls => return conn.tls_client.write(conn.stream, buffer),
825200 }
826201 }
827202
828 inline fn int16(array: *const [2]u8) u16 {
829 return @bitCast(u16, array.*);
830 }
203 pub const WriteError = net.Stream.WriteError || error{};
204 pub const Writer = std.io.Writer(*Connection, WriteError, write);
831205
832 inline fn int32(array: *const [4]u8) u32 {
833 return @bitCast(u32, array.*);
206 pub fn writer(conn: *Connection) Writer {
207 return Writer{ .context = conn };
834208 }
835209
836 inline fn int64(array: *const [8]u8) u64 {
837 return @bitCast(u64, array.*);
838 }
210 pub fn close(conn: *Connection, client: *const Client) void {
211 if (conn.protocol == .tls) {
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 {
841 _ = Response;
217 conn.stream.close();
218
219 client.allocator.free(conn.host);
842220 }
843221};
844222
845223pub fn deinit(client: *Client) void {
224 client.connection_pool.deinit(client);
225
846226 client.ca_bundle.deinit(client.allocator);
847227 client.* = undefined;
848228}
849229
850pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !Connection {
851 var conn: Connection = .{
230pub const ConnectError = std.mem.Allocator.Error || net.TcpConnectToHostError || std.crypto.tls.Client.InitError(net.Stream);
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 = .{
852245 .stream = try net.tcpConnectToHost(client.allocator, host, port),
853246 .tls_client = undefined,
854247 .protocol = protocol,
248 .host = try client.allocator.dupe(u8, host),
249 .port = port,
855250 };
856251
857252 switch (protocol) {
858253 .plain => {},
859254 .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);
861257 // This is appropriate for HTTPS because the HTTP headers contain
862258 // 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;
864260 },
865261 }
866262
263 client.connection_pool.addUsed(conn);
264
867265 return conn;
868266}
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 {
871280 const protocol: Connection.Protocol = if (mem.eql(u8, uri.scheme, "http"))
872281 .plain
873282 else if (mem.eql(u8, uri.scheme, "https"))
......@@ -883,34 +292,89 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req
883292 const host = uri.host orelse return error.UriMissingHost;
884293
885294 if (client.next_https_rescan_certs and protocol == .tls) {
886 try client.ca_bundle.rescan(client.allocator);
887 client.next_https_rescan_certs = false;
295 client.connection_pool.mutex.lock(); // TODO: this could be so much better than reusing the connection pool mutex.
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 }
888302 }
889303
890304 var req: Request = .{
305 .uri = uri,
891306 .client = client,
892307 .headers = headers,
893308 .connection = try client.connect(host, port, protocol),
894309 .redirects_left = options.max_redirects,
310 .handle_redirects = options.handle_redirects,
311 .compression_init = false,
895312 .response = switch (options.header_strategy) {
896 .dynamic => |max| Request.Response.initDynamic(max),
897 .static => |buf| Request.Response.initStatic(buf),
313 .dynamic => |max| Response.initDynamic(max),
314 .static => |buf| Response.initStatic(buf),
898315 },
316 .arena = undefined,
899317 };
900318
319 req.arena = std.heap.ArenaAllocator.init(client.allocator);
320
901321 {
902 var h = try std.BoundedArray(u8, 1000).init(0);
903 try h.appendSlice(@tagName(headers.method));
904 try h.appendSlice(" ");
905 try h.appendSlice(uri.path);
906 try h.appendSlice(" ");
907 try h.appendSlice(@tagName(headers.version));
908 try h.appendSlice("\r\nHost: ");
909 try h.appendSlice(host);
910 try h.appendSlice("\r\nConnection: close\r\n\r\n");
911
912 const header_bytes = h.slice();
913 try req.connection.writeAll(header_bytes);
322 var buffered = std.io.bufferedWriter(req.connection.data.writer());
323 const writer = buffered.writer();
324
325 const escaped_path = try Uri.escapePath(client.allocator, uri.path);
326 defer client.allocator.free(escaped_path);
327
328 const escaped_query = if (uri.query) |q| try Uri.escapeQuery(client.allocator, q) else null;
329 defer if (escaped_query) |q| client.allocator.free(q);
330
331 const escaped_fragment = if (uri.fragment) |f| try Uri.escapeQuery(client.allocator, f) else null;
332 defer if (escaped_fragment) |f| client.allocator.free(f);
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();
914378 }
915379
916380 return req;
......@@ -924,5 +388,7 @@ test {
924388 return error.SkipZigTest;
925389 }
926390
391 if (builtin.os.tag == .wasi) return error.SkipZigTest;
392
927393 _ = Request;
928394}
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 {
16741674
16751675 /// If a is positive, this passes through to truncate.
16761676 /// If a is negative, then r is set to positive with the bit pattern ~(a - 1).
1677 /// r may alias a.
16771678 ///
16781679 /// Asserts `r` has enough storage to store the result.
16791680 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
lib/std/mem.zig+4-9
......@@ -196,13 +196,8 @@ test "Allocator.resize" {
196196/// dest.len must be >= source.len.
197197/// If the slices overlap, dest.ptr must be <= src.ptr.
198198pub fn copy(comptime T: type, dest: []T, source: []const T) void {
199 // TODO instead of manually doing this check for the whole array
200 // and turning off runtime safety, the compiler should detect loops like
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;
199 for (dest[0..source.len], source) |*d, s|
200 d.* = s;
206201}
207202
208203/// Copy all of source into dest at position 0.
......@@ -611,8 +606,8 @@ test "lessThan" {
611606pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
612607 if (a.len != b.len) return false;
613608 if (a.ptr == b.ptr) return true;
614 for (a, 0..) |item, index| {
615 if (b[index] != item) return false;
609 for (a, b) |a_elem, b_elem| {
610 if (a_elem != b_elem) return false;
616611 }
617612 return true;
618613}
lib/std/multi_array_list.zig+13-2
......@@ -68,6 +68,15 @@ pub fn MultiArrayList(comptime S: type) type {
6868 other.deinit(gpa);
6969 self.* = undefined;
7070 }
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 }
7180 };
7281
7382 const Self = @This();
......@@ -463,16 +472,18 @@ pub fn MultiArrayList(comptime S: type) type {
463472 } });
464473 };
465474 /// 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.
467 fn dbHelper(self: *Self, child: *S, entry: *Entry) void {
475 /// child field order and entry type to facilitate fancy debug printing for this type.
476 fn dbHelper(self: *Self, child: *S, field: *Field, entry: *Entry) void {
468477 _ = self;
469478 _ = child;
479 _ = field;
470480 _ = entry;
471481 }
472482
473483 comptime {
474484 if (builtin.mode == .Debug) {
475485 _ = dbHelper;
486 _ = Slice.dbHelper;
476487 }
477488 }
478489 };
lib/std/net.zig+31-3
......@@ -702,8 +702,10 @@ pub const AddressList = struct {
702702 }
703703};
704704
705pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError;
706
705707/// 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 {
707709 const list = try getAddressList(allocator, name, port);
708710 defer list.deinit();
709711
......@@ -720,7 +722,9 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) !
720722 return std.os.ConnectError.ConnectionRefused;
721723}
722724
723pub fn tcpConnectToAddress(address: Address) !Stream {
725pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError;
726
727pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
724728 const nonblock = if (std.io.is_async) os.SOCK.NONBLOCK else 0;
725729 const sock_flags = os.SOCK.STREAM | nonblock |
726730 (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC);
......@@ -737,8 +741,32 @@ pub fn tcpConnectToAddress(address: Address) !Stream {
737741 return Stream{ .handle = sockfd };
738742}
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
740768/// 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 {
742770 const result = blk: {
743771 var arena = std.heap.ArenaAllocator.init(allocator);
744772 errdefer arena.deinit();
lib/std/os.zig+84-39
......@@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator;
2929const Preopen = std.fs.wasi.Preopen;
3030const PreopenList = std.fs.wasi.PreopenList;
3131
32pub const darwin = @import("os/darwin.zig");
32pub const darwin = std.c;
3333pub const dragonfly = std.c;
3434pub const freebsd = std.c;
3535pub const haiku = std.c;
......@@ -41,8 +41,6 @@ pub const plan9 = @import("os/plan9.zig");
4141pub const uefi = @import("os/uefi.zig");
4242pub const wasi = @import("os/wasi.zig");
4343pub const windows = @import("os/windows.zig");
44pub const posix_spawn = @import("os/posix_spawn.zig");
45pub const ptrace = @import("os/ptrace.zig");
4644
4745comptime {
4846 assert(@import("std") == std); // std lib tests require --zig-lib-dir
......@@ -56,7 +54,6 @@ test {
5654 }
5755 _ = wasi;
5856 _ = windows;
59 _ = posix_spawn;
6057
6158 _ = @import("os/test.zig");
6259}
......@@ -253,6 +250,25 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.
253250 else => undefined,
254251};
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
256272/// To obtain errno, call this function with the return value of the
257273/// system function call. For some systems this will obtain the value directly
258274/// from the return code; for others it will use a thread-local errno variable.
......@@ -575,22 +591,12 @@ pub fn abort() noreturn {
575591 raise(SIG.KILL) catch {};
576592 exit(127); // Pid 1 might not be signalled in some containers.
577593 }
578 if (builtin.os.tag == .uefi) {
579 exit(0); // TODO choose appropriate exit code
580 }
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"();
594 switch (builtin.os.tag) {
595 .uefi, .wasi, .cuda => @trap(),
596 else => system.abort(),
587597 }
588
589 system.abort();
590598}
591599
592extern fn @"llvm.trap"() noreturn;
593
594600pub const RaiseError = UnexpectedError;
595601
596602pub fn raise(sig: u8) RaiseError!void {
......@@ -759,6 +765,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
759765/// This operation is non-atomic on the following systems:
760766/// * Windows
761767/// 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.
762771pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
763772 if (builtin.os.tag == .windows) {
764773 // TODO improve this to use ReadFileScatter
......@@ -1036,6 +1045,8 @@ pub const WriteError = error{
10361045 FileTooBig,
10371046 InputOutput,
10381047 NoSpaceLeft,
1048 DeviceBusy,
1049 InvalidArgument,
10391050
10401051 /// In WASI, this error may occur when the file descriptor does
10411052 /// not hold the required rights to write to it.
......@@ -1122,7 +1133,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
11221133 switch (errno(rc)) {
11231134 .SUCCESS => return @intCast(usize, rc),
11241135 .INTR => continue,
1125 .INVAL => unreachable,
1136 .INVAL => return error.InvalidArgument,
11261137 .FAULT => unreachable,
11271138 .AGAIN => return error.WouldBlock,
11281139 .BADF => return error.NotOpenForWriting, // can be a race condition.
......@@ -1134,6 +1145,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
11341145 .PERM => return error.AccessDenied,
11351146 .PIPE => return error.BrokenPipe,
11361147 .CONNRESET => return error.ConnectionResetByPeer,
1148 .BUSY => return error.DeviceBusy,
11371149 else => |err| return unexpectedErrno(err),
11381150 }
11391151 }
......@@ -1157,6 +1169,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
11571169/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
11581170///
11591171/// 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.
11601175pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
11611176 if (builtin.os.tag == .windows) {
11621177 // TODO improve this to use WriteFileScatter
......@@ -1191,7 +1206,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
11911206 switch (errno(rc)) {
11921207 .SUCCESS => return @intCast(usize, rc),
11931208 .INTR => continue,
1194 .INVAL => unreachable,
1209 .INVAL => return error.InvalidArgument,
11951210 .FAULT => unreachable,
11961211 .AGAIN => return error.WouldBlock,
11971212 .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 {
12031218 .PERM => return error.AccessDenied,
12041219 .PIPE => return error.BrokenPipe,
12051220 .CONNRESET => return error.ConnectionResetByPeer,
1221 .BUSY => return error.DeviceBusy,
12061222 else => |err| return unexpectedErrno(err),
12071223 }
12081224 }
......@@ -1285,7 +1301,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
12851301 switch (errno(rc)) {
12861302 .SUCCESS => return @intCast(usize, rc),
12871303 .INTR => continue,
1288 .INVAL => unreachable,
1304 .INVAL => return error.InvalidArgument,
12891305 .FAULT => unreachable,
12901306 .AGAIN => return error.WouldBlock,
12911307 .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 {
12991315 .NXIO => return error.Unseekable,
13001316 .SPIPE => return error.Unseekable,
13011317 .OVERFLOW => return error.Unseekable,
1318 .BUSY => return error.DeviceBusy,
13021319 else => |err| return unexpectedErrno(err),
13031320 }
13041321 }
......@@ -1374,7 +1391,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
13741391 switch (errno(rc)) {
13751392 .SUCCESS => return @intCast(usize, rc),
13761393 .INTR => continue,
1377 .INVAL => unreachable,
1394 .INVAL => return error.InvalidArgument,
13781395 .FAULT => unreachable,
13791396 .AGAIN => return error.WouldBlock,
13801397 .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
13881405 .NXIO => return error.Unseekable,
13891406 .SPIPE => return error.Unseekable,
13901407 .OVERFLOW => return error.Unseekable,
1408 .BUSY => return error.DeviceBusy,
13911409 else => |err| return unexpectedErrno(err),
13921410 }
13931411 }
......@@ -3456,7 +3474,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi
34563474 const rc = system.bind(sock, addr, len);
34573475 switch (errno(rc)) {
34583476 .SUCCESS => return,
3459 .ACCES => return error.AccessDenied,
3477 .ACCES, .PERM => return error.AccessDenied,
34603478 .ADDRINUSE => return error.AddressInUse,
34613479 .BADF => unreachable, // always a race condition if this error is returned
34623480 .INVAL => unreachable, // invalid parameters
......@@ -3983,13 +4001,32 @@ pub const WaitPidResult = struct {
39834001};
39844002
39854003/// 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,
3987/// use `std.os.posix_spawn.waitpid` instead.
4004/// `fork` and `execve` method.
39884005pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
39894006 const Status = if (builtin.link_libc) c_int else u32;
39904007 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;
39914028 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);
39934030 switch (errno(rc)) {
39944031 .SUCCESS => return .{
39954032 .pid = @intCast(pid_t, rc),
......@@ -4310,6 +4347,8 @@ pub const MMapError = error{
43104347 /// a filesystem that was mounted no-exec.
43114348 PermissionDenied,
43124349 LockedMemoryLimitExceeded,
4350 ProcessFdQuotaExceeded,
4351 SystemFdQuotaExceeded,
43134352 OutOfMemory,
43144353} || UnexpectedError;
43154354
......@@ -4351,6 +4390,8 @@ pub fn mmap(
43514390 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
43524391 .NODEV => return error.MemoryMappingNotSupported,
43534392 .INVAL => unreachable, // Invalid parameters to mmap()
4393 .MFILE => return error.ProcessFdQuotaExceeded,
4394 .NFILE => return error.SystemFdQuotaExceeded,
43544395 .NOMEM => return error.OutOfMemory,
43554396 else => return unexpectedErrno(err),
43564397 }
......@@ -7086,20 +7127,24 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
70867127 };
70877128}
70887129
7089pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");
7090
7091fn noopSigHandler(_: c_int) callconv(.C) void {}
7130pub const PtraceError = error{
7131 DeviceBusy,
7132 ProcessNotFound,
7133 PermissionDenied,
7134} || UnexpectedError;
70927135
7093pub fn maybeIgnoreSigpipe() void {
7094 if (have_sigpipe_support and !std.options.keep_sigpipe) {
7095 const act = Sigaction{
7096 // We set handler to a noop function instead of SIG.IGN so we don't leak our
7097 // signal disposition to a child process
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)});
7136/// TODO on other OSes
7137pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
7138 switch (builtin.os.tag) {
7139 .macos, .ios, .tvos, .watchos => {},
7140 else => @compileError("TODO implement ptrace"),
71047141 }
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 };
71057150}
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 {
944944 return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);
945945}
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
947957pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {
948958 return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0);
949959}
......@@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u
17161726 );
17171727}
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 {
17201730 return syscall6(
17211731 .process_vm_readv,
17221732 @bitCast(usize, @as(isize, pid)),
1723 @ptrToInt(local),
1724 local_count,
1725 @ptrToInt(remote),
1726 remote_count,
1733 @ptrToInt(local.ptr),
1734 local.len,
1735 @ptrToInt(remote.ptr),
1736 remote.len,
17271737 flags,
17281738 );
17291739}
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 {
17321742 return syscall6(
17331743 .process_vm_writev,
17341744 @bitCast(usize, @as(isize, pid)),
1735 @ptrToInt(local),
1736 local_count,
1737 @ptrToInt(remote),
1738 remote_count,
1745 @ptrToInt(local.ptr),
1746 local.len,
1747 @ptrToInt(remote.ptr),
1748 remote.len,
17391749 flags,
17401750 );
17411751}
......@@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize {
18201830 return syscall3(.seccomp, operation, flags, @ptrToInt(args));
18211831}
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
18231850pub const E = switch (native_arch) {
18241851 .mips, .mipsel => @import("linux/errno/mips.zig").E,
18251852 .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E,
......@@ -5721,3 +5748,40 @@ pub const AUDIT = struct {
57215748 }
57225749 };
57235750};
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" {
17281728 };
17291729 defer ring.deinit();
17301730
1731 var tmp = std.testing.tmpDir(.{});
1732 defer tmp.cleanup();
1733
17311734 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 });
17331736 defer file.close();
1734 defer std.fs.cwd().deleteFile(path) catch {};
17351737 const fd = file.handle;
17361738
17371739 const buffer_write = [_]u8{42} ** 128;
......@@ -1796,10 +1798,11 @@ test "write/read" {
17961798 };
17971799 defer ring.deinit();
17981800
1801 var tmp = std.testing.tmpDir(.{});
1802 defer tmp.cleanup();
17991803 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 });
18011805 defer file.close();
1802 defer std.fs.cwd().deleteFile(path) catch {};
18031806 const fd = file.handle;
18041807
18051808 const buffer_write = [_]u8{97} ** 20;
......@@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" {
18421845 };
18431846 defer ring.deinit();
18441847
1848 var tmp = std.testing.tmpDir(.{});
1849 defer tmp.cleanup();
1850
18451851 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 });
18471853 defer file.close();
1848 defer std.fs.cwd().deleteFile(path) catch {};
18491854 const fd = file.handle;
18501855
18511856 var raw_buffers: [2][11]u8 = undefined;
......@@ -1899,8 +1904,10 @@ test "openat" {
18991904 };
19001905 defer ring.deinit();
19011906
1907 var tmp = std.testing.tmpDir(.{});
1908 defer tmp.cleanup();
1909
19021910 const path = "test_io_uring_openat";
1903 defer std.fs.cwd().deleteFile(path) catch {};
19041911
19051912 // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014
19061913 const path_addr = if (builtin.zig_backend == .stage2_llvm) p: {
......@@ -1910,12 +1917,12 @@ test "openat" {
19101917
19111918 const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT;
19121919 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);
19141921 try testing.expectEqual(linux.io_uring_sqe{
19151922 .opcode = .OPENAT,
19161923 .flags = 0,
19171924 .ioprio = 0,
1918 .fd = linux.AT.FDCWD,
1925 .fd = tmp.dir.fd,
19191926 .off = 0,
19201927 .addr = path_addr,
19211928 .len = mode,
......@@ -1931,12 +1938,6 @@ test "openat" {
19311938 const cqe_openat = try ring.copy_cqe();
19321939 try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data);
19331940 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 }
19401941 if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res});
19411942 try testing.expect(cqe_openat.res > 0);
19421943 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);
......@@ -1954,10 +1955,12 @@ test "close" {
19541955 };
19551956 defer ring.deinit();
19561957
1958 var tmp = std.testing.tmpDir(.{});
1959 defer tmp.cleanup();
1960
19571961 const path = "test_io_uring_close";
1958 const file = try std.fs.cwd().createFile(path, .{});
1962 const file = try tmp.dir.createFile(path, .{});
19591963 errdefer file.close();
1960 defer std.fs.cwd().deleteFile(path) catch {};
19611964
19621965 const sqe_close = try ring.close(0x44444444, file.handle);
19631966 try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode);
......@@ -1976,6 +1979,11 @@ test "close" {
19761979test "accept/connect/send/recv" {
19771980 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
19791987 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
19801988 error.SystemOutdated => return error.SkipZigTest,
19811989 error.PermissionDenied => return error.SkipZigTest,
......@@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" {
20172025test "sendmsg/recvmsg" {
20182026 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
20202033 var ring = IO_Uring.init(2, 0) catch |err| switch (err) {
20212034 error.SystemOutdated => return error.SkipZigTest,
20222035 error.PermissionDenied => return error.SkipZigTest,
......@@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" {
20242037 };
20252038 defer ring.deinit();
20262039
2040 if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907
20272041 const address_server = try net.Address.parseIp4("127.0.0.1", 3131);
20282042
20292043 const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);
......@@ -2223,6 +2237,11 @@ test "timeout_remove" {
22232237test "accept/connect/recv/link_timeout" {
22242238 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
22262245 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
22272246 error.SystemOutdated => return error.SkipZigTest,
22282247 error.PermissionDenied => return error.SkipZigTest,
......@@ -2279,10 +2298,12 @@ test "fallocate" {
22792298 };
22802299 defer ring.deinit();
22812300
2301 var tmp = std.testing.tmpDir(.{});
2302 defer tmp.cleanup();
2303
22822304 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 });
22842306 defer file.close();
2285 defer std.fs.cwd().deleteFile(path) catch {};
22862307
22872308 try testing.expectEqual(@as(u64, 0), (try file.stat()).size);
22882309
......@@ -2323,10 +2344,11 @@ test "statx" {
23232344 };
23242345 defer ring.deinit();
23252346
2347 var tmp = std.testing.tmpDir(.{});
2348 defer tmp.cleanup();
23262349 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 });
23282351 defer file.close();
2329 defer std.fs.cwd().deleteFile(path) catch {};
23302352
23312353 try testing.expectEqual(@as(u64, 0), (try file.stat()).size);
23322354
......@@ -2335,14 +2357,14 @@ test "statx" {
23352357 var buf: linux.Statx = undefined;
23362358 const sqe = try ring.statx(
23372359 0xaaaaaaaa,
2338 linux.AT.FDCWD,
2360 tmp.dir.fd,
23392361 path,
23402362 0,
23412363 linux.STATX_SIZE,
23422364 &buf,
23432365 );
23442366 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);
23462368 try testing.expectEqual(@as(u32, 1), try ring.submit());
23472369
23482370 const cqe = try ring.copy_cqe();
......@@ -2355,8 +2377,6 @@ test "statx" {
23552377 // The filesystem containing the file referred to by fd does not support this operation;
23562378 // or the mode is not supported by the filesystem containing the file referred to by fd:
23572379 .OPNOTSUPP => return error.SkipZigTest,
2358 // The kernel is too old to support FDCWD for dir_fd
2359 .BADF => return error.SkipZigTest,
23602380 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
23612381 }
23622382 try testing.expectEqual(linux.io_uring_cqe{
......@@ -2372,6 +2392,11 @@ test "statx" {
23722392test "accept/connect/recv/cancel" {
23732393 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
23752400 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
23762401 error.SystemOutdated => return error.SkipZigTest,
23772402 error.PermissionDenied => return error.SkipZigTest,
......@@ -2509,6 +2534,11 @@ test "register_files_update" {
25092534test "shutdown" {
25102535 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
25122542 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
25132543 error.SystemOutdated => return error.SkipZigTest,
25142544 error.PermissionDenied => return error.SkipZigTest,
......@@ -2516,6 +2546,7 @@ test "shutdown" {
25162546 };
25172547 defer ring.deinit();
25182548
2549 if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907
25192550 const address = try net.Address.parseIp4("127.0.0.1", 3131);
25202551
25212552 // Socket bound, expect shutdown to work
......@@ -2579,28 +2610,28 @@ test "renameat" {
25792610 const old_path = "test_io_uring_renameat_old";
25802611 const new_path = "test_io_uring_renameat_new";
25812612
2613 var tmp = std.testing.tmpDir(.{});
2614 defer tmp.cleanup();
2615
25822616 // Write old file with data
25832617
2584 const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 });
2585 defer {
2586 old_file.close();
2587 std.fs.cwd().deleteFile(new_path) catch {};
2588 }
2618 const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 });
2619 defer old_file.close();
25892620 try old_file.writeAll("hello");
25902621
25912622 // Submit renameat
25922623
25932624 var sqe = try ring.renameat(
25942625 0x12121212,
2595 linux.AT.FDCWD,
2626 tmp.dir.fd,
25962627 old_path,
2597 linux.AT.FDCWD,
2628 tmp.dir.fd,
25982629 new_path,
25992630 0,
26002631 );
26012632 try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode);
2602 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);
2603 try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len));
2633 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2634 try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len));
26042635 try testing.expectEqual(@as(u32, 1), try ring.submit());
26052636
26062637 const cqe = try ring.copy_cqe();
......@@ -2618,7 +2649,7 @@ test "renameat" {
26182649
26192650 // Validate that the old file doesn't exist anymore
26202651 {
2621 _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) {
2652 _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) {
26222653 error.FileNotFound => {},
26232654 else => std.debug.panic("unexpected error: {}", .{err}),
26242655 };
......@@ -2626,7 +2657,7 @@ test "renameat" {
26262657
26272658 // Validate that the new file exists with the proper content
26282659 {
2629 const new_file = try std.fs.cwd().openFile(new_path, .{});
2660 const new_file = try tmp.dir.openFile(new_path, .{});
26302661 defer new_file.close();
26312662
26322663 var new_file_data: [16]u8 = undefined;
......@@ -2647,22 +2678,24 @@ test "unlinkat" {
26472678
26482679 const path = "test_io_uring_unlinkat";
26492680
2681 var tmp = std.testing.tmpDir(.{});
2682 defer tmp.cleanup();
2683
26502684 // 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 });
26532687 defer file.close();
2654 defer std.fs.cwd().deleteFile(path) catch {};
26552688
26562689 // Submit unlinkat
26572690
26582691 var sqe = try ring.unlinkat(
26592692 0x12121212,
2660 linux.AT.FDCWD,
2693 tmp.dir.fd,
26612694 path,
26622695 0,
26632696 );
26642697 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);
26662699 try testing.expectEqual(@as(u32, 1), try ring.submit());
26672700
26682701 const cqe = try ring.copy_cqe();
......@@ -2679,7 +2712,7 @@ test "unlinkat" {
26792712 }, cqe);
26802713
26812714 // 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) {
26832716 error.FileNotFound => {},
26842717 else => std.debug.panic("unexpected error: {}", .{err}),
26852718 };
......@@ -2695,20 +2728,21 @@ test "mkdirat" {
26952728 };
26962729 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
27022736 // Submit mkdirat
27032737
27042738 var sqe = try ring.mkdirat(
27052739 0x12121212,
2706 linux.AT.FDCWD,
2740 tmp.dir.fd,
27072741 path,
27082742 0o0755,
27092743 );
27102744 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);
27122746 try testing.expectEqual(@as(u32, 1), try ring.submit());
27132747
27142748 const cqe = try ring.copy_cqe();
......@@ -2725,7 +2759,7 @@ test "mkdirat" {
27252759 }, cqe);
27262760
27272761 // Validate that the directory exist
2728 _ = try std.fs.cwd().openDir(path, .{});
2762 _ = try tmp.dir.openDir(path, .{});
27292763}
27302764
27312765test "symlinkat" {
......@@ -2738,26 +2772,25 @@ test "symlinkat" {
27382772 };
27392773 defer ring.deinit();
27402774
2775 var tmp = std.testing.tmpDir(.{});
2776 defer tmp.cleanup();
2777
27412778 const path = "test_io_uring_symlinkat";
27422779 const link_path = "test_io_uring_symlinkat_link";
27432780
2744 const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });
2745 defer {
2746 file.close();
2747 std.fs.cwd().deleteFile(path) catch {};
2748 std.fs.cwd().deleteFile(link_path) catch {};
2749 }
2781 const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
2782 defer file.close();
27502783
27512784 // Submit symlinkat
27522785
27532786 var sqe = try ring.symlinkat(
27542787 0x12121212,
27552788 path,
2756 linux.AT.FDCWD,
2789 tmp.dir.fd,
27572790 link_path,
27582791 );
27592792 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);
27612794 try testing.expectEqual(@as(u32, 1), try ring.submit());
27622795
27632796 const cqe = try ring.copy_cqe();
......@@ -2774,7 +2807,7 @@ test "symlinkat" {
27742807 }, cqe);
27752808
27762809 // Validate that the symlink exist
2777 _ = try std.fs.cwd().openFile(link_path, .{});
2810 _ = try tmp.dir.openFile(link_path, .{});
27782811}
27792812
27802813test "linkat" {
......@@ -2787,32 +2820,31 @@ test "linkat" {
27872820 };
27882821 defer ring.deinit();
27892822
2823 var tmp = std.testing.tmpDir(.{});
2824 defer tmp.cleanup();
2825
27902826 const first_path = "test_io_uring_linkat_first";
27912827 const second_path = "test_io_uring_linkat_second";
27922828
27932829 // Write file with data
27942830
2795 const first_file = try std.fs.cwd().createFile(first_path, .{ .truncate = true, .mode = 0o666 });
2796 defer {
2797 first_file.close();
2798 std.fs.cwd().deleteFile(first_path) catch {};
2799 std.fs.cwd().deleteFile(second_path) catch {};
2800 }
2831 const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 });
2832 defer first_file.close();
28012833 try first_file.writeAll("hello");
28022834
28032835 // Submit linkat
28042836
28052837 var sqe = try ring.linkat(
28062838 0x12121212,
2807 linux.AT.FDCWD,
2839 tmp.dir.fd,
28082840 first_path,
2809 linux.AT.FDCWD,
2841 tmp.dir.fd,
28102842 second_path,
28112843 0,
28122844 );
28132845 try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode);
2814 try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd);
2815 try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len));
2846 try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd);
2847 try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len));
28162848 try testing.expectEqual(@as(u32, 1), try ring.submit());
28172849
28182850 const cqe = try ring.copy_cqe();
......@@ -2829,7 +2861,7 @@ test "linkat" {
28292861 }, cqe);
28302862
28312863 // 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, .{});
28332865 defer second_file.close();
28342866
28352867 var second_file_data: [16]u8 = undefined;
......@@ -3060,6 +3092,11 @@ test "remove_buffers" {
30603092test "provide_buffers: accept/connect/send/recv" {
30613093 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
30633100 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
30643101 error.SystemOutdated => return error.SkipZigTest,
30653102 error.PermissionDenied => return error.SkipZigTest,
......@@ -3236,6 +3273,7 @@ const SocketTestHarness = struct {
32363273fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness {
32373274 // 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
32393277 const address = try net.Address.parseIp4("127.0.0.1", 3131);
32403278 const kernel_backlog = 1;
32413279 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;
88const fs = std.fs;
99
1010test "fallocate" {
11 var tmp = std.testing.tmpDir(.{});
12 defer tmp.cleanup();
13
1114 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 });
1316 defer file.close();
14 defer fs.cwd().deleteFile(path) catch {};
1517
1618 try expect((try file.stat()).size == 0);
1719
......@@ -67,12 +69,12 @@ test "timer" {
6769}
6870
6971test "statx" {
72 var tmp = std.testing.tmpDir(.{});
73 defer tmp.cleanup();
74
7075 const tmp_file_name = "just_a_temporary_file.txt";
71 var file = try fs.cwd().createFile(tmp_file_name, .{});
72 defer {
73 file.close();
74 fs.cwd().deleteFile(tmp_file_name) catch {};
75 }
76 var file = try tmp.dir.createFile(tmp_file_name, .{});
77 defer file.close();
7678
7779 var statx_buf: linux.Statx = undefined;
7880 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" {
105107}
106108
107109test "fadvise" {
110 var tmp = std.testing.tmpDir(.{});
111 defer tmp.cleanup();
112
108113 const tmp_file_name = "temp_posix_fadvise.txt";
109 var file = try fs.cwd().createFile(tmp_file_name, .{});
110 defer {
111 file.close();
112 fs.cwd().deleteFile(tmp_file_name) catch {};
113 }
114 var file = try tmp.dir.createFile(tmp_file_name, .{});
115 defer file.close();
114116
115117 var buf: [2048]u8 = undefined;
116118 try file.writeAll(&buf);
117119
118 const ret = linux.fadvise(
119 file.handle,
120 0,
121 0,
122 linux.POSIX_FADV.SEQUENTIAL,
123 );
120 const ret = linux.fadvise(file.handle, 0, 0, linux.POSIX_FADV.SEQUENTIAL);
124121 try expectEqual(@as(usize, 0), ret);
125122}
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
105105 // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
106106 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(
109 &result,
110 options.access_mask,
111 &attr,
112 &io,
113 null,
114 FILE_ATTRIBUTE_NORMAL,
115 options.share_access,
116 options.creation,
117 flags,
118 null,
119 0,
120 );
121 switch (rc) {
122 .SUCCESS => {
123 if (std.io.is_async and options.io_mode == .evented) {
124 _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined;
125 }
126 return result;
127 },
128 .OBJECT_NAME_INVALID => unreachable,
129 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
130 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
131 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
132 .INVALID_PARAMETER => unreachable,
133 .SHARING_VIOLATION => return error.AccessDenied,
134 .ACCESS_DENIED => return error.AccessDenied,
135 .PIPE_BUSY => return error.PipeBusy,
136 .OBJECT_PATH_SYNTAX_BAD => unreachable,
137 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
138 .FILE_IS_A_DIRECTORY => return error.IsDir,
139 .NOT_A_DIRECTORY => return error.NotDir,
140 .USER_MAPPED_FILE => return error.AccessDenied,
141 .INVALID_HANDLE => unreachable,
142 else => return unexpectedStatus(rc),
108 while (true) {
109 const rc = ntdll.NtCreateFile(
110 &result,
111 options.access_mask,
112 &attr,
113 &io,
114 null,
115 FILE_ATTRIBUTE_NORMAL,
116 options.share_access,
117 options.creation,
118 flags,
119 null,
120 0,
121 );
122 switch (rc) {
123 .SUCCESS => {
124 if (std.io.is_async and options.io_mode == .evented) {
125 _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined;
126 }
127 return result;
128 },
129 .OBJECT_NAME_INVALID => unreachable,
130 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
131 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
132 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
133 .INVALID_PARAMETER => unreachable,
134 .SHARING_VIOLATION => return error.AccessDenied,
135 .ACCESS_DENIED => return error.AccessDenied,
136 .PIPE_BUSY => return error.PipeBusy,
137 .OBJECT_PATH_SYNTAX_BAD => unreachable,
138 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
139 .FILE_IS_A_DIRECTORY => return error.IsDir,
140 .NOT_A_DIRECTORY => return error.NotDir,
141 .USER_MAPPED_FILE => return error.AccessDenied,
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 }
143155 }
144156}
145157
lib/std/os/windows/advapi32.zig+2
......@@ -27,6 +27,8 @@ pub extern "advapi32" fn RegQueryValueExW(
2727 lpcbData: ?*DWORD,
2828) callconv(WINAPI) LSTATUS;
2929
30pub extern "advapi32" fn RegCloseKey(hKey: HKEY) callconv(WINAPI) LSTATUS;
31
3032// RtlGenRandom is known as SystemFunction036 under advapi32
3133// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
3234pub 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;
6767const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
6868const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;
6969const MODULEENTRY32 = windows.MODULEENTRY32;
70const ULONGLONG = windows.ULONGLONG;
7071
7172pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
7273pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
......@@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW(
457458 samDesired: REGSAM,
458459 phkResult: *HKEY,
459460) 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
828828 return ArgIterator.initWithAllocator(allocator);
829829}
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
849831/// Caller must call argsFree on result.
850832pub fn argsAlloc(allocator: Allocator) ![][:0]u8 {
851833 // TODO refactor to only make 1 allocation.
......@@ -1169,3 +1151,51 @@ pub fn execve(
11691151
11701152 return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);
11711153}
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 {
185185 options_override.keep_sigpipe
186186 else
187187 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;
188198};
189199
190200// 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 {
724724
725725 /// Adds the specified feature set but not its dependencies.
726726 pub fn addFeatureSet(set: *Set, other_set: Set) void {
727 if (builtin.zig_backend == .stage2_c) {
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 }
727 set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints);
732728 }
733729
734730 /// Removes the specified feature but not its dependents.
......@@ -740,11 +736,7 @@ pub const Target = struct {
740736
741737 /// Removes the specified feature but not its dependents.
742738 pub fn removeFeatureSet(set: *Set, other_set: Set) void {
743 if (builtin.zig_backend == .stage2_c) {
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 }
739 set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints);
748740 }
749741
750742 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");
33const fmt = @import("zig/fmt.zig");
44const 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");
69pub const Token = tokenizer.Token;
710pub const Tokenizer = tokenizer.Tokenizer;
811pub 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 {
14071407 .type_expr = data.lhs,
14081408 .value_expr = extra.value_expr,
14091409 .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,
14111412 });
14121413}
14131414
......@@ -1420,7 +1421,8 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {
14201421 .type_expr = data.lhs,
14211422 .value_expr = data.rhs,
14221423 .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,
14241426 });
14251427}
14261428
......@@ -1433,7 +1435,8 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {
14331435 .type_expr = data.lhs,
14341436 .value_expr = 0,
14351437 .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,
14371440 });
14381441}
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(
10901090 switch (candidate.target.os.tag) {
10911091 .windows => {
10921092 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 }
10931098 switch (candidate.target.cpu.arch.ptrBitWidth()) {
10941099 32 => return Executor{ .wine = "wine" },
10951100 64 => return Executor{ .wine = "wine64" },
lib/test_runner.zig+123-45
......@@ -8,14 +8,126 @@ pub const std_options = struct {
88};
99
1010var log_err_count: usize = 0;
11var cmdline_buffer: [4096]u8 = undefined;
12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1113
1214pub fn main() void {
13 if (builtin.zig_backend != .stage1 and
14 builtin.zig_backend != .stage2_llvm and
15 builtin.zig_backend != .stage2_c)
15 if (builtin.zig_backend == .stage2_wasm or
16 builtin.zig_backend == .stage2_x86_64 or
17 builtin.zig_backend == .stage2_aarch64)
1618 {
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 }
18127 }
128}
129
130fn mainTerminal() void {
19131 const test_fn_list = builtin.test_functions;
20132 var ok_count: usize = 0;
21133 var skip_count: usize = 0;
......@@ -118,51 +230,17 @@ pub fn log(
118230 }
119231}
120232
121pub fn main2() anyerror!void {
122 var skipped: usize = 0;
123 var failed: usize = 0;
124 // Simpler main(), exercising fewer language features, so that stage2 can handle it.
233/// Simpler main(), exercising fewer language features, so that
234/// work-in-progress backends can handle it.
235pub fn mainSimple() anyerror!void {
236 //const stderr = std.io.getStdErr();
125237 for (builtin.test_functions) |test_fn| {
126238 test_fn.func() catch |err| {
127239 if (err != error.SkipZigTest) {
128 failed += 1;
129 } else {
130 skipped += 1;
240 //stderr.writeAll(test_fn.name) catch {};
241 //stderr.writeAll("\n") catch {};
242 return err;
131243 }
132244 };
133245 }
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);
168246}
lib/zig.h+1097-214
......@@ -37,6 +37,14 @@ typedef char bool;
3737#define zig_has_attribute(attribute) 0
3838#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
4048#if __STDC_VERSION__ >= 201112L
4149#define zig_threadlocal _Thread_local
4250#elif defined(__GNUC__)
......@@ -180,16 +188,38 @@ typedef char bool;
180188#define zig_export(sig, symbol, name) __asm(name " = " symbol)
181189#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
183206#if zig_has_builtin(debugtrap)
184207#define zig_breakpoint() __builtin_debugtrap()
185#elif zig_has_builtin(trap) || defined(zig_gnuc)
186#define zig_breakpoint() __builtin_trap()
187208#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
188209#define zig_breakpoint() __debugbreak()
189210#elif defined(__i386__) || defined(__x86_64__)
190211#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");
191216#else
217#include <signal.h>
218#if defined(SIGTRAP)
192219#define zig_breakpoint() raise(SIGTRAP)
220#else
221#define zig_breakpoint() zig_breakpoint_unavailable
222#endif
193223#endif
194224
195225#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
598628#endif
599629}
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
607631zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow);
608632static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
609633#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
618642 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
619643}
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
627645static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
628646#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
629647 uint64_t full_res;
......@@ -636,12 +654,6 @@ static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8
636654#endif
637655}
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
645657zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow);
646658static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
647659#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
656668 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
657669}
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
665671static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
666672#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
667673 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
676682#endif
677683}
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
685685static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
686686#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
687687 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
696696#endif
697697}
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
705699static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
706700#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
707701 uint16_t full_res;
......@@ -716,12 +710,6 @@ static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8
716710#endif
717711}
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
725713static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
726714#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
727715 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
736724#endif
737725}
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
745727static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
746728#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
747729 uint32_t full_res;
......@@ -754,12 +736,6 @@ static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8
754736#endif
755737}
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
763739zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow);
764740static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
765741#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
774750 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
775751}
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
783753static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
784754#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
785755 uint64_t full_res;
......@@ -792,12 +762,6 @@ static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8
792762#endif
793763}
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
801765zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow);
802766static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
803767#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
812776 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
813777}
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
821779static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
822780#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
823781 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
832790#endif
833791}
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
841793static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
842794#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
843795 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
852804#endif
853805}
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
862807static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
863808#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
864809 uint16_t full_res;
......@@ -873,13 +818,6 @@ static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8
873818#endif
874819}
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
883821static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
884822#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
885823 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
894832#endif
895833}
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
903835static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
904836#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
905837 uint32_t full_res;
......@@ -912,12 +844,6 @@ static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8
912844#endif
913845}
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
921847zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow);
922848static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
923849#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
932858 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
933859}
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
941861static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
942862#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
943863 uint64_t full_res;
......@@ -950,12 +870,6 @@ static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8
950870#endif
951871}
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
959873zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow);
960874static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
961875#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
970884 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
971885}
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
979887static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
980888#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
981889 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
990898#endif
991899}
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
999901static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
1000902#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
1001903 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
1010912#endif
1011913}
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
1019915static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
1020916#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
1021917 uint16_t full_res;
......@@ -1030,12 +926,6 @@ static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8
1030926#endif
1031927}
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
1039929static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
1040930#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
1041931 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
1050940#endif
1051941}
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
1059943#define zig_int_builtins(w) \
1060944 static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \
1061945 *res = zig_shlw_u##w(lhs, rhs, bits); \
......@@ -1354,8 +1238,8 @@ typedef signed __int128 zig_i128;
13541238
13551239#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
13561240#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)
1358#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)
1241#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1242#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
13591243#define zig_hi_u128(val) ((uint64_t)((val) >> 64))
13601244#define zig_lo_u128(val) ((uint64_t)((val) >> 0))
13611245#define zig_hi_i128(val) (( int64_t)((val) >> 64))
......@@ -1373,7 +1257,7 @@ typedef signed __int128 zig_i128;
13731257
13741258#else /* zig_has_int128 */
13751259
1376#if __LITTLE_ENDIAN__ || _MSC_VER
1260#if zig_little_endian
13771261typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
13781262typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
13791263#else
......@@ -1385,11 +1269,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
13851269#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })
13861270
13871271#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) }
1389#define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1272#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1273#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
13901274#else /* But non-MSVC doesn't like the unprotected commas */
1391#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)
1392#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)
1275#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1276#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
13931277#endif
13941278#define zig_hi_u128(val) ((val).hi)
13951279#define zig_lo_u128(val) ((val).lo)
......@@ -1632,7 +1516,9 @@ static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) {
16321516}
16331517
16341518static 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);
16361522}
16371523
16381524static 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) {
19031789 return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));
19041790}
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
19062796/* ========================= Floating Point Support ========================= */
19072797
19082798#if _MSC_VER
......@@ -1927,7 +2817,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
19272817#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg)
19282818#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg)
19292819#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)
19312820#else
19322821#define zig_has_float_builtins 0
19332822#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) {
19352824#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)
19362825#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)
19372826#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)
19392827#endif
19402828
19412829#define zig_has_f16 1
19422830#define zig_bitSizeOf_f16 16
2831typedef int16_t zig_repr_f16;
19432832#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)
19452834#if FLT_MANT_DIG == 11
19462835typedef float zig_f16;
19472836#define zig_make_f16(fp, repr) fp##f
......@@ -1950,7 +2839,9 @@ typedef double zig_f16;
19502839#define zig_make_f16(fp, repr) fp
19512840#elif LDBL_MANT_DIG == 11
19522841#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
19542845typedef long double zig_f16;
19552846#define zig_make_f16(fp, repr) fp##l
19562847#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))
......@@ -1967,17 +2858,18 @@ typedef int16_t zig_f16;
19672858#define zig_make_f16(fp, repr) repr
19682859#undef zig_make_special_f16
19692860#define zig_make_special_f16(sign, name, arg, repr) repr
1970#undef zig_make_special_constant_f16
1971#define zig_make_special_constant_f16(sign, name, arg, repr) repr
2861#undef zig_init_special_f16
2862#define zig_init_special_f16(sign, name, arg, repr) repr
19722863#endif
19732864
19742865#define zig_has_f32 1
19752866#define zig_bitSizeOf_f32 32
2867typedef int32_t zig_repr_f32;
19762868#define zig_libc_name_f32(name) name##f
19772869#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, )
19792871#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)
19812873#endif
19822874#if FLT_MANT_DIG == 24
19832875typedef float zig_f32;
......@@ -1987,7 +2879,9 @@ typedef double zig_f32;
19872879#define zig_make_f32(fp, repr) fp
19882880#elif LDBL_MANT_DIG == 24
19892881#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
19912885typedef long double zig_f32;
19922886#define zig_make_f32(fp, repr) fp##l
19932887#elif FLT32_MANT_DIG == 24
......@@ -2001,21 +2895,24 @@ typedef int32_t zig_f32;
20012895#define zig_make_f32(fp, repr) repr
20022896#undef zig_make_special_f32
20032897#define zig_make_special_f32(sign, name, arg, repr) repr
2004#undef zig_make_special_constant_f32
2005#define zig_make_special_constant_f32(sign, name, arg, repr) repr
2898#undef zig_init_special_f32
2899#define zig_init_special_f32(sign, name, arg, repr) repr
20062900#endif
20072901
20082902#define zig_has_f64 1
20092903#define zig_bitSizeOf_f64 64
2904typedef int64_t zig_repr_f64;
20102905#define zig_libc_name_f64(name) name
20112906#if _MSC_VER
20122907#ifdef ZIG_TARGET_ABI_MSVC
20132908#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;
20152911#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, )
20172914#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)
20192916#endif /* _MSC_VER */
20202917#if FLT_MANT_DIG == 53
20212918typedef float zig_f64;
......@@ -2025,7 +2922,9 @@ typedef double zig_f64;
20252922#define zig_make_f64(fp, repr) fp
20262923#elif LDBL_MANT_DIG == 53
20272924#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
20292928typedef long double zig_f64;
20302929#define zig_make_f64(fp, repr) fp##l
20312930#elif FLT64_MANT_DIG == 53
......@@ -2042,14 +2941,15 @@ typedef int64_t zig_f64;
20422941#define zig_make_f64(fp, repr) repr
20432942#undef zig_make_special_f64
20442943#define zig_make_special_f64(sign, name, arg, repr) repr
2045#undef zig_make_special_constant_f64
2046#define zig_make_special_constant_f64(sign, name, arg, repr) repr
2944#undef zig_init_special_f64
2945#define zig_init_special_f64(sign, name, arg, repr) repr
20472946#endif
20482947
20492948#define zig_has_f80 1
20502949#define zig_bitSizeOf_f80 80
2950typedef zig_i128 zig_repr_f80;
20512951#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)
20532953#if FLT_MANT_DIG == 64
20542954typedef float zig_f80;
20552955#define zig_make_f80(fp, repr) fp##f
......@@ -2058,7 +2958,9 @@ typedef double zig_f80;
20582958#define zig_make_f80(fp, repr) fp
20592959#elif LDBL_MANT_DIG == 64
20602960#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
20622964typedef long double zig_f80;
20632965#define zig_make_f80(fp, repr) fp##l
20642966#elif FLT80_MANT_DIG == 64
......@@ -2078,14 +2980,15 @@ typedef zig_i128 zig_f80;
20782980#define zig_make_f80(fp, repr) repr
20792981#undef zig_make_special_f80
20802982#define zig_make_special_f80(sign, name, arg, repr) repr
2081#undef zig_make_special_constant_f80
2082#define zig_make_special_constant_f80(sign, name, arg, repr) repr
2983#undef zig_init_special_f80
2984#define zig_init_special_f80(sign, name, arg, repr) repr
20832985#endif
20842986
20852987#define zig_has_f128 1
20862988#define zig_bitSizeOf_f128 128
2989typedef zig_i128 zig_repr_f128;
20872990#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)
20892992#if FLT_MANT_DIG == 113
20902993typedef float zig_f128;
20912994#define zig_make_f128(fp, repr) fp##f
......@@ -2094,7 +2997,9 @@ typedef double zig_f128;
20942997#define zig_make_f128(fp, repr) fp
20952998#elif LDBL_MANT_DIG == 113
20962999#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
20983003typedef long double zig_f128;
20993004#define zig_make_f128(fp, repr) fp##l
21003005#elif FLT128_MANT_DIG == 113
......@@ -2116,63 +3021,44 @@ typedef zig_i128 zig_f128;
21163021#define zig_make_f128(fp, repr) repr
21173022#undef zig_make_special_f128
21183023#define zig_make_special_f128(sign, name, arg, repr) repr
2119#undef zig_make_special_constant_f128
2120#define zig_make_special_constant_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
3024#undef zig_init_special_f128
3025#define zig_init_special_f128(sign, name, arg, repr) repr
21293026#endif
21303027
2131#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr)
21323028#ifdef zig_bitSizeOf_c_longdouble
21333029
3030#define zig_has_c_longdouble 1
21343031#ifdef ZIG_TARGET_ABI_MSVC
21353032#undef zig_bitSizeOf_c_longdouble
21363033#define zig_bitSizeOf_c_longdouble 64
2137typedef uint64_t zig_repr_c_longdouble;
21383034typedef zig_f64 zig_c_longdouble;
2139#define zig_make_c_longdouble(fp, repr) fp
3035typedef zig_repr_f64 zig_repr_c_longdouble;
21403036#else
21413037typedef long double zig_c_longdouble;
2142#define zig_make_c_longdouble(fp, repr) fp##l
21433038#endif
21443039
21453040#else /* zig_bitSizeOf_c_longdouble */
21463041
2147#undef zig_has_c_longdouble
21483042#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
21523043#define zig_bitSizeOf_repr_c_longdouble 128
2153typedef zig_i128 zig_c_longdouble;
2154#define zig_make_c_longdouble(fp, repr) repr
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
3044typedef zig_f128 zig_c_longdouble;
3045typedef zig_repr_f128 zig_repr_c_longdouble;
21593046
21603047#endif /* zig_bitSizeOf_c_longdouble */
21613048
21623049#if !zig_has_float_builtins
2163#define zig_float_from_repr(Type, ReprType) \
2164 static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \
3050#define zig_float_from_repr(Type) \
3051 static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \
21653052 zig_##Type result; \
21663053 memcpy(&result, &repr, sizeof(result)); \
21673054 return result; \
21683055 }
21693056
2170zig_float_from_repr(f16, uint16_t)
2171zig_float_from_repr(f32, uint32_t)
2172zig_float_from_repr(f64, uint64_t)
2173zig_float_from_repr(f80, zig_u128)
2174zig_float_from_repr(f128, zig_u128)
2175zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)
3057zig_float_from_repr(f16)
3058zig_float_from_repr(f32)
3059zig_float_from_repr(f64)
3060zig_float_from_repr(f80)
3061zig_float_from_repr(f128)
21763062#endif
21773063
21783064#define zig_cast_f16 (zig_f16)
......@@ -2181,11 +3067,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)
21813067
21823068#if _MSC_VER && !zig_has_f128
21833069#define zig_cast_f80
2184#define zig_cast_c_longdouble
21853070#define zig_cast_f128
21863071#else
21873072#define zig_cast_f80 (zig_f80)
2188#define zig_cast_c_longdouble (zig_c_longdouble)
21893073#define zig_cast_f128 (zig_f128)
21903074#endif
21913075
......@@ -2314,7 +3198,6 @@ zig_float_builtins(f32)
23143198zig_float_builtins(f64)
23153199zig_float_builtins(f80)
23163200zig_float_builtins(f128)
2317zig_float_builtins(c_longdouble)
23183201
23193202#if _MSC_VER && (_M_IX86 || _M_X64)
23203203
src/Air.zig+9-1
......@@ -232,7 +232,14 @@ pub const Inst = struct {
232232 /// Result type is always noreturn; no instructions in a block follow this one.
233233 /// Uses the `br` field.
234234 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.
236243 /// Result type is always void.
237244 breakpoint,
238245 /// Yields the return address of the current function.
......@@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
11861193 .ret,
11871194 .ret_load,
11881195 .unreach,
1196 .trap,
11891197 => return Type.initTag(.noreturn),
11901198
11911199 .breakpoint,
src/AstGen.zig+151-92
......@@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
148148 };
149149 defer gz_instructions.deinit(gpa);
150150
151 if (AstGen.structDeclInner(
152 &gen_scope,
153 &gen_scope.base,
154 0,
155 tree.containerDeclRoot(),
156 .Auto,
157 0,
158 )) |struct_decl_ref| {
159 assert(refToIndex(struct_decl_ref).? == 0);
160 } else |err| switch (err) {
161 error.OutOfMemory => return error.OutOfMemory,
162 error.AnalysisFail => {}, // Handled via compile_errors below.
151 // The AST -> ZIR lowering process assumes an AST that does not have any
152 // parse errors.
153 if (tree.errors.len == 0) {
154 if (AstGen.structDeclInner(
155 &gen_scope,
156 &gen_scope.base,
157 0,
158 tree.containerDeclRoot(),
159 .Auto,
160 0,
161 )) |struct_decl_ref| {
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);
163169 }
164170
165171 const err_index = @enumToInt(Zir.ExtraIndex.compile_errors);
......@@ -205,7 +211,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
205211 };
206212}
207213
208pub fn deinit(astgen: *AstGen, gpa: Allocator) void {
214fn deinit(astgen: *AstGen, gpa: Allocator) void {
209215 astgen.instructions.deinit(gpa);
210216 astgen.extra.deinit(gpa);
211217 astgen.string_table.deinit(gpa);
......@@ -216,7 +222,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void {
216222 astgen.ref_table.deinit(gpa);
217223}
218224
219pub const ResultInfo = struct {
225const ResultInfo = struct {
220226 /// The semantics requested for the result location
221227 rl: Loc,
222228
......@@ -245,7 +251,7 @@ pub const ResultInfo = struct {
245251 }
246252 }
247253
248 pub const Loc = union(enum) {
254 const Loc = union(enum) {
249255 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
250256 /// expression should be generated. The result instruction from the expression must
251257 /// be ignored.
......@@ -277,11 +283,11 @@ pub const ResultInfo = struct {
277283 src_node: ?Ast.Node.Index = null,
278284 };
279285
280 pub const Strategy = struct {
286 const Strategy = struct {
281287 elide_store_to_block_ptr_instructions: bool,
282288 tag: Tag,
283289
284 pub const Tag = enum {
290 const Tag = enum {
285291 /// Both branches will use break_void; result location is used to communicate the
286292 /// result instruction.
287293 break_void,
......@@ -331,7 +337,7 @@ pub const ResultInfo = struct {
331337 }
332338 };
333339
334 pub const Context = enum {
340 const Context = enum {
335341 /// The expression is the operand to a return expression.
336342 @"return",
337343 /// The expression is the input to an error-handling operator (if-else, try, or catch).
......@@ -349,11 +355,11 @@ pub const ResultInfo = struct {
349355 };
350356};
351357
352pub const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } };
353pub const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
354pub const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } };
355pub const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } };
356pub const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
358const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } };
359const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
360const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } };
361const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } };
362const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
357363
358364fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
359365 const prev_force_comptime = gz.force_comptime;
......@@ -1960,7 +1966,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
19601966 else
19611967 .@"break";
19621968
1969 block_gz.break_count += 1;
19631970 if (rhs == 0) {
1971 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
1972
19641973 try genDefers(parent_gz, scope, parent_scope, .normal_only);
19651974
19661975 // 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
19701979 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
19711980 return Zir.Inst.Ref.unreachable_value;
19721981 }
1973 block_gz.break_count += 1;
19741982
19751983 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
19761984 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
26092617 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
26102618 .breakpoint,
26112619 .fence,
2612 .set_align_stack,
26132620 .set_float_mode,
2621 .set_align_stack,
2622 .set_cold,
26142623 => break :b true,
26152624 else => break :b false,
26162625 },
......@@ -2630,6 +2639,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
26302639 .repeat_inline,
26312640 .panic,
26322641 .panic_comptime,
2642 .trap,
26332643 .check_comptime_control_flow,
26342644 => {
26352645 noreturn_src_node = statement;
......@@ -2658,7 +2668,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
26582668 .validate_struct_init_comptime,
26592669 .validate_array_init,
26602670 .validate_array_init_comptime,
2661 .set_cold,
26622671 .set_runtime_safety,
26632672 .closure_capture,
26642673 .memcpy,
......@@ -3506,7 +3515,7 @@ const WipMembers = struct {
35063515 /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )
35073516 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 {
35103519 const payload_top = @intCast(u32, payload.items.len);
35113520 const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;
35123521 const field_bits_start = decls_start + decl_count * max_decl_size;
......@@ -3527,7 +3536,7 @@ const WipMembers = struct {
35273536 };
35283537 }
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 {
35313540 const index = self.payload_top + self.decl_index / decls_per_u32;
35323541 assert(index < self.decls_start);
35333542 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 {
35393548 self.decl_index += 1;
35403549 }
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 {
35433552 const fields_per_u32 = 32 / bits_per_field;
35443553 const index = self.field_bits_start + self.field_index / fields_per_u32;
35453554 assert(index < self.fields_start);
......@@ -3553,25 +3562,25 @@ const WipMembers = struct {
35533562 self.field_index += 1;
35543563 }
35553564
3556 pub fn appendToDecl(self: *Self, data: u32) void {
3565 fn appendToDecl(self: *Self, data: u32) void {
35573566 assert(self.decls_end < self.field_bits_start);
35583567 self.payload.items[self.decls_end] = data;
35593568 self.decls_end += 1;
35603569 }
35613570
3562 pub fn appendToDeclSlice(self: *Self, data: []const u32) void {
3571 fn appendToDeclSlice(self: *Self, data: []const u32) void {
35633572 assert(self.decls_end + data.len <= self.field_bits_start);
35643573 mem.copy(u32, self.payload.items[self.decls_end..], data);
35653574 self.decls_end += @intCast(u32, data.len);
35663575 }
35673576
3568 pub fn appendToField(self: *Self, data: u32) void {
3577 fn appendToField(self: *Self, data: u32) void {
35693578 assert(self.fields_end < self.payload.items.len);
35703579 self.payload.items[self.fields_end] = data;
35713580 self.fields_end += 1;
35723581 }
35733582
3574 pub fn finishBits(self: *Self, comptime bits_per_field: u32) void {
3583 fn finishBits(self: *Self, comptime bits_per_field: u32) void {
35753584 const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);
35763585 if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {
35773586 const index = self.payload_top + self.decl_index / decls_per_u32;
......@@ -3587,15 +3596,15 @@ const WipMembers = struct {
35873596 }
35883597 }
35893598
3590 pub fn declsSlice(self: *Self) []u32 {
3599 fn declsSlice(self: *Self) []u32 {
35913600 return self.payload.items[self.payload_top..self.decls_end];
35923601 }
35933602
3594 pub fn fieldsSlice(self: *Self) []u32 {
3603 fn fieldsSlice(self: *Self) []u32 {
35953604 return self.payload.items[self.field_bits_start..self.fields_end];
35963605 }
35973606
3598 pub fn deinit(self: *Self) void {
3607 fn deinit(self: *Self) void {
35993608 self.payload.items.len = self.payload_top;
36003609 }
36013610};
......@@ -6583,6 +6592,9 @@ fn forExpr(
65836592 cond_block,
65846593 break_tag,
65856594 );
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 }
65866598 if (is_statement) {
65876599 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
65886600 }
......@@ -7976,6 +7988,9 @@ fn builtinCall(
79767988 switch (node_tags[params[0]]) {
79777989 .identifier => {
79787990 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 }
79797994 decl_name = try astgen.identAsString(ident_token);
79807995
79817996 var s = scope;
......@@ -8056,27 +8071,35 @@ fn builtinCall(
80568071 },
80578072 .fence => {
80588073 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{
80608075 .node = gz.nodeIndexToRelative(node),
80618076 .operand = order,
80628077 });
8063 return rvalue(gz, ri, result, node);
8078 return rvalue(gz, ri, .void_value, node);
80648079 },
80658080 .set_float_mode => {
80668081 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{
80688083 .node = gz.nodeIndexToRelative(node),
80698084 .operand = order,
80708085 });
8071 return rvalue(gz, ri, result, node);
8086 return rvalue(gz, ri, .void_value, node);
80728087 },
80738088 .set_align_stack => {
80748089 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{
80768091 .node = gz.nodeIndexToRelative(node),
80778092 .operand = order,
80788093 });
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);
80808103 },
80818104
80828105 .src => {
......@@ -8097,7 +8120,7 @@ fn builtinCall(
80978120 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
80988121 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
80998122 .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
81028125 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
81038126 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
......@@ -8111,7 +8134,6 @@ fn builtinCall(
81118134 .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int),
81128135 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file),
81138136 .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),
81158137 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety),
81168138 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),
81178139 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),
......@@ -8171,6 +8193,11 @@ fn builtinCall(
81718193 try emitDbgNode(gz, node);
81728194 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic);
81738195 },
8196 .trap => {
8197 try emitDbgNode(gz, node);
8198 _ = try gz.addNode(.trap, node);
8199 return rvalue(gz, ri, .void_value, node);
8200 },
81748201 .error_to_int => {
81758202 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
81768203 const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
......@@ -8357,14 +8384,14 @@ fn builtinCall(
83578384 },
83588385 .atomic_store => {
83598386 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{
83618388 // zig fmt: off
83628389 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
83638390 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
83648391 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]),
83658392 // zig fmt: on
83668393 });
8367 return rvalue(gz, ri, result, node);
8394 return rvalue(gz, ri, .void_value, node);
83688395 },
83698396 .mul_add => {
83708397 const float_type = try typeExpr(gz, scope, params[0]);
......@@ -8405,20 +8432,20 @@ fn builtinCall(
84058432 return rvalue(gz, ri, result, node);
84068433 },
84078434 .memcpy => {
8408 const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
8435 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
84098436 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
84108437 .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]),
84118438 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
84128439 });
8413 return rvalue(gz, ri, result, node);
8440 return rvalue(gz, ri, .void_value, node);
84148441 },
84158442 .memset => {
8416 const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
8443 _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
84178444 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
84188445 .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]),
84198446 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
84208447 });
8421 return rvalue(gz, ri, result, node);
8448 return rvalue(gz, ri, .void_value, node);
84228449 },
84238450 .shuffle => {
84248451 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
......@@ -8459,12 +8486,12 @@ fn builtinCall(
84598486 .prefetch => {
84608487 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
84618488 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{
84638490 .node = gz.nodeIndexToRelative(node),
84648491 .lhs = ptr,
84658492 .rhs = options,
84668493 });
8467 return rvalue(gz, ri, result, node);
8494 return rvalue(gz, ri, .void_value, node);
84688495 },
84698496 .c_va_arg => {
84708497 if (astgen.fn_block == null) {
......@@ -8509,16 +8536,6 @@ fn builtinCall(
85098536 }
85108537}
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
85228539fn hasDeclOrField(
85238540 gz: *GenZir,
85248541 scope: *Scope,
......@@ -8988,7 +9005,7 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
89889005});
89899006
89909007comptime {
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.
89929009 const primitives = std.zig.primitives;
89939010 for (primitive_instrs.kvs) |kv| {
89949011 if (!primitives.isPrimitive(kv.key)) {
......@@ -10369,7 +10386,7 @@ fn appendErrorTok(
1036910386 comptime format: []const u8,
1037010387 args: anytype,
1037110388) !void {
10372 try astgen.appendErrorTokNotes(token, format, args, &[0]u32{});
10389 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
1037310390}
1037410391
1037510392fn failTokNotes(
......@@ -10379,7 +10396,7 @@ fn failTokNotes(
1037910396 args: anytype,
1038010397 notes: []const u32,
1038110398) InnerError {
10382 try appendErrorTokNotes(astgen, token, format, args, notes);
10399 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
1038310400 return error.AnalysisFail;
1038410401}
1038510402
......@@ -10390,27 +10407,11 @@ fn appendErrorTokNotes(
1039010407 args: anytype,
1039110408 notes: []const u32,
1039210409) !void {
10393 @setCold(true);
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 });
10410 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
1041110411}
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.
1041410415fn failOff(
1041510416 astgen: *AstGen,
1041610417 token: Ast.TokenIndex,
......@@ -10418,27 +10419,36 @@ fn failOff(
1041810419 comptime format: []const u8,
1041910420 args: anytype,
1042010421) InnerError {
10421 try appendErrorOff(astgen, token, byte_offset, format, args);
10422 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
1042210423 return error.AnalysisFail;
1042310424}
1042410425
10425fn appendErrorOff(
10426fn appendErrorTokNotesOff(
1042610427 astgen: *AstGen,
1042710428 token: Ast.TokenIndex,
1042810429 byte_offset: u32,
1042910430 comptime format: []const u8,
1043010431 args: anytype,
10431) Allocator.Error!void {
10432 notes: []const u32,
10433) !void {
1043210434 @setCold(true);
10435 const gpa = astgen.gpa;
1043310436 const string_bytes = &astgen.string_bytes;
1043410437 const msg = @intCast(u32, string_bytes.items.len);
10435 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
10436 try astgen.compile_errors.append(astgen.gpa, .{
10438 try string_bytes.writer(gpa).print(format ++ "\x00", args);
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, .{
1043710447 .msg = msg,
1043810448 .node = 0,
1043910449 .token = token,
1044010450 .byte_offset = byte_offset,
10441 .notes = 0,
10451 .notes = notes_index,
1044210452 });
1044310453}
1044410454
......@@ -10447,6 +10457,16 @@ fn errNoteTok(
1044710457 token: Ast.TokenIndex,
1044810458 comptime format: []const u8,
1044910459 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,
1045010470) Allocator.Error!u32 {
1045110471 @setCold(true);
1045210472 const string_bytes = &astgen.string_bytes;
......@@ -10456,7 +10476,7 @@ fn errNoteTok(
1045610476 .msg = msg,
1045710477 .node = 0,
1045810478 .token = token,
10459 .byte_offset = 0,
10479 .byte_offset = byte_offset,
1046010480 .notes = 0,
1046110481 });
1046210482}
......@@ -10787,7 +10807,7 @@ const Scope = struct {
1078710807 /// ref of the capture for decls in this namespace
1078810808 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 {
1079110811 self.decls.deinit(gpa);
1079210812 self.captures.deinit(gpa);
1079310813 self.* = undefined;
......@@ -12623,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
1262312643 },
1262412644 } });
1262512645}
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(
13381338 .embed_file,
13391339 .error_name,
13401340 .panic,
1341 .set_cold, // @check
13421341 .set_runtime_safety, // @check
13431342 .sqrt,
13441343 .sin,
src/BuiltinFn.zig+8
......@@ -109,6 +109,7 @@ pub const Tag = enum {
109109 sub_with_overflow,
110110 tag_name,
111111 This,
112 trap,
112113 truncate,
113114 Type,
114115 type_info,
......@@ -915,6 +916,13 @@ pub const list = list: {
915916 .param_count = 0,
916917 },
917918 },
919 .{
920 "@trap",
921 .{
922 .tag = .trap,
923 .param_count = 0,
924 },
925 },
918926 .{
919927 "@truncate",
920928 .{
src/Compilation.zig+424-608
......@@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator;
77const assert = std.debug.assert;
88const log = std.log.scoped(.compilation);
99const Target = std.Target;
10const ThreadPool = std.Thread.Pool;
11const WaitGroup = std.Thread.WaitGroup;
12const ErrorBundle = std.zig.ErrorBundle;
1013
1114const Value = @import("value.zig").Value;
1215const Type = @import("type.zig").Type;
......@@ -30,8 +33,6 @@ const Cache = std.Build.Cache;
3033const translate_c = @import("translate_c.zig");
3134const clang = @import("clang.zig");
3235const c_codegen = @import("codegen/c.zig");
33const ThreadPool = @import("ThreadPool.zig");
34const WaitGroup = @import("WaitGroup.zig");
3536const libtsan = @import("libtsan.zig");
3637const Zir = @import("Zir.zig");
3738const Autodoc = @import("Autodoc.zig");
......@@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false,
99100job_queued_compiler_rt_obj: bool = false,
100101alloc_failure_occurred: bool = false,
101102formatted_panics: bool = false,
103last_update_was_cache_hit: bool = false,
102104
103105c_source_files: []const CSourceFile,
104106clang_argv: []const []const u8,
......@@ -334,12 +336,41 @@ pub const MiscTask = enum {
334336 libssp,
335337 zig_libc,
336338 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",
337368};
338369
339370pub const MiscError = struct {
340371 /// Allocated with gpa.
341372 msg: []u8,
342 children: ?AllErrors = null,
373 children: ?ErrorBundle = null,
343374
344375 pub fn deinit(misc_err: *MiscError, gpa: Allocator) void {
345376 gpa.free(misc_err.msg);
......@@ -365,448 +396,6 @@ pub const LldError = struct {
365396 }
366397};
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
810399pub const Directory = Cache.Directory;
811400
812401pub const EmitLoc = struct {
......@@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory(
22591848 }
22601849}
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
22621858/// 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 {
22641860 const tracy_trace = trace(@src());
22651861 defer tracy_trace.end();
22661862
22671863 comp.clearMiscFailures();
1864 comp.last_update_was_cache_hit = false;
22681865
22691866 var man: Cache.Manifest = undefined;
22701867 defer if (comp.whole_cache_manifest != null) man.deinit();
......@@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void {
22921889 return err;
22931890 };
22941891 if (is_hit) {
1892 comp.last_update_was_cache_hit = true;
22951893 log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name});
22961894 const digest = man.final();
22971895
......@@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void {
24072005 }
24082006 }
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
24252008 try comp.performAllTheWork(main_progress_node);
24262009
24272010 if (comp.bin_file.options.module) |module| {
......@@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void {
28912474}
28922475
28932476/// This function is temporally single-threaded.
2894pub fn totalErrorCount(self: *Compilation) usize {
2477pub fn totalErrorCount(self: *Compilation) u32 {
28952478 var total: usize = self.failed_c_objects.count() + self.misc_failures.count() +
28962479 @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len;
28972480
......@@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize {
29512534 }
29522535 }
29532536
2954 return total;
2537 return @intCast(u32, total);
29552538}
29562539
29572540/// This function is temporally single-threaded.
2958pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
2959 var arena = std.heap.ArenaAllocator.init(self.gpa);
2960 errdefer arena.deinit();
2961 const arena_allocator = arena.allocator();
2541pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
2542 const gpa = self.gpa;
29622543
2963 var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
2964 defer errors.deinit();
2544 var bundle: ErrorBundle.Wip = undefined;
2545 try bundle.init(gpa);
2546 defer bundle.deinit();
29652547
29662548 {
29672549 var it = self.failed_c_objects.iterator();
......@@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
29702552 const err_msg = entry.value_ptr.*;
29712553 // TODO these fields will need to be adjusted when we have proper
29722554 // C error reporting bubbling up.
2973 try errors.append(.{
2974 .src = .{
2975 .src_path = try arena_allocator.dupe(u8, c_object.src.src_path),
2976 .msg = try std.fmt.allocPrint(arena_allocator, "unable to build C object: {s}", .{
2977 err_msg.msg,
2978 }),
2979 .span = .{ .start = 0, .end = 1, .main = 0 },
2555 try bundle.addRootErrorMessage(.{
2556 .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}),
2557 .src_loc = try bundle.addSourceLocation(.{
2558 .src_path = try bundle.addString(c_object.src.src_path),
2559 .span_start = 0,
2560 .span_main = 0,
2561 .span_end = 1,
29802562 .line = err_msg.line,
29812563 .column = err_msg.column,
2982 .source_line = null, // TODO
2983 },
2564 .source_line = 0, // TODO
2565 }),
29842566 });
29852567 }
29862568 }
2569
29872570 for (self.lld_errors.items) |lld_error| {
2988 const notes = try arena_allocator.alloc(AllErrors.Message, 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 }
2571 const notes_len = @intCast(u32, lld_error.context_lines.len);
29942572
2995 try errors.append(.{
2996 .plain = .{
2997 .msg = try arena_allocator.dupe(u8, lld_error.msg),
2998 .notes = notes,
2999 },
2573 try bundle.addRootErrorMessage(.{
2574 .msg = try bundle.addString(lld_error.msg),
2575 .notes_len = notes_len,
30002576 });
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 }
30012583 }
30022584 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);
30042590 }
30052591 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 });
30072595 }
30082596 if (self.bin_file.options.module) |module| {
30092597 {
30102598 var it = module.failed_files.iterator();
30112599 while (it.next()) |entry| {
30122600 if (entry.value_ptr.*) |msg| {
3013 try AllErrors.add(module, &arena, &errors, msg.*);
2601 try addModuleErrorMsg(&bundle, msg.*);
30142602 } else {
3015 // Must be ZIR errors. In order for ZIR errors to exist, the parsing
3016 // must have completed successfully.
3017 const tree = try entry.key_ptr.*.getTree(module.gpa);
3018 assert(tree.errors.len == 0);
3019 try AllErrors.addZir(arena_allocator, &errors, entry.key_ptr.*);
2603 // Must be ZIR errors. Note that this may include AST errors.
2604 // addZirErrorMessages asserts that the tree is loaded.
2605 _ = try entry.key_ptr.*.getTree(gpa);
2606 try addZirErrorMessages(&bundle, entry.key_ptr.*);
30202607 }
30212608 }
30222609 }
......@@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
30242611 var it = module.failed_embed_files.iterator();
30252612 while (it.next()) |entry| {
30262613 const msg = entry.value_ptr.*;
3027 try AllErrors.add(module, &arena, &errors, msg.*);
2614 try addModuleErrorMsg(&bundle, msg.*);
30282615 }
30292616 }
30302617 {
......@@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
30342621 // Skip errors for Decls within files that had a parse failure.
30352622 // We'll try again once parsing succeeds.
30362623 if (decl.getFileScope().okToReportErrors()) {
3037 try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*);
2624 try addModuleErrorMsg(&bundle, entry.value_ptr.*.*);
30382625 if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| {
3039 if (c_error.path) |some|
3040 try errors.append(.{
3041 .src = .{
3042 .src_path = try arena_allocator.dupe(u8, std.mem.span(some)),
3043 .span = .{ .start = c_error.offset, .end = c_error.offset + 1, .main = c_error.offset },
3044 .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)),
3045 .line = c_error.line,
3046 .column = c_error.column,
3047 .source_line = if (c_error.source_line) |line| try arena_allocator.dupe(u8, std.mem.span(line)) else null,
3048 },
3049 })
3050 else
3051 try errors.append(.{
3052 .plain = .{ .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)) },
3053 });
2626 try bundle.addRootErrorMessage(.{
2627 .msg = try bundle.addString(std.mem.span(c_error.msg)),
2628 .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(.{
2629 .src_path = try bundle.addString(std.mem.span(some)),
2630 .span_start = c_error.offset,
2631 .span_main = c_error.offset,
2632 .span_end = c_error.offset + 1,
2633 .line = c_error.line,
2634 .column = c_error.column,
2635 .source_line = if (c_error.source_line) |line| try bundle.addString(std.mem.span(line)) else 0,
2636 }) else .none,
2637 });
30542638 };
30552639 }
30562640 }
......@@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
30622646 // Skip errors for Decls within files that had a parse failure.
30632647 // We'll try again once parsing succeeds.
30642648 if (decl.getFileScope().okToReportErrors()) {
3065 try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*);
2649 try addModuleErrorMsg(&bundle, entry.value_ptr.*.*);
30662650 }
30672651 }
30682652 }
30692653 for (module.failed_exports.values()) |value| {
3070 try AllErrors.add(module, &arena, &errors, value.*);
2654 try addModuleErrorMsg(&bundle, value.*);
30712655 }
30722656 }
30732657
3074 if (errors.items.len == 0) {
2658 if (bundle.root_list.items.len == 0) {
30752659 if (self.link_error_flags.no_entry_point_found) {
3076 try errors.append(.{
3077 .plain = .{
3078 .msg = try std.fmt.allocPrint(arena_allocator, "no entry point found", .{}),
3079 },
2660 try bundle.addRootErrorMessage(.{
2661 .msg = try bundle.addString("no entry point found"),
30802662 });
30812663 }
30822664 }
30832665
30842666 if (self.link_error_flags.missing_libc) {
3085 const notes = try arena_allocator.create([2]AllErrors.Message);
3086 notes.* = .{
3087 .{ .plain = .{
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 },
2667 try bundle.addRootErrorMessage(.{
2668 .msg = try bundle.addString("libc not available"),
2669 .notes_len = 2,
30992670 });
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 }));
31002678 }
31012679
31022680 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) {
31042682 const keys = module.compile_log_decls.keys();
31052683 const values = module.compile_log_decls.values();
31062684 // First one will be the error; subsequent ones will be notes.
......@@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
31092687 const err_msg = Module.ErrorMsg{
31102688 .src_loc = src_loc,
31112689 .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),
31132691 };
3114 defer self.gpa.free(err_msg.notes);
2692 defer gpa.free(err_msg.notes);
31152693
31162694 for (keys[1..], 0..) |key, i| {
31172695 const note_decl = module.declPtr(key);
......@@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
31212699 };
31222700 }
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);
31252726 }
2727
2728 return @truncate(u32, hasher.final());
31262729 }
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{
3131 .list = try arena_allocator.dupe(AllErrors.Message, errors.items),
3132 .arena = arena.state,
2758pub fn addModuleErrorMsg(eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void {
2759 const gpa = eb.gpa;
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;
31332769 };
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 }
31342871}
31352872
3136pub fn getCompileLogOutput(self: *Compilation) []const u8 {
3137 const module = self.bin_file.options.module orelse return &[0]u8{};
3138 return module.compile_log_text.items;
2873pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {
2874 assert(file.zir_loaded);
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 }
31392956}
31402957
31412958pub fn performAllTheWork(
......@@ -3231,11 +3048,11 @@ pub fn performAllTheWork(
32313048 // backend, preventing anonymous Decls from being prematurely destroyed.
32323049 while (true) {
32333050 if (comp.work_queue.readItem()) |work_item| {
3234 try processOneJob(comp, work_item);
3051 try processOneJob(comp, work_item, main_progress_node);
32353052 continue;
32363053 }
32373054 if (comp.anon_work_queue.readItem()) |work_item| {
3238 try processOneJob(comp, work_item);
3055 try processOneJob(comp, work_item, main_progress_node);
32393056 continue;
32403057 }
32413058 break;
......@@ -3243,16 +3060,16 @@ pub fn performAllTheWork(
32433060
32443061 if (comp.job_queued_compiler_rt_lib) {
32453062 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);
32473064 }
32483065
32493066 if (comp.job_queued_compiler_rt_obj) {
32503067 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);
32523069 }
32533070}
32543071
3255fn processOneJob(comp: *Compilation, job: Job) !void {
3072fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void {
32563073 switch (job) {
32573074 .codegen_decl => |decl_index| {
32583075 const module = comp.bin_file.options.module.?;
......@@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34043221 const named_frame = tracy.namedFrame("glibc_crt_file");
34053222 defer named_frame.end();
34063223
3407 glibc.buildCRTFile(comp, crt_file) catch |err| {
3224 glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| {
34083225 // TODO Surface more error details.
34093226 comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{
34103227 @errorName(err),
......@@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34153232 const named_frame = tracy.namedFrame("glibc_shared_objects");
34163233 defer named_frame.end();
34173234
3418 glibc.buildSharedObjects(comp) catch |err| {
3235 glibc.buildSharedObjects(comp, prog_node) catch |err| {
34193236 // TODO Surface more error details.
34203237 comp.lockAndSetMiscFailure(
34213238 .glibc_shared_objects,
......@@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34283245 const named_frame = tracy.namedFrame("musl_crt_file");
34293246 defer named_frame.end();
34303247
3431 musl.buildCRTFile(comp, crt_file) catch |err| {
3248 musl.buildCRTFile(comp, crt_file, prog_node) catch |err| {
34323249 // TODO Surface more error details.
34333250 comp.lockAndSetMiscFailure(
34343251 .musl_crt_file,
......@@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34413258 const named_frame = tracy.namedFrame("mingw_crt_file");
34423259 defer named_frame.end();
34433260
3444 mingw.buildCRTFile(comp, crt_file) catch |err| {
3261 mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| {
34453262 // TODO Surface more error details.
34463263 comp.lockAndSetMiscFailure(
34473264 .mingw_crt_file,
......@@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34683285 const named_frame = tracy.namedFrame("libunwind");
34693286 defer named_frame.end();
34703287
3471 libunwind.buildStaticLib(comp) catch |err| {
3288 libunwind.buildStaticLib(comp, prog_node) catch |err| {
34723289 // TODO Surface more error details.
34733290 comp.lockAndSetMiscFailure(
34743291 .libunwind,
......@@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34813298 const named_frame = tracy.namedFrame("libcxx");
34823299 defer named_frame.end();
34833300
3484 libcxx.buildLibCXX(comp) catch |err| {
3301 libcxx.buildLibCXX(comp, prog_node) catch |err| {
34853302 // TODO Surface more error details.
34863303 comp.lockAndSetMiscFailure(
34873304 .libcxx,
......@@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
34943311 const named_frame = tracy.namedFrame("libcxxabi");
34953312 defer named_frame.end();
34963313
3497 libcxx.buildLibCXXABI(comp) catch |err| {
3314 libcxx.buildLibCXXABI(comp, prog_node) catch |err| {
34983315 // TODO Surface more error details.
34993316 comp.lockAndSetMiscFailure(
35003317 .libcxxabi,
......@@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
35073324 const named_frame = tracy.namedFrame("libtsan");
35083325 defer named_frame.end();
35093326
3510 libtsan.buildTsan(comp) catch |err| {
3327 libtsan.buildTsan(comp, prog_node) catch |err| {
35113328 // TODO Surface more error details.
35123329 comp.lockAndSetMiscFailure(
35133330 .libtsan,
......@@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
35203337 const named_frame = tracy.namedFrame("wasi_libc_crt_file");
35213338 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| {
35243341 // TODO Surface more error details.
35253342 comp.lockAndSetMiscFailure(
35263343 .wasi_libc_crt_file,
......@@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
35383355 .Lib,
35393356 &comp.libssp_static_lib,
35403357 .libssp,
3358 prog_node,
35413359 ) catch |err| switch (err) {
35423360 error.OutOfMemory => return error.OutOfMemory,
35433361 error.SubCompilationFailed => return, // error reported already
......@@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
35573375 .Lib,
35583376 &comp.libc_static_lib,
35593377 .zig_libc,
3378 prog_node,
35603379 ) catch |err| switch (err) {
35613380 error.OutOfMemory => return error.OutOfMemory,
35623381 error.SubCompilationFailed => return, // error reported already
......@@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot(
38973716 comp: *Compilation,
38983717 output_mode: std.builtin.OutputMode,
38993718 out: *?CRTFile,
3719 prog_node: *std.Progress.Node,
39003720) 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) {
39023728 error.SubCompilationFailed => return, // error reported already
39033729 else => comp.lockAndSetMiscFailure(
39043730 .compiler_rt,
......@@ -5237,7 +5063,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
52375063 \\const std = @import("std");
52385064 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
52395065 \\/// 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}";
52415068 \\pub const zig_backend = std.builtin.CompilerBackend.{};
52425069 \\
52435070 \\pub const output_mode = std.builtin.OutputMode.{};
......@@ -5424,34 +5251,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
54245251 return buffer.toOwnedSliceSentinel(0);
54255252}
54265253
5427pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
5428 try sub_compilation.update();
5429
5430 // Look for compilation errors in this sub_compilation
5431 // TODO instead of logging these errors, handle them in the callsites
5432 // of updateSubCompilation and attach them as sub-errors, properly
5433 // surfacing the errors. You can see an example of this already
5434 // done inside buildOutputFromZig.
5435 var errors = try sub_compilation.getAllErrorsAlloc();
5436 defer errors.deinit(sub_compilation.gpa);
5437
5438 if (errors.list.len != 0) {
5439 for (errors.list) |full_err_msg| {
5440 switch (full_err_msg) {
5441 .src => |src| {
5442 log.err("{s}:{d}:{d}: {s}", .{
5443 src.src_path,
5444 src.line + 1,
5445 src.column + 1,
5446 src.msg,
5447 });
5448 },
5449 .plain => |plain| {
5450 log.err("{s}", .{plain.msg});
5451 },
5452 }
5453 }
5454 return error.BuildingLibCObjectFailed;
5254pub fn updateSubCompilation(
5255 parent_comp: *Compilation,
5256 sub_comp: *Compilation,
5257 misc_task: MiscTask,
5258 prog_node: *std.Progress.Node,
5259) !void {
5260 {
5261 var sub_node = prog_node.start(@tagName(misc_task), 0);
5262 sub_node.activate();
5263 defer sub_node.end();
5264
5265 try sub_comp.update(prog_node);
5266 }
5267
5268 // Look for compilation errors in this sub compilation
5269 const gpa = parent_comp.gpa;
5270 var keep_errors = false;
5271 var errors = try sub_comp.getAllErrorsAlloc();
5272 defer if (!keep_errors) errors.deinit(gpa);
5273
5274 if (errors.errorMessageCount() > 0) {
5275 try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1);
5276 parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{
5277 .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{
5278 @tagName(misc_task),
5279 }),
5280 .children = errors,
5281 });
5282 keep_errors = true;
5283 return error.SubCompilationFailed;
54555284 }
54565285}
54575286
......@@ -5461,6 +5290,7 @@ fn buildOutputFromZig(
54615290 output_mode: std.builtin.OutputMode,
54625291 out: *?CRTFile,
54635292 misc_task_tag: MiscTask,
5293 prog_node: *std.Progress.Node,
54645294) !void {
54655295 const tracy_trace = trace(@src());
54665296 defer tracy_trace.end();
......@@ -5527,23 +5357,7 @@ fn buildOutputFromZig(
55275357 });
55285358 defer sub_compilation.destroy();
55295359
5530 try sub_compilation.update();
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 }
5360 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
55475361
55485362 assert(out.* == null);
55495363 out.* = Compilation.CRTFile{
......@@ -5558,6 +5372,8 @@ pub fn build_crt_file(
55585372 comp: *Compilation,
55595373 root_name: []const u8,
55605374 output_mode: std.builtin.OutputMode,
5375 misc_task_tag: MiscTask,
5376 prog_node: *std.Progress.Node,
55615377 c_source_files: []const Compilation.CSourceFile,
55625378) !void {
55635379 const tracy_trace = trace(@src());
......@@ -5618,7 +5434,7 @@ pub fn build_crt_file(
56185434 });
56195435 defer sub_compilation.destroy();
56205436
5621 try sub_compilation.updateSubCompilation();
5437 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
56225438
56235439 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
56245440
src/Liveness.zig+2
......@@ -226,6 +226,7 @@ pub fn categorizeOperand(
226226 .ret_ptr,
227227 .constant,
228228 .const_ty,
229 .trap,
229230 .breakpoint,
230231 .dbg_stmt,
231232 .dbg_inline_begin,
......@@ -848,6 +849,7 @@ fn analyzeInst(
848849 .ret_ptr,
849850 .constant,
850851 .const_ty,
852 .trap,
851853 .breakpoint,
852854 .dbg_stmt,
853855 .dbg_inline_begin,
src/Module.zig+72-125
......@@ -3528,6 +3528,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
35283528 const digest = hash: {
35293529 var path_hash: Cache.HashHelper = .{};
35303530 path_hash.addBytes(build_options.version);
3531 path_hash.add(builtin.zig_backend);
35313532 if (!want_local_cache) {
35323533 path_hash.addOptionalBytes(file.pkg.root_src_directory.path);
35333534 }
......@@ -3537,44 +3538,66 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
35373538 const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache;
35383539 const zir_dir = cache_directory.handle;
35393540
3540 var cache_file: ?std.fs.File = null;
3541 defer if (cache_file) |f| f.close();
3542
35433541 // Determine whether we need to reload the file from disk and redo parsing and AstGen.
3544 switch (file.status) {
3545 .never_loaded, .retryable_failure => cached: {
3542 var lock: std.fs.File.Lock = switch (file.status) {
3543 .never_loaded, .retryable_failure => lock: {
35463544 // First, load the cached ZIR code, if any.
35473545 log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{
35483546 file.sub_file_path, want_local_cache, &digest,
35493547 });
35503548
3551 // We ask for a lock in order to coordinate with other zig processes.
3552 // If another process is already working on this file, we will get the cached
3553 // version. Likewise if we're working on AstGen and another process asks for
3554 // the cached file, they'll get it.
3555 cache_file = zir_dir.openFile(&digest, .{ .lock = .Shared }) catch |err| switch (err) {
3556 error.PathAlreadyExists => unreachable, // opening for reading
3557 error.NoSpaceLeft => unreachable, // opening for reading
3558 error.NotDir => unreachable, // no dir components
3559 error.InvalidUtf8 => unreachable, // it's a hex encoded name
3560 error.BadPathName => unreachable, // it's a hex encoded name
3561 error.NameTooLong => unreachable, // it's a fixed size name
3562 error.PipeBusy => unreachable, // it's not a pipe
3563 error.WouldBlock => unreachable, // not asking for non-blocking I/O
3564
3565 error.SymLinkLoop,
3566 error.FileNotFound,
3567 error.Unexpected,
3568 => break :cached,
3569
3570 else => |e| return e, // Retryable errors are handled at callsite.
3571 };
3549 break :lock .Shared;
3550 },
3551 .parse_failure, .astgen_failure, .success_zir => lock: {
3552 const unchanged_metadata =
3553 stat.size == file.stat.size and
3554 stat.mtime == file.stat.mtime and
3555 stat.inode == file.stat.inode;
3556
3557 if (unchanged_metadata) {
3558 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
3559 return;
3560 }
3561
3562 log.debug("metadata changed: {s}", .{file.sub_file_path});
3563
3564 break :lock .Exclusive;
3565 },
3566 };
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: {
35733596 // 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) {
35753598 // This can happen if Zig bails out of this function between creating
35763599 // the cached file and writing it.
3577 error.EndOfStream => break :cached,
3600 error.EndOfStream => break :update,
35783601 else => |e| return e,
35793602 };
35803603 const unchanged_metadata =
......@@ -3584,7 +3607,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
35843607
35853608 if (!unchanged_metadata) {
35863609 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
3587 break :cached;
3610 break :update;
35883611 }
35893612 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
35903613 file.sub_file_path, header.instructions_len,
......@@ -3636,13 +3659,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
36363659 .iov_len = header.extra_len * 4,
36373660 },
36383661 };
3639 const amt_read = try cache_file.?.readvAll(&iovecs);
3662 const amt_read = try cache_file.readvAll(&iovecs);
36403663 const amt_expected = zir.instructions.len * 9 +
36413664 zir.string_bytes.len +
36423665 zir.extra.len * 4;
36433666 if (amt_read != amt_expected) {
36443667 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
3645 break :cached;
3668 break :update;
36463669 }
36473670 if (data_has_safety_tag) {
36483671 const tags = zir.instructions.items(.tag);
......@@ -3678,42 +3701,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
36783701 return error.AnalysisFail;
36793702 }
36803703 return;
3681 },
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 }
3704 }
36923705
3693 log.debug("metadata changed: {s}", .{file.sub_file_path});
3694 },
3706 // If we already have the exclusive lock then it is our job to update.
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);
36953713 }
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| {
3710 const pkg_path = file.pkg.root_src_directory.path orelse ".";
3711 const cache_path = cache_directory.path orelse ".";
3712 log.warn("unable to save cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{
3713 pkg_path, file.sub_file_path, cache_path, &digest, @errorName(e),
3714 });
3715 return;
3716 },
3715 // The cache is definitely stale so delete the contents to avoid an underwrite later.
3716 cache_file.setEndPos(0) catch |err| switch (err) {
3717 error.FileTooBig => unreachable, // 0 is not too big
3718
3719 else => |e| return e,
37173720 };
37183721
37193722 mod.lockAndClearFileCompileError(file);
......@@ -3753,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
37533756 file.source_loaded = true;
37543757
37553758 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 }
38153759 file.tree_loaded = true;
38163760
3761 // Any potential AST errors are converted to ZIR errors here.
38173762 file.zir = try AstGen.generate(gpa, file.tree);
38183763 file.zir_loaded = true;
38193764 file.status = .success_zir;
......@@ -3870,7 +3815,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
38703815 .iov_len = file.zir.extra.len * 4,
38713816 },
38723817 };
3873 cache_file.?.writevAll(&iovecs) catch |err| {
3818 cache_file.writevAll(&iovecs) catch |err| {
38743819 const pkg_path = file.pkg.root_src_directory.path orelse ".";
38753820 const cache_path = cache_directory.path orelse ".";
38763821 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 {
39223867 const gpa = mod.gpa;
39233868 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
39253873 // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which
39263874 // creates a namespace, gets mapped from old to new here.
39273875 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 {
39393887 var decl_stack: ArrayListUnmanaged(Decl.Index) = .{};
39403888 defer decl_stack.deinit(gpa);
39413889
3942 const root_decl = file.root_decl.unwrap().?;
39433890 try decl_stack.append(gpa, root_decl);
39443891
39453892 file.deleted_decls.clearRetainingCapacity();
src/Package.zig+64-65
......@@ -8,11 +8,11 @@ const Allocator = mem.Allocator;
88const assert = std.debug.assert;
99const log = std.log.scoped(.package);
1010const main = @import("main.zig");
11const ThreadPool = std.Thread.Pool;
12const WaitGroup = std.Thread.WaitGroup;
1113
1214const Compilation = @import("Compilation.zig");
1315const Module = @import("Module.zig");
14const ThreadPool = @import("ThreadPool.zig");
15const WaitGroup = @import("WaitGroup.zig");
1616const Cache = std.Build.Cache;
1717const build_options = @import("build_options");
1818const Manifest = @import("Manifest.zig");
......@@ -215,6 +215,7 @@ pub const build_zig_basename = "build.zig";
215215
216216pub fn fetchAndAddDependencies(
217217 pkg: *Package,
218 root_pkg: *Package,
218219 arena: Allocator,
219220 thread_pool: *ThreadPool,
220221 http_client: *std.http.Client,
......@@ -224,7 +225,7 @@ pub fn fetchAndAddDependencies(
224225 dependencies_source: *std.ArrayList(u8),
225226 build_roots_source: *std.ArrayList(u8),
226227 name_prefix: []const u8,
227 color: main.Color,
228 error_bundle: *std.zig.ErrorBundle.Wip,
228229 all_modules: *AllModules,
229230) !void {
230231 const max_bytes = 10 * 1024 * 1024;
......@@ -249,7 +250,7 @@ pub fn fetchAndAddDependencies(
249250
250251 if (ast.errors.len > 0) {
251252 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);
253254 return error.PackageFetchFailed;
254255 }
255256
......@@ -257,14 +258,9 @@ pub fn fetchAndAddDependencies(
257258 defer manifest.deinit(gpa);
258259
259260 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 };
265261 const file_path = try directory.join(arena, &.{Manifest.basename});
266262 for (manifest.errors) |msg| {
267 Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{});
263 try Report.addErrorMessage(ast, file_path, error_bundle, 0, msg);
268264 }
269265 return error.PackageFetchFailed;
270266 }
......@@ -272,8 +268,7 @@ pub fn fetchAndAddDependencies(
272268 const report: Report = .{
273269 .ast = &ast,
274270 .directory = directory,
275 .color = color,
276 .arena = arena,
271 .error_bundle = error_bundle,
277272 };
278273
279274 var any_error = false;
......@@ -295,7 +290,8 @@ pub fn fetchAndAddDependencies(
295290 all_modules,
296291 );
297292
298 try pkg.fetchAndAddDependencies(
293 try sub_pkg.fetchAndAddDependencies(
294 root_pkg,
299295 arena,
300296 thread_pool,
301297 http_client,
......@@ -305,11 +301,12 @@ pub fn fetchAndAddDependencies(
305301 dependencies_source,
306302 build_roots_source,
307303 sub_prefix,
308 color,
304 error_bundle,
309305 all_modules,
310306 );
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
314311 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{
315312 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),
......@@ -347,8 +344,7 @@ pub fn createFilePkg(
347344const Report = struct {
348345 ast: *const std.zig.Ast,
349346 directory: Compilation.Directory,
350 color: main.Color,
351 arena: Allocator,
347 error_bundle: *std.zig.ErrorBundle.Wip,
352348
353349 fn fail(
354350 report: Report,
......@@ -356,52 +352,46 @@ const Report = struct {
356352 comptime fmt_string: []const u8,
357353 fmt_args: anytype,
358354 ) error{ PackageFetchFailed, OutOfMemory } {
359 return failWithNotes(report, &.{}, tok, fmt_string, fmt_args);
360 }
355 const gpa = report.error_bundle.gpa;
361356
362 fn failWithNotes(
363 report: Report,
364 notes: []const Compilation.AllErrors.Message,
365 tok: std.zig.Ast.TokenIndex,
366 comptime fmt_string: []const u8,
367 fmt_args: anytype,
368 ) error{ PackageFetchFailed, OutOfMemory } {
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, .{
357 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
358 defer gpa.free(file_path);
359
360 const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args);
361 defer gpa.free(msg);
362
363 try addErrorMessage(report.ast.*, file_path, report.error_bundle, 0, .{
376364 .tok = tok,
377365 .off = 0,
378 .msg = try std.fmt.allocPrint(report.arena, fmt_string, fmt_args),
379 }, notes);
366 .msg = msg,
367 });
368
380369 return error.PackageFetchFailed;
381370 }
382371
383 fn renderErrorMessage(
372 fn addErrorMessage(
384373 ast: std.zig.Ast,
385374 file_path: []const u8,
386 ttyconf: std.debug.TTY.Config,
375 eb: *std.zig.ErrorBundle.Wip,
376 notes_len: u32,
387377 msg: Manifest.ErrorMessage,
388 notes: []const Compilation.AllErrors.Message,
389 ) void {
378 ) error{OutOfMemory}!void {
390379 const token_starts = ast.tokens.items(.start);
391380 const start_loc = ast.tokenLocation(0, msg.tok);
392 Compilation.AllErrors.Message.renderToStdErr(.{ .src = .{
393 .msg = msg.msg,
394 .src_path = file_path,
395 .line = @intCast(u32, start_loc.line),
396 .column = @intCast(u32, start_loc.column),
397 .span = .{
398 .start = token_starts[msg.tok],
399 .end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
400 .main = token_starts[msg.tok] + msg.off,
401 },
402 .source_line = ast.source[start_loc.line_start..start_loc.line_end],
403 .notes = notes,
404 } }, ttyconf);
381
382 try eb.addRootErrorMessage(.{
383 .msg = try eb.addString(msg.msg),
384 .src_loc = try eb.addSourceLocation(.{
385 .src_path = try eb.addString(file_path),
386 .span_start = token_starts[msg.tok],
387 .span_end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
388 .span_main = token_starts[msg.tok] + msg.off,
389 .line = @intCast(u32, start_loc.line),
390 .column = @intCast(u32, start_loc.column),
391 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
392 }),
393 .notes_len = notes_len,
394 });
405395 }
406396};
407397
......@@ -432,6 +422,12 @@ fn fetchAndUnpack(
432422 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
433423 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
435431 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
436432 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
437433 });
......@@ -444,12 +440,6 @@ fn fetchAndUnpack(
444440 return gop.value_ptr.*;
445441 }
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
453443 const ptr = try gpa.create(Package);
454444 errdefer gpa.destroy(ptr);
455445
......@@ -501,9 +491,7 @@ fn fetchAndUnpack(
501491 // by default, so the same logic applies for buffering the reader as for gzip.
502492 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);
503493 } else {
504 return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{
505 uri.path,
506 });
494 return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{uri.path});
507495 }
508496
509497 // TODO: delete files not included in the package prior to computing the package hash.
......@@ -530,10 +518,21 @@ fn fetchAndUnpack(
530518 });
531519 }
532520 } else {
533 const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{
534 .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}),
535 } }};
536 return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{});
521 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
522 defer gpa.free(file_path);
523
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;
537536 }
538537
539538 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 {
574574 });
575575 }
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 {
578578 return block.addInst(.{
579579 .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector,
580580 .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 ),
582584 .payload = try block.sema.addExtra(Air.VectorCmp{
583585 .lhs = lhs,
584586 .rhs = rhs,
......@@ -1101,6 +1103,7 @@ fn analyzeBodyInner(
11011103 .@"unreachable" => break sema.zirUnreachable(block, inst),
11021104 .panic => break sema.zirPanic(block, inst, false),
11031105 .panic_comptime => break sema.zirPanic(block, inst, true),
1106 .trap => break sema.zirTrap(block, inst),
11041107 // zig fmt: on
11051108
11061109 .extended => ext: {
......@@ -1167,6 +1170,11 @@ fn analyzeBodyInner(
11671170 i += 1;
11681171 continue;
11691172 },
1173 .set_cold => {
1174 try sema.zirSetCold(block, extended);
1175 i += 1;
1176 continue;
1177 },
11701178 .breakpoint => {
11711179 if (!block.is_comptime) {
11721180 _ = try block.addNoOp(.breakpoint);
......@@ -1304,11 +1312,6 @@ fn analyzeBodyInner(
13041312 i += 1;
13051313 continue;
13061314 },
1307 .set_cold => {
1308 try sema.zirSetCold(block, inst);
1309 i += 1;
1310 continue;
1311 },
13121315 .set_runtime_safety => {
13131316 try sema.zirSetRuntimeSafety(block, inst);
13141317 i += 1;
......@@ -1609,6 +1612,12 @@ fn analyzeBodyInner(
16091612 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
16101613 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
16111614 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 }
16121621 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
16131622 assert(is_non_err != .none);
16141623 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(
16161625 return err;
16171626 };
16181627 if (is_non_err_tv.val.toBool()) {
1619 const err_union_ty = sema.typeOf(err_union);
16201628 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
16211629 }
16221630 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
......@@ -2203,29 +2211,27 @@ pub fn fail(
22032211
22042212fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
22052213 @setCold(true);
2214 const gpa = sema.gpa;
22062215
22072216 if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) {
22082217 if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation;
2209 var arena = std.heap.ArenaAllocator.init(sema.gpa);
2210 errdefer arena.deinit();
2211 var errors = std.ArrayList(Compilation.AllErrors.Message).init(sema.gpa);
2212 defer errors.deinit();
2213
2214 Compilation.AllErrors.add(sema.mod, &arena, &errors, err_msg.*) catch unreachable;
2215
2218 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2219 wip_errors.init(gpa) catch unreachable;
2220 Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable;
22162221 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 });
22182224 crash_report.compilerPanic("unexpected compile error occurred", null, null);
22192225 }
22202226
22212227 const mod = sema.mod;
22222228 ref: {
2223 errdefer err_msg.destroy(mod.gpa);
2229 errdefer err_msg.destroy(gpa);
22242230 if (err_msg.src_loc.lazy == .unneeded) {
22252231 return error.NeededSourceLocation;
22262232 }
2227 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
2228 try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1);
2233 try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
2234 try mod.failed_files.ensureUnusedCapacity(gpa, 1);
22292235
22302236 const max_references = blk: {
22312237 if (sema.mod.comp.reference_trace) |num| break :blk num;
......@@ -2235,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
22352241 };
22362242
22372243 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);
22392245 defer reference_stack.deinit();
22402246
22412247 // 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);
22432249 defer seen.deinit();
22442250
22452251 var cur_reference_trace: u32 = 0;
......@@ -2280,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
22802286 if (gop.found_existing) {
22812287 // If there are multiple errors for the same Decl, prefer the first one added.
22822288 sema.err = null;
2283 err_msg.destroy(mod.gpa);
2289 err_msg.destroy(gpa);
22842290 } else {
22852291 sema.err = err_msg;
22862292 gop.value_ptr.* = err_msg;
......@@ -5144,6 +5150,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo
51445150 return always_noreturn;
51455151}
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
51475161fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
51485162 const tracy = trace(@src());
51495163 defer tracy.end();
......@@ -5721,10 +5735,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
57215735 gop.value_ptr.* = .{ .alignment = alignment, .src = src };
57225736}
57235737
5724fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5725 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5726 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
5727 const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime-known");
5738fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
5739 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
5740 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
5741 const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known");
57285742 const func = sema.func orelse return; // does nothing outside a function
57295743 func.is_cold = is_cold;
57305744}
......@@ -8766,7 +8780,7 @@ fn funcCommon(
87668780 };
87678781 return sema.failWithOwnedErrorMsg(msg);
87688782 }
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)) {
87708784 const msg = msg: {
87718785 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
87728786 return_type.fmt(sema.mod), @tagName(cc_resolved),
......@@ -9403,7 +9417,7 @@ fn intCast(
94039417 const ok = if (is_vector) ok: {
94049418 const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero);
94059419 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);
94079421 const all_in_range = try block.addInst(.{
94089422 .tag = .reduce,
94099423 .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } },
......@@ -9457,7 +9471,7 @@ fn intCast(
94579471 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
94589472
94599473 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);
94619475 const all_in_range = try block.addInst(.{
94629476 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
94639477 .data = .{ .reduce = .{
......@@ -9474,7 +9488,7 @@ fn intCast(
94749488 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
94759489 } else {
94769490 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);
94789492 const all_in_range = try block.addInst(.{
94799493 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
94809494 .data = .{ .reduce = .{
......@@ -9495,7 +9509,7 @@ fn intCast(
94959509 const ok = if (is_vector) ok: {
94969510 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
94979511 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);
94999513 const all_in_range = try block.addInst(.{
95009514 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
95019515 .data = .{ .reduce = .{
......@@ -12007,7 +12021,7 @@ fn zirShl(
1200712021
1200812022 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
1200912023 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);
1201112025 break :ok try block.addInst(.{
1201212026 .tag = .reduce,
1201312027 .data = .{ .reduce = .{
......@@ -12163,7 +12177,7 @@ fn zirShr(
1216312177
1216412178 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
1216512179 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);
1216712181 break :ok try block.addInst(.{
1216812182 .tag = .reduce,
1216912183 .data = .{ .reduce = .{
......@@ -12182,7 +12196,7 @@ fn zirShr(
1218212196 const back = try block.addBinOp(.shl, result, rhs);
1218312197
1218412198 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);
1218612200 break :ok try block.addInst(.{
1218712201 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
1218812202 .data = .{ .reduce = .{
......@@ -13183,7 +13197,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1318313197 const floored = try block.addUnOp(.floor, result);
1318413198
1318513199 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);
1318713201 break :ok try block.addInst(.{
1318813202 .tag = switch (block.float_mode) {
1318913203 .Strict => .reduce,
......@@ -13207,7 +13221,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1320713221 if (resolved_type.zigTypeTag() == .Vector) {
1320813222 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
1320913223 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);
1321113225 break :ok try block.addInst(.{
1321213226 .tag = .reduce,
1321313227 .data = .{ .reduce = .{
......@@ -13505,14 +13519,13 @@ fn addDivIntOverflowSafety(
1350513519
1350613520 var ok: Air.Inst.Ref = .none;
1350713521 if (resolved_type.zigTypeTag() == .Vector) {
13508 const vector_ty_ref = try sema.addType(resolved_type);
1350913522 if (maybe_lhs_val == null) {
1351013523 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);
1351213525 }
1351313526 if (maybe_rhs_val == null) {
1351413527 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);
1351613529 if (ok == .none) {
1351713530 ok = rhs_ok;
1351813531 } else {
......@@ -13564,7 +13577,7 @@ fn addDivByZeroSafety(
1356413577 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
1356513578 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
1356613579 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);
1356813581 break :ok try block.addInst(.{
1356913582 .tag = if (is_int) .reduce else .reduce_optimized,
1357013583 .data = .{ .reduce = .{
......@@ -15193,9 +15206,7 @@ fn cmpSelf(
1519315206 };
1519415207 try sema.requireRuntimeBlock(block, src, runtime_src);
1519515208 if (resolved_type.zigTypeTag() == .Vector) {
15196 const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.bool);
15197 const result_ty_ref = try sema.addType(result_ty);
15198 return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref);
15209 return block.addCmpVector(casted_lhs, casted_rhs, op);
1519915210 }
1520015211 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized);
1520115212 return block.addBinOp(tag, casted_lhs, casted_rhs);
......@@ -18360,7 +18371,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
1836018371 const union_val = val.cast(Value.Payload.Union).?.data;
1836118372 const target = mod.getTarget();
1836218373 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);
1836418375 switch (@intToEnum(std.builtin.TypeId, tag_index)) {
1836518376 .Type => return Air.Inst.Ref.type_type,
1836618377 .Void => return Air.Inst.Ref.void_type,
......@@ -22274,7 +22285,6 @@ fn zirBuiltinExtern(
2227422285 extended: Zir.Inst.Extended.InstData,
2227522286) CompileError!Air.Inst.Ref {
2227622287 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
22277 const src = LazySrcLoc.nodeOffset(extra.node);
2227822288 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
2227922289 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
2228022290
......@@ -22302,39 +22312,41 @@ fn zirBuiltinExtern(
2230222312 const new_decl = sema.mod.declPtr(new_decl_index);
2230322313 new_decl.name = try sema.gpa.dupeZ(u8, options.name);
2230422314
22305 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
22306 errdefer new_decl_arena.deinit();
22307 const new_decl_arena_allocator = new_decl_arena.allocator();
22315 {
22316 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
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);
22310 errdefer new_decl_arena_allocator.destroy(new_var);
22320 const new_var = try new_decl_arena_allocator.create(Module.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.* = .{
22313 .owner_decl = sema.owner_decl_index,
22314 .init = Value.initTag(.unreachable_value),
22315 .is_extern = true,
22316 .is_mutable = false,
22317 .is_threadlocal = options.is_thread_local,
22318 .is_weak_linkage = options.linkage == .Weak,
22319 .lib_name = null,
22320 };
22331 new_decl.src_line = sema.owner_decl.src_line;
22332 // We only access this decl through the decl_ref with the correct type created
22333 // below, so this type doesn't matter
22334 new_decl.ty = Type.Tag.init(.anyopaque);
22335 new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
22336 new_decl.@"align" = 0;
22337 new_decl.@"linksection" = null;
22338 new_decl.has_tv = true;
22339 new_decl.analysis = .complete;
22340 new_decl.generation = sema.mod.generation;
2232122341
22322 new_decl.src_line = sema.owner_decl.src_line;
22323 new_decl.ty = try ty.copy(new_decl_arena_allocator);
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;
22342 try new_decl.finalizeNewArena(&new_decl_arena);
22343 }
2233022344
22331 const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
22332 arena_state.* = new_decl_arena.state;
22333 new_decl.value_arena = arena_state;
22345 try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
22346 try sema.ensureDeclAnalyzed(new_decl_index);
2233422347
22335 const ref = try sema.analyzeDeclRef(new_decl_index);
22336 try sema.requireRuntimeBlock(block, src, null);
22337 return block.addBitCast(ty, ref);
22348 const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
22349 return sema.addConstant(ty, ref);
2233822350}
2233922351
2234022352fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
......@@ -23026,7 +23038,7 @@ fn panicSentinelMismatch(
2302623038
2302723039 const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: {
2302823040 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);
2303023042 break :ok try parent_block.addInst(.{
2303123043 .tag = .reduce,
2303223044 .data = .{ .reduce = .{
......@@ -23592,10 +23604,13 @@ fn fieldCallBind(
2359223604 }
2359323605
2359423606 // If we get here, we need to look for a decl in the struct type instead.
23595 switch (concrete_ty.zigTypeTag()) {
23596 .Struct, .Opaque, .Union, .Enum => {
23607 const found_decl = switch (concrete_ty.zigTypeTag()) {
23608 .Struct, .Opaque, .Union, .Enum => found_decl: {
2359723609 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
2359923614 const decl_val = try sema.analyzeLoad(block, src, inst, src);
2360023615 const decl_type = sema.typeOf(decl_val);
2360123616 if (decl_type.zigTypeTag() == .Fn and
......@@ -23612,7 +23627,7 @@ fn fieldCallBind(
2361223627 first_param_type.ptrSize() == .C) and
2361323628 first_param_type.childType().eql(concrete_ty, sema.mod)))
2361423629 {
23615 // zig fmt: on
23630 // zig fmt: on
2361623631 // TODO: bound fn calls on rvalues should probably
2361723632 // generate a by-value argument somehow.
2361823633 const ty = Type.Tag.bound_fn.init();
......@@ -23651,16 +23666,22 @@ fn fieldCallBind(
2365123666 return sema.addConstant(ty, value);
2365223667 }
2365323668 }
23669 break :found_decl decl_idx;
2365423670 }
2365523671 }
23672 break :found_decl null;
2365623673 },
23657 else => {},
23658 }
23674 else => null,
23675 };
2365923676
2366023677 const msg = msg: {
2366123678 const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) });
2366223679 errdefer msg.destroy(sema.gpa);
2366323680 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 }
2366423685 break :msg msg;
2366523686 };
2366623687 return sema.failWithOwnedErrorMsg(msg);
......@@ -24832,6 +24853,7 @@ fn coerceExtra(
2483224853 const array_ty = dest_info.pointee_type;
2483324854 if (array_ty.zigTypeTag() != .Array) break :single_item;
2483424855 const array_elem_ty = array_ty.childType();
24856 if (array_ty.arrayLen() != 1) break :single_item;
2483524857 const dest_is_mut = dest_info.mutable;
2483624858 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
2483724859 .ok => {},
......@@ -26625,6 +26647,23 @@ fn beginComptimePtrMutation(
2662526647 });
2662626648 }
2662726649 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
2662826667 switch (val_ptr.tag()) {
2662926668 .undef => {
2663026669 // An array has been initialized to undefined at comptime and now we
......@@ -29359,8 +29398,7 @@ fn cmpVector(
2935929398 };
2936029399
2936129400 try sema.requireRuntimeBlock(block, src, runtime_src);
29362 const result_ty_inst = try sema.addType(result_ty);
29363 return block.addCmpVector(lhs, rhs, op, result_ty_inst);
29401 return block.addCmpVector(lhs, rhs, op);
2936429402}
2936529403
2936629404fn 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 {
617617 /// Uses the `un_node` field.
618618 typeof_log2_int_type,
619619 /// Asserts control-flow will not reach this instruction (`unreachable`).
620 /// Uses the `unreachable` union field.
620 /// Uses the `@"unreachable"` union field.
621621 @"unreachable",
622622 /// Bitwise XOR. `^`
623623 /// Uses the `pl_node` union field. Payload is `Bin`.
......@@ -808,8 +808,9 @@ pub const Inst = struct {
808808 panic,
809809 /// Same as `panic` but forces comptime.
810810 panic_comptime,
811 /// Implement builtin `@setCold`. Uses `un_node`.
812 set_cold,
811 /// Implements `@trap`.
812 /// Uses the `node` field.
813 trap,
813814 /// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
814815 set_runtime_safety,
815816 /// Implement builtin `@sqrt`. Uses `un_node`.
......@@ -1187,7 +1188,6 @@ pub const Inst = struct {
11871188 .bool_to_int,
11881189 .embed_file,
11891190 .error_name,
1190 .set_cold,
11911191 .set_runtime_safety,
11921192 .sqrt,
11931193 .sin,
......@@ -1277,6 +1277,7 @@ pub const Inst = struct {
12771277 .repeat_inline,
12781278 .panic,
12791279 .panic_comptime,
1280 .trap,
12801281 .check_comptime_control_flow,
12811282 => true,
12821283 };
......@@ -1323,7 +1324,6 @@ pub const Inst = struct {
13231324 .validate_deref,
13241325 .@"export",
13251326 .export_value,
1326 .set_cold,
13271327 .set_runtime_safety,
13281328 .memcpy,
13291329 .memset,
......@@ -1553,6 +1553,7 @@ pub const Inst = struct {
15531553 .repeat_inline,
15541554 .panic,
15551555 .panic_comptime,
1556 .trap,
15561557 .for_len,
15571558 .@"try",
15581559 .try_ptr,
......@@ -1561,7 +1562,7 @@ pub const Inst = struct {
15611562 => false,
15621563
15631564 .extended => switch (data.extended.opcode) {
1564 .breakpoint, .fence => true,
1565 .fence, .set_cold, .breakpoint => true,
15651566 else => false,
15661567 },
15671568 };
......@@ -1750,7 +1751,7 @@ pub const Inst = struct {
17501751 .error_name = .un_node,
17511752 .panic = .un_node,
17521753 .panic_comptime = .un_node,
1753 .set_cold = .un_node,
1754 .trap = .node,
17541755 .set_runtime_safety = .un_node,
17551756 .sqrt = .un_node,
17561757 .sin = .un_node,
......@@ -1979,11 +1980,15 @@ pub const Inst = struct {
19791980 /// Implement builtin `@setAlignStack`.
19801981 /// `operand` is payload index to `UnNode`.
19811982 set_align_stack,
1983 /// Implements `@setCold`.
1984 /// `operand` is payload index to `UnNode`.
1985 set_cold,
19821986 /// Implements the `@errSetCast` builtin.
19831987 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
19841988 err_set_cast,
19851989 /// `operand` is payload index to `UnNode`.
19861990 await_nosuspend,
1991 /// Implements `@breakpoint`.
19871992 /// `operand` is `src_node: i32`.
19881993 breakpoint,
19891994 /// Implements the `@select` builtin.
......@@ -1997,7 +2002,7 @@ pub const Inst = struct {
19972002 int_to_error,
19982003 /// Implement builtin `@Type`.
19992004 /// `operand` is payload index to `UnNode`.
2000 /// `small` contains `NameStrategy
2005 /// `small` contains `NameStrategy`.
20012006 reify,
20022007 /// Implements the `@asyncCall` builtin.
20032008 /// `operand` is payload index to `AsyncCall`.
......@@ -2040,8 +2045,7 @@ pub const Inst = struct {
20402045
20412046 /// A reference to a TypedValue or ZIR instruction.
20422047 ///
2043 /// If the Ref has a tag in this enum, it refers to a TypedValue which may be
2044 /// retrieved with Ref.toTypedValue().
2048 /// If the Ref has a tag in this enum, it refers to a TypedValue.
20452049 ///
20462050 /// If the value of a Ref does not have a tag, it refers to a ZIR instruction.
20472051 ///
......@@ -3590,6 +3594,12 @@ pub const Inst = struct {
35903594 /// 0 or a payload index of a `Block`, each is a payload
35913595 /// index of another `Item`.
35923596 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 }
35933603 };
35943604 };
35953605
src/arch/aarch64/CodeGen.zig+30-200
......@@ -23,7 +23,7 @@ const leb128 = std.leb;
2323const log = std.log.scoped(.codegen);
2424const build_options = @import("build_options");
2525
26const GenerateSymbolError = codegen.GenerateSymbolError;
26const CodeGenError = codegen.CodeGenError;
2727const Result = codegen.Result;
2828const DebugInfoOutput = codegen.DebugInfoOutput;
2929
......@@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
4141const c_abi_int_return_regs = abi.c_abi_int_return_regs;
4242const gp = abi.RegisterClass.gp;
4343
44const InnerError = error{
45 OutOfMemory,
46 CodegenFail,
47 OutOfRegisters,
48};
44const InnerError = CodeGenError || error{OutOfRegisters};
4945
5046gpa: Allocator,
5147air: Air,
......@@ -337,7 +333,7 @@ pub fn generate(
337333 liveness: Liveness,
338334 code: *std.ArrayList(u8),
339335 debug_output: DebugInfoOutput,
340) GenerateSymbolError!Result {
336) CodeGenError!Result {
341337 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
342338 @panic("Attempted to compile for architecture that was disabled by build configuration");
343339 }
......@@ -737,6 +733,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
737733 .bitcast => try self.airBitCast(inst),
738734 .block => try self.airBlock(inst),
739735 .br => try self.airBr(inst),
736 .trap => try self.airTrap(),
740737 .breakpoint => try self.airBreakpoint(),
741738 .ret_addr => try self.airRetAddr(inst),
742739 .frame_addr => try self.airFrameAddress(inst),
......@@ -4198,10 +4195,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
41984195 return self.finishAir(inst, result, .{ .none, .none, .none });
41994196}
42004197
4198fn airTrap(self: *Self) !void {
4199 _ = try self.addInst(.{
4200 .tag = .brk,
4201 .data = .{ .imm16 = 0x0001 },
4202 });
4203 return self.finishAirBookkeeping();
4204}
4205
42014206fn airBreakpoint(self: *Self) !void {
42024207 _ = try self.addInst(.{
42034208 .tag = .brk,
4204 .data = .{ .imm16 = 1 },
4209 .data = .{ .imm16 = 0xf000 },
42054210 });
42064211 return self.finishAirBookkeeping();
42074212}
......@@ -6137,201 +6142,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
61376142 }
61386143}
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
62086145fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6209 var typed_value = arg_tv;
6210 if (typed_value.val.castTag(.runtime_value)) |rt| {
6211 typed_value.val = rt.data;
6212 }
6213 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
6214 if (typed_value.val.isUndef())
6215 return MCValue{ .undef = {} };
6216
6217 if (typed_value.val.castTag(.decl_ref)) |payload| {
6218 return self.lowerDeclRef(typed_value, payload.data);
6219 }
6220 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
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 }
6146 const mcv: MCValue = switch (try codegen.genTypedValue(
6147 self.bin_file,
6148 self.src_loc,
6149 arg_tv,
6150 self.mod_fn.owner_decl,
6151 )) {
6152 .mcv => |mcv| switch (mcv) {
6153 .none => .none,
6154 .undef => .undef,
6155 .linker_load => |ll| .{ .linker_load = ll },
6156 .immediate => |imm| .{ .immediate = imm },
6157 .memory => |addr| .{ .memory = addr },
63056158 },
6306 .ErrorUnion => {
6307 const error_type = typed_value.ty.errorUnionSet();
6308 const payload_type = typed_value.ty.errorUnionPayload();
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);
6159 .fail => |msg| {
6160 self.err_msg = msg;
6161 return error.CodegenFail;
63196162 },
6320
6321 .ComptimeInt => unreachable, // semantic analysis prevents this
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);
6163 };
6164 return mcv;
63356165}
63366166
63376167const CallMCValues = struct {
src/arch/arm/CodeGen.zig+29-176
......@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);
2424const build_options = @import("build_options");
2525
2626const Result = codegen.Result;
27const GenerateSymbolError = codegen.GenerateSymbolError;
27const CodeGenError = codegen.CodeGenError;
2828const DebugInfoOutput = codegen.DebugInfoOutput;
2929
3030const bits = @import("bits.zig");
......@@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
4242const c_abi_int_return_regs = abi.c_abi_int_return_regs;
4343const gp = abi.RegisterClass.gp;
4444
45const InnerError = error{
46 OutOfMemory,
47 CodegenFail,
48 OutOfRegisters,
49};
45const InnerError = CodeGenError || error{OutOfRegisters};
5046
5147gpa: Allocator,
5248air: Air,
......@@ -343,7 +339,7 @@ pub fn generate(
343339 liveness: Liveness,
344340 code: *std.ArrayList(u8),
345341 debug_output: DebugInfoOutput,
346) GenerateSymbolError!Result {
342) CodeGenError!Result {
347343 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
348344 @panic("Attempted to compile for architecture that was disabled by build configuration");
349345 }
......@@ -721,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
721717 .bitcast => try self.airBitCast(inst),
722718 .block => try self.airBlock(inst),
723719 .br => try self.airBr(inst),
720 .trap => try self.airTrap(),
724721 .breakpoint => try self.airBreakpoint(),
725722 .ret_addr => try self.airRetAddr(inst),
726723 .frame_addr => try self.airFrameAddress(inst),
......@@ -4146,6 +4143,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
41464143 return self.finishAir(inst, result, .{ .none, .none, .none });
41474144}
41484145
4146fn airTrap(self: *Self) !void {
4147 _ = try self.addInst(.{
4148 .tag = .undefined_instruction,
4149 .data = .{ .nop = {} },
4150 });
4151 return self.finishAirBookkeeping();
4152}
4153
41494154fn airBreakpoint(self: *Self) !void {
41504155 _ = try self.addInst(.{
41514156 .tag = .bkpt,
......@@ -6087,178 +6092,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
60876092 }
60886093}
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
61356095fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6136 var typed_value = arg_tv;
6137 if (typed_value.val.castTag(.runtime_value)) |rt| {
6138 typed_value.val = rt.data;
6139 }
6140 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
6141 if (typed_value.val.isUndef())
6142 return MCValue{ .undef = {} };
6143 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6144
6145 if (typed_value.val.castTag(.decl_ref)) |payload| {
6146 return self.lowerDeclRef(typed_value, payload.data);
6147 }
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 }
6096 const mcv: MCValue = switch (try codegen.genTypedValue(
6097 self.bin_file,
6098 self.src_loc,
6099 arg_tv,
6100 self.mod_fn.owner_decl,
6101 )) {
6102 .mcv => |mcv| switch (mcv) {
6103 .none => .none,
6104 .undef => .undef,
6105 .linker_load => unreachable, // TODO
6106 .immediate => |imm| .{ .immediate = @truncate(u32, imm) },
6107 .memory => |addr| .{ .memory = addr },
62206108 },
6221 .ErrorSet => {
6222 switch (typed_value.val.tag()) {
6223 .@"error" => {
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 }
6109 .fail => |msg| {
6110 self.err_msg = msg;
6111 return error.CodegenFail;
62466112 },
6247
6248 .ComptimeInt => unreachable, // semantic analysis prevents this
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);
6113 };
6114 return mcv;
62626115}
62636116
62646117const CallMCValues = struct {
src/arch/arm/Emit.zig+7-2
......@@ -1,4 +1,4 @@
1//! This file contains the functionality for lowering AArch64 MIR into
1//! This file contains the functionality for lowering AArch32 MIR into
22//! machine code
33
44const Emit = @This();
......@@ -15,7 +15,7 @@ const Target = std.Target;
1515const assert = std.debug.assert;
1616const Instruction = bits.Instruction;
1717const Register = bits.Register;
18const log = std.log.scoped(.aarch64_emit);
18const log = std.log.scoped(.aarch32_emit);
1919const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2020const CodeGen = @import("CodeGen.zig");
2121
......@@ -100,6 +100,7 @@ pub fn emitMir(
100100
101101 .b => try emit.mirBranch(inst),
102102
103 .undefined_instruction => try emit.mirUndefinedInstruction(),
103104 .bkpt => try emit.mirExceptionGeneration(inst),
104105
105106 .blx => try emit.mirBranchExchange(inst),
......@@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
494495 }
495496}
496497
498fn mirUndefinedInstruction(emit: *Emit) !void {
499 try emit.writeInstruction(Instruction.undefinedInstruction());
500}
501
497502fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
498503 const tag = emit.mir.instructions.items(.tag)[inst];
499504 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
src/arch/arm/Mir.zig+2
......@@ -35,6 +35,8 @@ pub const Inst = struct {
3535 asr,
3636 /// Branch
3737 b,
38 /// Undefined instruction
39 undefined_instruction,
3840 /// Breakpoint
3941 bkpt,
4042 /// Branch with Link and Exchange
src/arch/arm/bits.zig+11
......@@ -307,6 +307,9 @@ pub const Instruction = union(enum) {
307307 fixed: u4 = 0b1111,
308308 cond: u4,
309309 },
310 undefined_instruction: packed struct {
311 imm32: u32 = 0xe7ffdefe,
312 },
310313 breakpoint: packed struct {
311314 imm4: u4,
312315 fixed_1: u4 = 0b0111,
......@@ -613,6 +616,7 @@ pub const Instruction = union(enum) {
613616 .branch => |v| @bitCast(u32, v),
614617 .branch_exchange => |v| @bitCast(u32, v),
615618 .supervisor_call => |v| @bitCast(u32, v),
619 .undefined_instruction => |v| v.imm32,
616620 .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),
617621 };
618622 }
......@@ -890,6 +894,13 @@ pub const Instruction = union(enum) {
890894 };
891895 }
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
893904 fn breakpoint(imm: u16) Instruction {
894905 return Instruction{
895906 .breakpoint = .{
src/arch/riscv64/CodeGen.zig+32-145
......@@ -21,10 +21,11 @@ const DW = std.dwarf;
2121const leb128 = std.leb;
2222const log = std.log.scoped(.codegen);
2323const build_options = @import("build_options");
24const codegen = @import("../../codegen.zig");
2425
25const Result = @import("../../codegen.zig").Result;
26const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
27const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
26const CodeGenError = codegen.CodeGenError;
27const Result = codegen.Result;
28const DebugInfoOutput = codegen.DebugInfoOutput;
2829
2930const bits = @import("bits.zig");
3031const abi = @import("abi.zig");
......@@ -35,11 +36,7 @@ const Instruction = abi.Instruction;
3536const callee_preserved_regs = abi.callee_preserved_regs;
3637const gp = abi.RegisterClass.gp;
3738
38const InnerError = error{
39 OutOfMemory,
40 CodegenFail,
41 OutOfRegisters,
42};
39const InnerError = CodeGenError || error{OutOfRegisters};
4340
4441gpa: Allocator,
4542air: Air,
......@@ -225,7 +222,7 @@ pub fn generate(
225222 liveness: Liveness,
226223 code: *std.ArrayList(u8),
227224 debug_output: DebugInfoOutput,
228) GenerateSymbolError!Result {
225) CodeGenError!Result {
229226 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
230227 @panic("Attempted to compile for architecture that was disabled by build configuration");
231228 }
......@@ -550,6 +547,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
550547 .bitcast => try self.airBitCast(inst),
551548 .block => try self.airBlock(inst),
552549 .br => try self.airBr(inst),
550 .trap => try self.airTrap(),
553551 .breakpoint => try self.airBreakpoint(),
554552 .ret_addr => try self.airRetAddr(inst),
555553 .frame_addr => try self.airFrameAddress(inst),
......@@ -1652,6 +1650,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
16521650 return self.finishAir(inst, mcv, .{ .none, .none, .none });
16531651}
16541652
1653fn airTrap(self: *Self) !void {
1654 _ = try self.addInst(.{
1655 .tag = .unimp,
1656 .data = .{ .nop = {} },
1657 });
1658 return self.finishAirBookkeeping();
1659}
1660
16551661fn airBreakpoint(self: *Self) !void {
16561662 _ = try self.addInst(.{
16571663 .tag = .ebreak,
......@@ -2552,145 +2558,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
25522558 }
25532559}
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
25802561fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
2581 if (typed_value.val.isUndef())
2582 return MCValue{ .undef = {} };
2583
2584 if (typed_value.val.castTag(.decl_ref)) |payload| {
2585 return self.lowerDeclRef(typed_value, payload.data);
2586 }
2587 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
2588 return self.lowerDeclRef(typed_value, payload.data.decl_index);
2589 }
2590 const target = self.target.*;
2591 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2592 switch (typed_value.ty.zigTypeTag()) {
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", .{});
2562 const mcv: MCValue = switch (try codegen.genTypedValue(
2563 self.bin_file,
2564 self.src_loc,
2565 typed_value,
2566 self.mod_fn.owner_decl,
2567 )) {
2568 .mcv => |mcv| switch (mcv) {
2569 .none => .none,
2570 .undef => .undef,
2571 .linker_load => unreachable, // TODO
2572 .immediate => |imm| .{ .immediate = imm },
2573 .memory => |addr| .{ .memory = addr },
26412574 },
2642 .Enum => {
2643 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
2644 switch (typed_value.ty.tag()) {
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 }
2575 .fail => |msg| {
2576 self.err_msg = msg;
2577 return error.CodegenFail;
26642578 },
2665 .ErrorSet => {
2666 switch (typed_value.val.tag()) {
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 }
2579 };
2580 return mcv;
26942581}
26952582
26962583const CallMCValues = struct {
src/arch/riscv64/Emit.zig+2
......@@ -51,6 +51,7 @@ pub fn emitMir(
5151
5252 .ebreak => try emit.mirSystem(inst),
5353 .ecall => try emit.mirSystem(inst),
54 .unimp => try emit.mirSystem(inst),
5455
5556 .dbg_line => try emit.mirDbgLine(inst),
5657
......@@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
153154 switch (tag) {
154155 .ebreak => try emit.writeInstruction(Instruction.ebreak),
155156 .ecall => try emit.writeInstruction(Instruction.ecall),
157 .unimp => try emit.writeInstruction(Instruction.unimp),
156158 else => unreachable,
157159 }
158160}
src/arch/riscv64/Mir.zig+1
......@@ -32,6 +32,7 @@ pub const Inst = struct {
3232 dbg_epilogue_begin,
3333 /// Pseudo-instruction: Update debug line
3434 dbg_line,
35 unimp,
3536 ebreak,
3637 ecall,
3738 jalr,
src/arch/riscv64/bits.zig+1
......@@ -380,6 +380,7 @@ pub const Instruction = union(enum) {
380380
381381 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
382382 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
383 pub const unimp = iType(0, 0, .zero, .zero, 0);
383384};
384385
385386pub const Register = enum(u6) {
src/arch/sparc64/CodeGen.zig+36-154
......@@ -19,7 +19,7 @@ const Mir = @import("Mir.zig");
1919const Emit = @import("Emit.zig");
2020const Liveness = @import("../../Liveness.zig");
2121const Type = @import("../../type.zig").Type;
22const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
22const CodeGenError = codegen.CodeGenError;
2323const Result = @import("../../codegen.zig").Result;
2424const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2525
......@@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp;
3838
3939const Self = @This();
4040
41const InnerError = error{
42 OutOfMemory,
43 CodegenFail,
44 OutOfRegisters,
45};
41const InnerError = CodeGenError || error{OutOfRegisters};
4642
4743const RegisterView = enum(u1) {
4844 caller,
......@@ -265,7 +261,7 @@ pub fn generate(
265261 liveness: Liveness,
266262 code: *std.ArrayList(u8),
267263 debug_output: DebugInfoOutput,
268) GenerateSymbolError!Result {
264) CodeGenError!Result {
269265 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
270266 @panic("Attempted to compile for architecture that was disabled by build configuration");
271267 }
......@@ -566,6 +562,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
566562 .bitcast => try self.airBitCast(inst),
567563 .block => try self.airBlock(inst),
568564 .br => try self.airBr(inst),
565 .trap => try self.airTrap(),
569566 .breakpoint => try self.airBreakpoint(),
570567 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
571568 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
......@@ -1160,6 +1157,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
11601157 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
11611158}
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
11631175fn airBreakpoint(self: *Self) !void {
11641176 // ta 0x01
11651177 _ = try self.addInst(.{
......@@ -3898,133 +3910,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t
38983910}
38993911
39003912fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
3901 var tv = typed_value;
3902 log.debug("genTypedValue: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
3903
3904 if (tv.val.castTag(.runtime_value)) |rt| {
3905 tv.val = rt.data;
3906 }
3907
3908 if (tv.val.isUndef())
3909 return MCValue{ .undef = {} };
3910
3911 if (tv.val.castTag(.decl_ref)) |payload| {
3912 return self.lowerDeclRef(tv, payload.data);
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 }
3913 const mcv: MCValue = switch (try codegen.genTypedValue(
3914 self.bin_file,
3915 self.src_loc,
3916 typed_value,
3917 self.mod_fn.owner_decl,
3918 )) {
3919 .mcv => |mcv| switch (mcv) {
3920 .none => .none,
3921 .undef => .undef,
3922 .linker_load => unreachable, // TODO
3923 .immediate => |imm| .{ .immediate = imm },
3924 .memory => |addr| .{ .memory = addr },
39493925 },
3950 .Optional => {
3951 if (tv.ty.isPtrLikeOptional()) {
3952 if (tv.val.isNull())
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 }
3926 .fail => |msg| {
3927 self.err_msg = msg;
3928 return error.CodegenFail;
40143929 },
4015 .ComptimeInt => unreachable, // semantic analysis prevents this
4016 .ComptimeFloat => unreachable, // semantic analysis prevents this
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()});
3930 };
3931 return mcv;
40283932}
40293933
40303934fn 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
42004104 }
42014105}
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
42254107fn minMax(
42264108 self: *Self,
42274109 tag: Air.Inst.Tag,
src/arch/wasm/CodeGen.zig+8-3
......@@ -733,8 +733,6 @@ const InnerError = error{
733733 OutOfMemory,
734734 /// An error occurred when trying to lower AIR to MIR.
735735 CodegenFail,
736 /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
737 AnalysisFail,
738736 /// Compiler implementation could not handle a large integer.
739737 Overflow,
740738};
......@@ -1164,7 +1162,7 @@ pub fn generate(
11641162 liveness: Liveness,
11651163 code: *std.ArrayList(u8),
11661164 debug_output: codegen.DebugInfoOutput,
1167) codegen.GenerateSymbolError!codegen.Result {
1165) codegen.CodeGenError!codegen.Result {
11681166 _ = src_loc;
11691167 var code_gen: CodeGen = .{
11701168 .gpa = bin_file.allocator,
......@@ -1829,6 +1827,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18291827 .arg => func.airArg(inst),
18301828 .bitcast => func.airBitcast(inst),
18311829 .block => func.airBlock(inst),
1830 .trap => func.airTrap(inst),
18321831 .breakpoint => func.airBreakpoint(inst),
18331832 .br => func.airBr(inst),
18341833 .bool_to_int => func.airBoolToInt(inst),
......@@ -3289,9 +3288,15 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
32893288 func.finishAir(inst, result, &.{ty_op.operand});
32903289}
32913290
3291fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3292 try func.addTag(.@"unreachable");
3293 func.finishAir(inst, .none, &.{});
3294}
3295
32923296fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
32933297 // unsupported by wasm itfunc. Can be implemented once we support DWARF
32943298 // for wasm
3299 try func.addTag(.@"unreachable");
32953300 func.finishAir(inst, .none, &.{});
32963301}
32973302
src/arch/x86_64/CodeGen.zig+957-1494
......@@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace;
1212
1313const Air = @import("../../Air.zig");
1414const Allocator = mem.Allocator;
15const CodeGenError = codegen.CodeGenError;
1516const Compilation = @import("../../Compilation.zig");
1617const DebugInfoOutput = codegen.DebugInfoOutput;
1718const DW = std.dwarf;
1819const ErrorMsg = Module.ErrorMsg;
1920const Result = codegen.Result;
20const GenerateSymbolError = codegen.GenerateSymbolError;
2121const Emit = @import("Emit.zig");
2222const Liveness = @import("../../Liveness.zig");
2323const Mir = @import("Mir.zig");
......@@ -33,18 +33,16 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
3333const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
3535const Condition = bits.Condition;
36const Immediate = bits.Immediate;
37const Memory = bits.Memory;
38const Register = bits.Register;
3639const RegisterManager = abi.RegisterManager;
3740const RegisterLock = RegisterManager.RegisterLock;
38const Register = bits.Register;
3941
4042const gp = abi.RegisterClass.gp;
4143const sse = abi.RegisterClass.sse;
4244
43const InnerError = error{
44 OutOfMemory,
45 CodegenFail,
46 OutOfRegisters,
47};
45const InnerError = CodeGenError || error{OutOfRegisters};
4846
4947gpa: Allocator,
5048air: Air,
......@@ -186,8 +184,7 @@ const Branch = struct {
186184 _ = options;
187185 comptime assert(unused_format_string.len == 0);
188186 try writer.writeAll("Branch {\n");
189 for (ctx.insts, 0..) |inst, i| {
190 const mcv = ctx.mcvs[i];
187 for (ctx.insts, ctx.mcvs) |inst, mcv| {
191188 try writer.print(" %{d} => {}\n", .{ inst, mcv });
192189 }
193190 try writer.writeAll("}");
......@@ -210,7 +207,7 @@ const Branch = struct {
210207};
211208
212209const StackAllocation = struct {
213 inst: Air.Inst.Index,
210 inst: ?Air.Inst.Index,
214211 /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*)
215212 size: u32,
216213};
......@@ -257,7 +254,7 @@ pub fn generate(
257254 liveness: Liveness,
258255 code: *std.ArrayList(u8),
259256 debug_output: DebugInfoOutput,
260) GenerateSymbolError!Result {
257) CodeGenError!Result {
261258 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
262259 @panic("Attempted to compile for architecture that was disabled by build configuration");
263260 }
......@@ -307,7 +304,12 @@ pub fn generate(
307304 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
308305 error.CodegenFail => return Result{ .fail = function.err_msg.? },
309306 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 ),
311313 },
312314 else => |e| return e,
313315 };
......@@ -346,6 +348,20 @@ pub fn generate(
346348 defer emit.deinit();
347349 emit.lowerMir() catch |err| switch (err) {
348350 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 },
349365 else => |e| return e,
350366 };
351367
......@@ -359,52 +375,263 @@ pub fn generate(
359375fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
360376 const gpa = self.gpa;
361377 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);
363379 self.mir_instructions.appendAssumeCapacity(inst);
364380 return result_index;
365381}
366382
367pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
383fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
368384 const fields = std.meta.fields(@TypeOf(extra));
369385 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
370386 return self.addExtraAssumeCapacity(extra);
371387}
372388
373pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
389fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
374390 const fields = std.meta.fields(@TypeOf(extra));
375391 const result = @intCast(u32, self.mir_extra.items.len);
376392 inline for (fields) |field| {
377393 self.mir_extra.appendAssumeCapacity(switch (field.type) {
378394 u32 => @field(extra, field.name),
379395 i32 => @bitCast(u32, @field(extra, field.name)),
380 else => @compileError("bad field type"),
396 else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
381397 });
382398 }
383399 return result;
384400}
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
386623fn gen(self: *Self) InnerError!void {
387624 const cc = self.fn_type.fnCallingConvention();
388625 if (cc != .Naked) {
389 _ = try self.addInst(.{
390 .tag = .push,
391 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
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 });
626 try self.asmRegister(.push, .rbp);
627 try self.asmRegisterRegister(.mov, .rbp, .rsp);
628
402629 // We want to subtract the aligned stack frame size from rsp here, but we don't
403630 // yet know how big it will be, so we leave room for a 4-byte stack size.
404631 // TODO During semantic analysis, check if there are no function calls. If there
405632 // are none, here we can omit the part where we subtract and then add rsp.
406633 const backpatch_stack_sub = try self.addInst(.{
407 .tag = .nop,
634 .tag = .dead,
408635 .ops = undefined,
409636 .data = undefined,
410637 });
......@@ -431,7 +658,7 @@ fn gen(self: *Self) InnerError!void {
431658
432659 // Push callee-preserved regs that were used actually in use.
433660 const backpatch_push_callee_preserved_regs = try self.addInst(.{
434 .tag = .nop,
661 .tag = .dead,
435662 .ops = undefined,
436663 .data = undefined,
437664 });
......@@ -462,7 +689,7 @@ fn gen(self: *Self) InnerError!void {
462689
463690 // Pop saved callee-preserved regs.
464691 const backpatch_pop_callee_preserved_regs = try self.addInst(.{
465 .tag = .nop,
692 .tag = .dead,
466693 .ops = undefined,
467694 .data = undefined,
468695 });
......@@ -475,22 +702,13 @@ fn gen(self: *Self) InnerError!void {
475702
476703 // Maybe add rsp, x if required. This is backpatched later.
477704 const backpatch_stack_add = try self.addInst(.{
478 .tag = .nop,
705 .tag = .dead,
479706 .ops = undefined,
480707 .data = undefined,
481708 });
482709
483 _ = try self.addInst(.{
484 .tag = .pop,
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 });
710 try self.asmRegister(.pop, .rbp);
711 try self.asmOpOnly(.ret);
494712
495713 // Adjust the stack
496714 if (self.max_end_stack > math.maxInt(i32)) {
......@@ -504,27 +722,34 @@ fn gen(self: *Self) InnerError!void {
504722 if (aligned_stack_end > 0) {
505723 self.mir_instructions.set(backpatch_stack_sub, .{
506724 .tag = .sub,
507 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
508 .data = .{ .imm = aligned_stack_end },
725 .ops = .ri_u,
726 .data = .{ .ri = .{
727 .r1 = .rsp,
728 .imm = aligned_stack_end,
729 } },
509730 });
510731 self.mir_instructions.set(backpatch_stack_add, .{
511732 .tag = .add,
512 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
513 .data = .{ .imm = aligned_stack_end },
733 .ops = .ri_u,
734 .data = .{ .ri = .{
735 .r1 = .rsp,
736 .imm = aligned_stack_end,
737 } },
514738 });
515739
516740 const save_reg_list = try self.addExtra(Mir.SaveRegisterList{
741 .base_reg = @enumToInt(Register.rbp),
517742 .register_list = reg_list.asInt(),
518743 .stack_end = aligned_stack_end,
519744 });
520745 self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{
521746 .tag = .push_regs,
522 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
747 .ops = undefined,
523748 .data = .{ .payload = save_reg_list },
524749 });
525750 self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{
526751 .tag = .pop_regs,
527 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }),
752 .ops = undefined,
528753 .data = .{ .payload = save_reg_list },
529754 });
530755 }
......@@ -638,6 +863,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
638863 .bitcast => try self.airBitCast(inst),
639864 .block => try self.airBlock(inst),
640865 .br => try self.airBr(inst),
866 .trap => try self.airTrap(),
641867 .breakpoint => try self.airBreakpoint(),
642868 .ret_addr => try self.airRetAddr(inst),
643869 .frame_addr => try self.airFrameAddress(inst),
......@@ -811,17 +1037,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
8111037fn processDeath(self: *Self, inst: Air.Inst.Index) void {
8121038 const air_tags = self.air.instructions.items(.tag);
8131039 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 });
8151042 // When editing this function, note that the logic must synchronize with `reuseOperand`.
816 const prev_value = self.getResolvedInstValue(inst);
8171043 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
8181044 branch.inst_table.putAssumeCapacity(inst, .dead);
8191045 switch (prev_value) {
8201046 .register => |reg| {
821 self.register_manager.freeReg(reg.to64());
1047 self.register_manager.freeReg(reg);
8221048 },
8231049 .register_overflow => |ro| {
824 self.register_manager.freeReg(ro.reg.to64());
1050 self.register_manager.freeReg(ro.reg);
8251051 self.eflags_inst = null;
8261052 },
8271053 .eflags => {
......@@ -882,7 +1108,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
8821108 try table.ensureUnusedCapacity(self.gpa, additional_count);
8831109}
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 {
8861112 if (abi_align > self.stack_align)
8871113 self.stack_align = abi_align;
8881114 // TODO find a free slot instead of always appending
......@@ -915,7 +1141,14 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
9151141}
9161142
9171143fn 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 {
9191152 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {
9201153 const mod = self.bin_file.options.module.?;
9211154 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 {
9921225pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
9931226 const stack_mcv = try self.allocRegOrMem(inst, false);
9941227 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).?;
9961229 switch (reg_mcv) {
9971230 .register => |other| {
9981231 assert(reg.to64() == other.to64());
......@@ -1009,7 +1242,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
10091242
10101243pub fn spillEflagsIfOccupied(self: *Self) !void {
10111244 if (self.eflags_inst) |inst_to_save| {
1012 const mcv = self.getResolvedInstValue(inst_to_save);
1245 const mcv = self.getResolvedInstValue(inst_to_save).?;
10131246 const new_mcv = switch (mcv) {
10141247 .register_overflow => try self.allocRegOrMem(inst_to_save, false),
10151248 .eflags => try self.allocRegOrMem(inst_to_save, true),
......@@ -1219,7 +1452,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
12191452 };
12201453 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 };
12231462 try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask });
12241463
12251464 break :result dst_mcv;
......@@ -1266,14 +1505,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void {
12661505 .unsigned => .b,
12671506 .signed => .l,
12681507 };
1269 _ = try self.addInst(.{
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 });
1508 try self.asmCmovccRegisterRegister(dst_mcv.register, lhs_reg, cc);
12771509
12781510 break :result dst_mcv;
12791511 };
......@@ -1473,13 +1705,7 @@ fn genSetStackTruncatedOverflowCompare(
14731705 .signed => .o,
14741706 .unsigned => .c,
14751707 };
1476 _ = try self.addInst(.{
1477 .tag = .cond_set_byte,
1478 .ops = Mir.Inst.Ops.encode(.{
1479 .reg1 = overflow_reg.to8(),
1480 }),
1481 .data = .{ .cc = cc },
1482 });
1708 try self.asmSetccRegister(overflow_reg.to8(), cc);
14831709
14841710 const scratch_reg = temp_regs[1];
14851711 try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg });
......@@ -1492,12 +1718,7 @@ fn genSetStackTruncatedOverflowCompare(
14921718 );
14931719
14941720 const eq_reg = temp_regs[2];
1495 _ = try self.addInst(.{
1496 .tag = .cond_set_byte,
1497 .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }),
1498 .data = .{ .cc = .ne },
1499 });
1500
1721 try self.asmSetccRegister(eq_reg.to8(), .ne);
15011722 try self.genBinOpMir(
15021723 .@"or",
15031724 Type.u8,
......@@ -1641,23 +1862,8 @@ fn genIntMulDivOpMir(
16411862 }
16421863
16431864 switch (signedness) {
1644 .signed => {
1645 _ = try self.addInst(.{
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 },
1865 .signed => try self.asmOpOnly(.cqo),
1866 .unsigned => try self.asmRegisterRegister(.xor, .rdx, .rdx),
16611867 }
16621868
16631869 const factor = switch (rhs) {
......@@ -1670,29 +1876,11 @@ fn genIntMulDivOpMir(
16701876 };
16711877
16721878 switch (factor) {
1673 .register => |reg| {
1674 _ = try self.addInst(.{
1675 .tag = tag,
1676 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
1677 .data = undefined,
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 },
1879 .register => |reg| try self.asmRegister(tag, reg),
1880 .stack_offset => |off| try self.asmMemory(tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
1881 .base = .rbp,
1882 .disp = -off,
1883 })),
16961884 else => unreachable,
16971885 }
16981886}
......@@ -1720,38 +1908,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
17201908 .unsigned => .div,
17211909 }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor });
17221910
1723 _ = try self.addInst(.{
1724 .tag = .xor,
1725 .ops = Mir.Inst.Ops.encode(.{
1726 .reg1 = divisor.to64(),
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 });
1911 try self.asmRegisterRegister(.xor, divisor.to64(), dividend.to64());
1912 try self.asmRegisterImmediate(.sar, divisor.to64(), Immediate.u(63));
1913 try self.asmRegisterRegister(.@"test", .rdx, .rdx);
1914 try self.asmCmovccRegisterRegister(divisor.to64(), .rdx, .e);
17551915 try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });
17561916 return MCValue{ .register = divisor };
17571917}
......@@ -2185,18 +2345,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
21852345
21862346 const addr_reg = try self.register_manager.allocReg(null, gp);
21872347 switch (slice_mcv) {
2188 .stack_offset => |off| {
2189 // mov reg, [rbp - 8]
2190 _ = try self.addInst(.{
2191 .tag = .mov,
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 },
2348 .stack_offset => |off| try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
2349 .base = .rbp,
2350 .disp = -off,
2351 })),
22002352 else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}),
22012353 }
22022354 // 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 {
22712423 array_ty.abiAlignment(self.target.*),
22722424 ));
22732425 try self.genSetStack(array_ty, off, array, .{});
2274 // lea reg, [rbp]
2275 _ = try self.addInst(.{
2276 .tag = .lea,
2277 .ops = Mir.Inst.Ops.encode(.{
2278 .reg1 = addr_reg.to64(),
2279 .reg2 = .rbp,
2280 }),
2281 .data = .{ .imm = @bitCast(u32, -off) },
2282 });
2426 try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{
2427 .base = .rbp,
2428 .disp = -off,
2429 }));
22832430 },
22842431 .stack_offset => |off| {
2285 // lea reg, [rbp]
2286 _ = try self.addInst(.{
2287 .tag = .lea,
2288 .ops = Mir.Inst.Ops.encode(.{
2289 .reg1 = addr_reg.to64(),
2290 .reg2 = .rbp,
2291 }),
2292 .data = .{ .imm = @bitCast(u32, -off) },
2293 });
2432 try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{
2433 .base = .rbp,
2434 .disp = -off,
2435 }));
22942436 },
22952437 .memory, .linker_load => {
22962438 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);
......@@ -2327,7 +2469,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
23272469 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
23282470
23292471 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.*));
23312473 const index_ty = self.air.typeOf(bin_op.rhs);
23322474 const index = try self.resolveInst(bin_op.rhs);
23332475 const index_lock: ?RegisterLock = switch (index) {
......@@ -2347,16 +2489,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
23472489 if (elem_abi_size > 8) {
23482490 return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size});
23492491 } else {
2350 // mov dst_mcv, [dst_mcv]
2351 _ = try self.addInst(.{
2352 .tag = .mov,
2353 .ops = Mir.Inst.Ops.encode(.{
2354 .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)),
2355 .reg2 = dst_mcv.register,
2356 .flags = 0b01,
2492 try self.asmRegisterMemory(
2493 .mov,
2494 registerAlias(dst_mcv.register, elem_abi_size),
2495 Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{
2496 .base = dst_mcv.register,
2497 .disp = 0,
23572498 }),
2358 .data = .{ .imm = 0 },
2359 });
2499 );
23602500 break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) };
23612501 }
23622502 };
......@@ -2583,7 +2723,7 @@ fn reuseOperand(
25832723
25842724fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
25852725 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.*));
25872727 switch (ptr) {
25882728 .none => unreachable,
25892729 .undef => unreachable,
......@@ -2610,16 +2750,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
26102750 .undef => unreachable,
26112751 .eflags => unreachable,
26122752 .register => |dst_reg| {
2613 // mov dst_reg, [reg]
2614 _ = try self.addInst(.{
2615 .tag = .mov,
2616 .ops = Mir.Inst.Ops.encode(.{
2617 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
2618 .reg2 = reg,
2619 .flags = 0b01,
2620 }),
2621 .data = .{ .imm = 0 },
2622 });
2753 try self.asmRegisterMemory(
2754 .mov,
2755 registerAlias(dst_reg, abi_size),
2756 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg, .disp = 0 }),
2757 );
26232758 },
26242759 .stack_offset => |off| {
26252760 if (abi_size <= 8) {
......@@ -2679,23 +2814,19 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26792814 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
26802815 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
26812816 } else unreachable;
2682 const flags: u2 = switch (load_struct.type) {
2683 .got => 0b00,
2684 .direct => 0b01,
2685 .import => 0b10,
2817 const ops: Mir.Inst.Ops = switch (load_struct.type) {
2818 .got => .got_reloc,
2819 .direct => .direct_reloc,
2820 .import => .import_reloc,
26862821 };
26872822 _ = try self.addInst(.{
2688 .tag = .lea_pic,
2689 .ops = Mir.Inst.Ops.encode(.{
2690 .reg1 = registerAlias(reg, abi_size),
2691 .flags = flags,
2692 }),
2693 .data = .{
2694 .relocation = .{
2695 .atom_index = atom_index,
2696 .sym_index = load_struct.sym_index,
2697 },
2698 },
2823 .tag = .lea_linker,
2824 .ops = ops,
2825 .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
2826 .reg = @enumToInt(registerAlias(reg, abi_size)),
2827 .atom_index = atom_index,
2828 .sym_index = load_struct.sym_index,
2829 }) },
26992830 });
27002831 },
27012832 .memory => |addr| {
......@@ -2708,7 +2839,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
27082839}
27092840
27102841fn 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.*));
27122843 switch (ptr) {
27132844 .none => unreachable,
27142845 .undef => unreachable,
......@@ -2736,30 +2867,25 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27362867 .unreach => unreachable,
27372868 .eflags => unreachable,
27382869 .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 }
27402877 },
27412878 .immediate => |imm| {
27422879 switch (abi_size) {
27432880 1, 2, 4 => {
2744 // TODO this is wasteful!
2745 // introduce new MIR tag specifically for mov [reg + 0], imm
2746 const payload = try self.addExtra(Mir.ImmPair{
2747 .dest_off = 0,
2748 .operand = @truncate(u32, imm),
2749 });
2750 _ = try self.addInst(.{
2751 .tag = .mov_mem_imm,
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 });
2881 const immediate = if (value_ty.isSignedInt())
2882 Immediate.s(@intCast(i32, @bitCast(i64, imm)))
2883 else
2884 Immediate.u(@truncate(u32, imm));
2885 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
2886 .base = reg.to64(),
2887 .disp = 0,
2888 }), immediate);
27632889 },
27642890 8 => {
27652891 // 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
27892915 const overflow_bit_ty = value_ty.structFieldType(1);
27902916 const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*);
27912917 const tmp_reg = try self.register_manager.allocReg(null, gp);
2792 _ = try self.addInst(.{
2793 .tag = .cond_set_byte,
2794 .ops = Mir.Inst.Ops.encode(.{
2795 .reg1 = tmp_reg.to8(),
2796 }),
2797 .data = .{ .cc = ro.eflags },
2798 });
2918 try self.asmSetccRegister(tmp_reg.to8(), ro.eflags);
27992919 try self.genInlineMemcpyRegisterRegister(
28002920 overflow_bit_ty,
28012921 reg,
......@@ -2836,17 +2956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28362956
28372957 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 GOT
2840 // mov reg, [reg]
2841 _ = try self.addInst(.{
2842 .tag = .mov,
2843 .ops = Mir.Inst.Ops.encode(.{
2844 .reg1 = addr_reg.to64(),
2845 .reg2 = addr_reg.to64(),
2846 .flags = 0b01,
2847 }),
2848 .data = .{ .imm = 0 },
2849 });
2959 // To get the actual address of the value we want to modify we have to go through the GOT
2960 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
2961 .base = addr_reg.to64(),
2962 .disp = 0,
2963 }));
28502964
28512965 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
28562970 return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size});
28572971 }
28582972
2859 const payload = try self.addExtra(Mir.ImmPair{
2860 .dest_off = 0,
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) {
2973 if (abi_size == 8) {
2974 // TODO
28722975 const top_bits: u32 = @intCast(u32, imm >> 32);
28732976 const can_extend = if (value_ty.isUnsignedInt())
28742977 (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
28792982 return self.fail("TODO imm64 would get incorrectly sign extended", .{});
28802983 }
28812984 }
2882 _ = try self.addInst(.{
2883 .tag = .mov_mem_imm,
2884 .ops = Mir.Inst.Ops.encode(.{
2885 .reg1 = addr_reg.to64(),
2886 .flags = flags,
2887 }),
2888 .data = .{ .payload = payload },
2889 });
2985 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
2986 .base = addr_reg.to64(),
2987 .disp = 0,
2988 }), Immediate.u(@intCast(u32, imm)));
28902989 },
28912990 .register => {
28922991 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
28982997 defer self.register_manager.unlockReg(tmp_reg_lock);
28992998
29002999 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 });
29113005 return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
29123006 }
29133007
......@@ -2959,6 +3053,9 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
29593053 const mcv = try self.resolveInst(operand);
29603054 const ptr_ty = self.air.typeOf(operand);
29613055 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 }
29623059 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
29633060
29643061 const dst_mcv: MCValue = result: {
......@@ -3022,6 +3119,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30223119
30233120 const mcv = try self.resolveInst(operand);
30243121 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 }
30253125 const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*);
30263126 const struct_field_ty = struct_ty.structFieldType(index);
30273127
......@@ -3055,8 +3155,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30553155 const shift = @intCast(u8, struct_field_offset * @sizeOf(usize));
30563156 try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift });
30573157
3058 // Mask with reg.size() - struct_field_size
3059 const max_reg_bit_width = Register.rax.size();
3158 // Mask with reg.bitSize() - struct_field_size
3159 const max_reg_bit_width = Register.rax.bitSize();
30603160 const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*)));
30613161 const mask = (~@as(u64, 0)) >> mask_shift;
30623162
......@@ -3069,14 +3169,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30693169 };
30703170 const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
30713171 if (signedness == .signed and field_size < 8) {
3072 _ = try self.addInst(.{
3073 .tag = .mov_sign_extend,
3074 .ops = Mir.Inst.Ops.encode(.{
3075 .reg1 = dst_mcv.register,
3076 .reg2 = registerAlias(dst_mcv.register, field_size),
3077 }),
3078 .data = undefined,
3079 });
3172 try self.asmRegisterRegister(
3173 .movsx,
3174 dst_mcv.register,
3175 registerAlias(dst_mcv.register, field_size),
3176 );
30803177 }
30813178
30823179 break :result dst_mcv;
......@@ -3093,13 +3190,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
30933190 defer self.register_manager.unlockReg(reg_lock);
30943191
30953192 const dst_reg = try self.register_manager.allocReg(inst, gp);
3096 _ = try self.addInst(.{
3097 .tag = .cond_set_byte,
3098 .ops = Mir.Inst.Ops.encode(.{
3099 .reg1 = dst_reg.to8(),
3100 }),
3101 .data = .{ .cc = ro.eflags },
3102 });
3193 try self.asmSetccRegister(dst_reg.to8(), ro.eflags);
31033194 break :result MCValue{ .register = dst_reg.to8() };
31043195 },
31053196 else => unreachable,
......@@ -3135,25 +3226,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
31353226 switch (shift) {
31363227 .immediate => |imm| switch (imm) {
31373228 0 => return,
3138 1 => {
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 },
3229 else => return self.asmRegisterImmediate(tag, registerAlias(reg, abi_size), Immediate.u(imm)),
31573230 },
31583231 .register => |shift_reg| {
31593232 if (shift_reg == .rcx) break :blk;
......@@ -3165,14 +3238,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi
31653238 try self.genSetReg(Type.u8, .rcx, shift);
31663239 }
31673240
3168 _ = try self.addInst(.{
3169 .tag = tag,
3170 .ops = Mir.Inst.Ops.encode(.{
3171 .reg1 = registerAlias(reg, abi_size),
3172 .flags = 0b01,
3173 }),
3174 .data = undefined,
3175 });
3241 try self.asmRegisterRegister(tag, registerAlias(reg, abi_size), .cl);
31763242}
31773243
31783244/// Result is always a register.
......@@ -3545,59 +3611,67 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
35453611 if (intrinsicsAllowed(self.target.*, dst_ty)) {
35463612 const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
35473613 .f32 => switch (mir_tag) {
3548 .add => if (hasAvxSupport(self.target.*))
3549 Mir.Inst.Tag.add_f32_avx
3550 else
3551 Mir.Inst.Tag.add_f32_sse,
3552 .cmp => if (hasAvxSupport(self.target.*))
3553 Mir.Inst.Tag.cmp_f32_avx
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}),
3614 .add => .addss,
3615 .cmp => .ucomiss,
3616 else => return self.fail(
3617 "TODO genBinOpMir for f32 register-register with MIR tag {}",
3618 .{mir_tag},
3619 ),
35573620 },
35583621 .f64 => switch (mir_tag) {
3559 .add => if (hasAvxSupport(self.target.*))
3560 Mir.Inst.Tag.add_f64_avx
3561 else
3562 Mir.Inst.Tag.add_f64_sse,
3563 .cmp => if (hasAvxSupport(self.target.*))
3564 Mir.Inst.Tag.cmp_f64_avx
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}),
3622 .add => .addsd,
3623 .cmp => .ucomisd,
3624 else => return self.fail(
3625 "TODO genBinOpMir for f64 register-register with MIR tag {}",
3626 .{mir_tag},
3627 ),
35683628 },
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 ),
35703633 };
3571 _ = try self.addInst(.{
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;
3634 return self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128());
35803635 }
35813636
35823637 return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{});
35833638 },
3584 else => {
3585 _ = try self.addInst(.{
3586 .tag = mir_tag,
3587 .ops = Mir.Inst.Ops.encode(.{
3588 .reg1 = registerAlias(dst_reg, abi_size),
3589 .reg2 = registerAlias(src_reg, abi_size),
3590 }),
3591 .data = undefined,
3592 });
3593 },
3639 else => try self.asmRegisterRegister(
3640 mir_tag,
3641 registerAlias(dst_reg, abi_size),
3642 registerAlias(src_reg, abi_size),
3643 ),
35943644 },
35953645 .immediate => |imm| {
3596 _ = try self.addInst(.{
3597 .tag = mir_tag,
3598 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }),
3599 .data = .{ .imm = @truncate(u32, imm) },
3600 });
3646 switch (abi_size) {
3647 0 => unreachable,
3648 1...4 => {
3649 try self.asmRegisterImmediate(
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 }
36013675 },
36023676 .memory,
36033677 .linker_load,
......@@ -3614,15 +3688,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36143688 if (off > math.maxInt(i32)) {
36153689 return self.fail("stack offset too large", .{});
36163690 }
3617 _ = try self.addInst(.{
3618 .tag = mir_tag,
3619 .ops = Mir.Inst.Ops.encode(.{
3620 .reg1 = registerAlias(dst_reg, abi_size),
3621 .reg2 = .rbp,
3622 .flags = 0b01,
3623 }),
3624 .data = .{ .imm = @bitCast(u32, -off) },
3625 });
3691 try self.asmRegisterMemory(
3692 mir_tag,
3693 registerAlias(dst_reg, abi_size),
3694 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
3695 );
36263696 },
36273697 }
36283698 },
......@@ -3640,45 +3710,17 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36403710 .dead, .unreach => unreachable,
36413711 .register_overflow => unreachable,
36423712 .register => |src_reg| {
3643 _ = try self.addInst(.{
3644 .tag = mir_tag,
3645 .ops = Mir.Inst.Ops.encode(.{
3646 .reg1 = .rbp,
3647 .reg2 = registerAlias(src_reg, abi_size),
3648 .flags = 0b10,
3649 }),
3650 .data = .{ .imm = @bitCast(u32, -off) },
3651 });
3713 try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
3714 .base = .rbp,
3715 .disp = -off,
3716 }), registerAlias(src_reg, abi_size));
36523717 },
36533718 .immediate => |imm| {
3654 const tag: Mir.Inst.Tag = switch (mir_tag) {
3655 .add => .add_mem_imm,
3656 .@"or" => .or_mem_imm,
3657 .@"and" => .and_mem_imm,
3658 .sub => .sub_mem_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 });
3719 // TODO
3720 try self.asmMemoryImmediate(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
3721 .base = .rbp,
3722 .disp = -off,
3723 }), Immediate.u(@intCast(u32, imm)));
36823724 },
36833725 .memory,
36843726 .stack_offset,
......@@ -3721,30 +3763,21 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37213763 .dead, .unreach => unreachable,
37223764 .ptr_stack_offset => unreachable,
37233765 .register_overflow => unreachable,
3724 .register => |src_reg| {
3725 // register, register
3726 _ = try self.addInst(.{
3727 .tag = .imul_complex,
3728 .ops = Mir.Inst.Ops.encode(.{
3729 .reg1 = registerAlias(dst_reg, abi_size),
3730 .reg2 = registerAlias(src_reg, abi_size),
3731 }),
3732 .data = undefined,
3733 });
3734 },
3766 .register => |src_reg| try self.asmRegisterRegister(
3767 .imul,
3768 registerAlias(dst_reg, abi_size),
3769 registerAlias(src_reg, abi_size),
3770 ),
37353771 .immediate => |imm| {
3736 // TODO take into account the type's ABI size when selecting the register alias
3737 // register, immediate
37383772 if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {
3739 _ = try self.addInst(.{
3740 .tag = .imul_complex,
3741 .ops = Mir.Inst.Ops.encode(.{
3742 .reg1 = dst_reg.to32(),
3743 .reg2 = dst_reg.to32(),
3744 .flags = 0b10,
3745 }),
3746 .data = .{ .imm = @truncate(u32, imm) },
3747 });
3773 // TODO take into account the type's ABI size when selecting the register alias
3774 // register, immediate
3775 try self.asmRegisterRegisterImmediate(
3776 .imul,
3777 dst_reg.to32(),
3778 dst_reg.to32(),
3779 Immediate.u(@intCast(u32, imm)),
3780 );
37483781 } else {
37493782 // TODO verify we don't spill and assign to the same register as dst_mcv
37503783 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
37523785 }
37533786 },
37543787 .stack_offset => |off| {
3755 _ = try self.addInst(.{
3756 .tag = .imul_complex,
3757 .ops = Mir.Inst.Ops.encode(.{
3758 .reg1 = registerAlias(dst_reg, abi_size),
3759 .reg2 = .rbp,
3760 .flags = 0b01,
3761 }),
3762 .data = .{ .imm = @bitCast(u32, -off) },
3763 });
3788 try self.asmRegisterMemory(
3789 .imul,
3790 registerAlias(dst_reg, abi_size),
3791 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
3792 );
37643793 },
37653794 .memory => {
37663795 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
37833812 .register => |src_reg| {
37843813 // copy dst to a register
37853814 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
3786 // multiply into dst_reg
3787 // register, register
3788 _ = try self.addInst(.{
3789 .tag = .imul_complex,
3790 .ops = Mir.Inst.Ops.encode(.{
3791 .reg1 = registerAlias(dst_reg, abi_size),
3792 .reg2 = registerAlias(src_reg, abi_size),
3793 }),
3794 .data = undefined,
3795 });
3815 try self.asmRegisterRegister(
3816 .imul,
3817 registerAlias(dst_reg, abi_size),
3818 registerAlias(src_reg, abi_size),
3819 );
37963820 // copy dst_reg back out
37973821 return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{});
37983822 },
......@@ -3917,12 +3941,13 @@ fn genVarDbgInfo(
39173941 }
39183942}
39193943
3944fn airTrap(self: *Self) !void {
3945 try self.asmOpOnly(.ud2);
3946 return self.finishAirBookkeeping();
3947}
3948
39203949fn airBreakpoint(self: *Self) !void {
3921 _ = try self.addInst(.{
3922 .tag = .interrupt,
3923 .ops = Mir.Inst.Ops.encode(.{}),
3924 .data = undefined,
3925 });
3950 try self.asmOpOnly(.int3);
39263951 return self.finishAirBookkeeping();
39273952}
39283953
......@@ -3985,10 +4010,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
39854010 };
39864011 defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock);
39874012
3988 for (args, 0..) |arg, arg_i| {
3989 const mc_arg = info.args[arg_i];
4013 for (args, info.args) |arg, info_arg| {
4014 const mc_arg = info_arg;
39904015 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);
39924017 // Here we do not use setRegOrMem even though the logic is similar, because
39934018 // the function call will move the stack pointer, so the offsets are different.
39944019 switch (mc_arg) {
......@@ -4017,11 +4042,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40174042
40184043 if (info.stack_byte_count > 0) {
40194044 // Adjust the stack
4020 _ = try self.addInst(.{
4021 .tag = .sub,
4022 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
4023 .data = .{ .imm = info.stack_byte_count },
4024 });
4045 try self.asmRegisterImmediate(.sub, .rsp, Immediate.u(info.stack_byte_count));
40254046 }
40264047
40274048 // 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
40344055 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
40354056 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
40364057 const atom = elf_file.getAtom(atom_index);
4037 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4038 _ = try self.addInst(.{
4039 .tag = .call,
4040 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4041 .data = .{ .imm = got_addr },
4042 });
4058 const got_addr = atom.getOffsetTableAddress(elf_file);
4059 try self.asmMemory(.call, Memory.sib(.qword, .{
4060 .base = .ds,
4061 .disp = @intCast(i32, got_addr),
4062 }));
40434063 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
40444064 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
40454065 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
40494069 .sym_index = sym_index,
40504070 },
40514071 });
4052 _ = try self.addInst(.{
4053 .tag = .call,
4054 .ops = Mir.Inst.Ops.encode(.{
4055 .reg1 = .rax,
4056 .flags = 0b01,
4057 }),
4058 .data = undefined,
4059 });
4072 try self.asmRegister(.call, .rax);
40604073 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
40614074 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
40624075 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
40664079 .sym_index = sym_index,
40674080 },
40684081 });
4069 _ = try self.addInst(.{
4070 .tag = .call,
4071 .ops = Mir.Inst.Ops.encode(.{
4072 .reg1 = .rax,
4073 .flags = 0b01,
4074 }),
4075 .data = undefined,
4076 });
4082 try self.asmRegister(.call, .rax);
40774083 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
40784084 const decl_block_index = try p9.seeDecl(func.owner_decl);
40794085 const decl_block = p9.getDeclBlock(decl_block_index);
......@@ -4082,11 +4088,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40824088 const got_addr = p9.bases.data;
40834089 const got_index = decl_block.got_index.?;
40844090 const fn_got_addr = got_addr + got_index * ptr_bytes;
4085 _ = try self.addInst(.{
4086 .tag = .call,
4087 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4088 .data = .{ .imm = @intCast(u32, fn_got_addr) },
4089 });
4091 try self.asmMemory(.call, Memory.sib(.qword, .{
4092 .base = .ds,
4093 .disp = @intCast(i32, fn_got_addr),
4094 }));
40904095 } else unreachable;
40914096 } else if (func_value.castTag(.extern_fn)) |func_payload| {
40924097 const extern_fn = func_payload.data;
......@@ -4106,14 +4111,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
41064111 .sym_index = sym_index,
41074112 },
41084113 });
4109 _ = try self.addInst(.{
4110 .tag = .call,
4111 .ops = Mir.Inst.Ops.encode(.{
4112 .reg1 = .rax,
4113 .flags = 0b01,
4114 }),
4115 .data = undefined,
4116 });
4114 try self.asmRegister(.call, .rax);
41174115 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
41184116 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
41194117 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
41364134 assert(ty.zigTypeTag() == .Pointer);
41374135 const mcv = try self.resolveInst(callee);
41384136 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4139 _ = try self.addInst(.{
4140 .tag = .call,
4141 .ops = Mir.Inst.Ops.encode(.{
4142 .reg1 = .rax,
4143 .flags = 0b01,
4144 }),
4145 .data = undefined,
4146 });
4137 try self.asmRegister(.call, .rax);
41474138 }
41484139
41494140 if (info.stack_byte_count > 0) {
41504141 // Readjust the stack
4151 _ = try self.addInst(.{
4152 .tag = .add,
4153 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }),
4154 .data = .{ .imm = info.stack_byte_count },
4155 });
4142 try self.asmRegisterImmediate(.add, .rsp, Immediate.u(info.stack_byte_count));
41564143 }
41574144
41584145 const result: MCValue = result: {
......@@ -4209,11 +4196,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
42094196 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
42104197 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
42114198 // which is available if the jump is 127 bytes or less forward.
4212 const jmp_reloc = try self.addInst(.{
4213 .tag = .jmp,
4214 .ops = Mir.Inst.Ops.encode(.{}),
4215 .data = .{ .inst = undefined },
4216 });
4199 const jmp_reloc = try self.asmJmpReloc(undefined);
42174200 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
42184201 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
42194202}
......@@ -4245,11 +4228,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
42454228 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
42464229 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
42474230 // which is available if the jump is 127 bytes or less forward.
4248 const jmp_reloc = try self.addInst(.{
4249 .tag = .jmp,
4250 .ops = Mir.Inst.Ops.encode(.{}),
4251 .data = .{ .inst = undefined },
4252 });
4231 const jmp_reloc = try self.asmJmpReloc(undefined);
42534232 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
42544233 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
42554234}
......@@ -4424,33 +4403,13 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
44244403 const abi_size = ty.abiSize(self.target.*);
44254404 switch (mcv) {
44264405 .eflags => |cc| {
4427 return self.addInst(.{
4428 .tag = .cond_jmp,
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 });
4406 // Here we map the opposites since the jump is to the false branch.
4407 return self.asmJccReloc(undefined, cc.negate());
44384408 },
44394409 .register => |reg| {
44404410 try self.spillEflagsIfOccupied();
4441 _ = try self.addInst(.{
4442 .tag = .@"test",
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 });
4411 try self.asmRegisterImmediate(.@"test", reg, Immediate.u(1));
4412 return self.asmJccReloc(undefined, .e);
44544413 },
44554414 .immediate,
44564415 .stack_offset,
......@@ -4464,6 +4423,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
44644423 },
44654424 else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}),
44664425 }
4426 return 0; // TODO
44674427}
44684428
44694429fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
......@@ -4491,16 +4451,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
44914451 // Capture the state of register and stack allocation state so that we can revert to it.
44924452 const saved_state = try self.captureState();
44934453
4494 try self.branch_stack.append(.{});
4495 errdefer {
4496 _ = self.branch_stack.pop();
4497 }
4454 {
4455 try self.branch_stack.append(.{});
4456 errdefer _ = self.branch_stack.pop();
44984457
4499 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
4500 for (liveness_condbr.then_deaths) |operand| {
4501 self.processDeath(operand);
4458 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
4459 for (liveness_condbr.then_deaths) |operand| {
4460 self.processDeath(operand);
4461 }
4462 try self.genBody(then_body);
45024463 }
4503 try self.genBody(then_body);
45044464
45054465 // Revert to the previous register and stack allocation state.
45064466
......@@ -4511,16 +4471,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
45114471
45124472 try self.performReloc(reloc);
45134473
4514 try self.branch_stack.append(.{});
4515 errdefer {
4516 _ = self.branch_stack.pop();
4517 }
4474 {
4475 try self.branch_stack.append(.{});
4476 errdefer _ = self.branch_stack.pop();
45184477
4519 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
4520 for (liveness_condbr.else_deaths) |operand| {
4521 self.processDeath(operand);
4478 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
4479 for (liveness_condbr.else_deaths) |operand| {
4480 self.processDeath(operand);
4481 }
4482 try self.genBody(else_body);
45224483 }
4523 try self.genBody(else_body);
45244484
45254485 var else_branch = self.branch_stack.pop();
45264486 defer else_branch.deinit(self.gpa);
......@@ -4646,18 +4606,16 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
46464606 };
46474607 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 };
46574609 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);
46584616 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
46624620 return self.finishAir(inst, result, .{ un_op, .none, .none });
46634621}
......@@ -4686,15 +4644,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
46864644 };
46874645 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 };
46974647 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);
46984654 try self.load(operand, operand_ptr, ptr_ty);
46994655
47004656 const result = try self.isNonNull(inst, ptr_ty.elemType(), operand);
......@@ -4789,11 +4745,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
47894745 const body = self.air.extra[loop.end..][0..loop.data.body_len];
47904746 const jmp_target = @intCast(u32, self.mir_instructions.len);
47914747 try self.genBody(body);
4792 _ = try self.addInst(.{
4793 .tag = .jmp,
4794 .ops = Mir.Inst.Ops.encode(.{}),
4795 .data = .{ .inst = jmp_target },
4796 });
4748 _ = try self.asmJmpReloc(jmp_target);
47974749 return self.finishAirBookkeeping();
47984750}
47994751
......@@ -4806,7 +4758,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
48064758 // break instruction will choose a MCValue for the block result and overwrite
48074759 // this field. Following break instructions will use that MCValue to put their
48084760 // block results.
4809 .mcv = MCValue{ .none = {} },
4761 .mcv = .none,
48104762 });
48114763 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
48384790 .none => unreachable,
48394791 .undef => unreachable,
48404792 .dead, .unreach => unreachable,
4841 .immediate => |imm| {
4842 _ = try self.addInst(.{
4843 .tag = .xor,
4844 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }),
4845 .data = .{ .imm = @intCast(u32, imm) },
4846 });
4847 },
4848 .register => |reg| {
4849 _ = try self.addInst(.{
4850 .tag = .xor,
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 },
4793 .immediate => |imm| try self.asmRegisterImmediate(
4794 .xor,
4795 registerAlias(cond_reg, abi_size),
4796 Immediate.u(imm),
4797 ),
4798 .register => |reg| try self.asmRegisterRegister(
4799 .xor,
4800 registerAlias(cond_reg, abi_size),
4801 registerAlias(reg, abi_size),
4802 ),
48584803 .stack_offset => {
48594804 if (abi_size <= 8) {
48604805 const reg = try self.copyToTmpRegister(ty, case);
......@@ -4868,22 +4813,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
48684813 },
48694814 }
48704815
4871 _ = try self.addInst(.{
4872 .tag = .@"test",
4873 .ops = Mir.Inst.Ops.encode(.{
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 });
4816 const aliased_reg = registerAlias(cond_reg, abi_size);
4817 try self.asmRegisterRegister(.@"test", aliased_reg, aliased_reg);
4818 return self.asmJccReloc(undefined, .ne);
48874819 },
48884820 .stack_offset => {
48894821 try self.spillEflagsIfOccupied();
......@@ -4901,6 +4833,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
49014833 return self.fail("TODO implemenent switch mir when condition is {}", .{condition});
49024834 },
49034835 }
4836 return 0; // TODO
49044837}
49054838
49064839fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
......@@ -4946,25 +4879,25 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
49464879 var relocs = try self.gpa.alloc(u32, items.len);
49474880 defer self.gpa.free(relocs);
49484881
4949 for (items, 0..) |item, item_i| {
4882 for (items, relocs) |item, *reloc| {
49504883 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);
49524885 }
49534886
49544887 // Capture the state of register and stack allocation state so that we can revert to it.
49554888 const saved_state = try self.captureState();
49564889
4957 try self.branch_stack.append(.{});
4958 errdefer {
4959 _ = self.branch_stack.pop();
4960 }
4890 {
4891 try self.branch_stack.append(.{});
4892 errdefer _ = self.branch_stack.pop();
49614893
4962 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
4963 for (liveness.deaths[case_i]) |operand| {
4964 self.processDeath(operand);
4965 }
4894 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
4895 for (liveness.deaths[case_i]) |operand| {
4896 self.processDeath(operand);
4897 }
49664898
4967 try self.genBody(case_body);
4899 try self.genBody(case_body);
4900 }
49684901
49694902 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
49704903
......@@ -4982,18 +4915,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
49824915 // Capture the state of register and stack allocation state so that we can revert to it.
49834916 const saved_state = try self.captureState();
49844917
4985 try self.branch_stack.append(.{});
4986 errdefer {
4987 _ = self.branch_stack.pop();
4988 }
4918 {
4919 try self.branch_stack.append(.{});
4920 errdefer _ = self.branch_stack.pop();
49894921
4990 const else_deaths = liveness.deaths.len - 1;
4991 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
4992 for (liveness.deaths[else_deaths]) |operand| {
4993 self.processDeath(operand);
4994 }
4922 const else_deaths = liveness.deaths.len - 1;
4923 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
4924 for (liveness.deaths[else_deaths]) |operand| {
4925 self.processDeath(operand);
4926 }
49954927
4996 try self.genBody(else_body);
4928 try self.genBody(else_body);
4929 }
49974930
49984931 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
49994932
......@@ -5030,11 +4963,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
50304963 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count());
50314964
50324965 const target_slice = target_branch.inst_table.entries.slice();
5033 const target_keys = target_slice.items(.key);
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];
4966 for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| {
50384967 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
50394968 // The instruction's MCValue is overridden in both branches.
50404969 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
......@@ -5064,10 +4993,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
50644993 }
50654994 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());
50664995 const canon_slice = canon_branch.inst_table.entries.slice();
5067 const canon_keys = canon_slice.items(.key);
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];
4996 for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| {
50714997 // We already deleted the items from this table that matched the target_branch.
50724998 // So these are all instructions that are only overridden in the canon branch.
50734999 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
......@@ -5095,10 +5021,10 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
50955021fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
50965022 const next_inst = @intCast(u32, self.mir_instructions.len);
50975023 switch (self.mir_instructions.items(.tag)[reloc]) {
5098 .cond_jmp => {
5024 .jcc => {
50995025 self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst;
51005026 },
5101 .jmp => {
5027 .jmp_reloc => {
51025028 self.mir_instructions.items(.data)[reloc].inst = next_inst;
51035029 },
51045030 else => unreachable,
......@@ -5140,11 +5066,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
51405066 // Emit a jump with a relocation. It will be patched up after the block ends.
51415067 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
51425068 // Leave the jump offset undefined
5143 const jmp_reloc = try self.addInst(.{
5144 .tag = .jmp,
5145 .ops = Mir.Inst.Ops.encode(.{}),
5146 .data = .{ .inst = undefined },
5147 });
5069 const jmp_reloc = try self.asmJmpReloc(undefined);
51485070 block_data.relocs.appendAssumeCapacity(jmp_reloc);
51495071}
51505072
......@@ -5217,31 +5139,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
52175139 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
52185140 while (iter.next()) |ins| {
52195141 if (mem.eql(u8, ins, "syscall")) {
5220 _ = try self.addInst(.{
5221 .tag = .syscall,
5222 .ops = undefined,
5223 .data = undefined,
5224 });
5142 try self.asmOpOnly(.syscall);
52255143 } else if (mem.indexOf(u8, ins, "push")) |_| {
52265144 const arg = ins[4..];
52275145 if (mem.indexOf(u8, arg, "$")) |l| {
52285146 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch {
52295147 return self.fail("TODO implement more inline asm int parsing", .{});
52305148 };
5231 _ = try self.addInst(.{
5232 .tag = .push,
5233 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b10 }),
5234 .data = .{ .imm = n },
5235 });
5149 try self.asmImmediate(.push, Immediate.u(n));
52365150 } else if (mem.indexOf(u8, arg, "%%")) |l| {
52375151 const reg_name = ins[4 + l + 2 ..];
52385152 const reg = parseRegName(reg_name) orelse
52395153 return self.fail("unrecognized register: '{s}'", .{reg_name});
5240 _ = try self.addInst(.{
5241 .tag = .push,
5242 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
5243 .data = undefined,
5244 });
5154 try self.asmRegister(.push, reg);
52455155 } else return self.fail("TODO more push operands", .{});
52465156 } else if (mem.indexOf(u8, ins, "pop")) |_| {
52475157 const arg = ins[3..];
......@@ -5249,11 +5159,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
52495159 const reg_name = ins[3 + l + 2 ..];
52505160 const reg = parseRegName(reg_name) orelse
52515161 return self.fail("unrecognized register: '{s}'", .{reg_name});
5252 _ = try self.addInst(.{
5253 .tag = .pop,
5254 .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }),
5255 .data = undefined,
5256 });
5162 try self.asmRegister(.pop, reg);
52575163 } else return self.fail("TODO more pop operands", .{});
52585164 } else {
52595165 return self.fail("TODO implement support for more x86 assembly instructions", .{});
......@@ -5268,9 +5174,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
52685174 const reg_name = output[2 .. output.len - 1];
52695175 const reg = parseRegName(reg_name) orelse
52705176 return self.fail("unrecognized register: '{s}'", .{reg_name});
5271 break :result MCValue{ .register = reg };
5177 break :result .{ .register = reg };
52725178 } else {
5273 break :result MCValue{ .none = {} };
5179 break :result .none;
52745180 }
52755181 };
52765182
......@@ -5326,7 +5232,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
53265232}
53275233
53285234fn 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.*));
53305236 switch (mcv) {
53315237 .dead => unreachable,
53325238 .unreach, .none => return,
......@@ -5350,26 +5256,17 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
53505256 .immediate => |imm| {
53515257 switch (abi_size) {
53525258 1, 2, 4 => {
5259 // TODO
53535260 // We have a positive stack offset value but we want a twos complement negative
53545261 // offset from rbp, which is at the top of the stack frame.
5355 // mov [rbp+offset], immediate
5356 const payload = try self.addExtra(Mir.ImmPair{
5357 .dest_off = @bitCast(u32, -stack_offset),
5358 .operand = @truncate(u32, imm),
5359 });
5360 _ = try self.addInst(.{
5361 .tag = .mov_mem_imm,
5362 .ops = Mir.Inst.Ops.encode(.{
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 });
5262 const immediate = if (ty.isSignedInt())
5263 Immediate.s(@intCast(i32, @bitCast(i64, imm)))
5264 else
5265 Immediate.u(@intCast(u32, imm));
5266 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5267 .base = .rsp,
5268 .disp = -stack_offset,
5269 }), immediate);
53735270 },
53745271 8 => {
53755272 const reg = try self.copyToTmpRegister(ty, mcv);
......@@ -5394,44 +5291,32 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
53945291 .Float => {
53955292 if (intrinsicsAllowed(self.target.*, ty)) {
53965293 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5397 .f32 => if (hasAvxSupport(self.target.*))
5398 Mir.Inst.Tag.mov_f32_avx
5399 else
5400 Mir.Inst.Tag.mov_f32_sse,
5401 .f64 => if (hasAvxSupport(self.target.*))
5402 Mir.Inst.Tag.mov_f64_avx
5403 else
5404 Mir.Inst.Tag.mov_f64_sse,
5405 else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}),
5294 .f32 => .movss,
5295 .f64 => .movsd,
5296 else => return self.fail(
5297 "TODO genSetStackArg for register for type {}",
5298 .{ty.fmtDebug()},
5299 ),
54065300 };
5407 _ = try self.addInst(.{
5408 .tag = tag,
5409 .ops = Mir.Inst.Ops.encode(.{
5410 .reg1 = switch (ty.tag()) {
5411 .f32 => .esp,
5412 .f64 => .rsp,
5413 else => unreachable,
5414 },
5415 .reg2 = reg.to128(),
5416 .flags = 0b01,
5417 }),
5418 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5419 });
5420 return;
5301 // TODO verify this
5302 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5303 .f32 => .dword,
5304 .f64 => .qword,
5305 else => unreachable,
5306 };
5307 return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{
5308 .base = .rsp,
5309 .disp = -stack_offset,
5310 }), reg.to128());
54215311 }
54225312
54235313 return self.fail("TODO genSetStackArg for register with no intrinsics", .{});
54245314 },
54255315 else => {
5426 _ = try self.addInst(.{
5427 .tag = .mov,
5428 .ops = Mir.Inst.Ops.encode(.{
5429 .reg1 = .rsp,
5430 .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
5431 .flags = 0b10,
5432 }),
5433 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5434 });
5316 try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5317 .base = .rsp,
5318 .disp = -stack_offset,
5319 }), registerAlias(reg, abi_size));
54355320 },
54365321 }
54375322 },
......@@ -5454,7 +5339,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
54545339}
54555340
54565341fn 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.*));
54585343 switch (mcv) {
54595344 .dead => unreachable,
54605345 .unreach, .none => return, // Nothing to do.
......@@ -5462,10 +5347,19 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
54625347 if (!self.wantSafety())
54635348 return; // The already existing value will do just fine.
54645349 // TODO Upgrade this to a memset call when we have that available.
5465 switch (ty.abiSize(self.target.*)) {
5466 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }, opts),
5467 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }, opts),
5468 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }, opts),
5350 switch (abi_size) {
5351 1, 2, 4 => {
5352 const value: u64 = switch (abi_size) {
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 },
54695363 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts),
54705364 else => |x| return self.genInlineMemset(
54715365 .{ .stack_offset = stack_offset },
......@@ -5485,13 +5379,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
54855379 const overflow_bit_ty = ty.structFieldType(1);
54865380 const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);
54875381 const tmp_reg = try self.register_manager.allocReg(null, gp);
5488 _ = try self.addInst(.{
5489 .tag = .cond_set_byte,
5490 .ops = Mir.Inst.Ops.encode(.{
5491 .reg1 = tmp_reg.to8(),
5492 }),
5493 .data = .{ .cc = ro.eflags },
5494 });
5382 try self.asmSetccRegister(tmp_reg.to8(), ro.eflags);
54955383
54965384 return self.genSetStack(
54975385 overflow_bit_ty,
......@@ -5506,72 +5394,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55065394 },
55075395 .immediate => |x_big| {
55085396 const base_reg = opts.dest_stack_base orelse .rbp;
5397 // TODO
55095398 switch (abi_size) {
55105399 0 => {
55115400 assert(ty.isError());
5512 const payload = try self.addExtra(Mir.ImmPair{
5513 .dest_off = @bitCast(u32, -stack_offset),
5514 .operand = @truncate(u32, x_big),
5515 });
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 });
5401 try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{
5402 .base = base_reg,
5403 .disp = -stack_offset,
5404 }), Immediate.u(@truncate(u8, x_big)));
55245405 },
55255406 1, 2, 4 => {
5526 const payload = try self.addExtra(Mir.ImmPair{
5527 .dest_off = @bitCast(u32, -stack_offset),
5528 .operand = @truncate(u32, x_big),
5529 });
5530 _ = try self.addInst(.{
5531 .tag = .mov_mem_imm,
5532 .ops = Mir.Inst.Ops.encode(.{
5533 .reg1 = base_reg,
5534 .flags = switch (abi_size) {
5535 1 => 0b00,
5536 2 => 0b01,
5537 4 => 0b10,
5538 else => unreachable,
5539 },
5540 }),
5541 .data = .{ .payload = payload },
5542 });
5407 const immediate = if (ty.isSignedInt())
5408 Immediate.s(@truncate(i32, @bitCast(i64, x_big)))
5409 else
5410 Immediate.u(@intCast(u32, x_big));
5411 try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5412 .base = base_reg,
5413 .disp = -stack_offset,
5414 }), immediate);
55435415 },
55445416 8 => {
55455417 // 64 bit write to memory would take two mov's anyways so we
55465418 // insted just use two 32 bit writes to avoid register allocation
5547 {
5548 const payload = try self.addExtra(Mir.ImmPair{
5549 .dest_off = @bitCast(u32, -stack_offset + 4),
5550 .operand = @truncate(u32, x_big >> 32),
5551 });
5552 _ = try self.addInst(.{
5553 .tag = .mov_mem_imm,
5554 .ops = Mir.Inst.Ops.encode(.{
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 }
5419 try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{
5420 .base = base_reg,
5421 .disp = -stack_offset + 4,
5422 }), Immediate.u(@truncate(u32, x_big >> 32)));
5423 try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{
5424 .base = base_reg,
5425 .disp = -stack_offset,
5426 }), Immediate.u(@truncate(u32, x_big)));
55755427 },
55765428 else => {
55775429 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
55895441 .Float => {
55905442 if (intrinsicsAllowed(self.target.*, ty)) {
55915443 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5592 .f32 => if (hasAvxSupport(self.target.*))
5593 Mir.Inst.Tag.mov_f32_avx
5594 else
5595 Mir.Inst.Tag.mov_f32_sse,
5596 .f64 => if (hasAvxSupport(self.target.*))
5597 Mir.Inst.Tag.mov_f64_avx
5598 else
5599 Mir.Inst.Tag.mov_f64_sse,
5600 else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
5444 .f32 => .movss,
5445 .f64 => .movsd,
5446 else => return self.fail(
5447 "TODO genSetStack for register for type {}",
5448 .{ty.fmtDebug()},
5449 ),
56015450 };
5602 _ = try self.addInst(.{
5603 .tag = tag,
5604 .ops = Mir.Inst.Ops.encode(.{
5605 .reg1 = switch (ty.tag()) {
5606 .f32 => base_reg.to32(),
5607 .f64 => base_reg.to64(),
5608 else => unreachable,
5609 },
5610 .reg2 = reg.to128(),
5611 .flags = 0b01,
5612 }),
5613 .data = .{ .imm = @bitCast(u32, -stack_offset) },
5614 });
5615 return;
5451 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5452 .f32 => .dword,
5453 .f64 => .qword,
5454 else => unreachable,
5455 };
5456 return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{
5457 .base = base_reg.to64(),
5458 .disp = -stack_offset,
5459 }), reg.to128());
56165460 }
56175461
56185462 return self.fail("TODO genSetStack for register for type float with no intrinsics", .{});
......@@ -5660,7 +5504,7 @@ fn genInlineMemcpyRegisterRegister(
56605504 src_reg: Register,
56615505 offset: i32,
56625506) InnerError!void {
5663 assert(dst_reg.size() == 64);
5507 assert(dst_reg.bitSize() == 64);
56645508
56655509 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
56665510 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -5677,16 +5521,10 @@ fn genInlineMemcpyRegisterRegister(
56775521 var remainder = abi_size;
56785522 while (remainder > 0) {
56795523 const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder));
5680
5681 _ = try self.addInst(.{
5682 .tag = .mov,
5683 .ops = Mir.Inst.Ops.encode(.{
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 });
5524 try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(nearest_power_of_two), .{
5525 .base = dst_reg,
5526 .disp = -next_offset,
5527 }), registerAlias(tmp_reg, nearest_power_of_two));
56905528
56915529 if (nearest_power_of_two > 1) {
56925530 try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{
......@@ -5698,15 +5536,10 @@ fn genInlineMemcpyRegisterRegister(
56985536 next_offset -= nearest_power_of_two;
56995537 }
57005538 } else {
5701 _ = try self.addInst(.{
5702 .tag = .mov,
5703 .ops = Mir.Inst.Ops.encode(.{
5704 .reg1 = dst_reg,
5705 .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
5706 .flags = 0b10,
5707 }),
5708 .data = .{ .imm = @bitCast(u32, -offset) },
5709 });
5539 try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5540 .base = dst_reg,
5541 .disp = -offset,
5542 }), registerAlias(src_reg, abi_size));
57105543 }
57115544}
57125545
......@@ -5746,24 +5579,17 @@ fn genInlineMemcpy(
57465579 try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr);
57475580 },
57485581 .ptr_stack_offset, .stack_offset => |off| {
5749 _ = try self.addInst(.{
5750 .tag = .lea,
5751 .ops = Mir.Inst.Ops.encode(.{
5752 .reg1 = dst_addr_reg.to64(),
5753 .reg2 = opts.dest_stack_base orelse .rbp,
5754 }),
5755 .data = .{ .imm = @bitCast(u32, -off) },
5756 });
5582 try self.asmRegisterMemory(.lea, dst_addr_reg.to64(), Memory.sib(.qword, .{
5583 .base = opts.dest_stack_base orelse .rbp,
5584 .disp = -off,
5585 }));
57575586 },
57585587 .register => |reg| {
5759 _ = try self.addInst(.{
5760 .tag = .mov,
5761 .ops = Mir.Inst.Ops.encode(.{
5762 .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)),
5763 .reg2 = reg,
5764 }),
5765 .data = undefined,
5766 });
5588 try self.asmRegisterRegister(
5589 .mov,
5590 registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))),
5591 reg,
5592 );
57675593 },
57685594 else => {
57695595 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
......@@ -5775,24 +5601,17 @@ fn genInlineMemcpy(
57755601 try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr);
57765602 },
57775603 .ptr_stack_offset, .stack_offset => |off| {
5778 _ = try self.addInst(.{
5779 .tag = .lea,
5780 .ops = Mir.Inst.Ops.encode(.{
5781 .reg1 = src_addr_reg.to64(),
5782 .reg2 = opts.source_stack_base orelse .rbp,
5783 }),
5784 .data = .{ .imm = @bitCast(u32, -off) },
5785 });
5604 try self.asmRegisterMemory(.lea, src_addr_reg.to64(), Memory.sib(.qword, .{
5605 .base = opts.source_stack_base orelse .rbp,
5606 .disp = -off,
5607 }));
57865608 },
57875609 .register => |reg| {
5788 _ = try self.addInst(.{
5789 .tag = .mov,
5790 .ops = Mir.Inst.Ops.encode(.{
5791 .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)),
5792 .reg2 = reg,
5793 }),
5794 .data = undefined,
5795 });
5610 try self.asmRegisterRegister(
5611 .mov,
5612 registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))),
5613 reg,
5614 );
57965615 },
57975616 else => {
57985617 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
......@@ -5800,74 +5619,35 @@ fn genInlineMemcpy(
58005619 }
58015620
58025621 try self.genSetReg(Type.usize, count_reg, len);
5803
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
5622 try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0));
58135623 const loop_start = try self.addInst(.{
58145624 .tag = .cmp,
5815 .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }),
5816 .data = .{ .imm = 0 },
5817 });
5818
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,
5625 .ops = .ri_u,
5626 .data = .{ .ri = .{
5627 .r1 = count_reg,
5628 .imm = 0,
58265629 } },
58275630 });
5828
5829 // mov tmp, [addr + index_reg]
5830 _ = try self.addInst(.{
5831 .tag = .mov_scale_src,
5832 .ops = Mir.Inst.Ops.encode(.{
5833 .reg1 = tmp_reg.to8(),
5834 .reg2 = src_addr_reg,
5835 }),
5836 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },
5837 });
5838
5839 // mov [stack_offset + index_reg], tmp
5840 _ = try self.addInst(.{
5841 .tag = .mov_scale_dst,
5842 .ops = Mir.Inst.Ops.encode(.{
5843 .reg1 = dst_addr_reg,
5844 .reg2 = tmp_reg.to8(),
5845 }),
5846 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },
5847 });
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:
5631 const loop_reloc = try self.asmJccReloc(undefined, .e);
5632 try self.asmRegisterMemory(.mov, tmp_reg.to8(), Memory.sib(.byte, .{
5633 .base = src_addr_reg,
5634 .scale_index = .{
5635 .scale = 1,
5636 .index = index_reg,
5637 },
5638 .disp = 0,
5639 }));
5640 try self.asmMemoryRegister(.mov, Memory.sib(.byte, .{
5641 .base = dst_addr_reg,
5642 .scale_index = .{
5643 .scale = 1,
5644 .index = index_reg,
5645 },
5646 .disp = 0,
5647 }), tmp_reg.to8());
5648 try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1));
5649 try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1));
5650 _ = try self.asmJmpReloc(loop_start);
58715651 try self.performReloc(loop_reloc);
58725652}
58735653
......@@ -5899,24 +5679,17 @@ fn genInlineMemset(
58995679 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr);
59005680 },
59015681 .ptr_stack_offset, .stack_offset => |off| {
5902 _ = try self.addInst(.{
5903 .tag = .lea,
5904 .ops = Mir.Inst.Ops.encode(.{
5905 .reg1 = addr_reg.to64(),
5906 .reg2 = opts.dest_stack_base orelse .rbp,
5907 }),
5908 .data = .{ .imm = @bitCast(u32, -off) },
5909 });
5682 try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{
5683 .base = opts.dest_stack_base orelse .rbp,
5684 .disp = -off,
5685 }));
59105686 },
59115687 .register => |reg| {
5912 _ = try self.addInst(.{
5913 .tag = .mov,
5914 .ops = Mir.Inst.Ops.encode(.{
5915 .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)),
5916 .reg2 = reg,
5917 }),
5918 .data = undefined,
5919 });
5688 try self.asmRegisterRegister(
5689 .mov,
5690 registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))),
5691 reg,
5692 );
59205693 },
59215694 else => {
59225695 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
......@@ -5926,54 +5699,35 @@ fn genInlineMemset(
59265699 try self.genSetReg(Type.usize, index_reg, len);
59275700 try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 });
59285701
5929 // loop:
5930 // cmp index_reg, -1
59315702 const loop_start = try self.addInst(.{
59325703 .tag = .cmp,
5933 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
5934 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },
5935 });
5936
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,
5704 .ops = .ri_s,
5705 .data = .{ .ri = .{
5706 .r1 = index_reg,
5707 .imm = @bitCast(u32, @as(i32, -1)),
59445708 } },
59455709 });
5710 const loop_reloc = try self.asmJccReloc(undefined, .e);
59465711
59475712 switch (value) {
59485713 .immediate => |x| {
59495714 if (x > math.maxInt(i32)) {
59505715 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});
59515716 }
5952 // mov byte ptr [rbp + index_reg + stack_offset], imm
5953 _ = try self.addInst(.{
5954 .tag = .mov_mem_index_imm,
5955 .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }),
5956 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) },
5957 });
5717 try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{
5718 .base = addr_reg,
5719 .scale_index = .{
5720 .scale = 1,
5721 .index = index_reg,
5722 },
5723 .disp = 0,
5724 }), Immediate.u(@intCast(u8, x)));
59585725 },
59595726 else => return self.fail("TODO inline memset for value of type {}", .{value}),
59605727 }
59615728
5962 // sub index_reg, 1
5963 _ = try self.addInst(.{
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:
5729 try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1));
5730 _ = try self.asmJmpReloc(loop_start);
59775731 try self.performReloc(loop_reloc);
59785732}
59795733
......@@ -5986,21 +5740,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
59865740 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
59875741 return self.fail("stack offset too large", .{});
59885742 }
5989 _ = try self.addInst(.{
5990 .tag = .lea,
5991 .ops = Mir.Inst.Ops.encode(.{
5992 .reg1 = registerAlias(reg, abi_size),
5993 .reg2 = .rbp,
5994 }),
5995 .data = .{ .imm = @bitCast(u32, -off) },
5996 });
5743 try self.asmRegisterMemory(
5744 .lea,
5745 registerAlias(reg, abi_size),
5746 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
5747 );
59975748 },
59985749 .unreach, .none => return, // Nothing to do.
59995750 .undef => {
60005751 if (!self.wantSafety())
60015752 return; // The already existing value will do just fine.
60025753 // Write the debug undefined value.
6003 switch (registerAlias(reg, abi_size).size()) {
5754 switch (registerAlias(reg, abi_size).bitSize()) {
60045755 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }),
60055756 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }),
60065757 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
......@@ -6009,50 +5760,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
60095760 }
60105761 },
60115762 .eflags => |cc| {
6012 _ = try self.addInst(.{
6013 .tag = .cond_set_byte,
6014 .ops = Mir.Inst.Ops.encode(.{
6015 .reg1 = reg.to8(),
6016 }),
6017 .data = .{ .cc = cc },
6018 });
5763 return self.asmSetccRegister(reg.to8(), cc);
60195764 },
60205765 .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.
60235766 if (x == 0) {
6024 _ = try self.addInst(.{
6025 .tag = .xor,
6026 .ops = Mir.Inst.Ops.encode(.{
6027 .reg1 = reg.to32(),
6028 .reg2 = reg.to32(),
6029 }),
6030 .data = undefined,
6031 });
6032 return;
5767 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
5768 // register is the fastest way to zero a register.
5769 return self.asmRegisterRegister(.xor, reg.to32(), reg.to32());
60335770 }
6034 if (x <= math.maxInt(i32)) {
6035 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
6036 _ = try self.addInst(.{
6037 .tag = .mov,
6038 .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }),
6039 .data = .{ .imm = @truncate(u32, x) },
6040 });
6041 return;
5771 if (ty.isSignedInt()) {
5772 const signed_x = @bitCast(i64, x);
5773 if (math.minInt(i32) <= signed_x and signed_x <= math.maxInt(i32)) {
5774 return self.asmRegisterImmediate(
5775 .mov,
5776 registerAlias(reg, abi_size),
5777 Immediate.s(@intCast(i32, signed_x)),
5778 );
5779 }
60425780 }
6043 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
6044 // this `movabs`, though this is officially just a different variant of the plain `mov`
6045 // instruction.
6046 //
6047 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
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 });
5781 return self.asmRegisterImmediate(
5782 .mov,
5783 registerAlias(reg, abi_size),
5784 Immediate.u(x),
5785 );
60565786 },
60575787 .register => |src_reg| {
60585788 // 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
60635793 .Int => switch (ty.intInfo(self.target.*).signedness) {
60645794 .signed => {
60655795 if (abi_size <= 4) {
6066 _ = try self.addInst(.{
6067 .tag = .mov_sign_extend,
6068 .ops = Mir.Inst.Ops.encode(.{
6069 .reg1 = reg.to64(),
6070 .reg2 = registerAlias(src_reg, abi_size),
6071 }),
6072 .data = undefined,
6073 });
6074 return;
5796 return self.asmRegisterRegister(
5797 .movsx,
5798 reg.to64(),
5799 registerAlias(src_reg, abi_size),
5800 );
60755801 }
60765802 },
60775803 .unsigned => {
60785804 if (abi_size <= 2) {
6079 _ = try self.addInst(.{
6080 .tag = .mov_zero_extend,
6081 .ops = Mir.Inst.Ops.encode(.{
6082 .reg1 = reg.to64(),
6083 .reg2 = registerAlias(src_reg, abi_size),
6084 }),
6085 .data = undefined,
6086 });
6087 return;
5805 return self.asmRegisterRegister(
5806 .movzx,
5807 reg.to64(),
5808 registerAlias(src_reg, abi_size),
5809 );
60885810 }
60895811 },
60905812 },
60915813 .Float => {
60925814 if (intrinsicsAllowed(self.target.*, ty)) {
60935815 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6094 .f32 => if (hasAvxSupport(self.target.*))
6095 Mir.Inst.Tag.mov_f32_avx
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,
5816 .f32 => .movss,
5817 .f64 => .movsd,
61025818 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
61035819 };
6104 _ = try self.addInst(.{
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;
5820 return self.asmRegisterRegister(tag, reg.to128(), src_reg.to128());
61145821 }
6115
61165822 return self.fail("TODO genSetReg from register for float with no intrinsics", .{});
61175823 },
61185824 else => {},
61195825 }
61205826
6121 _ = try self.addInst(.{
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 });
5827 try self.asmRegisterRegister(.mov, registerAlias(reg, abi_size), registerAlias(src_reg, abi_size));
61295828 },
61305829 .linker_load => {
61315830 switch (ty.zigTypeTag()) {
......@@ -6135,45 +5834,30 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61355834
61365835 if (intrinsicsAllowed(self.target.*, ty)) {
61375836 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6138 .f32 => if (hasAvxSupport(self.target.*))
6139 Mir.Inst.Tag.mov_f32_avx
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,
5837 .f32 => .movss,
5838 .f64 => .movsd,
61465839 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
61475840 };
6148
6149 _ = try self.addInst(.{
6150 .tag = tag,
6151 .ops = Mir.Inst.Ops.encode(.{
6152 .reg1 = reg.to128(),
6153 .reg2 = switch (ty.tag()) {
6154 .f32 => base_reg.to32(),
6155 .f64 => base_reg.to64(),
6156 else => unreachable,
6157 },
6158 }),
6159 .data = .{ .imm = 0 },
6160 });
6161 return;
5841 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5842 .f32 => .dword,
5843 .f64 => .qword,
5844 else => unreachable,
5845 };
5846 return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{
5847 .base = base_reg.to64(),
5848 .disp = 0,
5849 }));
61625850 }
61635851
61645852 return self.fail("TODO genSetReg from memory for float with no intrinsics", .{});
61655853 },
61665854 else => {
61675855 try self.loadMemPtrIntoRegister(reg, Type.usize, mcv);
6168 _ = try self.addInst(.{
6169 .tag = .mov,
6170 .ops = Mir.Inst.Ops.encode(.{
6171 .reg1 = registerAlias(reg, abi_size),
6172 .reg2 = reg.to64(),
6173 .flags = 0b01,
6174 }),
6175 .data = .{ .imm = 0 },
6176 });
5856 try self.asmRegisterMemory(
5857 .mov,
5858 registerAlias(reg, abi_size),
5859 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }),
5860 );
61775861 },
61785862 }
61795863 },
......@@ -6184,73 +5868,56 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61845868
61855869 if (intrinsicsAllowed(self.target.*, ty)) {
61865870 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6187 .f32 => if (hasAvxSupport(self.target.*))
6188 Mir.Inst.Tag.mov_f32_avx
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,
5871 .f32 => .movss,
5872 .f64 => .movsd,
61955873 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
61965874 };
6197
6198 _ = try self.addInst(.{
6199 .tag = tag,
6200 .ops = Mir.Inst.Ops.encode(.{
6201 .reg1 = reg.to128(),
6202 .reg2 = switch (ty.tag()) {
6203 .f32 => base_reg.to32(),
6204 .f64 => base_reg.to64(),
6205 else => unreachable,
6206 },
6207 }),
6208 .data = .{ .imm = 0 },
6209 });
6210 return;
5875 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5876 .f32 => .dword,
5877 .f64 => .qword,
5878 else => unreachable,
5879 };
5880 return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{
5881 .base = base_reg.to64(),
5882 .disp = 0,
5883 }));
62115884 }
62125885
62135886 return self.fail("TODO genSetReg from memory for float with no intrinsics", .{});
62145887 },
62155888 else => {
62165889 if (x <= math.maxInt(i32)) {
6217 // mov reg, [ds:imm32]
6218 _ = try self.addInst(.{
6219 .tag = .mov,
6220 .ops = Mir.Inst.Ops.encode(.{
6221 .reg1 = registerAlias(reg, abi_size),
6222 .flags = 0b01,
5890 try self.asmRegisterMemory(
5891 .mov,
5892 registerAlias(reg, abi_size),
5893 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5894 .base = .ds,
5895 .disp = @intCast(i32, x),
62235896 }),
6224 .data = .{ .imm = @truncate(u32, x) },
6225 });
5897 );
62265898 } else {
6227 // If this is RAX, we can use a direct load.
6228 // Otherwise, we need to load the address, then indirectly load the value.
6229 if (reg.id() == 0) {
6230 // movabs rax, ds:moffs64
6231 const payload = try self.addExtra(Mir.Imm64.encode(x));
5899 if (reg.to64() == .rax) {
5900 // If this is RAX, we can use a direct load.
5901 // Otherwise, we need to load the address, then indirectly load the value.
5902 var moffs: Mir.MemoryMoffs = .{
5903 .seg = @enumToInt(Register.ds),
5904 .msb = undefined,
5905 .lsb = undefined,
5906 };
5907 moffs.encodeOffset(x);
62325908 _ = try self.addInst(.{
6233 .tag = .movabs,
6234 .ops = Mir.Inst.Ops.encode(.{
6235 .reg1 = .rax,
6236 .flags = 0b01, // imm64 will become moffs64
6237 }),
6238 .data = .{ .payload = payload },
5909 .tag = .mov_moffs,
5910 .ops = .rax_moffs,
5911 .data = .{ .payload = try self.addExtra(moffs) },
62395912 });
62405913 } else {
62415914 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
62425915 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
6243
6244 // mov reg, [reg + 0x0]
6245 _ = try self.addInst(.{
6246 .tag = .mov,
6247 .ops = Mir.Inst.Ops.encode(.{
6248 .reg1 = registerAlias(reg, abi_size),
6249 .reg2 = reg.to64(),
6250 .flags = 0b01,
6251 }),
6252 .data = .{ .imm = 0 },
6253 });
5916 try self.asmRegisterMemory(
5917 .mov,
5918 registerAlias(reg, abi_size),
5919 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }),
5920 );
62545921 }
62555922 }
62565923 },
......@@ -6264,85 +5931,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
62645931 .Int => switch (ty.intInfo(self.target.*).signedness) {
62655932 .signed => {
62665933 if (abi_size <= 4) {
6267 const flags: u2 = switch (abi_size) {
6268 1 => 0b01,
6269 2 => 0b10,
6270 4 => 0b11,
6271 else => unreachable,
6272 };
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,
5934 return self.asmRegisterMemory(
5935 .movsx,
5936 reg.to64(),
5937 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5938 .base = .rbp,
5939 .disp = -off,
62795940 }),
6280 .data = .{ .imm = @bitCast(u32, -off) },
6281 });
6282 return;
5941 );
62835942 }
62845943 },
62855944 .unsigned => {
62865945 if (abi_size <= 2) {
6287 const flags: u2 = switch (abi_size) {
6288 1 => 0b01,
6289 2 => 0b10,
6290 else => unreachable,
6291 };
6292 _ = try self.addInst(.{
6293 .tag = .mov_zero_extend,
6294 .ops = Mir.Inst.Ops.encode(.{
6295 .reg1 = reg.to64(),
6296 .reg2 = .rbp,
6297 .flags = flags,
5946 return self.asmRegisterMemory(
5947 .movzx,
5948 reg.to64(),
5949 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
5950 .base = .rbp,
5951 .disp = -off,
62985952 }),
6299 .data = .{ .imm = @bitCast(u32, -off) },
6300 });
6301 return;
5953 );
63025954 }
63035955 },
63045956 },
63055957 .Float => {
63065958 if (intrinsicsAllowed(self.target.*, ty)) {
63075959 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6308 .f32 => if (hasAvxSupport(self.target.*))
6309 Mir.Inst.Tag.mov_f32_avx
6310 else
6311 Mir.Inst.Tag.mov_f32_sse,
6312 .f64 => if (hasAvxSupport(self.target.*))
6313 Mir.Inst.Tag.mov_f64_avx
6314 else
6315 Mir.Inst.Tag.mov_f64_sse,
6316 else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
5960 .f32 => .movss,
5961 .f64 => .movsd,
5962 else => return self.fail(
5963 "TODO genSetReg from stack offset for {}",
5964 .{ty.fmtDebug()},
5965 ),
63175966 };
6318 _ = try self.addInst(.{
6319 .tag = tag,
6320 .ops = Mir.Inst.Ops.encode(.{
6321 .reg1 = reg.to128(),
6322 .reg2 = switch (ty.tag()) {
6323 .f32 => .ebp,
6324 .f64 => .rbp,
6325 else => unreachable,
6326 },
6327 }),
6328 .data = .{ .imm = @bitCast(u32, -off) },
6329 });
6330 return;
5967 const ptr_size: Memory.PtrSize = switch (ty.tag()) {
5968 .f32 => .dword,
5969 .f64 => .qword,
5970 else => unreachable,
5971 };
5972 return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{
5973 .base = .rbp,
5974 .disp = -off,
5975 }));
63315976 }
63325977 return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{});
63335978 },
63345979 else => {},
63355980 }
63365981
6337 _ = try self.addInst(.{
6338 .tag = .mov,
6339 .ops = Mir.Inst.Ops.encode(.{
6340 .reg1 = registerAlias(reg, abi_size),
6341 .reg2 = .rbp,
6342 .flags = 0b01,
6343 }),
6344 .data = .{ .imm = @bitCast(u32, -off) },
6345 });
5982 try self.asmRegisterMemory(
5983 .mov,
5984 registerAlias(reg, abi_size),
5985 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
5986 );
63465987 },
63475988 }
63485989}
......@@ -6406,6 +6047,16 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
64066047 const src_ty = self.air.typeOf(ty_op.operand);
64076048 const dst_ty = self.air.typeOfIndex(inst);
64086049 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
64106061 // move float src to ST(0)
64116062 const stack_offset = switch (operand) {
......@@ -6413,41 +6064,24 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
64136064 else => blk: {
64146065 const offset = @intCast(i32, try self.allocMem(
64156066 inst,
6416 @intCast(u32, src_ty.abiSize(self.target.*)),
6067 src_abi_size,
64176068 src_ty.abiAlignment(self.target.*),
64186069 ));
64196070 try self.genSetStack(src_ty, offset, operand, .{});
64206071 break :blk offset;
64216072 },
64226073 };
6423 _ = try self.addInst(.{
6424 .tag = .fld,
6425 .ops = Mir.Inst.Ops.encode(.{
6426 .reg1 = .rbp,
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 });
6074 try self.asmMemory(.fld, Memory.sib(Memory.PtrSize.fromSize(src_abi_size), .{
6075 .base = .rbp,
6076 .disp = -stack_offset,
6077 }));
64356078
64366079 // convert
64376080 const stack_dst = try self.allocRegOrMem(inst, false);
6438 _ = try self.addInst(.{
6439 .tag = .fisttp,
6440 .ops = Mir.Inst.Ops.encode(.{
6441 .reg1 = .rbp,
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 });
6081 try self.asmMemory(.fisttp, Memory.sib(Memory.PtrSize.fromSize(dst_abi_size), .{
6082 .base = .rbp,
6083 .disp = -stack_dst.stack_offset,
6084 }));
64516085
64526086 return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none });
64536087}
......@@ -6538,15 +6172,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
65386172 .linker_load, .memory => {
65396173 const reg = try self.register_manager.allocReg(null, gp);
65406174 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);
6541 _ = try self.addInst(.{
6542 .tag = .mov,
6543 .ops = Mir.Inst.Ops.encode(.{
6544 .reg1 = reg,
6545 .reg2 = reg,
6546 .flags = 0b01,
6547 }),
6548 .data = .{ .imm = 0 },
6549 });
6175 try self.asmRegisterMemory(.mov, reg, Memory.sib(.qword, .{
6176 .base = reg,
6177 .disp = 0,
6178 }));
65506179 break :blk MCValue{ .register = reg };
65516180 },
65526181 else => break :blk src_ptr,
......@@ -6619,6 +6248,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
66196248 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
66206249 switch (result_ty.zigTypeTag()) {
66216250 .Struct => {
6251 if (result_ty.containerLayout() == .Packed) {
6252 return self.fail("TODO airAggregateInit implement packed structs", .{});
6253 }
66226254 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
66236255 for (elements, 0..) |elem, elem_i| {
66246256 if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem
......@@ -6683,13 +6315,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
66836315 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
66846316}
66856317
6686pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
6318fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
66876319 // First section of indexes correspond to a set number of constant values.
66886320 const ref_int = @enumToInt(inst);
66896321 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
66906322 const tv = Air.Inst.Ref.typed_value_map[ref_int];
66916323 if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) {
6692 return MCValue{ .none = {} };
6324 return .none;
66936325 }
66946326 return self.genTypedValue(tv);
66956327 }
......@@ -6697,7 +6329,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
66976329 // If the type has no codegen bits, no need to store it.
66986330 const inst_ty = self.air.typeOf(inst);
66996331 if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError())
6700 return MCValue{ .none = {} };
6332 return .none;
67016333
67026334 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
67036335 switch (self.air.instructions.items(.tag)[inst_index]) {
......@@ -6715,18 +6347,17 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
67156347 return gop.value_ptr.*;
67166348 },
67176349 .const_ty => unreachable,
6718 else => return self.getResolvedInstValue(inst_index),
6350 else => return self.getResolvedInstValue(inst_index).?,
67196351 }
67206352}
67216353
6722fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
6354fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue {
67236355 // Treat each stack item as a "layer" on top of the previous one.
67246356 var i: usize = self.branch_stack.items.len;
67256357 while (true) {
67266358 i -= 1;
67276359 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
6728 assert(mcv != .dead);
6729 return mcv;
6360 return if (mcv != .dead) mcv else null;
67306361 }
67316362 }
67326363}
......@@ -6752,200 +6383,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV
67526383 return mcv;
67536384}
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
68286386fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6829 var typed_value = arg_tv;
6830 if (typed_value.val.castTag(.runtime_value)) |rt| {
6831 typed_value.val = rt.data;
6832 }
6833 log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
6834 if (typed_value.val.isUndef())
6835 return MCValue{ .undef = {} };
6836 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6837
6838 if (typed_value.val.castTag(.decl_ref)) |payload| {
6839 return self.lowerDeclRef(typed_value, payload.data);
6840 }
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 }
6387 const mcv: MCValue = switch (try codegen.genTypedValue(
6388 self.bin_file,
6389 self.src_loc,
6390 arg_tv,
6391 self.mod_fn.owner_decl,
6392 )) {
6393 .mcv => |mcv| switch (mcv) {
6394 .none => .none,
6395 .undef => .undef,
6396 .linker_load => |ll| .{ .linker_load = ll },
6397 .immediate => |imm| .{ .immediate = imm },
6398 .memory => |addr| .{ .memory = addr },
68686399 },
6869 .Bool => {
6870 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
6400 .fail => |msg| {
6401 self.err_msg = msg;
6402 return error.CodegenFail;
68716403 },
6872 .Optional => {
6873 if (typed_value.ty.isPtrLikeOptional()) {
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);
6404 };
6405 return mcv;
69496406}
69506407
69516408const CallMCValues = struct {
......@@ -6980,7 +6437,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
69806437 switch (cc) {
69816438 .Naked => {
69826439 assert(result.args.len == 0);
6983 result.return_value = .{ .unreach = {} };
6440 result.return_value = .unreach;
69846441 result.stack_byte_count = 0;
69856442 result.stack_align = 1;
69866443 return result;
......@@ -6988,10 +6445,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
69886445 .C => {
69896446 // Return values
69906447 if (ret_ty.zigTypeTag() == .NoReturn) {
6991 result.return_value = .{ .unreach = {} };
6448 result.return_value = .unreach;
69926449 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
69936450 // TODO: is this even possible for C calling convention?
6994 result.return_value = .{ .none = {} };
6451 result.return_value = .none;
69956452 } else {
69966453 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
69976454 if (ret_ty_size == 0) {
......@@ -7012,7 +6469,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70126469 else => 0,
70136470 };
70146471
7015 for (param_types, 0..) |ty, i| {
6472 for (param_types, result.args, 0..) |ty, *arg, i| {
70166473 assert(ty.hasRuntimeBits());
70176474
70186475 const classes: []const abi.Class = switch (self.target.os.tag) {
......@@ -7025,7 +6482,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70256482 switch (classes[0]) {
70266483 .integer => blk: {
70276484 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] };
70296486 continue;
70306487 },
70316488 .memory => {}, // fallthrough
......@@ -7037,7 +6494,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70376494 const param_size = @intCast(u32, ty.abiSize(self.target.*));
70386495 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
70396496 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) };
70416498 next_stack_offset = offset;
70426499 }
70436500
......@@ -7063,15 +6520,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70636520 .Unspecified => {
70646521 // Return values
70656522 if (ret_ty.zigTypeTag() == .NoReturn) {
7066 result.return_value = .{ .unreach = {} };
6523 result.return_value = .unreach;
70676524 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7068 result.return_value = .{ .none = {} };
6525 result.return_value = .none;
70696526 } else {
70706527 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
70716528 if (ret_ty_size == 0) {
70726529 assert(ret_ty.isError());
70736530 result.return_value = .{ .immediate = 0 };
7074 } else if (ret_ty_size <= 8) {
6531 } else if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) {
70756532 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
70766533 result.return_value = .{ .register = aliased_reg };
70776534 } else {
......@@ -7088,15 +6545,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70886545 else => 0,
70896546 };
70906547
7091 for (param_types, 0..) |ty, i| {
6548 for (param_types, result.args) |ty, *arg| {
70926549 if (!ty.hasRuntimeBits()) {
7093 result.args[i] = .{ .none = {} };
6550 arg.* = .none;
70946551 continue;
70956552 }
70966553 const param_size = @intCast(u32, ty.abiSize(self.target.*));
70976554 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
70986555 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) };
71006557 next_stack_offset = offset;
71016558 }
71026559
......@@ -7146,28 +6603,34 @@ fn parseRegName(name: []const u8) ?Register {
71466603
71476604/// Returns register wide enough to hold at least `size_bytes`.
71486605fn registerAlias(reg: Register, size_bytes: u32) Register {
7149 if (size_bytes == 0) {
7150 unreachable; // should be comptime-known
7151 } else if (size_bytes <= 1) {
7152 return reg.to8();
7153 } else if (size_bytes <= 2) {
7154 return reg.to16();
7155 } else if (size_bytes <= 4) {
7156 return reg.to32();
7157 } else if (size_bytes <= 8) {
7158 return reg.to64();
7159 } else if (size_bytes <= 16) {
7160 return reg.to128();
7161 } else if (size_bytes <= 32) {
7162 return reg.to256();
7163 } else unreachable;
6606 return switch (reg.class()) {
6607 .general_purpose => if (size_bytes == 0)
6608 unreachable // should be comptime-known
6609 else if (size_bytes <= 1)
6610 reg.to8()
6611 else if (size_bytes <= 2)
6612 reg.to16()
6613 else if (size_bytes <= 4)
6614 reg.to32()
6615 else if (size_bytes <= 8)
6616 reg.to64()
6617 else
6618 unreachable,
6619 .floating_point => if (size_bytes <= 16)
6620 reg.to128()
6621 else if (size_bytes <= 32)
6622 reg.to256()
6623 else
6624 unreachable,
6625 .segment => unreachable,
6626 };
71646627}
71656628
71666629/// Truncates the value in the register in place.
71676630/// Clobbers any remaining bits.
71686631fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
71696632 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();
71716634 switch (int_info.signedness) {
71726635 .signed => {
71736636 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
......@@ -7177,7 +6640,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
71776640 .unsigned => {
71786641 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
71796642 const mask = (~@as(u64, 0)) >> shift;
7180 if (int_info.bits <= 32) {
6643 if (int_info.bits < 32) {
71816644 try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask });
71826645 } else {
71836646 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");
77const assert = std.debug.assert;
88const bits = @import("bits.zig");
99const abi = @import("abi.zig");
10const encoder = @import("encoder.zig");
1011const link = @import("../../link.zig");
1112const log = std.log.scoped(.codegen);
1213const math = std.math;
......@@ -19,12 +20,14 @@ const CodeGen = @import("CodeGen.zig");
1920const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2021const Encoder = bits.Encoder;
2122const ErrorMsg = Module.ErrorMsg;
23const Immediate = bits.Immediate;
24const Instruction = encoder.Instruction;
2225const MCValue = @import("CodeGen.zig").MCValue;
26const Memory = bits.Memory;
2327const Mir = @import("Mir.zig");
2428const Module = @import("../../Module.zig");
25const Instruction = bits.Instruction;
26const Type = @import("../../type.zig").Type;
2729const Register = bits.Register;
30const Type = @import("../../type.zig").Type;
2831
2932mir: Mir,
3033bin_file: *link.File,
......@@ -45,6 +48,8 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{},
4548const InnerError = error{
4649 OutOfMemory,
4750 EmitFail,
51 InvalidInstruction,
52 CannotEncode,
4853};
4954
5055const Reloc = struct {
......@@ -65,132 +70,66 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
6570 const inst = @intCast(u32, index);
6671 try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);
6772 switch (tag) {
68 // GPR instructions
69 .adc => try emit.mirArith(.adc, inst),
70 .add => try emit.mirArith(.add, inst),
71 .sub => try emit.mirArith(.sub, inst),
72 .xor => try emit.mirArith(.xor, inst),
73 .@"and" => try emit.mirArith(.@"and", inst),
74 .@"or" => try emit.mirArith(.@"or", inst),
75 .sbb => try emit.mirArith(.sbb, inst),
76 .cmp => try emit.mirArith(.cmp, inst),
77 .mov => try emit.mirArith(.mov, inst),
78
79 .adc_mem_imm => try emit.mirArithMemImm(.adc, inst),
80 .add_mem_imm => try emit.mirArithMemImm(.add, inst),
81 .sub_mem_imm => try emit.mirArithMemImm(.sub, inst),
82 .xor_mem_imm => try emit.mirArithMemImm(.xor, inst),
83 .and_mem_imm => try emit.mirArithMemImm(.@"and", inst),
84 .or_mem_imm => try emit.mirArithMemImm(.@"or", inst),
85 .sbb_mem_imm => try emit.mirArithMemImm(.sbb, inst),
86 .cmp_mem_imm => try emit.mirArithMemImm(.cmp, inst),
87 .mov_mem_imm => try emit.mirArithMemImm(.mov, inst),
88
89 .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst),
90 .add_scale_src => try emit.mirArithScaleSrc(.add, inst),
91 .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst),
92 .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst),
93 .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst),
94 .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst),
95 .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst),
96 .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst),
97 .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst),
98
99 .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst),
100 .add_scale_dst => try emit.mirArithScaleDst(.add, inst),
101 .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst),
102 .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst),
103 .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst),
104 .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst),
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),
73 .adc,
74 .add,
75 .@"and",
76 .call,
77 .cbw,
78 .cwde,
79 .cdqe,
80 .cwd,
81 .cdq,
82 .cqo,
83 .cmp,
84 .div,
85 .fisttp,
86 .fld,
87 .idiv,
88 .imul,
89 .int3,
90 .jmp,
91 .lea,
92 .mov,
93 .movzx,
94 .mul,
95 .nop,
96 .@"or",
97 .pop,
98 .push,
99 .ret,
100 .sal,
101 .sar,
102 .sbb,
103 .shl,
104 .shr,
105 .sub,
106 .syscall,
107 .@"test",
108 .ud2,
109 .xor,
175110
176 .add_f64_sse => try emit.mirAddFloatSse(.addsd, inst),
177 .add_f32_sse => try emit.mirAddFloatSse(.addss, inst),
111 .addss,
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),
180 .cmp_f32_sse => try emit.mirCmpFloatSse(.ucomiss, inst),
121 .jmp_reloc => try emit.mirJmpReloc(inst),
181122
182 // AVX instructions
183 .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst),
184 .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst),
123 .call_extern => try emit.mirCallExtern(inst),
185124
186 .add_f64_avx => try emit.mirAddFloatAvx(.vaddsd, inst),
187 .add_f32_avx => try emit.mirAddFloatAvx(.vaddss, inst),
125 .lea_linker => try emit.mirLeaLinker(inst),
188126
189 .cmp_f64_avx => try emit.mirCmpFloatAvx(.vucomisd, inst),
190 .cmp_f32_avx => try emit.mirCmpFloatAvx(.vucomiss, inst),
127 .mov_moffs => try emit.mirMovMoffs(inst),
191128
192 // Pseudo-instructions
193 .call_extern => try emit.mirCallExtern(inst),
129 .movsx => try emit.mirMovsx(inst),
130 .cmovcc => try emit.mirCmovcc(inst),
131 .setcc => try emit.mirSetcc(inst),
132 .jcc => try emit.mirJcc(inst),
194133
195134 .dbg_line => try emit.mirDbgLine(inst),
196135 .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst),
......@@ -199,9 +138,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
199138 .push_regs => try emit.mirPushPopRegisterList(.push, inst),
200139 .pop_regs => try emit.mirPushPopRegisterList(.pop, inst),
201140
202 else => {
203 return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag});
204 },
141 .dead => {},
205142 }
206143 }
207144
......@@ -234,794 +171,385 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
234171 }
235172}
236173
237fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
238 const tag = emit.mir.instructions.items(.tag)[inst];
239 assert(tag == .interrupt);
240 const ops = emit.mir.instructions.items(.ops)[inst].decode();
241 switch (ops.flags) {
242 0b00 => return lowerToZoEnc(.int3, emit.code),
243 else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}),
244 }
174fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct {
175 op1: Instruction.Operand = .none,
176 op2: Instruction.Operand = .none,
177 op3: Instruction.Operand = .none,
178 op4: Instruction.Operand = .none,
179}) InnerError!void {
180 const inst = try Instruction.new(mnemonic, .{
181 .op1 = ops.op1,
182 .op2 = ops.op2,
183 .op3 = ops.op3,
184 .op4 = ops.op4,
185 });
186 return inst.encode(emit.code.writer());
245187}
246188
247fn mirSyscall(emit: *Emit) InnerError!void {
248 return lowerToZoEnc(.syscall, emit.code);
249}
189fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
190 const mnemonic = inline for (@typeInfo(Instruction.Mnemonic).Enum.fields) |field| {
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 {
252 const ops = emit.mir.instructions.items(.ops)[inst].decode();
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}
194 const ops = emit.mir.instructions.items(.ops)[inst];
195 const data = emit.mir.instructions.items(.data)[inst];
279196
280fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
281 const ops = emit.mir.instructions.items(.ops)[inst].decode();
282 const payload = emit.mir.instructions.items(.data)[inst].payload;
283 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;
284 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
285 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
286 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
287 for (callee_preserved_regs) |reg| {
288 if (reg_list.isSet(callee_preserved_regs, reg)) {
289 switch (tag) {
290 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
291 .disp = @bitCast(u32, disp),
292 .base = ops.reg1,
293 }), reg, emit.code),
294 .pop => try lowerToRmEnc(.mov, reg, RegisterOrMemory.mem(.qword_ptr, .{
295 .disp = @bitCast(u32, disp),
296 .base = ops.reg1,
297 }), emit.code),
197 var op1: Instruction.Operand = .none;
198 var op2: Instruction.Operand = .none;
199 var op3: Instruction.Operand = .none;
200 var op4: Instruction.Operand = .none;
201
202 switch (ops) {
203 .none => {},
204 .imm_s => op1 = .{ .imm = Immediate.s(@bitCast(i32, data.imm)) },
205 .imm_u => op1 = .{ .imm = Immediate.u(data.imm) },
206 .r => op1 = .{ .reg = data.r },
207 .rr => {
208 op1 = .{ .reg = data.rr.r1 };
209 op2 = .{ .reg = data.rr.r2 };
210 },
211 .ri_s, .ri_u => {
212 const imm = switch (ops) {
213 .ri_s => Immediate.s(@bitCast(i32, data.ri.imm)),
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 },
298276 else => unreachable,
299277 }
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 });
318278 },
319 0b01 => {
320 if (ops.reg1 == .none) {
321 // JMP/CALL [imm]
322 const imm = emit.mir.instructions.items(.data)[inst].imm;
323 const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) {
324 16 => .word_ptr,
325 else => .qword_ptr,
326 };
327 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code);
279 .rm_rip, .mr_rip => {
280 const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data;
281 const op_r = .{ .reg = data.rx.r1 };
282 const op_m = .{ .mem = Mir.MemoryRip.decode(mrip) };
283 switch (ops) {
284 .rm_sib => {
285 op1 = op_r;
286 op2 = op_m;
287 },
288 .mr_sib => {
289 op1 = op_m;
290 op2 = op_r;
291 },
292 else => unreachable,
328293 }
329 // JMP/CALL reg
330 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
331294 },
332 0b10 => {
333 // JMP/CALL r/m64
334 const imm = emit.mir.instructions.items(.data)[inst].imm;
335 return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
336 .disp = imm,
337 .base = ops.reg1,
338 }), emit.code);
339 },
340 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}),
295 else => return emit.fail("TODO handle generic encoding: {s}, {s}", .{
296 @tagName(mnemonic),
297 @tagName(ops),
298 }),
341299 }
342}
343300
344fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
345 const mir_tag = emit.mir.instructions.items(.tag)[inst];
346 assert(mir_tag == .cond_jmp);
347 const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc;
348 const tag: Tag = switch (inst_cc.cc) {
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,
301 return emit.encode(mnemonic, .{
302 .op1 = op1,
303 .op2 = op2,
304 .op3 = op3,
305 .op4 = op4,
387306 });
388307}
389308
390fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
391 const mir_tag = emit.mir.instructions.items(.tag)[inst];
392 assert(mir_tag == .cond_set_byte);
393 const ops = emit.mir.instructions.items(.ops)[inst].decode();
394 const cc = emit.mir.instructions.items(.data)[inst].cc;
395 const tag: Tag = switch (cc) {
396 .a => .seta,
397 .ae => .setae,
398 .b => .setb,
399 .be => .setbe,
400 .c => .setc,
401 .e => .sete,
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);
309fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
310 const ops = emit.mir.instructions.items(.ops)[inst];
311 const payload = emit.mir.instructions.items(.data)[inst].payload;
312 const moffs = emit.mir.extraData(Mir.MemoryMoffs, payload).data;
313 const seg = @intToEnum(Register, moffs.seg);
314 const offset = moffs.decodeOffset();
315 switch (ops) {
316 .rax_moffs => {
317 try emit.encode(.mov, .{
318 .op1 = .{ .reg = .rax },
319 .op2 = .{ .mem = Memory.moffs(seg, offset) },
320 });
527321 },
528 0b11 => {
529 return lowerToZoEnc(.ret_near, emit.code);
322 .moffs_rax => {
323 try emit.encode(.mov, .{
324 .op1 = .{ .mem = Memory.moffs(seg, offset) },
325 .op2 = .{ .reg = .rax },
326 });
530327 },
328 else => unreachable,
531329 }
532330}
533331
534fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
535 const ops = emit.mir.instructions.items(.ops)[inst].decode();
536 switch (ops.flags) {
537 0b00 => {
538 if (ops.reg2 == .none) {
539 // mov reg1, imm32
540 // MI
541 const imm = emit.mir.instructions.items(.data)[inst].imm;
542 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
543 }
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);
332fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
333 const ops = emit.mir.instructions.items(.ops)[inst];
334 const data = emit.mir.instructions.items(.data)[inst];
335
336 var op1: Instruction.Operand = .none;
337 var op2: Instruction.Operand = .none;
338 switch (ops) {
339 .rr => {
340 op1 = .{ .reg = data.rr.r1 };
341 op2 = .{ .reg = data.rr.r2 };
557342 },
558 0b10 => {
559 if (ops.reg2 == .none) {
560 return emit.fail("TODO unused variant: mov reg1, none, 0b10", .{});
561 }
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);
343 .rm_sib => {
344 const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data;
345 op1 = .{ .reg = data.rx.r1 };
346 op2 = .{ .mem = Mir.MemorySib.decode(msib) };
569347 },
570 0b11 => {
571 return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{});
348 .rm_rip => {
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) };
572352 },
353 else => unreachable, // TODO
573354 }
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 {
614 const ops = emit.mir.instructions.items(.ops)[inst].decode();
615 const scale = ops.flags;
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,
356 const mnemonic: Instruction.Mnemonic = switch (op1.bitSize()) {
357 32, 64 => if (op2.bitSize() == 32) .movsxd else .movsx,
358 else => .movsx,
638359 };
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 {
649 const ops = emit.mir.instructions.items(.ops)[inst].decode();
650 const scale = ops.flags;
651 const payload = emit.mir.instructions.items(.data)[inst].payload;
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);
361 return emit.encode(mnemonic, .{
362 .op1 = op1,
363 .op2 = op2,
364 });
663365}
664366
665fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
666 const ops = emit.mir.instructions.items(.ops)[inst].decode();
667 assert(ops.reg2 == .none);
668 const payload = emit.mir.instructions.items(.data)[inst].payload;
669 const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode();
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);
367fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic {
368 inline for (@typeInfo(bits.Condition).Enum.fields) |field| {
369 if (mem.eql(u8, field.name, @tagName(cc)))
370 return @field(Instruction.Mnemonic, basename ++ field.name);
371 } else unreachable;
686372}
687373
688fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
689 const mir_tag = emit.mir.instructions.items(.tag)[inst];
690 assert(mir_tag == .mov_sign_extend);
691 const ops = emit.mir.instructions.items(.ops)[inst].decode();
692 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;
693 switch (ops.flags) {
694 0b00 => {
695 const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx;
696 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
697 },
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);
374fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
375 const ops = emit.mir.instructions.items(.ops)[inst];
376 switch (ops) {
377 .rr_c => {
378 const data = emit.mir.instructions.items(.data)[inst].rr_c;
379 const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
380 return emit.encode(mnemonic, .{
381 .op1 = .{ .reg = data.r1 },
382 .op2 = .{ .reg = data.r2 },
383 });
715384 },
385 else => unreachable, // TODO
716386 }
717387}
718388
719fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
720 const mir_tag = emit.mir.instructions.items(.tag)[inst];
721 assert(mir_tag == .mov_zero_extend);
722 const ops = emit.mir.instructions.items(.ops)[inst].decode();
723 const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined;
724 switch (ops.flags) {
725 0b00 => {
726 return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
727 },
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", .{});
389fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
390 const ops = emit.mir.instructions.items(.ops)[inst];
391 switch (ops) {
392 .r_c => {
393 const data = emit.mir.instructions.items(.data)[inst].r_c;
394 const mnemonic = mnemonicFromConditionCode("set", data.cc);
395 return emit.encode(mnemonic, .{
396 .op1 = .{ .reg = data.r1 },
397 });
742398 },
399 else => unreachable, // TODO
743400 }
744401}
745402
746fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
747 const tag = emit.mir.instructions.items(.tag)[inst];
748 assert(tag == .movabs);
749 const ops = emit.mir.instructions.items(.ops)[inst].decode();
750 switch (ops.flags) {
751 0b00 => {
752 const imm: u64 = if (ops.reg1.size() == 64) blk: {
753 const payload = emit.mir.instructions.items(.data)[inst].payload;
754 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
755 break :blk imm.decode();
756 } else emit.mir.instructions.items(.data)[inst].imm;
757 // movabs reg, imm64
758 // OI
759 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code);
760 },
761 0b01 => {
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);
403fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
404 const ops = emit.mir.instructions.items(.ops)[inst];
405 switch (ops) {
406 .inst_cc => {
407 const data = emit.mir.instructions.items(.data)[inst].inst_cc;
408 const mnemonic = mnemonicFromConditionCode("j", data.cc);
409 const source = emit.code.items.len;
410 try emit.encode(mnemonic, .{
411 .op1 = .{ .imm = Immediate.s(0) },
412 });
413 try emit.relocs.append(emit.bin_file.allocator, .{
414 .source = source,
415 .target = data.inst,
416 .offset = emit.code.items.len - 4,
417 .length = 6,
418 });
780419 },
781 else => return emit.fail("TODO unused movabs variant", .{}),
420 else => unreachable, // TODO
782421 }
783422}
784423
785fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
786 const tag = emit.mir.instructions.items(.tag)[inst];
787 assert(tag == .fisttp);
788 const ops = emit.mir.instructions.items(.ops)[inst].decode();
789
790 // the selecting between operand sizes for this particular `fisttp` instruction
791 // is done via opcode instead of the usual prefixes.
792
793 const opcode: Tag = switch (ops.flags) {
794 0b00 => .fisttp16,
795 0b01 => .fisttp32,
796 0b10 => .fisttp64,
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);
424fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
425 const target = emit.mir.instructions.items(.data)[inst].inst;
426 const source = emit.code.items.len;
427 try emit.encode(.jmp, .{
428 .op1 = .{ .imm = Immediate.s(0) },
429 });
430 try emit.relocs.append(emit.bin_file.allocator, .{
431 .source = source,
432 .target = target,
433 .offset = emit.code.items.len - 4,
434 .length = 5,
435 });
826436}
827437
828fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
829 const ops = emit.mir.instructions.items(.ops)[inst].decode();
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}
438fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
439 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
852440
853fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
854 const ops = emit.mir.instructions.items(.ops)[inst].decode();
855 if (ops.reg1 != .none) {
856 assert(ops.reg2 == .none);
857 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
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,
441 const offset = blk: {
442 try emit.encode(.call, .{
443 .op1 = .{ .imm = Immediate.s(0) },
444 });
445 break :blk @intCast(u32, emit.code.items.len) - 4;
866446 };
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 {
874 const tag = emit.mir.instructions.items(.tag)[inst];
875 assert(tag == .imul_complex);
876 const ops = emit.mir.instructions.items(.ops)[inst].decode();
877 switch (ops.flags) {
878 0b00 => {
879 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
880 },
881 0b01 => {
882 const imm = emit.mir.instructions.items(.data)[inst].imm;
883 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
884 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
885 .disp = imm,
886 .base = src_reg,
887 }), emit.code);
888 },
889 0b10 => {
890 const imm = emit.mir.instructions.items(.data)[inst].imm;
891 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
892 },
893 0b11 => {
894 const payload = emit.mir.instructions.items(.data)[inst].payload;
895 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
896 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
897 .disp = imm_pair.dest_off,
898 .base = ops.reg2,
899 }), imm_pair.operand, emit.code);
900 },
448 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
449 // Add relocation to the decl.
450 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
451 const target = macho_file.getGlobalByIndex(relocation.sym_index);
452 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
453 .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
454 .target = target,
455 .offset = offset,
456 .addend = 0,
457 .pcrel = true,
458 .length = 2,
459 });
460 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
461 // Add relocation to the decl.
462 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
463 const target = coff_file.getGlobalByIndex(relocation.sym_index);
464 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
465 .type = .direct,
466 .target = target,
467 .offset = offset,
468 .addend = 0,
469 .pcrel = true,
470 .length = 2,
471 });
472 } else {
473 return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{});
901474 }
902475}
903476
904fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
905 const ops = emit.mir.instructions.items(.ops)[inst].decode();
906 const tag: Tag = switch (ops.flags) {
907 0b00 => .cbw,
908 0b01 => .cwd,
909 0b10 => .cdq,
910 0b11 => .cqo,
911 };
912 return lowerToZoEnc(tag, emit.code);
913}
914
915fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
916 const tag = emit.mir.instructions.items(.tag)[inst];
917 assert(tag == .lea);
918 const ops = emit.mir.instructions.items(.ops)[inst].decode();
919 switch (ops.flags) {
920 0b00 => {
921 // lea reg1, [reg2 + imm32]
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,
477fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
478 const payload = emit.mir.instructions.items(.data)[inst].payload;
479 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;
480 const base = @intToEnum(Register, save_reg_list.base_reg);
481 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
482 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
483 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
484 for (callee_preserved_regs) |reg| {
485 if (reg_list.isSet(callee_preserved_regs, reg)) {
486 const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{
487 .base = base,
488 .disp = disp,
489 }) };
490 const op2: Instruction.Operand = .{ .reg = reg };
491 switch (tag) {
492 .push => try emit.encode(.mov, .{
493 .op1 = op1,
494 .op2 = op2,
931495 }),
932 emit.code,
933 );
934 },
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,
496 .pop => try emit.encode(.mov, .{
497 .op1 = op2,
498 .op2 = op1,
968499 }),
969 emit.code,
970 );
971 },
972 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}),
500 else => unreachable,
501 }
502 disp += 8;
503 }
973504 }
974505}
975506
976fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
977 const tag = emit.mir.instructions.items(.tag)[inst];
978 assert(tag == .lea_pic);
979 const ops = emit.mir.instructions.items(.ops)[inst].decode();
980 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
981
982 switch (ops.flags) {
983 0b00, 0b01, 0b10 => {},
984 else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}),
985 }
507fn mirLeaLinker(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
508 const ops = emit.mir.instructions.items(.ops)[inst];
509 const payload = emit.mir.instructions.items(.data)[inst].payload;
510 const metadata = emit.mir.extraData(Mir.LeaRegisterReloc, payload).data;
511 const reg = @intToEnum(Register, metadata.reg);
986512
987 // lea reg1, [rip + reloc]
988 // RM
989 try lowerToRmEnc(
990 .lea,
991 ops.reg1,
992 RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0),
993 emit.code,
994 );
513 try emit.encode(.lea, .{
514 .op1 = .{ .reg = reg },
515 .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
516 });
995517
996518 const end_offset = emit.code.items.len;
997519
998520 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
999 const reloc_type = switch (ops.flags) {
1000 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
521 const reloc_type = switch (ops) {
522 .got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
523 .direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1002524 else => unreachable,
1003525 };
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 }).?;
1005530 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
1006531 .type = reloc_type,
1007 .target = .{ .sym_index = relocation.sym_index, .file = null },
532 .target = .{ .sym_index = metadata.sym_index, .file = null },
1008533 .offset = @intCast(u32, end_offset - 4),
1009534 .addend = 0,
1010535 .pcrel = true,
1011536 .length = 2,
1012537 });
1013538 } 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 }).?;
1015543 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
1016 .type = switch (ops.flags) {
1017 0b00 => .got,
1018 0b01 => .direct,
1019 0b10 => .import,
544 .type = switch (ops) {
545 .got_reloc => .got,
546 .direct_reloc => .direct,
547 .import_reloc => .import,
1020548 else => unreachable,
1021549 },
1022 .target = switch (ops.flags) {
1023 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null },
1024 0b10 => coff_file.getGlobalByIndex(relocation.sym_index),
550 .target = switch (ops) {
551 .got_reloc, .direct_reloc => .{ .sym_index = metadata.sym_index, .file = null },
552 .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index),
1025553 else => unreachable,
1026554 },
1027555 .offset = @intCast(u32, end_offset - 4),
......@@ -1034,158 +562,23 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1034562 }
1035563}
1036564
1037// SSE instructions
1038
1039fn mirMovFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1040 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1041 switch (ops.flags) {
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 }
565fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
566 const payload = emit.mir.instructions.items(.data)[inst].payload;
567 const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data;
568 log.debug("mirDbgLine", .{});
569 try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column);
1061570}
1062571
1063fn mirAddFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1064 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1065 switch (ops.flags) {
1066 0b00 => {
1067 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1068 },
1069 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1070 }
1071}
1072
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;
572fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
573 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
574 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
575 log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc });
576 switch (emit.debug_output) {
577 .dwarf => |dw| {
578 try dw.advancePCAndLine(delta_line, delta_pc);
579 emit.prev_di_line = line;
580 emit.prev_di_column = column;
581 emit.prev_di_pc = emit.code.items.len;
1189582 },
1190583 .plan9 => |dbg_out| {
1191584 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 {
1227620}
1228621
1229622fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1230 const tag = emit.mir.instructions.items(.tag)[inst];
1231 assert(tag == .dbg_prologue_end);
623 _ = inst;
1232624 switch (emit.debug_output) {
1233625 .dwarf => |dw| {
1234626 try dw.setPrologueEnd();
......@@ -1244,8 +636,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1244636}
1245637
1246638fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1247 const tag = emit.mir.instructions.items(.tag)[inst];
1248 assert(tag == .dbg_epilogue_begin);
639 _ = inst;
1249640 switch (emit.debug_output) {
1250641 .dwarf => |dw| {
1251642 try dw.setEpilogueBegin();
......@@ -1259,1839 +650,3 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1259650 .none => {},
1260651 }
1261652}
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");
1212const assert = std.debug.assert;
1313
1414const bits = @import("bits.zig");
15const encoder = @import("encoder.zig");
16
1517const Air = @import("../../Air.zig");
1618const CodeGen = @import("CodeGen.zig");
1719const IntegerBitSet = std.bit_set.IntegerBitSet;
20const Memory = bits.Memory;
1821const Register = bits.Register;
1922
2023instructions: std.MultiArrayList(Inst).Slice,
......@@ -24,428 +27,300 @@ extra: []const u32,
2427pub const Inst = struct {
2528 tag: Tag,
2629 ops: Ops,
27 /// The meaning of this depends on `tag` and `ops`.
2830 data: Data,
2931
30 pub const Tag = enum(u16) {
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`.
32 pub const Index = u32;
9133
34 pub const Tag = enum(u8) {
35 /// Add with carry
36 adc,
37 /// Add
9238 add,
93 add_mem_imm,
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,
39 /// Logical and
11040 @"and",
111 and_mem_imm,
112 and_scale_src,
113 and_scale_dst,
114 and_scale_imm,
115 and_mem_index_imm,
116 @"or",
117 or_mem_imm,
118 or_scale_src,
119 or_scale_dst,
120 or_scale_imm,
121 or_mem_index_imm,
122 rol,
123 rol_mem_imm,
124 rol_scale_src,
125 rol_scale_dst,
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,
41 /// Call
42 call,
43 /// Convert byte to word
44 cbw,
45 /// Convert word to doubleword
46 cwde,
47 /// Convert doubleword to quadword
48 cdqe,
49 /// Convert word to doubleword
50 cwd,
51 /// Convert doubleword to quadword
52 cdq,
53 /// Convert doubleword to quadword
54 cqo,
55 /// Logical compare
15256 cmp,
153 cmp_mem_imm,
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,
57 /// Unsigned division
23558 div,
236
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
59 /// Store integer with truncation
27060 fisttp,
271
272 /// ops flags: form:
273 /// 0b01 dword ptr [reg1 + imm32]
274 /// 0b10 qword ptr [reg1 + imm32]
61 /// Load floating-point value
27562 fld,
276
277 /// ops flags: form:
278 /// 0b00 inst
279 /// 0b01 reg1
280 /// 0b01 [imm32] if reg1 is none
281 /// 0b10 [reg1 + imm32]
63 /// Signed division
64 idiv,
65 /// Signed multiplication
66 imul,
67 ///
68 int3,
69 /// Jump
28270 jmp,
283 call,
284
285 /// ops flags:
286 /// unused
287 /// Notes:
288 /// * uses `inst_cc` in Data.
289 cond_jmp,
290
291 /// ops flags:
292 /// 0b00 reg1
293 /// Notes:
294 /// * uses condition code (CC) stored as part of data
295 cond_set_byte,
296
297 /// ops flags:
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,
71 /// Load effective address
72 lea,
73 /// Move
74 mov,
75 /// Move with sign extension
76 movsx,
77 /// Move with zero extension
78 movzx,
79 /// Multiply
80 mul,
81 /// No-op
82 nop,
83 /// Logical or
84 @"or",
85 /// Pop
31486 pop,
315
316 /// ops flags: form:
317 /// 0b00 retf imm16
318 /// 0b01 retf
319 /// 0b10 retn imm16
320 /// 0b11 retn
87 /// Push
88 push,
89 /// Return
32190 ret,
322
323 /// Fast system call
91 /// Arithmetic shift left
92 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
324104 syscall,
325
326 /// ops flags: form:
327 /// 0b00 reg1, imm32 if reg2 == .none
328 /// 0b00 reg1, reg2
329 /// TODO handle more cases
105 /// Test condition
330106 @"test",
107 /// Undefined instruction
108 ud2,
109 /// Logical exclusive-or
110 xor,
331111
332 /// Breakpoint form:
333 /// 0b00 int3
334 interrupt,
335
336 /// Nop
337 nop,
338
339 /// SSE instructions
340 /// ops flags: form:
341 /// 0b00 reg1, qword ptr [reg2 + imm32]
342 /// 0b01 qword ptr [reg1 + imm32], reg2
343 /// 0b10 reg1, reg2
344 mov_f64_sse,
345 mov_f32_sse,
346
347 /// ops flags: form:
348 /// 0b00 reg1, reg2
349 add_f64_sse,
350 add_f32_sse,
351
352 /// ops flags: form:
353 /// 0b00 reg1, reg2
354 cmp_f64_sse,
355 cmp_f32_sse,
356
357 /// AVX instructions
358 /// ops flags: form:
359 /// 0b00 reg1, qword ptr [reg2 + imm32]
360 /// 0b01 qword ptr [reg1 + imm32], reg2
361 /// 0b10 reg1, reg1, reg2
362 mov_f64_avx,
363 mov_f32_avx,
364
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.
112 /// Add single precision floating point
113 addss,
114 /// Compare scalar single-precision floating-point values
115 cmpss,
116 /// Move scalar single-precision floating-point value
117 movss,
118 /// Unordered compare scalar single-precision floating-point values
119 ucomiss,
120 /// Add double precision floating point
121 addsd,
122 /// Compare scalar double-precision floating-point values
123 cmpsd,
124 /// Move scalar double-precision floating-point value
125 movsd,
126 /// Unordered compare scalar double-precision floating-point values
127 ucomisd,
128
129 /// Conditional move
130 cmovcc,
131 /// Conditional jump
132 jcc,
133 /// Set byte on condition
134 setcc,
135
136 /// Mov absolute to/from memory wrt segment register to/from rax
137 mov_moffs,
138
139 /// Jump with relocation to another local MIR instruction
140 /// Uses `inst` payload.
141 jmp_reloc,
142
143 /// Call to an extern symbol via linker relocation.
144 /// Uses `relocation` payload.
379145 call_extern,
380146
381 /// end of prologue
382 dbg_prologue_end,
147 /// Load effective address of a symbol not yet allocated in VM.
148 lea_linker,
383149
384 /// start of epilogue
150 /// End of prologue
151 dbg_prologue_end,
152 /// Start of epilogue
385153 dbg_epilogue_begin,
386
387 /// update debug line
154 /// Update debug line
155 /// Uses `payload` payload with data of type `DbgLineColumn`.
388156 dbg_line,
389
390 /// push registers
391 /// Uses `payload` field with `SaveRegisterList` as payload.
157 /// Push registers
158 /// Uses `payload` payload with data of type `SaveRegisterList`.
392159 push_regs,
393
394 /// pop registers
395 /// Uses `payload` field with `SaveRegisterList` as payload.
160 /// Pop registers
161 /// Uses `payload` payload with data of type `SaveRegisterList`.
396162 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 {
402 reg1: u7,
403 reg2: u7,
404 flags: u2,
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 }
164 /// Tombstone
165 /// Emitter should skip this instruction.
166 dead,
167 };
417168
418 pub fn decode(ops: Ops) struct {
419 reg1: Register,
420 reg2: Register,
421 flags: u2,
422 } {
423 return .{
424 .reg1 = @intToEnum(Register, ops.reg1),
425 .reg2 = @intToEnum(Register, ops.reg2),
426 .flags = ops.flags,
427 };
428 }
169 pub const Ops = enum(u8) {
170 /// No data associated with this instruction (only mnemonic is used).
171 none,
172 /// Single register operand.
173 /// Uses `r` payload.
174 r,
175 /// Register, register operands.
176 /// Uses `rr` payload.
177 rr,
178 /// Register, register, register operands.
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,
429268 };
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.
434270 pub const Data = union {
435 /// Another instruction.
271 /// References another Mir instruction.
436272 inst: Index,
437 /// A 32-bit immediate value.
438 imm: u32,
439 /// A condition code for use with EFLAGS register.
440 cc: bits.Condition,
441 /// Another instruction with condition code.
442 /// Used by `cond_jmp`.
273 /// Another instruction with condition code (CC).
274 /// Used by `jcc`.
443275 inst_cc: struct {
444276 /// Another instruction.
445277 inst: Index,
446278 /// A condition code for use with EFLAGS register.
447279 cc: bits.Condition,
448280 },
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 },
449324 /// Relocation for the linker where:
450325 /// * `atom_index` is the index of the source
451326 /// * `sym_index` is the index of the target
......@@ -468,62 +343,13 @@ pub const Inst = struct {
468343 }
469344};
470345
471pub const IndexRegisterDisp = struct {
472 /// Index register to use with SIB-based encoding
473 index: u32,
474
475 /// Displacement value
476 disp: u32,
477
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 }
346pub const LeaRegisterReloc = struct {
347 /// Destination register.
348 reg: u32,
349 /// Index of the containing atom.
350 atom_index: u32,
351 /// Index into the linker's symbol table.
352 sym_index: u32,
527353};
528354
529355/// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers
......@@ -567,16 +393,13 @@ pub const RegisterList = struct {
567393};
568394
569395pub const SaveRegisterList = struct {
396 /// Base register
397 base_reg: u32,
570398 /// Use `RegisterList` to populate.
571399 register_list: u32,
572400 stack_end: u32,
573401};
574402
575pub const ImmPair = struct {
576 dest_off: u32,
577 operand: u32,
578};
579
580403pub const Imm64 = struct {
581404 msb: u32,
582405 lsb: u32,
......@@ -596,6 +419,90 @@ pub const Imm64 = struct {
596419 }
597420};
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
599506pub const DbgLineColumn = struct {
600507 line: u32,
601508 column: u32,
......@@ -607,9 +514,9 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
607514 mir.* = undefined;
608515}
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 } {
611518 const fields = std.meta.fields(T);
612 var i: usize = index;
519 var i: u32 = index;
613520 var result: T = undefined;
614521 inline for (fields) |field| {
615522 @field(result, field.name) = switch (field.type) {
src/arch/x86_64/bits.zig+301-845
......@@ -1,9 +1,9 @@
11const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
42const assert = std.debug.assert;
5const ArrayList = std.ArrayList;
3const expect = std.testing.expect;
4
65const Allocator = std.mem.Allocator;
6const ArrayList = std.ArrayList;
77const DW = std.dwarf;
88
99/// EFLAGS condition codes
......@@ -135,131 +135,199 @@ pub const Condition = enum(u5) {
135135 }
136136};
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.
152138pub const Register = enum(u7) {
153139 // zig fmt: off
154 // 0 through 15, 64-bit registers. 8-15 are extended.
155 // id is just the int value.
156140 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
157141 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.
161143 eax, ecx, edx, ebx, esp, ebp, esi, edi,
162144 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.
166146 ax, cx, dx, bx, sp, bp, si, di,
167147 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
168148
169 // 48-63, 8-bit registers. 56-63 are extended.
170 // id is int value - 48.
171 al, cl, dl, bl, ah, ch, dh, bh,
149 al, cl, dl, bl, spl, bpl, sil, dil,
172150 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
173151
174 // 64-79, 256-bit registers.
175 // id is int value - 64.
152 ah, ch, dh, bh,
153
176154 ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,
177155 ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
178156
179 // 80-95, 128-bit registers.
180 // id is int value - 80.
181157 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
182158 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
183159
184 // Pseudo-value for MIR instructions.
160 es, cs, ss, ds, fs, gs,
161
185162 none,
186163 // zig fmt: on
187164
188 pub fn id(self: Register) u7 {
189 return switch (@enumToInt(self)) {
190 0...63 => @as(u7, @truncate(u4, @enumToInt(self))),
191 64...79 => @enumToInt(self),
165 pub const Class = enum(u2) {
166 general_purpose,
167 floating_point,
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
192185 else => unreachable,
186 // zig fmt: on
193187 };
194188 }
195189
196 /// Returns the bit-width of the register.
197 pub fn size(self: Register) u9 {
198 return switch (@enumToInt(self)) {
199 0...15 => 64,
200 16...31 => 32,
201 32...47 => 16,
202 48...63 => 8,
203 64...79 => 256,
204 80...95 => 128,
190 pub fn id(reg: Register) u6 {
191 const base = switch (@enumToInt(reg)) {
192 // zig fmt: off
193 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax),
194 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax),
195 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax),
196 @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al),
197 @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4,
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
205204 else => unreachable,
205 // zig fmt: on
206206 };
207 return @intCast(u6, @enumToInt(reg) - base);
207208 }
208209
209 /// Returns whether the register is *extended*. Extended registers are the
210 /// new registers added with amd64, r8 through r15. This also includes any
211 /// other variant of access to those registers, such as r8b, r15d, and so
212 /// on. This is needed because access to these registers requires special
213 /// handling via the REX prefix, via the B or R bits, depending on context.
214 pub fn isExtended(self: Register) bool {
215 return @enumToInt(self) & 0x08 != 0;
210 pub fn bitSize(reg: Register) u64 {
211 return switch (@enumToInt(reg)) {
212 // zig fmt: off
213 @enumToInt(Register.rax) ... @enumToInt(Register.r15) => 64,
214 @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => 32,
215 @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => 16,
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 };
216227 }
217228
218 /// This returns the 4-bit register ID, which is used in practically every
219 /// opcode. Note that bit 3 (the highest bit) is *never* used directly in
220 /// an instruction (@see isExtended), and requires special handling. The
221 /// lower three bits are often embedded directly in instructions (such as
222 /// the B8 variant of moves), or used in R/M bytes.
223 pub fn enc(self: Register) u4 {
224 return @truncate(u4, @enumToInt(self));
229 pub fn isExtended(reg: Register) bool {
230 return switch (@enumToInt(reg)) {
231 // zig fmt: off
232 @enumToInt(Register.r8) ... @enumToInt(Register.r15) => true,
233 @enumToInt(Register.r8d) ... @enumToInt(Register.r15d) => true,
234 @enumToInt(Register.r8w) ... @enumToInt(Register.r15w) => true,
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 };
225243 }
226244
227 /// Like enc, but only returns the lower 3 bits.
228 pub fn lowEnc(self: Register) u3 {
229 return @truncate(u3, @enumToInt(self));
245 pub fn enc(reg: Register) u4 {
246 const base = switch (@enumToInt(reg)) {
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 };
230293 }
231294
232 pub fn to256(self: Register) Register {
233 return @intToEnum(Register, @as(u8, self.enc()) + 64);
295 pub fn to64(reg: Register) Register {
296 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.rax));
234297 }
235298
236 pub fn to128(self: Register) Register {
237 return @intToEnum(Register, @as(u8, self.enc()) + 80);
299 pub fn to32(reg: Register) Register {
300 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.eax));
238301 }
239302
240 /// Convert from any register to its 64 bit alias.
241 pub fn to64(self: Register) Register {
242 return @intToEnum(Register, self.enc());
303 pub fn to16(reg: Register) Register {
304 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.ax));
243305 }
244306
245 /// Convert from any register to its 32 bit alias.
246 pub fn to32(self: Register) Register {
247 return @intToEnum(Register, @as(u8, self.enc()) + 16);
307 pub fn to8(reg: Register) Register {
308 return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.al));
248309 }
249310
250 /// Convert from any register to its 16 bit alias.
251 pub fn to16(self: Register) Register {
252 return @intToEnum(Register, @as(u8, self.enc()) + 32);
311 fn fpBase(reg: Register) u7 {
312 assert(reg.class() == .floating_point);
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 };
253318 }
254319
255 /// Convert from any register to its 8 bit alias.
256 pub fn to8(self: Register) Register {
257 return @intToEnum(Register, @as(u8, self.enc()) + 48);
320 pub fn to256(reg: Register) Register {
321 return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.ymm0));
258322 }
259323
260 pub fn dwarfLocOp(self: Register) u8 {
261 switch (@enumToInt(self)) {
262 0...63 => return switch (self.to64()) {
324 pub fn to128(reg: Register) Register {
325 return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.xmm0));
326 }
327
328 pub fn dwarfLocOp(reg: Register) u8 {
329 return switch (reg.class()) {
330 .general_purpose => switch (reg.to64()) {
263331 .rax => DW.OP.reg0,
264332 .rdx => DW.OP.reg1,
265333 .rcx => DW.OP.reg2,
......@@ -268,31 +336,19 @@ pub const Register = enum(u7) {
268336 .rdi => DW.OP.reg5,
269337 .rbp => DW.OP.reg6,
270338 .rsp => DW.OP.reg7,
271
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,
339 else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.reg0,
282340 },
283
284 64...79 => return @as(u8, self.enc()) + DW.OP.reg17,
285
341 .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.reg17,
286342 else => unreachable,
287 }
343 };
288344 }
289345
290346 /// DWARF encodings that push a value onto the DWARF stack that is either
291347 /// the contents of a register or the result of adding the contents a given
292348 /// register to a given signed offset.
293 pub fn dwarfLocOpDeref(self: Register) u8 {
294 switch (@enumToInt(self)) {
295 0...63 => return switch (self.to64()) {
349 pub fn dwarfLocOpDeref(reg: Register) u8 {
350 return switch (reg.class()) {
351 .general_purpose => switch (reg.to64()) {
296352 .rax => DW.OP.breg0,
297353 .rdx => DW.OP.breg1,
298354 .rcx => DW.OP.breg2,
......@@ -300,795 +356,195 @@ pub const Register = enum(u7) {
300356 .rsi => DW.OP.breg4,
301357 .rdi => DW.OP.breg5,
302358 .rbp => DW.OP.breg6,
303 .rsp => DW.OP.fbreg,
304
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,
359 .rsp => DW.OP.breg7,
360 else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.breg0,
315361 },
316
317 64...79 => return @as(u8, self.enc()) + DW.OP.breg17,
318
362 .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.breg17,
319363 else => unreachable,
320 }
364 };
321365 }
322366};
323367
324// zig fmt: on
325
326/// Encoding helper functions for x86_64 instructions
327///
328/// Many of these helpers do very little, but they can help make things
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 }
368test "Register id - different classes" {
369 try expect(Register.al.id() == Register.ax.id());
370 try expect(Register.ah.id() == Register.spl.id());
371 try expect(Register.ax.id() == Register.eax.id());
372 try expect(Register.eax.id() == Register.rax.id());
372373
373 // --------
374 // Prefixes
375 // --------
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 };
374 try expect(Register.ymm0.id() == 0b10000);
375 try expect(Register.ymm0.id() != Register.rax.id());
376 try expect(Register.xmm0.id() == Register.ymm0.id());
407377
408 /// Encodes legacy prefixes
409 pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void {
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 }
378 try expect(Register.es.id() == 0b100000);
379}
487380
488 pub fn simd_prefix_66(self: *Vex) void {
489 self.simd_prefix = 0b01;
490 }
381test "Register enc - different classes" {
382 try expect(Register.al.enc() == Register.ax.enc());
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 {
493 self.simd_prefix = 0b10;
494 }
390test "Register classes" {
391 try expect(Register.r11.class() == .general_purpose);
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 {
497 self.simd_prefix = 0b11;
498 }
396pub const Memory = union(enum) {
397 sib: Sib,
398 rip: Rip,
399 moffs: Moffs,
499400
500 pub fn wig(self: *Vex) void {
501 self.wig_desc = true;
502 }
401 pub const ScaleIndex = packed struct {
402 scale: u4,
403 index: Register,
404 };
503405
504 pub fn lig(self: *Vex) void {
505 self.lig_desc = true;
406 pub const PtrSize = enum {
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;
506426 }
507427
508 pub fn lz(self: *Vex) void {
509 self.lz_desc = true;
428 pub fn fromBitSize(bit_size: u64) PtrSize {
429 return switch (bit_size) {
430 8 => .byte,
431 16 => .word,
432 32 => .dword,
433 64 => .qword,
434 80 => .tbyte,
435 else => unreachable,
436 };
510437 }
511438
512 pub fn write(self: Vex, writer: anytype) usize {
513 var buf: [3]u8 = .{0} ** 3;
514 const form_3byte: bool = blk: {
515 if (self.rex_prefix.w and !self.wig_desc) break :blk true;
516 if (self.rex_prefix.x or self.rex_prefix.b) break :blk true;
517 break :blk self.lead_opc != 0b0_0001;
439 pub fn bitSize(s: PtrSize) u64 {
440 return switch (s) {
441 .byte => 8,
442 .word => 16,
443 .dword => 32,
444 .qword => 64,
445 .tbyte => 80,
518446 };
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;
551447 }
552448 };
553449
554 pub fn vex(self: Self, prefix: Vex) void {
555 _ = prefix.write(self.code.writer());
556 }
557
558 /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB
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,
450 pub const Sib = struct {
451 ptr_size: PtrSize,
452 base: ?Register,
453 scale_index: ?ScaleIndex,
454 disp: i32,
570455 };
571456
572 /// Encodes a REX prefix byte given all the fields
573 ///
574 /// Use this byte whenever you need 64 bit operation,
575 /// or one of reg, index, r/m, base, or opcode-reg might be extended.
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 }
457 pub const Rip = struct {
458 ptr_size: PtrSize,
459 disp: i32,
460 };
795461
796 /// Construct a SIB byte with scale * index + base + disp32
797 /// r/m effective address: [base + scale * index + disp32]
798 ///
799 /// Remember to add a REX prefix byte if index or base are extended!
800 pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void {
801 self.sib(scale, index, base);
802 }
462 pub const Moffs = struct {
463 seg: Register,
464 offset: u64,
465 };
803466
804 /// Construct a SIB byte with base + disp32, no index
805 /// r/m effective address: [base + disp32]
806 ///
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);
467 pub fn moffs(reg: Register, offset: u64) Memory {
468 assert(reg.class() == .segment);
469 return .{ .moffs = .{ .seg = reg, .offset = offset } };
812470 }
813471
814 // -------------------------
815 // Trivial (no bit fiddling)
816 // -------------------------
817
818 /// Encode an 8 bit immediate
819 ///
820 /// It is sign-extended to 64 bits by the cpu.
821 pub fn imm8(self: Self, imm: i8) void {
822 self.code.appendAssumeCapacity(@bitCast(u8, imm));
472 pub fn sib(ptr_size: PtrSize, args: struct {
473 disp: i32,
474 base: ?Register = null,
475 scale_index: ?ScaleIndex = null,
476 }) Memory {
477 return .{ .sib = .{
478 .base = args.base,
479 .disp = args.disp,
480 .ptr_size = ptr_size,
481 .scale_index = args.scale_index,
482 } };
823483 }
824484
825 /// Encode an 8 bit displacement
826 ///
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));
485 pub fn rip(ptr_size: PtrSize, disp: i32) Memory {
486 return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } };
830487 }
831488
832 /// Encode an 16 bit immediate
833 ///
834 /// It is sign-extended to 64 bits by the cpu.
835 pub fn imm16(self: Self, imm: i16) void {
836 self.writeIntLittle(i16, imm);
489 pub fn isSegmentRegister(mem: Memory) bool {
490 return switch (mem) {
491 .moffs => true,
492 .rip => false,
493 .sib => |s| if (s.base) |r| r.class() == .segment else false,
494 };
837495 }
838496
839 /// Encode an 32 bit immediate
840 ///
841 /// It is sign-extended to 64 bits by the cpu.
842 pub fn imm32(self: Self, imm: i32) void {
843 self.writeIntLittle(i32, imm);
497 pub fn base(mem: Memory) ?Register {
498 return switch (mem) {
499 .moffs => |m| m.seg,
500 .sib => |s| s.base,
501 .rip => null,
502 };
844503 }
845504
846 /// Encode an 32 bit displacement
847 ///
848 /// It is sign-extended to 64 bits by the cpu.
849 pub fn disp32(self: Self, disp: i32) void {
850 self.writeIntLittle(i32, disp);
505 pub fn scaleIndex(mem: Memory) ?ScaleIndex {
506 return switch (mem) {
507 .moffs, .rip => null,
508 .sib => |s| s.scale_index,
509 };
851510 }
852511
853 /// Encode an 64 bit immediate
854 ///
855 /// It is sign-extended to 64 bits by the cpu.
856 pub fn imm64(self: Self, imm: u64) void {
857 self.writeIntLittle(u64, imm);
512 pub fn bitSize(mem: Memory) u64 {
513 return switch (mem) {
514 .rip => |r| r.ptr_size.bitSize(),
515 .sib => |s| s.ptr_size.bitSize(),
516 .moffs => unreachable,
517 };
858518 }
859519};
860520
861test "Encoder helpers - general purpose registers" {
862 var code = ArrayList(u8).init(testing.allocator);
863 defer code.deinit();
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 }
521pub const Immediate = union(enum) {
522 signed: i32,
523 unsigned: u64,
954524
955 {
956 stream.reset();
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]);
525 pub fn u(x: u64) Immediate {
526 return .{ .unsigned = x };
964527 }
965528
966 {
967 stream.reset();
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]);
529 pub fn s(x: i32) Immediate {
530 return .{ .signed = x };
978531 }
979532
980 var code = ArrayList(u8).init(testing.allocator);
981 defer code.deinit();
982
983 {
984 // vmovapd xmm1, xmm2
985 const encoder = try Encoder.init(&code, 4);
986 var vex = Encoder.Vex{};
987 vex.simd_prefix_66();
988 encoder.vex(vex); // use 64 bit operation
989 encoder.opcode_1byte(0x28);
990 encoder.modRm_direct(0, Register.xmm1.lowEnc());
991 try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items);
992 }
993
994 {
995 try code.resize(0);
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);
533 pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
534 return switch (imm) {
535 .signed => |x| switch (bit_size) {
536 1, 8 => @bitCast(u8, @intCast(i8, x)),
537 16 => @bitCast(u16, @intCast(i16, x)),
538 32, 64 => @bitCast(u32, x),
539 else => unreachable,
540 },
541 .unsigned => |x| switch (bit_size) {
542 1, 8 => @intCast(u8, x),
543 16 => @intCast(u16, x),
544 32 => @intCast(u32, x),
545 64 => x,
546 else => unreachable,
547 },
548 };
1010549 }
1011}
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"),
550};
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) {
2929 fail: *ErrorMsg,
3030};
3131
32pub const GenerateSymbolError = error{
32pub const CodeGenError = error{
3333 OutOfMemory,
3434 Overflow,
35 /// A Decl that this symbol depends on had a semantic analysis failure.
36 AnalysisFail,
35 CodegenFail,
3736};
3837
3938pub const DebugInfoOutput = union(enum) {
......@@ -63,19 +62,6 @@ pub const DebugInfoOutput = union(enum) {
6362 none,
6463};
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
7965pub fn generateFunction(
8066 bin_file: *link.File,
8167 src_loc: Module.SrcLoc,
......@@ -84,7 +70,7 @@ pub fn generateFunction(
8470 liveness: Liveness,
8571 code: *std.ArrayList(u8),
8672 debug_output: DebugInfoOutput,
87) GenerateSymbolError!Result {
73) CodeGenError!Result {
8874 switch (bin_file.options.target.cpu.arch) {
8975 .arm,
9076 .armeb,
......@@ -120,7 +106,7 @@ pub fn generateSymbol(
120106 code: *std.ArrayList(u8),
121107 debug_output: DebugInfoOutput,
122108 reloc_info: RelocInfo,
123) GenerateSymbolError!Result {
109) CodeGenError!Result {
124110 const tracy = trace(@src());
125111 defer tracy.end();
126112
......@@ -823,7 +809,7 @@ fn lowerDeclRef(
823809 code: *std.ArrayList(u8),
824810 debug_output: DebugInfoOutput,
825811 reloc_info: RelocInfo,
826) GenerateSymbolError!Result {
812) CodeGenError!Result {
827813 const target = bin_file.options.target;
828814 const module = bin_file.options.module.?;
829815 if (typed_value.ty.isSlice()) {
......@@ -880,6 +866,288 @@ fn lowerDeclRef(
880866 return Result.ok;
881867}
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
8831151pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
8841152 const payload_align = payload_ty.abiAlignment(target);
8851153 const error_align = Type.anyerror.abiAlignment(target);
src/codegen/c.zig+912-515
......@@ -17,12 +17,6 @@ const LazySrcLoc = Module.LazySrcLoc;
1717const Air = @import("../Air.zig");
1818const 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
2620const BigIntLimb = std.math.big.Limb;
2721const BigInt = std.math.big.int;
2822
......@@ -112,11 +106,7 @@ const ValueRenderLocation = enum {
112106 }
113107};
114108
115const BuiltinInfo = enum {
116 None,
117 Range,
118 Bits,
119};
109const BuiltinInfo = enum { none, bits };
120110
121111const reserved_idents = std.ComptimeStringMap(void, .{
122112 // C language
......@@ -440,16 +430,24 @@ pub const Function = struct {
440430 return f.object.dg.typeToCType(ty, kind);
441431 }
442432
433 fn byteSize(f: *Function, cty: CType) u64 {
434 return f.object.dg.byteSize(cty);
435 }
436
443437 fn renderType(f: *Function, w: anytype, t: Type) !void {
444438 return f.object.dg.renderType(w, t);
445439 }
446440
447 fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void {
448 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src } }, src_ty, location);
441 fn renderCType(f: *Function, w: anytype, t: CType.Index) !void {
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);
449447 }
450448
451449 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);
453451 }
454452
455453 fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 {
......@@ -574,9 +572,9 @@ pub const DeclGen = struct {
574572 const len_val = Value.initPayload(&len_pl.base);
575573
576574 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)});
578576 } 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)});
580578 }
581579 }
582580
......@@ -606,7 +604,7 @@ pub const DeclGen = struct {
606604 try writer.writeByte(')');
607605 }
608606 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)}),
610608 .decl_ref_mut, .decl_ref, .variable => {
611609 const decl_index = switch (ptr_val.tag()) {
612610 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
......@@ -670,7 +668,9 @@ pub const DeclGen = struct {
670668 container_ptr_ty,
671669 location,
672670 );
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 });
674674 },
675675 .end => {
676676 try writer.writeAll("((");
......@@ -680,7 +680,9 @@ pub const DeclGen = struct {
680680 container_ptr_ty,
681681 location,
682682 );
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 });
684686 },
685687 }
686688 },
......@@ -746,7 +748,7 @@ pub const DeclGen = struct {
746748 return writer.writeAll("false");
747749 }
748750 },
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)}),
750752 .Float => {
751753 const bits = ty.floatBits(target);
752754 var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits };
......@@ -780,11 +782,11 @@ pub const DeclGen = struct {
780782 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
781783 const ptr_ty = ty.slicePtrFieldType(&buf);
782784 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)});
784786 } else {
785787 try writer.writeAll("((");
786788 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)});
788790 },
789791 .Optional => {
790792 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -831,7 +833,7 @@ pub const DeclGen = struct {
831833
832834 return writer.writeByte('}');
833835 },
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)}),
835837 },
836838 .Union => {
837839 if (!location.isInitializer()) {
......@@ -854,7 +856,7 @@ pub const DeclGen = struct {
854856 if (!field.ty.hasRuntimeBits()) continue;
855857 try dg.renderValue(writer, field.ty, val, initializer_type);
856858 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)});
858860 if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}');
859861 return writer.writeByte('}');
860862 },
......@@ -868,7 +870,7 @@ pub const DeclGen = struct {
868870 try writer.writeAll("{ .payload = ");
869871 try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type);
870872 return writer.print(", .error = {x} }}", .{
871 try dg.fmtIntLiteral(ty.errorUnionSet(), val),
873 try dg.fmtIntLiteral(ty.errorUnionSet(), val, .Other),
872874 });
873875 },
874876 .Array, .Vector => {
......@@ -927,7 +929,7 @@ pub const DeclGen = struct {
927929 .decl_ref_mut,
928930 .decl_ref,
929931 => 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)}),
931933 },
932934 .Float => {
933935 const bits = ty.floatBits(target);
......@@ -999,8 +1001,9 @@ pub const DeclGen = struct {
9991001 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});
10001002 }
10011003
1002 try writer.writeAll("zig_make_special_");
1003 if (location == .StaticInitializer) try writer.writeAll("constant_");
1004 try writer.writeAll("zig_");
1005 try writer.writeAll(if (location == .StaticInitializer) "init" else "make");
1006 try writer.writeAll("_special_");
10041007 try dg.renderTypeForBuiltinFnName(writer, ty);
10051008 try writer.writeByte('(');
10061009 if (std.math.signbit(f128_val)) try writer.writeByte('-');
......@@ -1020,7 +1023,7 @@ pub const DeclGen = struct {
10201023 try writer.writeAll(", ");
10211024 empty = false;
10221025 }
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)});
10241027 if (!empty) try writer.writeByte(')');
10251028 return;
10261029 },
......@@ -1069,7 +1072,7 @@ pub const DeclGen = struct {
10691072 .int_u64, .one => {
10701073 try writer.writeAll("((");
10711074 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)});
10731076 },
10741077 .field_ptr,
10751078 .elem_ptr,
......@@ -1561,6 +1564,10 @@ pub const DeclGen = struct {
15611564 return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind);
15621565 }
15631566
1567 fn byteSize(dg: *DeclGen, cty: CType) u64 {
1568 return cty.byteSize(dg.ctypes.set, dg.module.getTarget());
1569 }
1570
15641571 /// Renders a type as a single identifier, generating intermediate typedefs
15651572 /// if necessary.
15661573 ///
......@@ -1573,9 +1580,12 @@ pub const DeclGen = struct {
15731580 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" |
15741581 ///
15751582 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 {
15761587 const store = &dg.ctypes.set;
15771588 const module = dg.module;
1578 const idx = try dg.typeToIndex(t, .complete);
15791589 _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{});
15801590 try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{});
15811591 }
......@@ -1584,6 +1594,7 @@ pub const DeclGen = struct {
15841594 c_value: struct {
15851595 f: *Function,
15861596 value: CValue,
1597 v: Vectorizer,
15871598 },
15881599 value: struct {
15891600 value: Value,
......@@ -1593,6 +1604,7 @@ pub const DeclGen = struct {
15931604 switch (self.*) {
15941605 .c_value => |v| {
15951606 try v.f.writeCValue(w, v.value, location);
1607 try v.v.elem(v.f, w);
15961608 },
15971609 .value => |v| {
15981610 try dg.renderValue(w, value_ty, v.value, location);
......@@ -1829,111 +1841,93 @@ pub const DeclGen = struct {
18291841 dg.module.markDeclAlive(decl);
18301842
18311843 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);
18331845 } else if (decl.isExtern()) {
1834 return 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) });
1846 try writer.writeAll(mem.sliceTo(decl.name, 0));
18401847 } else {
1841 const gpa = dg.gpa;
1842 const name = try decl.getFullyQualifiedName(dg.module);
1843 defer gpa.free(name);
1844
1845 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), expand
1846 // to 3x the length of its input
1847 if (name.len > 1365) {
1848 var hash = ident_hasher_init;
1849 hash.update(name);
1850 const ident_hash = hash.finalInt();
1851 try writer.writeAll("zig_D_");
1852 return std.fmt.formatIntValue(ident_hash, "x", .{}, writer);
1853 } else {
1854 return writer.print("{}", .{fmtIdent(name)});
1855 }
1848 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
1849 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
1850 var name: [100]u8 = undefined;
1851 var name_stream = std.io.fixedBufferStream(&name);
1852 decl.renderFullyQualifiedName(dg.module, name_stream.writer()) catch |err| switch (err) {
1853 error.NoSpaceLeft => {},
1854 };
1855 try writer.print("{}__{d}", .{
1856 fmtIdent(name_stream.getWritten()),
1857 @enumToInt(decl_index),
1858 });
18561859 }
18571860 }
18581861
18591862 fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void {
1860 const target = dg.module.getTarget();
1861 if (ty.isAbiInt()) {
1862 const int_info = ty.intInfo(target);
1863 const c_bits = toCIntBits(int_info.bits) orelse
1864 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
1865 try writer.print("{c}{d}", .{ signAbbrev(int_info.signedness), c_bits });
1866 } else if (ty.isRuntimeFloat()) {
1867 try ty.print(writer, dg.module);
1868 } else if (ty.isPtrAtRuntime()) {
1869 try writer.print("p{d}", .{ty.bitSize(target)});
1870 } else if (ty.zigTypeTag() == .Bool) {
1871 try writer.print("u8", .{});
1872 } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{
1873 ty.fmt(dg.module),
1874 });
1863 try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete));
1864 }
1865
1866 fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void {
1867 switch (cty.tag()) {
1868 else => try writer.print("{c}{d}", .{
1869 if (cty.isBool())
1870 signAbbrev(.unsigned)
1871 else if (cty.isInteger())
1872 signAbbrev(cty.signedness() orelse .unsigned)
1873 else if (cty.isFloat())
1874 @as(u8, 'f')
1875 else if (cty.isPointer())
1876 @as(u8, 'p')
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 }
18751885 }
18761886
18771887 fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void {
1878 const target = dg.module.getTarget();
1879 switch (info) {
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 }
1888 const cty = try dg.typeToCType(ty, .complete);
1889 const is_big = cty.tag() == .array;
18941890
1895 const max_val = try ty.maxInt(stack.get(), target);
1896 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)});
1897 },
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 },
1891 switch (info) {
1892 .none => if (!is_big) return,
1893 .bits => {},
19061894 }
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 )});
19071910 }
19081911
19091912 fn fmtIntLiteral(
19101913 dg: *DeclGen,
19111914 ty: Type,
19121915 val: Value,
1916 loc: ValueRenderLocation,
19131917 ) !std.fmt.Formatter(formatIntLiteral) {
1914 const int_info = ty.intInfo(dg.module.getTarget());
1915 const c_bits = toCIntBits(int_info.bits);
1916 if (c_bits == null or c_bits.? > 128)
1917 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
1918 const kind: CType.Kind = switch (loc) {
1919 .FunctionArgument => .parameter,
1920 .Initializer, .Other => .complete,
1921 .StaticInitializer => .global,
1922 };
19181923 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),
19201928 .val = val,
1921 .mod = dg.module,
19221929 } };
19231930 }
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 }
19371931};
19381932
19391933const CTypeFix = enum { prefix, suffix };
......@@ -2450,7 +2444,7 @@ pub fn genErrDecls(o: *Object) !void {
24502444 const len_val = Value.initPayload(&len_pl.base);
24512445
24522446 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),
24542448 });
24552449 }
24562450 try writer.writeAll("};\n");
......@@ -2501,7 +2495,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
25012495 var int_pl: Value.Payload.U64 = undefined;
25022496 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 };
25052502 const name_ty = Type.initPayload(&name_ty_pl.base);
25062503
25072504 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 {
25102507 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
25112508 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 });
25142513 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete);
25152514 try w.writeAll(" = ");
25162515 try o.dg.renderValue(w, name_ty, name_val, .Initializer);
25172516 try w.writeAll(";\n return (");
25182517 try o.dg.renderType(w, name_slice_ty);
25192518 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),
25212520 });
25222521
25232522 try w.writeAll(" }\n");
......@@ -2535,7 +2534,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
25352534
25362535 const fwd_decl_writer = o.dg.fwd_decl.writer();
25372536 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 );
25392543 try fwd_decl_writer.writeAll(";\n");
25402544
25412545 try w.print("static zig_{s} ", .{@tagName(key)});
......@@ -2741,6 +2745,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
27412745 .const_ty => unreachable, // excluded from function bodies
27422746 .arg => try airArg(f, inst),
27432747
2748 .trap => try airTrap(f.object.writer()),
27442749 .breakpoint => try airBreakpoint(f.object.writer()),
27452750 .ret_addr => try airRetAddr(f, inst),
27462751 .frame_addr => try airFrameAddress(f, inst),
......@@ -2752,35 +2757,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
27522757
27532758 // TODO use a different strategy for add, sub, mul, div
27542759 // that communicates to the optimizer that wrapping is UB.
2755 .add => try airBinOp(f, inst, "+", "add", .None),
2756 .sub => try airBinOp(f, inst, "-", "sub", .None),
2757 .mul => try airBinOp(f, inst, "*", "mul", .None),
2760 .add => try airBinOp(f, inst, "+", "add", .none),
2761 .sub => try airBinOp(f, inst, "-", "sub", .none),
2762 .mul => try airBinOp(f, inst, "*", "mul", .none),
27582763
27592764 .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),
27632768 .rem => blk: {
27642769 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();
27662771 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
27672772 // so we only check one.
2768 break :blk if (lhs_ty.isInt())
2769 try airBinOp(f, inst, "%", "rem", .None)
2773 break :blk if (lhs_scalar_ty.isInt())
2774 try airBinOp(f, inst, "%", "rem", .none)
27702775 else
27712776 try airBinFloatOp(f, inst, "fmod");
27722777 },
2773 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None),
2774 .mod => try airBinBuiltinCall(f, inst, "mod", .None),
2778 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
2779 .mod => try airBinBuiltinCall(f, inst, "mod", .none),
27752780
2776 .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits),
2777 .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits),
2778 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits),
2781 .addwrap => try airBinBuiltinCall(f, inst, "addw", .bits),
2782 .subwrap => try airBinBuiltinCall(f, inst, "subw", .bits),
2783 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .bits),
27792784
2780 .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits),
2781 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits),
2782 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits),
2783 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits),
2785 .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits),
2786 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits),
2787 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits),
2788 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits),
27842789
27852790 .sqrt => try airUnFloatOp(f, inst, "sqrt"),
27862791 .sin => try airUnFloatOp(f, inst, "sin"),
......@@ -2799,34 +2804,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
27992804
28002805 .mul_add => try airMulAdd(f, inst),
28012806
2802 .add_with_overflow => try airOverflow(f, inst, "add", .Bits),
2803 .sub_with_overflow => try airOverflow(f, inst, "sub", .Bits),
2804 .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits),
2805 .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits),
2807 .add_with_overflow => try airOverflow(f, inst, "add", .bits),
2808 .sub_with_overflow => try airOverflow(f, inst, "sub", .bits),
2809 .mul_with_overflow => try airOverflow(f, inst, "mul", .bits),
2810 .shl_with_overflow => try airOverflow(f, inst, "shl", .bits),
28062811
28072812 .min => try airMinMax(f, inst, '<', "fmin"),
28082813 .max => try airMinMax(f, inst, '>', "fmax"),
28092814
28102815 .slice => try airSlice(f, inst),
28112816
2812 .cmp_gt => try airCmpOp(f, inst, ">", "gt"),
2813 .cmp_gte => try airCmpOp(f, inst, ">=", "ge"),
2814 .cmp_lt => try airCmpOp(f, inst, "<", "lt"),
2815 .cmp_lte => try airCmpOp(f, inst, "<=", "le"),
2817 .cmp_gt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gt),
2818 .cmp_gte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gte),
2819 .cmp_lt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lt),
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"),
2818 .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"),
2822 .cmp_eq => try airEquality(f, inst, .eq),
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 },
28212830 .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst),
28222831
28232832 // bool_and and bool_or are non-short-circuit operations
2824 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None),
2825 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None),
2826 .xor => try airBinOp(f, inst, "^", "xor", .None),
2827 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None),
2828 .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits),
2829 .shl_exact => try airBinOp(f, inst, "<<", "shl", .None),
2833 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .none),
2834 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .none),
2835 .xor => try airBinOp(f, inst, "^", "xor", .none),
2836 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .none),
2837 .shl, => try airBinBuiltinCall(f, inst, "shlw", .bits),
2838 .shl_exact => try airBinOp(f, inst, "<<", "shl", .none),
28302839 .not => try airNot (f, inst),
28312840
28322841 .optional_payload => try airOptionalPayload(f, inst),
......@@ -2871,11 +2880,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
28712880 .memcpy => try airMemcpy(f, inst),
28722881 .set_union_tag => try airSetUnionTag(f, inst),
28732882 .get_union_tag => try airGetUnionTag(f, inst),
2874 .clz => try airUnBuiltinCall(f, inst, "clz", .Bits),
2875 .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits),
2876 .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits),
2877 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits),
2878 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits),
2883 .clz => try airUnBuiltinCall(f, inst, "clz", .bits),
2884 .ctz => try airUnBuiltinCall(f, inst, "ctz", .bits),
2885 .popcount => try airUnBuiltinCall(f, inst, "popcount", .bits),
2886 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .bits),
2887 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .bits),
28792888 .tag_name => try airTagName(f, inst),
28802889 .error_name => try airErrorName(f, inst),
28812890 .splat => try airSplat(f, inst),
......@@ -3266,7 +3275,10 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
32663275
32673276fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
32683277 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;
32703282 const src_ty = ptr_info.pointee_type;
32713283
32723284 if (!src_ty.hasRuntimeBitsIgnoreComptime() or
......@@ -3284,20 +3296,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
32843296 const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target);
32853297 const is_array = lowersToArray(src_ty, target);
32863298 const need_memcpy = !is_aligned or is_array;
3287 const writer = f.object.writer();
32883299
3300 const writer = f.object.writer();
32893301 const local = try f.allocLocal(inst, src_ty);
3302 const v = try Vectorizer.start(f, inst, writer, ptr_ty);
32903303
32913304 if (need_memcpy) {
32923305 try writer.writeAll("memcpy(");
32933306 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);
32953309 try writer.writeAll(", (const char *)");
32963310 try f.writeCValue(writer, operand, .Other);
3311 try v.elem(f, writer);
32973312 try writer.writeAll(", sizeof(");
32983313 try f.renderType(writer, src_ty);
32993314 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) {
33013316 var host_pl = Type.Payload.Bits{
33023317 .base = .{ .tag = .int_unsigned },
33033318 .data = ptr_info.host_size * 8,
......@@ -3323,6 +3338,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
33233338 const field_ty = Type.initPayload(&field_pl.base);
33243339
33253340 try f.writeCValue(writer, local, .Other);
3341 try v.elem(f, writer);
33263342 try writer.writeAll(" = (");
33273343 try f.renderType(writer, src_ty);
33283344 try writer.writeAll(")zig_wrap_");
......@@ -3341,16 +3357,21 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
33413357 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
33423358 try writer.writeByte('(');
33433359 try f.writeCValueDeref(writer, operand);
3360 try v.elem(f, writer);
33443361 try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});
33453362 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);
33473364 try writer.writeByte(')');
33483365 } else {
33493366 try f.writeCValue(writer, local, .Other);
3367 try v.elem(f, writer);
33503368 try writer.writeAll(" = ");
33513369 try f.writeCValueDeref(writer, operand);
3370 try v.elem(f, writer);
33523371 }
33533372 try writer.writeAll(";\n");
3373 try v.end(f, inst, writer);
3374
33543375 return local;
33553376}
33563377
......@@ -3416,15 +3437,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
34163437
34173438 const operand = try f.resolveInst(ty_op.operand);
34183439 try reap(f, inst, &.{ty_op.operand});
3419 const writer = f.object.writer();
3440
34203441 const inst_ty = f.air.typeOfIndex(inst);
3421 const local = try f.allocLocal(inst, inst_ty);
3442 const inst_scalar_ty = inst_ty.scalarType();
34223443 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);
34243449 try f.writeCValue(writer, local, .Other);
3450 try v.elem(f, writer);
34253451 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);
34273453 try writer.writeAll(";\n");
3454 try v.end(f, inst, writer);
3455
34283456 return local;
34293457}
34303458
......@@ -3438,34 +3466,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
34383466 const operand = try f.resolveInst(ty_op.operand);
34393467 try reap(f, inst, &.{ty_op.operand});
34403468 const inst_ty = f.air.typeOfIndex(inst);
3441 const writer = f.object.writer();
3442 const local = try f.allocLocal(inst, inst_ty);
3469 const inst_scalar_ty = inst_ty.scalarType();
34433470 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);
34453472 const dest_bits = dest_int_info.bits;
34463473 const dest_c_bits = toCIntBits(dest_int_info.bits) orelse
34473474 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
34483475 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
34513483 try f.writeCValue(writer, local, .Other);
3484 try v.elem(f, writer);
34523485 try writer.writeAll(" = ");
34533486
34543487 if (dest_c_bits < 64) {
34553488 try writer.writeByte('(');
3456 try f.renderType(writer, inst_ty);
3489 try f.renderType(writer, inst_scalar_ty);
34573490 try writer.writeByte(')');
34583491 }
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;
34613494 if (needs_lo) {
34623495 try writer.writeAll("zig_lo_");
3463 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
3496 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
34643497 try writer.writeByte('(');
34653498 }
34663499
34673500 if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) {
34683501 try f.writeCValue(writer, operand, .Other);
3502 try v.elem(f, writer);
34693503 } else switch (dest_int_info.signedness) {
34703504 .unsigned => {
34713505 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa);
......@@ -3475,15 +3509,16 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
34753509 var stack align(@alignOf(ExpectedContents)) =
34763510 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);
34793513 try writer.writeAll("zig_and_");
3480 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
3514 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
34813515 try writer.writeByte('(');
34823516 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)});
34843519 },
34853520 .signed => {
3486 const c_bits = toCIntBits(operand_int_info.bits) orelse
3521 const c_bits = toCIntBits(scalar_int_info.bits) orelse
34873522 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
34883523 var shift_pl = Value.Payload.U64{
34893524 .base = .{ .tag = .int_u64 },
......@@ -3492,7 +3527,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
34923527 const shift_val = Value.initPayload(&shift_pl.base);
34933528
34943529 try writer.writeAll("zig_shr_");
3495 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
3530 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
34963531 if (c_bits == 128) {
34973532 try writer.print("(zig_bitcast_i{d}(", .{c_bits});
34983533 } else {
......@@ -3505,6 +3540,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
35053540 try writer.print("(uint{d}_t)", .{c_bits});
35063541 }
35073542 try f.writeCValue(writer, operand, .FunctionArgument);
3543 try v.elem(f, writer);
35083544 if (c_bits == 128) try writer.writeByte(')');
35093545 try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)});
35103546 if (c_bits == 128) try writer.writeByte(')');
......@@ -3514,6 +3550,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
35143550
35153551 if (needs_lo) try writer.writeByte(')');
35163552 try writer.writeAll(";\n");
3553 try v.end(f, inst, writer);
3554
35173555 return local;
35183556}
35193557
......@@ -3550,7 +3588,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {
35503588fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
35513589 // *a = b;
35523590 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;
35543595 if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) {
35553596 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
35563597 return .none;
......@@ -3573,11 +3614,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
35733614 ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target);
35743615 const is_array = lowersToArray(ptr_info.pointee_type, target);
35753616 const need_memcpy = !is_aligned or is_array;
3576 const writer = f.object.writer();
35773617
35783618 const src_val = try f.resolveInst(bin_op.rhs);
35793619 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
35813624 if (need_memcpy) {
35823625 // For this memcpy to safely work we need the rhs to have the same
35833626 // 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 {
35983641
35993642 try writer.writeAll("memcpy((char *)");
36003643 try f.writeCValue(writer, ptr_val, .FunctionArgument);
3644 try v.elem(f, writer);
36013645 try writer.writeAll(", ");
36023646 if (!is_array) try writer.writeByte('&');
36033647 try f.writeCValue(writer, array_src, .FunctionArgument);
3648 try v.elem(f, writer);
36043649 try writer.writeAll(", sizeof(");
36053650 try f.renderType(writer, src_ty);
36063651 try writer.writeAll("))");
36073652 if (src_val == .constant) {
36083653 try freeLocal(f, inst, array_src.new_local, 0);
36093654 }
3610 } else if (ptr_info.host_size != 0) {
3655 } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) {
36113656 const host_bits = ptr_info.host_size * 8;
36123657 var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits };
36133658 const host_ty = Type.initPayload(&host_pl.base);
......@@ -3644,12 +3689,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
36443689 const mask_val = Value.initPayload(&mask_pl.base);
36453690
36463691 try f.writeCValueDeref(writer, ptr_val);
3692 try v.elem(f, writer);
36473693 try writer.writeAll(" = zig_or_");
36483694 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
36493695 try writer.writeAll("(zig_and_");
36503696 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
36513697 try writer.writeByte('(');
36523698 try f.writeCValueDeref(writer, ptr_val);
3699 try v.elem(f, writer);
36533700 try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)});
36543701 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
36553702 try writer.writeByte('(');
......@@ -3671,14 +3718,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
36713718 try writer.writeByte(')');
36723719 }
36733720 try f.writeCValue(writer, src_val, .Other);
3721 try v.elem(f, writer);
36743722 if (cant_cast) try writer.writeByte(')');
36753723 try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});
36763724 } else {
36773725 try f.writeCValueDeref(writer, ptr_val);
3726 try v.elem(f, writer);
36783727 try writer.writeAll(" = ");
36793728 try f.writeCValue(writer, src_val, .Other);
3729 try v.elem(f, writer);
36803730 }
36813731 try writer.writeAll(";\n");
3732 try v.end(f, inst, writer);
3733
36823734 return .none;
36833735}
36843736
......@@ -3696,51 +3748,39 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
36963748 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36973749
36983750 const inst_ty = f.air.typeOfIndex(inst);
3699 const vector_ty = f.air.typeOf(bin_op.lhs);
3700 const scalar_ty = vector_ty.scalarType();
3701 const w = f.object.writer();
3751 const operand_ty = f.air.typeOf(bin_op.lhs);
3752 const scalar_ty = operand_ty.scalarType();
37023753
3754 const w = f.object.writer();
37033755 const local = try f.allocLocal(inst, inst_ty);
3704
3705 switch (vector_ty.zigTypeTag()) {
3706 .Vector => {
3707 try w.writeAll("zig_v");
3708 try w.writeAll(operation);
3709 try w.writeAll("o_");
3710 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3711 try w.writeAll("(");
3712 try f.writeCValueMember(w, local, .{ .field = 1 });
3713 try w.writeAll(", ");
3714 try f.writeCValueMember(w, local, .{ .field = 0 });
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
3756 const v = try Vectorizer.start(f, inst, w, operand_ty);
3757 try f.writeCValueMember(w, local, .{ .field = 1 });
3758 try v.elem(f, w);
3759 try w.writeAll(" = zig_");
3760 try w.writeAll(operation);
3761 try w.writeAll("o_");
3762 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3763 try w.writeAll("(&");
3764 try f.writeCValueMember(w, local, .{ .field = 0 });
3765 try v.elem(f, w);
3766 try w.writeAll(", ");
37293767 try f.writeCValue(w, lhs, .FunctionArgument);
3768 try v.elem(f, w);
37303769 try w.writeAll(", ");
37313770 try f.writeCValue(w, rhs, .FunctionArgument);
3771 try v.elem(f, w);
37323772 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
37333773 try w.writeAll(");\n");
3774 try v.end(f, inst, w);
37343775
37353776 return local;
37363777}
37373778
37383779fn 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
37433780 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
37453785 if (f.liveness.isUnused(inst)) {
37463786 try reap(f, inst, &.{ty_op.operand});
......@@ -3750,14 +3790,20 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
37503790 const op = try f.resolveInst(ty_op.operand);
37513791 try reap(f, inst, &.{ty_op.operand});
37523792
3793 const inst_ty = f.air.typeOfIndex(inst);
3794
37533795 const writer = f.object.writer();
37543796 const local = try f.allocLocal(inst, inst_ty);
3755
3797 const v = try Vectorizer.start(f, inst, writer, operand_ty);
37563798 try f.writeCValue(writer, local, .Other);
3799 try v.elem(f, writer);
37573800 try writer.writeAll(" = ");
37583801 try writer.writeByte('!');
37593802 try f.writeCValue(writer, op, .Other);
3803 try v.elem(f, writer);
37603804 try writer.writeAll(";\n");
3805 try v.end(f, inst, writer);
3806
37613807 return local;
37623808}
37633809
......@@ -3770,63 +3816,89 @@ fn airBinOp(
37703816) !CValue {
37713817 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
37723818 const operand_ty = f.air.typeOf(bin_op.lhs);
3819 const scalar_ty = operand_ty.scalarType();
37733820 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())
37753822 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
37773829 const lhs = try f.resolveInst(bin_op.lhs);
37783830 const rhs = try f.resolveInst(bin_op.rhs);
3779
37803831 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37813832
3782 if (f.liveness.isUnused(inst)) return .none;
3783
37843833 const inst_ty = f.air.typeOfIndex(inst);
37853834
37863835 const writer = f.object.writer();
37873836 const local = try f.allocLocal(inst, inst_ty);
3837 const v = try Vectorizer.start(f, inst, writer, operand_ty);
37883838 try f.writeCValue(writer, local, .Other);
3839 try v.elem(f, writer);
37893840 try writer.writeAll(" = ");
37903841 try f.writeCValue(writer, lhs, .Other);
3842 try v.elem(f, writer);
37913843 try writer.writeByte(' ');
37923844 try writer.writeAll(operator);
37933845 try writer.writeByte(' ');
37943846 try f.writeCValue(writer, rhs, .Other);
3847 try v.elem(f, writer);
37953848 try writer.writeAll(";\n");
3849 try v.end(f, inst, writer);
37963850
37973851 return local;
37983852}
37993853
3800fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8) !CValue {
3801 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3802
3854fn airCmpOp(
3855 f: *Function,
3856 inst: Air.Inst.Index,
3857 data: anytype,
3858 operator: std.math.CompareOperator,
3859) !CValue {
38033860 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 });
38053862 return .none;
38063863 }
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
38093868 const target = f.object.dg.module.getTarget();
3810 if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)
3811 return try cmpBuiltinCall(f, inst, operator, "cmp");
3812 if (operand_ty.isRuntimeFloat())
3813 return try cmpBuiltinCall(f, inst, operator, operation);
3869 const scalar_bits = scalar_ty.bitSize(target);
3870 if (scalar_ty.isInt() and scalar_bits > 64)
3871 return airCmpBuiltinCall(
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
38153882 const inst_ty = f.air.typeOfIndex(inst);
3816 const lhs = try f.resolveInst(bin_op.lhs);
3817 const rhs = try f.resolveInst(bin_op.rhs);
3818 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3883 const lhs = try f.resolveInst(data.lhs);
3884 const rhs = try f.resolveInst(data.rhs);
3885 try reap(f, inst, &.{ data.lhs, data.rhs });
38193886
38203887 const writer = f.object.writer();
38213888 const local = try f.allocLocal(inst, inst_ty);
3889 const v = try Vectorizer.start(f, inst, writer, operand_ty);
38223890 try f.writeCValue(writer, local, .Other);
3891 try v.elem(f, writer);
38233892 try writer.writeAll(" = ");
38243893 try f.writeCValue(writer, lhs, .Other);
3894 try v.elem(f, writer);
38253895 try writer.writeByte(' ');
3826 try writer.writeAll(operator);
3896 try writer.writeAll(compareOperatorC(operator));
38273897 try writer.writeByte(' ');
38283898 try f.writeCValue(writer, rhs, .Other);
3899 try v.elem(f, writer);
38293900 try writer.writeAll(";\n");
3901 try v.end(f, inst, writer);
38303902
38313903 return local;
38323904}
......@@ -3834,9 +3906,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation:
38343906fn airEquality(
38353907 f: *Function,
38363908 inst: Air.Inst.Index,
3837 negate_prefix: []const u8,
3838 operator: []const u8,
3839 operation: []const u8,
3909 operator: std.math.CompareOperator,
38403910) !CValue {
38413911 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
38423912
......@@ -3847,10 +3917,18 @@ fn airEquality(
38473917
38483918 const operand_ty = f.air.typeOf(bin_op.lhs);
38493919 const target = f.object.dg.module.getTarget();
3850 if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)
3851 return try cmpBuiltinCall(f, inst, operator, "cmp");
3920 const operand_bits = operand_ty.bitSize(target);
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 );
38523930 if (operand_ty.isRuntimeFloat())
3853 return try cmpBuiltinCall(f, inst, operator, operation);
3931 return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none);
38543932
38553933 const lhs = try f.resolveInst(bin_op.lhs);
38563934 const rhs = try f.resolveInst(bin_op.rhs);
......@@ -3866,7 +3944,12 @@ fn airEquality(
38663944 // (A && B) || (C && (A == B))
38673945 // 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("((");
38703953 try f.writeCValue(writer, lhs, .Other);
38713954 try writer.writeAll(".is_null && ");
38723955 try f.writeCValue(writer, rhs, .Other);
......@@ -3885,7 +3968,7 @@ fn airEquality(
38853968
38863969 try f.writeCValue(writer, lhs, .Other);
38873970 try writer.writeByte(' ');
3888 try writer.writeAll(operator);
3971 try writer.writeAll(compareOperatorC(operator));
38893972 try writer.writeByte(' ');
38903973 try f.writeCValue(writer, rhs, .Other);
38913974 try writer.writeAll(";\n");
......@@ -3927,11 +4010,14 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
39274010 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
39284011
39294012 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
39324016 const local = try f.allocLocal(inst, inst_ty);
39334017 const writer = f.object.writer();
4018 const v = try Vectorizer.start(f, inst, writer, inst_ty);
39344019 try f.writeCValue(writer, local, .Other);
4020 try v.elem(f, writer);
39354021 try writer.writeAll(" = ");
39364022
39374023 if (elem_ty.hasRuntimeBitsIgnoreComptime()) {
......@@ -3939,19 +4025,26 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
39394025 // results in a NULL pointer, or if LHS is NULL. The operation is only UB
39404026 // if the result is NULL and then dereferenced.
39414027 try writer.writeByte('(');
3942 try f.renderType(writer, inst_ty);
4028 try f.renderType(writer, inst_scalar_ty);
39434029 try writer.writeAll(")(((uintptr_t)");
39444030 try f.writeCValue(writer, lhs, .Other);
4031 try v.elem(f, writer);
39454032 try writer.writeAll(") ");
39464033 try writer.writeByte(operator);
39474034 try writer.writeAll(" (");
39484035 try f.writeCValue(writer, rhs, .Other);
4036 try v.elem(f, writer);
39494037 try writer.writeAll("*sizeof(");
39504038 try f.renderType(writer, elem_ty);
39514039 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
39544045 try writer.writeAll(";\n");
4046 try v.end(f, inst, writer);
4047
39554048 return local;
39564049}
39574050
......@@ -3964,10 +4057,12 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
39644057 }
39654058
39664059 const inst_ty = f.air.typeOfIndex(inst);
4060 const inst_scalar_ty = inst_ty.scalarType();
4061
39674062 const target = f.object.dg.module.getTarget();
3968 if (inst_ty.isInt() and inst_ty.bitSize(target) > 64)
3969 return try airBinBuiltinCall(f, inst, operation[1..], .None);
3970 if (inst_ty.isRuntimeFloat())
4063 if (inst_scalar_ty.isInt() and inst_scalar_ty.bitSize(target) > 64)
4064 return try airBinBuiltinCall(f, inst, operation[1..], .none);
4065 if (inst_scalar_ty.isRuntimeFloat())
39714066 return try airBinFloatOp(f, inst, operation);
39724067
39734068 const lhs = try f.resolveInst(bin_op.lhs);
......@@ -3976,19 +4071,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
39764071
39774072 const writer = f.object.writer();
39784073 const local = try f.allocLocal(inst, inst_ty);
4074 const v = try Vectorizer.start(f, inst, writer, inst_ty);
39794075 try f.writeCValue(writer, local, .Other);
4076 try v.elem(f, writer);
39804077 // (lhs <> rhs) ? lhs : rhs
39814078 try writer.writeAll(" = (");
39824079 try f.writeCValue(writer, lhs, .Other);
4080 try v.elem(f, writer);
39834081 try writer.writeByte(' ');
39844082 try writer.writeByte(operator);
39854083 try writer.writeByte(' ');
39864084 try f.writeCValue(writer, rhs, .Other);
4085 try v.elem(f, writer);
39874086 try writer.writeAll(") ? ");
39884087 try f.writeCValue(writer, lhs, .Other);
4088 try v.elem(f, writer);
39894089 try writer.writeAll(" : ");
39904090 try f.writeCValue(writer, rhs, .Other);
4091 try v.elem(f, writer);
39914092 try writer.writeAll(";\n");
4093 try v.end(f, inst, writer);
39924094
39934095 return local;
39944096}
......@@ -4412,12 +4514,56 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
44124514
44134515 // Ensure padding bits have the expected value.
44144516 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
44154526 try f.writeCValue(writer, local, .Other);
4416 try writer.writeAll(" = zig_wrap_");
4417 try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty);
4527 if (dest_cty.castTag(.array)) |pl| {
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);
44184551 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 }
44194557 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(')');
44214567 try writer.writeAll(");\n");
44224568 }
44234569
......@@ -4428,6 +4574,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
44284574 return local;
44294575}
44304576
4577fn airTrap(writer: anytype) !CValue {
4578 try writer.writeAll("zig_trap();\n");
4579 return .none;
4580}
4581
44314582fn airBreakpoint(writer: anytype) !CValue {
44324583 try writer.writeAll("zig_breakpoint();\n");
44334584 return .none;
......@@ -5427,7 +5578,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
54275578 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
54285579 try writer.writeByte(')');
54295580 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);
54315582 try writer.writeAll(");\n");
54325583 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 {
58606011 try f.writeCValue(writer, operand, .FunctionArgument);
58616012 try writer.writeByte(')');
58626013 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);
58646015 try writer.writeByte(')');
58656016 }
58666017 try writer.writeAll(";\n");
......@@ -5906,19 +6057,35 @@ fn airUnBuiltinCall(
59066057 const operand = try f.resolveInst(ty_op.operand);
59076058 try reap(f, inst, &.{ty_op.operand});
59086059 const inst_ty = f.air.typeOfIndex(inst);
6060 const inst_scalar_ty = inst_ty.scalarType();
59096061 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
59116067 const writer = f.object.writer();
59126068 const local = try f.allocLocal(inst, inst_ty);
5913 try f.writeCValue(writer, local, .Other);
5914 try writer.writeAll(" = zig_");
5915 try writer.writeAll(operation);
5916 try writer.writeByte('_');
5917 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
6069 const v = try Vectorizer.start(f, inst, writer, operand_ty);
6070 if (!ref_ret) {
6071 try f.writeCValue(writer, local, .Other);
6072 try v.elem(f, writer);
6073 try writer.writeAll(" = ");
6074 }
6075 try writer.print("zig_{s}_", .{operation});
6076 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
59186077 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 }
59196083 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);
59216086 try writer.writeAll(");\n");
6087 try v.end(f, inst, writer);
6088
59226089 return local;
59236090}
59246091
......@@ -5935,55 +6102,108 @@ fn airBinBuiltinCall(
59356102 return .none;
59366103 }
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
59386109 const lhs = try f.resolveInst(bin_op.lhs);
59396110 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
59426113 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
59456120 const writer = f.object.writer();
59466121 const local = try f.allocLocal(inst, inst_ty);
5947 try f.writeCValue(writer, local, .Other);
5948 try writer.writeAll(" = zig_");
5949 try writer.writeAll(operation);
5950 try writer.writeByte('_');
5951 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
6122 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6123 const v = try Vectorizer.start(f, inst, writer, operand_ty);
6124 if (!ref_ret) {
6125 try f.writeCValue(writer, local, .Other);
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);
59526131 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 }
59536137 try f.writeCValue(writer, lhs, .FunctionArgument);
6138 try v.elem(f, writer);
59546139 try writer.writeAll(", ");
59556140 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);
59576143 try writer.writeAll(");\n");
6144 try v.end(f, inst, writer);
6145
59586146 return local;
59596147}
59606148
5961fn cmpBuiltinCall(
6149fn airCmpBuiltinCall(
59626150 f: *Function,
59636151 inst: Air.Inst.Index,
5964 operator: []const u8,
5965 operation: []const u8,
6152 data: anytype,
6153 operator: std.math.CompareOperator,
6154 operation: enum { cmp, operator },
6155 info: BuiltinInfo,
59666156) !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
59676166 const inst_ty = f.air.typeOfIndex(inst);
5968 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
5969 const operand_ty = f.air.typeOf(bin_op.lhs);
6167 const inst_scalar_ty = inst_ty.scalarType();
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);
5972 const rhs = try f.resolveInst(bin_op.rhs);
5973 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6171 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete);
6172 const ref_ret = inst_scalar_cty.tag() == .array;
59746173
59756174 const writer = f.object.writer();
59766175 const local = try f.allocLocal(inst, inst_ty);
5977 try f.writeCValue(writer, local, .Other);
5978 try writer.writeAll(" = zig_");
5979 try writer.writeAll(operation);
5980 try writer.writeByte('_');
5981 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
6176 const v = try Vectorizer.start(f, inst, writer, operand_ty);
6177 if (!ref_ret) {
6178 try f.writeCValue(writer, local, .Other);
6179 try v.elem(f, writer);
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);
59826187 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 }
59836193 try f.writeCValue(writer, lhs, .FunctionArgument);
6194 try v.elem(f, writer);
59846195 try writer.writeAll(", ");
59856196 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
59876207 return local;
59886208}
59896209
......@@ -6328,33 +6548,120 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
63286548
63296549fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
63306550 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
6551
63316552 if (f.liveness.isUnused(inst)) {
63326553 try reap(f, inst, &.{ty_op.operand});
63336554 return .none;
63346555 }
63356556
6336 const inst_ty = f.air.typeOfIndex(inst);
63376557 const operand = try f.resolveInst(ty_op.operand);
63386558 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
63396565 const writer = f.object.writer();
63406566 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(&");
63416569 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;
6345 return f.fail("TODO: C backend: implement airSplat", .{});
6581 return local;
63466582}
63476583
63486584fn 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;
63526618}
63536619
63546620fn 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;
63586665}
63596666
63606667fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
......@@ -6370,65 +6677,45 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
63706677 const operand = try f.resolveInst(reduce.operand);
63716678 try reap(f, inst, &.{reduce.operand});
63726679 const operand_ty = f.air.typeOf(reduce.operand);
6373 const vector_len = operand_ty.vectorLen();
63746680 const writer = f.object.writer();
63756681
6376 const Op = union(enum) {
6377 call_fn: []const u8,
6682 const use_operator = scalar_ty.bitSize(target) <= 64;
6683 const op: union(enum) {
6684 const Func = struct { operation: []const u8, info: BuiltinInfo = .none };
6685 float_op: Func,
6686 builtin: Func,
63786687 infix: []const u8,
63796688 ternary: []const u8,
6380 };
6381 var fn_name_buf: [64]u8 = undefined;
6382 const op: Op = switch (reduce.operation) {
6383 .And => .{ .infix = " &= " },
6384 .Or => .{ .infix = " |= " },
6385 .Xor => .{ .infix = " ^= " },
6689 } = switch (reduce.operation) {
6690 .And => if (use_operator) .{ .infix = " &= " } else .{ .builtin = .{ .operation = "and" } },
6691 .Or => if (use_operator) .{ .infix = " |= " } else .{ .builtin = .{ .operation = "or" } },
6692 .Xor => if (use_operator) .{ .infix = " ^= " } else .{ .builtin = .{ .operation = "xor" } },
63866693 .Min => switch (scalar_ty.zigTypeTag()) {
6387 .Int => Op{ .ternary = " < " },
6388 .Float => op: {
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 };
6694 .Int => if (use_operator) .{ .ternary = " < " } else .{
6695 .builtin = .{ .operation = "min" },
63956696 },
6697 .Float => .{ .float_op = .{ .operation = "fmin" } },
63966698 else => unreachable,
63976699 },
63986700 .Max => switch (scalar_ty.zigTypeTag()) {
6399 .Int => Op{ .ternary = " > " },
6400 .Float => op: {
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 };
6701 .Int => if (use_operator) .{ .ternary = " > " } else .{
6702 .builtin = .{ .operation = "max" },
64076703 },
6704 .Float => .{ .float_op = .{ .operation = "fmax" } },
64086705 else => unreachable,
64096706 },
64106707 .Add => switch (scalar_ty.zigTypeTag()) {
6411 .Int => Op{ .infix = " += " },
6412 .Float => op: {
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 };
6708 .Int => if (use_operator) .{ .infix = " += " } else .{
6709 .builtin = .{ .operation = "addw", .info = .bits },
64196710 },
6711 .Float => .{ .builtin = .{ .operation = "add" } },
64206712 else => unreachable,
64216713 },
64226714 .Mul => switch (scalar_ty.zigTypeTag()) {
6423 .Int => Op{ .infix = " *= " },
6424 .Float => op: {
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 };
6715 .Int => if (use_operator) .{ .infix = " *= " } else .{
6716 .builtin = .{ .operation = "mulw", .info = .bits },
64316717 },
6718 .Float => .{ .builtin = .{ .operation = "mul" } },
64326719 else => unreachable,
64336720 },
64346721 };
......@@ -6444,75 +6731,96 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
64446731 // }
64456732 // break :reduce accum;
64466733 // }
6447 const it = try f.allocLocal(inst, Type.usize);
6448 try f.writeCValue(writer, it, .Other);
6449 try writer.writeAll(" = 0;\n");
64506734
64516735 const accum = try f.allocLocal(inst, scalar_ty);
64526736 try f.writeCValue(writer, accum, .Other);
64536737 try writer.writeAll(" = ");
64546738
6455 const init_val = switch (reduce.operation) {
6456 .And, .Or, .Xor, .Add => "0",
6739 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa);
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 },
64576763 .Min => switch (scalar_ty.zigTypeTag()) {
6458 .Int => "TODO_intmax",
6459 .Float => "TODO_nan",
6764 .Bool => Value.one,
6765 .Int => try scalar_ty.maxInt(stack.get(), target),
6766 .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target),
64606767 else => unreachable,
64616768 },
64626769 .Max => switch (scalar_ty.zigTypeTag()) {
6463 .Int => "TODO_intmin",
6464 .Float => "TODO_nan",
6770 .Bool => Value.zero,
6771 .Int => try scalar_ty.minInt(stack.get(), target),
6772 .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target),
64656773 else => unreachable,
64666774 },
6467 .Mul => "1",
6468 };
6469 try writer.writeAll(init_val);
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);
6775 .Mul => Value.one,
6776 }, .Initializer);
6777 try writer.writeAll(";\n");
64786778
6779 const v = try Vectorizer.start(f, inst, writer, operand_ty);
6780 try f.writeCValue(writer, accum, .Other);
64796781 switch (op) {
6480 .call_fn => |fn_name| {
6481 try writer.print(" = {s}(", .{fn_name});
6782 .float_op => |func| {
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('(');
64826797 try f.writeCValue(writer, accum, .FunctionArgument);
64836798 try writer.writeAll(", ");
64846799 try f.writeCValue(writer, operand, .Other);
6485 try writer.writeAll("[");
6486 try f.writeCValue(writer, it, .Other);
6487 try writer.writeAll("])");
6800 try v.elem(f, writer);
6801 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info);
6802 try writer.writeByte(')');
64886803 },
64896804 .infix => |ass| {
64906805 try writer.writeAll(ass);
64916806 try f.writeCValue(writer, operand, .Other);
6492 try writer.writeAll("[");
6493 try f.writeCValue(writer, it, .Other);
6494 try writer.writeAll("]");
6807 try v.elem(f, writer);
64956808 },
64966809 .ternary => |cmp| {
64976810 try writer.writeAll(" = ");
64986811 try f.writeCValue(writer, accum, .Other);
64996812 try writer.writeAll(cmp);
65006813 try f.writeCValue(writer, operand, .Other);
6501 try writer.writeAll("[");
6502 try f.writeCValue(writer, it, .Other);
6503 try writer.writeAll("] ? ");
6814 try v.elem(f, writer);
6815 try writer.writeAll(" ? ");
65046816 try f.writeCValue(writer, accum, .Other);
65056817 try writer.writeAll(" : ");
65066818 try f.writeCValue(writer, operand, .Other);
6507 try writer.writeAll("[");
6508 try f.writeCValue(writer, it, .Other);
6509 try writer.writeAll("]");
6819 try v.elem(f, writer);
65106820 },
65116821 }
6512
65136822 try writer.writeAll(";\n");
6514
6515 try freeLocal(f, inst, it.new_local, 0);
6823 try v.end(f, inst, writer);
65166824
65176825 return accum;
65186826}
......@@ -6646,7 +6954,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
66466954 try writer.writeByte('(');
66476955
66486956 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);
66506958 } else {
66516959 try writer.writeByte('(');
66526960 try f.renderType(writer, inst_ty);
......@@ -6664,7 +6972,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
66646972
66656973 try writer.writeAll(", ");
66666974 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);
66686976 try writer.writeByte(')');
66696977 if (!empty) try writer.writeByte(')');
66706978
......@@ -6788,7 +7096,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
67887096}
67897097
67907098fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
6791 const inst_ty = f.air.typeOfIndex(inst);
67927099 const un_op = f.air.instructions.items(.data)[inst].un_op;
67937100 if (f.liveness.isUnused(inst)) {
67947101 try reap(f, inst, &.{un_op});
......@@ -6797,16 +7104,23 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
67977104
67987105 const operand = try f.resolveInst(un_op);
67997106 try reap(f, inst, &.{un_op});
7107
68007108 const operand_ty = f.air.typeOf(un_op);
7109 const scalar_ty = operand_ty.scalarType();
68017110
68027111 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);
68047114 try f.writeCValue(writer, local, .Other);
7115 try v.elem(f, writer);
68057116 try writer.writeAll(" = zig_neg_");
6806 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
7117 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
68077118 try writer.writeByte('(');
68087119 try f.writeCValue(writer, operand, .FunctionArgument);
7120 try v.elem(f, writer);
68097121 try writer.writeAll(");\n");
7122 try v.end(f, inst, writer);
7123
68107124 return local;
68117125}
68127126
......@@ -6816,19 +7130,28 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal
68167130 try reap(f, inst, &.{un_op});
68177131 return .none;
68187132 }
7133
68197134 const operand = try f.resolveInst(un_op);
68207135 try reap(f, inst, &.{un_op});
6821 const writer = f.object.writer();
7136
68227137 const inst_ty = f.air.typeOfIndex(inst);
7138 const inst_scalar_ty = inst_ty.scalarType();
7139
7140 const writer = f.object.writer();
68237141 const local = try f.allocLocal(inst, inst_ty);
7142 const v = try Vectorizer.start(f, inst, writer, inst_ty);
68247143 try f.writeCValue(writer, local, .Other);
7144 try v.elem(f, writer);
68257145 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);
68277147 try writer.writeByte('(');
68287148 try writer.writeAll(operation);
68297149 try writer.writeAll(")(");
68307150 try f.writeCValue(writer, operand, .FunctionArgument);
7151 try v.elem(f, writer);
68317152 try writer.writeAll(");\n");
7153 try v.end(f, inst, writer);
7154
68327155 return local;
68337156}
68347157
......@@ -6838,23 +7161,32 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa
68387161 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
68397162 return .none;
68407163 }
7164
68417165 const lhs = try f.resolveInst(bin_op.lhs);
68427166 const rhs = try f.resolveInst(bin_op.rhs);
68437167 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
68447168
6845 const writer = f.object.writer();
68467169 const inst_ty = f.air.typeOfIndex(inst);
7170 const inst_scalar_ty = inst_ty.scalarType();
7171
7172 const writer = f.object.writer();
68477173 const local = try f.allocLocal(inst, inst_ty);
7174 const v = try Vectorizer.start(f, inst, writer, inst_ty);
68487175 try f.writeCValue(writer, local, .Other);
7176 try v.elem(f, writer);
68497177 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);
68517179 try writer.writeByte('(');
68527180 try writer.writeAll(operation);
68537181 try writer.writeAll(")(");
68547182 try f.writeCValue(writer, lhs, .FunctionArgument);
7183 try v.elem(f, writer);
68557184 try writer.writeAll(", ");
68567185 try f.writeCValue(writer, rhs, .FunctionArgument);
7186 try v.elem(f, writer);
68577187 try writer.writeAll(");\n");
7188 try v.end(f, inst, writer);
7189
68587190 return local;
68597191}
68607192
......@@ -6865,23 +7197,34 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
68657197 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
68667198 return .none;
68677199 }
6868 const inst_ty = f.air.typeOfIndex(inst);
7200
68697201 const mulend1 = try f.resolveInst(bin_op.lhs);
68707202 const mulend2 = try f.resolveInst(bin_op.rhs);
68717203 const addend = try f.resolveInst(pl_op.operand);
68727204 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
68737209 const writer = f.object.writer();
68747210 const local = try f.allocLocal(inst, inst_ty);
7211 const v = try Vectorizer.start(f, inst, writer, inst_ty);
68757212 try f.writeCValue(writer, local, .Other);
7213 try v.elem(f, writer);
68767214 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);
68787216 try writer.writeAll("(fma)(");
68797217 try f.writeCValue(writer, mulend1, .FunctionArgument);
7218 try v.elem(f, writer);
68807219 try writer.writeAll(", ");
68817220 try f.writeCValue(writer, mulend2, .FunctionArgument);
7221 try v.elem(f, writer);
68827222 try writer.writeAll(", ");
68837223 try f.writeCValue(writer, addend, .FunctionArgument);
7224 try v.elem(f, writer);
68847225 try writer.writeAll(");\n");
7226 try v.end(f, inst, writer);
7227
68857228 return local;
68867229}
68877230
......@@ -7083,6 +7426,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 {
70837426 } else unreachable;
70847427}
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
70867451fn StringLiteral(comptime WriterType: type) type {
70877452 // MSVC has a length limit of 16380 per string literal (before concatenation)
70887453 const max_char_len = 4;
......@@ -7171,30 +7536,33 @@ fn undefPattern(comptime IntType: type) IntType {
71717536 return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3));
71727537}
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};
71757546fn formatIntLiteral(
71767547 data: FormatIntLiteralContext,
71777548 comptime fmt: []const u8,
71787549 options: std.fmt.FormatOptions,
71797550 writer: anytype,
71807551) @TypeOf(writer).Error!void {
7181 const target = data.mod.getTarget();
7182 const int_info = data.ty.intInfo(target);
7552 const target = data.dg.module.getTarget();
71837553
71847554 const ExpectedContents = struct {
71857555 const base = 10;
7186 const limbs_count_128 = BigInt.calcTwosCompLimbCount(128);
7187 const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base);
7188 const worst_case_int = BigInt.Const{
7189 .limbs = &([1]BigIntLimb{std.math.maxInt(BigIntLimb)} ** expected_needed_limbs_count),
7190 .positive = false,
7191 };
7556 const bits = 128;
7557 const limbs_count = BigInt.calcTwosCompLimbCount(bits);
71927558
7193 undef_limbs: [limbs_count_128]BigIntLimb,
7194 wrap_limbs: [limbs_count_128]BigIntLimb,
7559 undef_limbs: [limbs_count]BigIntLimb,
7560 wrap_limbs: [limbs_count]BigIntLimb,
7561 to_string_buf: [bits]u8,
7562 to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,
71957563 };
71967564 var stack align(@alignOf(ExpectedContents)) =
7197 std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa);
7565 std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);
71987566 const allocator = stack.get();
71997567
72007568 var undef_limbs: []BigIntLimb = &.{};
......@@ -7202,7 +7570,7 @@ fn formatIntLiteral(
72027570
72037571 var int_buf: Value.BigIntSpace = undefined;
72047572 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));
72067574 std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));
72077575
72087576 var undef_int = BigInt.Mutable{
......@@ -7210,164 +7578,193 @@ fn formatIntLiteral(
72107578 .len = undef_limbs.len,
72117579 .positive = true,
72127580 };
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);
72147582 break :blk undef_int.toConst();
72157583 } 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);
72197587 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;
72207588 const one = BigInt.Mutable.init(&one_limbs, 1).toConst();
72217589
7222 const wrap_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits));
7223 defer allocator.free(wrap_limbs);
7224 var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined };
7225 if (wrap.addWrap(int, one, int_info.signedness, c_bits) or
7226 int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits))
7227 {
7228 const abbrev = switch (data.ty.tag()) {
7229 .c_short, .c_ushort => "SHRT",
7230 .c_int, .c_uint => "INT",
7231 .c_long, .c_ulong => "LONG",
7232 .c_longlong, .c_ulonglong => "LLONG",
7233 .isize, .usize => "INTPTR",
7234 else => return writer.print("zig_{s}Int_{c}{d}", .{
7235 if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits,
7236 }),
7237 };
7238 if (int_info.signedness == .unsigned) try writer.writeByte('U');
7239 return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" });
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 }
7590 var wrap = BigInt.Mutable{
7591 .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)),
7592 .len = undefined,
7593 .positive = undefined,
7594 };
7595 defer allocator.free(wrap.limbs);
7596
7597 const c_limb_info: struct {
7598 cty: CType,
7599 count: usize,
7600 endian: std.builtin.Endian,
7601 homogeneous: bool,
7602 } = switch (data.cty.tag()) {
7603 else => .{
7604 .cty = CType.initTag(.void),
7605 .count = 1,
7606 .endian = .Little,
7607 .homogeneous = true,
72717608 },
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));
7275 if (c_bits <= 64) {
7276 var base: u8 = undefined;
7277 var case: std.fmt.Case = undefined;
7278 switch (fmt.len) {
7279 0 => base = 10,
7638 const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) {
7639 0 => .{ .base = 10 },
72807640 1 => switch (fmt[0]) {
7281 'b' => {
7282 base = 2;
7641 'b' => style: {
72837642 try writer.writeAll("0b");
7643 break :style .{ .base = 2 };
72847644 },
7285 'o' => {
7286 base = 8;
7645 'o' => style: {
72877646 try writer.writeByte('0');
7647 break :style .{ .base = 8 };
72887648 },
7289 'd' => base = 10,
7290 'x' => {
7291 base = 16;
7292 case = .lower;
7293 try writer.writeAll("0x");
7294 },
7295 'X' => {
7296 base = 16;
7297 case = .upper;
7649 'd' => .{ .base = 10 },
7650 'x', 'X' => |base| style: {
72987651 try writer.writeAll("0x");
7652 break :style .{ .base = 16, .case = switch (base) {
7653 'x' => .lower,
7654 'X' => .upper,
7655 else => unreachable,
7656 } };
72997657 },
73007658 else => @compileError("Invalid fmt: " ++ fmt),
73017659 },
73027660 else => @compileError("Invalid fmt: " ++ fmt),
7303 }
7661 };
73047662
7305 var str: [64]u8 = undefined;
7306 var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]BigIntLimb = undefined;
7307 try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]);
7663 const string = try int.abs().toStringAlloc(allocator, style.base, style.case);
7664 defer allocator.free(string);
7665 try writer.writeAll(string);
73087666 } else {
7309 assert(c_bits == 128);
7310 const split = std.math.min(int.limbs.len, limbs_count_64);
7311 var twos_comp_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined;
7312
7313 // Adding a negation in the C code before the doesn't work in all cases:
7314 // - struct versions would require an extra zig_sub_ call to negate, which wouldn't work in constant expressions
7315 // - negating the f80 int representation (i128) doesn't make sense
7316 // Instead we write out the literal as a negative number in twos complement
7317 var limbs = int.limbs;
7318
7319 if (use_twos_comp) {
7320 var twos_comp = BigInt.Mutable{
7321 .limbs = &twos_comp_limbs,
7322 .positive = undefined,
7667 try data.cty.renderLiteralPrefix(writer, data.kind);
7668 wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits);
7669 std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
7670 wrap.len = wrap.limbs.len;
7671 const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
7672
7673 var c_limb_int_info = std.builtin.Type.Int{
7674 .signedness = undefined,
7675 .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)),
7676 };
7677 var c_limb_cty: CType = undefined;
7678
7679 var limb_offset: usize = 0;
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],
73237688 .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],
73247713 };
73257714
7326 twos_comp.convertToTwosComplement(int, .signed, int_info.bits);
7327 limbs = twos_comp.limbs;
7715 if (limb_offset > 0) try writer.writeAll(", ");
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);
73287723 }
7724 }
7725 try data.cty.renderLiteralSuffix(writer);
7726}
73297727
7330 var upper_pl = Value.Payload.BigInt{
7331 .base = .{ .tag = .int_big_positive },
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);
7728const Vectorizer = struct {
7729 index: CValue = .none,
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{
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);
7735 const local = try f.allocLocal(inst, Type.usize);
73567736
7357 if (!int.positive and c_bits > 64 and !use_twos_comp) try writer.writeByte(')');
7358 return writer.writeByte(')');
7737 try writer.writeAll("for (");
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 .{};
73597750 }
73607751
7361 switch (data.ty.tag()) {
7362 .c_short, .c_ushort, .c_int => {},
7363 .c_uint => try writer.writeAll("u"),
7364 .c_long => try writer.writeAll("l"),
7365 .c_ulong => try writer.writeAll("ul"),
7366 .c_longlong => try writer.writeAll("ll"),
7367 .c_ulonglong => try writer.writeAll("ull"),
7368 else => try writer.writeByte(')'),
7752 pub fn elem(self: Vectorizer, f: *Function, writer: anytype) !void {
7753 if (self.index != .none) {
7754 try writer.writeByte('[');
7755 try f.writeCValue(writer, self.index, .Other);
7756 try writer.writeByte(']');
7757 }
73697758 }
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
73727769fn isByRef(ty: Type) bool {
73737770 _ = ty;
src/codegen/c/type.zig+449-32
......@@ -496,6 +496,427 @@ pub const CType = extern union {
496496 }
497497 };
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
499920 pub fn isPacked(self: CType) bool {
500921 return switch (self.tag()) {
501922 else => false,
......@@ -787,26 +1208,26 @@ pub const CType = extern union {
7871208 };
7881209 }
7891210
790 fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag {
791 return switch (bits) {
1211 fn tagFromIntInfo(int_info: std.builtin.Type.Int) Tag {
1212 return switch (int_info.bits) {
7921213 0 => .void,
793 1...8 => switch (signedness) {
1214 1...8 => switch (int_info.signedness) {
7941215 .unsigned => .uint8_t,
7951216 .signed => .int8_t,
7961217 },
797 9...16 => switch (signedness) {
1218 9...16 => switch (int_info.signedness) {
7981219 .unsigned => .uint16_t,
7991220 .signed => .int16_t,
8001221 },
801 17...32 => switch (signedness) {
1222 17...32 => switch (int_info.signedness) {
8021223 .unsigned => .uint32_t,
8031224 .signed => .int32_t,
8041225 },
805 33...64 => switch (signedness) {
1226 33...64 => switch (int_info.signedness) {
8061227 .unsigned => .uint64_t,
8071228 .signed => .int64_t,
8081229 },
809 65...128 => switch (signedness) {
1230 65...128 => switch (int_info.signedness) {
8101231 .unsigned => .zig_u128,
8111232 .signed => .zig_i128,
8121233 },
......@@ -945,31 +1366,27 @@ pub const CType = extern union {
9451366 .c_ulong => self.init(.@"unsigned long"),
9461367 .c_longlong => self.init(.@"long long"),
9471368 .c_ulonglong => self.init(.@"unsigned long long"),
948 else => {
949 const info = ty.intInfo(target);
950 const t = tagFromIntInfo(info.signedness, info.bits);
951 switch (t) {
952 .void => unreachable,
953 else => self.init(t),
954 .array => switch (kind) {
955 .forward, .complete, .global => {
956 const abi_size = ty.abiSize(target);
957 const abi_align = ty.abiAlignment(target);
958 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
959 .len = @divExact(abi_size, abi_align),
960 .elem_type = tagFromIntInfo(
961 .unsigned,
962 @intCast(u16, abi_align * 8),
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,
1369 else => switch (tagFromIntInfo(ty.intInfo(target))) {
1370 .void => unreachable,
1371 else => |t| self.init(t),
1372 .array => switch (kind) {
1373 .forward, .complete, .global => {
1374 const abi_size = ty.abiSize(target);
1375 const abi_align = ty.abiAlignment(target);
1376 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
1377 .len = @divExact(abi_size, abi_align),
1378 .elem_type = tagFromIntInfo(.{
1379 .signedness = .unsigned,
1380 .bits = @intCast(u16, abi_align * 8),
1381 }).toIndex(),
1382 } } };
1383 self.value = .{ .cty = initPayload(&self.storage.seq) };
9711384 },
972 }
1385 .forward_parameter,
1386 .parameter,
1387 => try self.initArrayParameter(ty, kind, lookup),
1388 .payload => unreachable,
1389 },
9731390 },
9741391 } else switch (ty.zigTypeTag()) {
9751392 .Frame => unreachable,
......@@ -1048,7 +1465,7 @@ pub const CType = extern union {
10481465 .base = .{ .tag = .int_unsigned },
10491466 .data = info.host_size * 8,
10501467 };
1051 const pointee_ty = if (info.host_size > 0)
1468 const pointee_ty = if (info.host_size > 0 and info.vector_index == .none)
10521469 Type.initPayload(&host_int_pl.base)
10531470 else
10541471 info.pointee_type;
src/codegen/llvm.zig+10-1
......@@ -1778,7 +1778,7 @@ pub const Object = struct {
17781778 if (ty.optionalReprIsPayload()) {
17791779 const ptr_di_ty = try o.lowerDebugType(child_ty, resolve);
17801780 // 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 });
17821782 return ptr_di_ty;
17831783 }
17841784
......@@ -4595,6 +4595,7 @@ pub const FuncGen = struct {
45954595 .block => try self.airBlock(inst),
45964596 .br => try self.airBr(inst),
45974597 .switch_br => try self.airSwitchBr(inst),
4598 .trap => try self.airTrap(inst),
45984599 .breakpoint => try self.airBreakpoint(inst),
45994600 .ret_addr => try self.airRetAddr(inst),
46004601 .frame_addr => try self.airFrameAddress(inst),
......@@ -8261,6 +8262,14 @@ pub const FuncGen = struct {
82618262 return fg.load(ptr, ptr_ty);
82628263 }
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
82648273 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
82658274 _ = inst;
82668275 const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
src/glibc.zig+11-8
......@@ -161,7 +161,7 @@ pub const CRTFile = enum {
161161 libc_nonshared_a,
162162};
163163
164pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
164pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
165165 if (!build_options.have_llvm) {
166166 return error.ZigCompilerNotBuiltWithLLVMExtensions;
167167 }
......@@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
196196 "-DASSEMBLER",
197197 "-Wa,--noexecstack",
198198 });
199 return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{
199 return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{
200200 .{
201201 .src_path = try start_asm_path(comp, arena, "crti.S"),
202202 .cache_exempt_flags = args.items,
......@@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
215215 "-DASSEMBLER",
216216 "-Wa,--noexecstack",
217217 });
218 return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{
218 return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{
219219 .{
220220 .src_path = try start_asm_path(comp, arena, "crtn.S"),
221221 .cache_exempt_flags = args.items,
......@@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
265265 .cache_exempt_flags = args.items,
266266 };
267267 };
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 });
269271 },
270272 .libc_nonshared_a => {
271273 const s = path.sep_str;
......@@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
366368 files_index += 1;
367369 }
368370 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);
370372 },
371373 }
372374}
......@@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct {
639641
640642const all_map_basename = "all.map";
641643
642pub fn buildSharedObjects(comp: *Compilation) !void {
644pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !void {
643645 const tracy = trace(@src());
644646 defer tracy.end();
645647
......@@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
10231025 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
10241026 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);
10271029 }
10281030
10291031 man.writeManifest() catch |err| {
......@@ -1046,6 +1048,7 @@ fn buildSharedLib(
10461048 bin_directory: Compilation.Directory,
10471049 asm_file_basename: []const u8,
10481050 lib: Lib,
1051 prog_node: *std.Progress.Node,
10491052) !void {
10501053 const tracy = trace(@src());
10511054 defer tracy.end();
......@@ -1105,7 +1108,7 @@ fn buildSharedLib(
11051108 });
11061109 defer sub_compilation.destroy();
11071110
1108 try sub_compilation.updateSubCompilation();
1111 try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node);
11091112}
11101113
11111114// 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{
9696 "src/verbose_abort.cpp",
9797};
9898
99pub fn buildLibCXX(comp: *Compilation) !void {
99pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void {
100100 if (!build_options.have_llvm) {
101101 return error.ZigCompilerNotBuiltWithLLVMExtensions;
102102 }
......@@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
258258 });
259259 defer sub_compilation.destroy();
260260
261 try sub_compilation.updateSubCompilation();
261 try comp.updateSubCompilation(sub_compilation, .libcxx, prog_node);
262262
263263 assert(comp.libcxx_static_lib == null);
264264 comp.libcxx_static_lib = Compilation.CRTFile{
......@@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
269269 };
270270}
271271
272pub fn buildLibCXXABI(comp: *Compilation) !void {
272pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void {
273273 if (!build_options.have_llvm) {
274274 return error.ZigCompilerNotBuiltWithLLVMExtensions;
275275 }
......@@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
418418 });
419419 defer sub_compilation.destroy();
420420
421 try sub_compilation.updateSubCompilation();
421 try comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node);
422422
423423 assert(comp.libcxxabi_static_lib == null);
424424 comp.libcxxabi_static_lib = Compilation.CRTFile{
src/libtsan.zig+2-2
......@@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig");
55const build_options = @import("build_options");
66const trace = @import("tracy.zig").trace;
77
8pub fn buildTsan(comp: *Compilation) !void {
8pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void {
99 if (!build_options.have_llvm) {
1010 return error.ZigCompilerNotBuiltWithLLVMExtensions;
1111 }
......@@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void {
235235 });
236236 defer sub_compilation.destroy();
237237
238 try sub_compilation.updateSubCompilation();
238 try comp.updateSubCompilation(sub_compilation, .libtsan, prog_node);
239239
240240 assert(comp.tsan_static_lib == null);
241241 comp.tsan_static_lib = Compilation.CRTFile{
src/libunwind.zig+2-2
......@@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig");
77const build_options = @import("build_options");
88const trace = @import("tracy.zig").trace;
99
10pub fn buildStaticLib(comp: *Compilation) !void {
10pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void {
1111 if (!build_options.have_llvm) {
1212 return error.ZigCompilerNotBuiltWithLLVMExtensions;
1313 }
......@@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
130130 });
131131 defer sub_compilation.destroy();
132132
133 try sub_compilation.updateSubCompilation();
133 try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node);
134134
135135 assert(comp.libunwind_static_lib == null);
136136
src/link.zig+42-3
......@@ -264,6 +264,8 @@ pub const File = struct {
264264 /// of this linking operation.
265265 lock: ?Cache.Lock = null,
266266
267 child_pid: ?std.ChildProcess.Id = null,
268
267269 /// Attempts incremental linking, if the file already exists. If
268270 /// incremental linking fails, falls back to truncating the file and
269271 /// rewriting it. A malicious file is detected as incremental link failure
......@@ -376,6 +378,26 @@ pub const File = struct {
376378 if (build_options.only_c) unreachable;
377379 if (base.file != null) return;
378380 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 }
379401 base.file = try emit.directory.handle.createFile(emit.sub_path, .{
380402 .truncate = false,
381403 .read = true,
......@@ -424,6 +446,18 @@ pub const File = struct {
424446 }
425447 f.close();
426448 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 }
427461 },
428462 .c, .spirv, .nvptx => {},
429463 }
......@@ -460,6 +494,9 @@ pub const File = struct {
460494 CurrentWorkingDirectoryUnlinked,
461495 LockViolation,
462496 NetNameDeleted,
497 DeviceBusy,
498 InvalidArgument,
499 HotSwapUnavailableOnHostOperatingSystem,
463500 };
464501
465502 /// Called from within the CodeGen to lower a local variable instantion as an unnamed
......@@ -1051,9 +1088,11 @@ pub const File = struct {
10511088 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)});
10521089 };
10531090
1054 man.writeManifest() catch |err| {
1055 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});
1056 };
1091 if (man.have_exclusive_lock) {
1092 man.writeManifest() catch |err| {
1093 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});
1094 };
1095 }
10571096
10581097 base.lock = man.toOwnedLock();
10591098 }
src/link/C.zig+14-8
......@@ -221,14 +221,19 @@ pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void
221221 return self.flushModule(comp, prog_node);
222222}
223223
224fn abiDefine(comp: *Compilation) ?[]const u8 {
225 return switch (comp.getTarget().abi) {
226 .msvc => "#define ZIG_TARGET_ABI_MSVC\n",
227 else => null,
228 };
224fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
225 var defines = std.ArrayList(u8).init(self.base.allocator);
226 errdefer defines.deinit();
227 const writer = defines.writer();
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;
229234}
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 {
232237 const tracy = trace(@src());
233238 defer tracy.end();
234239
......@@ -245,12 +250,13 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)
245250 var f: Flush = .{};
246251 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
250256 // Covers defines, zig.h, ctypes, asm, lazy fwd.
251257 try f.all_buffers.ensureUnusedCapacity(gpa, 5);
252258
253 if (abi_define) |buf| f.appendBufAssumeCapacity(buf);
259 f.appendBufAssumeCapacity(abi_defines.items);
254260 f.appendBufAssumeCapacity(zig_h);
255261
256262 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
10601060 decl.analysis = .codegen_failure;
10611061 try mod.failed_decls.put(mod.gpa, decl_index, em);
10621062 log.err("{s}", .{em.msg});
1063 return error.AnalysisFail;
1063 return error.CodegenFail;
10641064 },
10651065 };
10661066
src/link/Elf.zig+60-8
......@@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
467467 .p_paddr = entry_addr,
468468 .p_memsz = file_size,
469469 .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,
471471 });
472472 self.entry_addr = null;
473473 self.phdr_table_dirty = true;
......@@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
493493 .p_paddr = got_addr,
494494 .p_memsz = file_size,
495495 .p_align = p_align,
496 .p_flags = elf.PF_R,
496 .p_flags = elf.PF_R | elf.PF_W,
497497 });
498498 self.phdr_table_dirty = true;
499499 }
......@@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
516516 .p_paddr = rodata_addr,
517517 .p_memsz = file_size,
518518 .p_align = p_align,
519 .p_flags = elf.PF_R,
519 .p_flags = elf.PF_R | elf.PF_W,
520520 });
521521 self.phdr_table_dirty = true;
522522 }
......@@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
20972097 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
20982098 const local_sym_index = atom.getSymbolIndex().?;
20992099
2100 log.debug("adding %{d} to local symbols free list", .{local_sym_index});
21002101 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].st_shndx = 0;
2102 self.local_symbols.items[local_sym_index] = .{
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 };
21032110 _ = self.atom_by_index_table.remove(local_sym_index);
21042111 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
21592166 // First we look for an appropriately sized free list node.
21602167 // The list is unordered. We'll just take the first thing that works.
21612168 const vaddr = blk: {
2162 var i: usize = 0;
2169 var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len;
21632170 while (i < free_list.items.len) {
21642171 const big_atom_index = free_list.items[i];
21652172 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
23902397 const atom = self.getAtom(atom_index);
23912398
23922399 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) {
23942401 const local_sym = atom.getSymbolPtr(self);
23952402 local_sym.st_name = try self.shstrtab.insert(gpa, decl_name);
23962403 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
24442451 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
24452452 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
24462453 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
24472476 try self.base.file.?.pwriteAll(code, file_offset);
24482477
24492478 return local_sym;
......@@ -2618,7 +2647,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
26182647 decl.analysis = .codegen_failure;
26192648 try mod.failed_decls.put(mod.gpa, decl_index, em);
26202649 log.err("{s}", .{em.msg});
2621 return error.AnalysisFail;
2650 return error.CodegenFail;
26222651 },
26232652 };
26242653
......@@ -2813,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
28132842 const endian = self.base.options.target.cpu.arch.endian();
28142843 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
28152844 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;
28162847 switch (entry_size) {
28172848 2 => {
28182849 var buf: [2]u8 = undefined;
......@@ -2828,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
28282859 var buf: [8]u8 = undefined;
28292860 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
28302861 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 }
28312883 },
28322884 else => unreachable,
28332885 }
src/link/MachO.zig+1-1
......@@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
20892089 decl.analysis = .codegen_failure;
20902090 try module.failed_decls.put(module.gpa, decl_index, em);
20912091 log.err("{s}", .{em.msg});
2092 return error.AnalysisFail;
2092 return error.CodegenFail;
20932093 },
20942094 };
20952095
src/link/MachO/CodeSignature.zig+2-2
......@@ -7,12 +7,12 @@ const log = std.log.scoped(.link);
77const macho = std.macho;
88const mem = std.mem;
99const testing = std.testing;
10const ThreadPool = std.Thread.Pool;
11const WaitGroup = std.Thread.WaitGroup;
1012
1113const Allocator = mem.Allocator;
1214const Compilation = @import("../../Compilation.zig");
1315const Sha256 = std.crypto.hash.sha2.Sha256;
14const ThreadPool = @import("../../ThreadPool.zig");
15const WaitGroup = @import("../../WaitGroup.zig");
1616
1717const 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
377377 decl.analysis = .codegen_failure;
378378 try mod.failed_decls.put(mod.gpa, decl_index, em);
379379 log.err("{s}", .{em.msg});
380 return error.AnalysisFail;
380 return error.CodegenFail;
381381 },
382382 };
383383 // 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
468468 .flags = 0,
469469 .tag = tag,
470470 .index = undefined,
471 .virtual_address = undefined,
471472 });
472473 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});
473474 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
......@@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
886887 const loc = try wasm.createSyntheticSymbol("__heap_base", .data);
887888 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
888889 _ = 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);
900890 }
901891
902892 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
903893 const loc = try wasm.createSyntheticSymbol("__heap_end", .data);
904894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
905895 _ = 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);
915896 }
916897}
917898
......@@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 {
1011992 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1012993 .tag = undefined, // will be set after updateDecl
1013994 .index = undefined, // will be set after updateDecl
995 .virtual_address = undefined, // will be set during atom allocation
1014996 };
1015997 if (wasm.symbols_free_list.popOrNull()) |index| {
1016998 wasm.symbols.items[index] = symbol;
......@@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
12461228 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
12471229 .tag = .data,
12481230 .index = undefined,
1231 .virtual_address = undefined,
12491232 };
12501233 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
12651248 .fail => |em| {
12661249 decl.analysis = .codegen_failure;
12671250 try mod.failed_decls.put(mod.gpa, decl_index, em);
1268 return error.AnalysisFail;
1251 return error.CodegenFail;
12691252 },
12701253 };
12711254 };
......@@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 {
12921275 .flags = 0,
12931276 .index = undefined, // index to type will be set after merging function symbols
12941277 .tag = .function,
1278 .virtual_address = undefined,
12951279 };
12961280 symbol.setGlobal(true);
12971281 symbol.setUndefined(true);
......@@ -1610,7 +1594,6 @@ const Kind = union(enum) {
16101594 read_only,
16111595 uninitialized,
16121596 initialized,
1613 synthetic,
16141597 },
16151598 function: void,
16161599
......@@ -1621,7 +1604,6 @@ const Kind = union(enum) {
16211604 .read_only => return ".rodata.",
16221605 .uninitialized => return ".bss.",
16231606 .initialized => return ".data.",
1624 .synthetic => return ".synthetic",
16251607 }
16261608 }
16271609};
......@@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void {
17881770 }
17891771}
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
17911797fn sortDataSegments(wasm: *Wasm) !void {
17921798 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
17931799 try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count());
......@@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void {
18051811 if (mem.startsWith(u8, name, ".rodata")) return 0;
18061812 if (mem.startsWith(u8, name, ".data")) return 1;
18071813 if (mem.startsWith(u8, name, ".text")) return 2;
1808 if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end
18091814 return 3;
18101815 }
18111816 };
......@@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void {
21372142 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
21382143 };
21392144 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);
21432145 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
21442146 try wasm.wasm_globals.append(wasm.base.allocator, .{
21452147 .global_type = .{ .valtype = .i32, .mutable = false },
2146 .init = .{ .i32_const = @intCast(i32, va) },
2148 .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) },
21472149 });
21482150 break :exp .{
21492151 .name = export_name,
......@@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void {
22202222 var offset: u32 = @intCast(u32, memory_ptr);
22212223 var data_seg_it = wasm.data_segments.iterator();
22222224 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 }
22272225 const segment = &wasm.segments.items[entry.value_ptr.*];
22282226 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
22292227 memory_ptr += segment.size;
......@@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void {
22402238 // One of the linked object files has a reference to the __heap_base symbol.
22412239 // We must set its virtual address so it can be used in relocations.
22422240 if (wasm.findGlobalSymbol("__heap_base")) |loc| {
2243 const segment_index = wasm.data_segments.get(".synthetic").?;
2244 const segment = &wasm.segments.items[segment_index];
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));
2241 const symbol = loc.getSymbol(wasm);
2242 symbol.virtual_address = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
22492243 }
22502244
22512245 // Setup the max amount of pages
......@@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void {
22742268 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
22752269
22762270 if (wasm.findGlobalSymbol("__heap_end")) |loc| {
2277 const segment_index = wasm.data_segments.get(".synthetic").?;
2278 const segment = &wasm.segments.items[segment_index];
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);
2271 const symbol = loc.getSymbol(wasm);
2272 symbol.virtual_address = @intCast(u32, memory_ptr);
22832273 }
22842274
22852275 if (wasm.base.options.max_memory) |max_memory| {
......@@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
24172407 .tag = .data,
24182408 .flags = 0,
24192409 .index = 0,
2410 .virtual_address = undefined,
24202411 };
24212412 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24222413
......@@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24492440 .tag = .data,
24502441 .flags = 0,
24512442 .index = 0,
2443 .virtual_address = undefined,
24522444 };
24532445 names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
24542446
......@@ -2635,6 +2627,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
26352627 try man.addOptionalFile(compiler_rt_path);
26362628 man.hash.addOptionalBytes(options.entry);
26372629 man.hash.addOptional(options.stack_size_override);
2630 man.hash.add(wasm.base.options.build_id);
26382631 man.hash.add(options.import_memory);
26392632 man.hash.add(options.import_table);
26402633 man.hash.add(options.export_table);
......@@ -2748,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
27482741
27492742 try wasm.allocateAtoms();
27502743 try wasm.setupMemory();
2744 wasm.allocateVirtualAddresses();
27512745 wasm.mapFunctionTable();
27522746 try wasm.mergeSections();
27532747 try wasm.mergeTypes();
......@@ -2866,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28662860
28672861 try wasm.allocateAtoms();
28682862 try wasm.setupMemory();
2863 wasm.allocateVirtualAddresses();
28692864 wasm.mapFunctionTable();
28702865 try wasm.mergeSections();
28712866 try wasm.mergeTypes();
......@@ -3225,6 +3220,12 @@ fn writeToFile(
32253220 }
32263221
32273222 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
32283229 // if (wasm.dwarf) |*dwarf| {
32293230 // const mod = wasm.base.options.module.?;
32303231 // try dwarf.writeDbgAbbrev();
......@@ -3363,6 +3364,33 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
33633364 );
33643365}
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
33663394fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {
33673395 const header_offset = try reserveCustomSectionHeader(binary_bytes);
33683396
......@@ -3426,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
34263454 // bss section is not emitted when this condition holds true, so we also
34273455 // do not output a name for it.
34283456 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;
34313457 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
34323458 data_segment_index += 1;
34333459 }
......@@ -3594,6 +3620,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
35943620 try man.addOptionalFile(compiler_rt_path);
35953621 man.hash.addOptionalBytes(wasm.base.options.entry);
35963622 man.hash.addOptional(wasm.base.options.stack_size_override);
3623 man.hash.add(wasm.base.options.build_id);
35973624 man.hash.add(wasm.base.options.import_memory);
35983625 man.hash.add(wasm.base.options.import_table);
35993626 man.hash.add(wasm.base.options.export_table);
......@@ -3760,6 +3787,12 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
37603787 if (wasm.base.options.import_symbols) {
37613788 try argv.append("--allow-undefined");
37623789 }
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
37633796 try argv.appendSlice(&.{ "-o", full_out_path });
37643797
37653798 if (target.cpu.arch == .wasm64) {
src/link/Wasm/Atom.zig+1-23
......@@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 {
8989 return atom.sym_index;
9090}
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
10792/// Resolves the relocations within the atom, writing the new value
10893/// at the calculated offset.
10994pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
......@@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
186171 if (symbol.isUndefined()) {
187172 return 0;
188173 }
189 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
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));
174 const va = @intCast(i64, symbol.virtual_address);
197175 return @intCast(u32, va + relocation.addend);
198176 },
199177 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
src/link/Wasm/Object.zig+2
......@@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
270270 .name = table_import.name,
271271 .tag = .table,
272272 .index = 0,
273 .virtual_address = undefined,
273274 };
274275 table_symbol.setFlag(.WASM_SYM_UNDEFINED);
275276 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
......@@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type {
758759 .tag = tag,
759760 .name = undefined,
760761 .index = undefined,
762 .virtual_address = undefined,
761763 };
762764
763765 switch (tag) {
src/link/Wasm/Symbol.zig+3
......@@ -20,6 +20,9 @@ name: u32,
2020index: u32,
2121/// Represents the kind of the symbol, such as a function or global.
2222tag: 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
2427pub const Tag = enum {
2528 function,
src/main.zig+501-342
......@@ -9,6 +9,8 @@ const Allocator = mem.Allocator;
99const ArrayList = std.ArrayList;
1010const Ast = std.zig.Ast;
1111const warn = std.log.warn;
12const ThreadPool = std.Thread.Pool;
13const cleanExit = std.process.cleanExit;
1214
1315const tracy = @import("tracy.zig");
1416const Compilation = @import("Compilation.zig");
......@@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig");
2224const clang = @import("clang.zig");
2325const Cache = std.Build.Cache;
2426const target_util = @import("target.zig");
25const ThreadPool = @import("ThreadPool.zig");
2627const crash_report = @import("crash_report.zig");
28const Module = @import("Module.zig");
29const AstGen = @import("AstGen.zig");
30const Server = std.zig.Server;
2731
2832pub const std_options = struct {
2933 pub const wasiCwd = wasi_cwd;
......@@ -361,7 +365,6 @@ const usage_build_generic =
361365 \\
362366 \\General Options:
363367 \\ -h, --help Print this help and exit
364 \\ --watch Enable compiler REPL
365368 \\ --color [auto|off|on] Enable or disable colored error messages
366369 \\ -femit-bin[=path] (default) Output machine code
367370 \\ -fno-emit-bin Do not output machine code
......@@ -666,6 +669,16 @@ const ArgMode = union(enum) {
666669 run,
667670};
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
669682fn buildOutputType(
670683 gpa: Allocator,
671684 arena: Allocator,
......@@ -686,7 +699,7 @@ fn buildOutputType(
686699 var formatted_panics: ?bool = null;
687700 var function_sections = false;
688701 var no_builtin = false;
689 var watch = false;
702 var listen: Listen = .none;
690703 var debug_compile_errors = false;
691704 var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK");
692705 var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC");
......@@ -1144,6 +1157,23 @@ fn buildOutputType(
11441157 } else {
11451158 try log_scopes.append(gpa, args_iter.nextOrFatal());
11461159 }
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;
11471177 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
11481178 if (!build_options.enable_link_snapshots) {
11491179 std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
......@@ -1172,8 +1202,6 @@ fn buildOutputType(
11721202 test_evented_io = true;
11731203 } else if (mem.eql(u8, arg, "--test-no-exec")) {
11741204 test_no_exec = true;
1175 } else if (mem.eql(u8, arg, "--watch")) {
1176 watch = true;
11771205 } else if (mem.eql(u8, arg, "-ftime-report")) {
11781206 time_report = true;
11791207 } else if (mem.eql(u8, arg, "-fstack-report")) {
......@@ -2999,7 +3027,7 @@ fn buildOutputType(
29993027 defer zig_lib_directory.handle.close();
30003028
30013029 var thread_pool: ThreadPool = undefined;
3002 try thread_pool.init(gpa);
3030 try thread_pool.init(.{ .allocator = gpa });
30033031 defer thread_pool.deinit();
30043032
30053033 var libc_installation: ?LibCInstallation = null;
......@@ -3259,8 +3287,52 @@ fn buildOutputType(
32593287 if (show_builtin) {
32603288 return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
32613289 }
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
32623334 if (arg_mode == .translate_c) {
3263 return cmdTranslateC(comp, arena, have_enable_cache);
3335 return cmdTranslateC(comp, arena, null);
32643336 }
32653337
32663338 const hook: AfterUpdateHook = blk: {
......@@ -3276,7 +3348,7 @@ fn buildOutputType(
32763348 };
32773349
32783350 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),
32803352 else => |e| return e,
32813353 };
32823354 if (build_options.only_c) return cleanExit();
......@@ -3332,7 +3404,6 @@ fn buildOutputType(
33323404 self_exe_path.?,
33333405 arg_mode,
33343406 target_info,
3335 watch,
33363407 &comp_destroyed,
33373408 all_args,
33383409 runtime_args_start,
......@@ -3340,109 +3411,215 @@ fn buildOutputType(
33403411 );
33413412 }
33423413
3343 const stdin = std.io.getStdIn().reader();
3344 const stderr = std.io.getStdErr().writer();
3345 var repl_buf: [1024]u8 = undefined;
3414 // Skip resource deallocation in release builds; let the OS do it.
3415 return cleanExit();
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 {
3348 update,
3349 help,
3350 run,
3351 update_and_run,
3430 var server = try Server.init(.{
3431 .gpa = gpa,
3432 .in = in,
3433 .out = out,
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,
33523453 };
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) {
3357 try stderr.print("(zig) ", .{});
3358 try comp.makeBinFileExecutable();
3359 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
3360 try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)});
3361 continue;
3362 }) |line| {
3363 const actual_line = mem.trimRight(u8, line, "\r\n ");
3364 const cmd: ReplCmd = blk: {
3365 if (mem.eql(u8, actual_line, "update")) {
3366 break :blk .update;
3367 } else if (mem.eql(u8, actual_line, "exit")) {
3368 break;
3369 } else if (mem.eql(u8, actual_line, "help")) {
3370 break :blk .help;
3371 } else if (mem.eql(u8, actual_line, "run")) {
3372 break :blk .run;
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});
3460 switch (hdr.tag) {
3461 .exit => {
3462 return cleanExit();
3463 },
3464 .update => {
3465 assert(main_progress_node.recently_updated_child == null);
3466 tracy.frameMark();
3467
3468 if (arg_mode == .translate_c) {
3469 var arena_instance = std.heap.ArenaAllocator.init(gpa);
3470 defer arena_instance.deinit();
3471 const arena = arena_instance.allocator();
3472 var output: TranslateCOutput = undefined;
3473 try cmdTranslateC(comp, arena, &output);
3474 try server.serveEmitBinPath(output.path, .{
3475 .flags = .{ .cache_hit = output.cache_hit },
3476 });
33793477 continue;
33803478 }
3381 };
3382 last_cmd = cmd;
3383 switch (cmd) {
3384 .update => {
3385 tracy.frameMark();
3386 if (output_mode == .Exe) {
3387 try comp.makeBinFileWritable();
3479
3480 if (comp.bin_file.options.output_mode == .Exe) {
3481 try comp.makeBinFileWritable();
3482 }
3483
3484 {
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();
33883493 }
3389 updateModule(gpa, comp, hook) catch |err| switch (err) {
3390 error.SemanticAnalyzeFail => continue,
3391 else => |e| return e,
3392 };
3393 },
3394 .help => {
3395 try stderr.writeAll(repl_help);
3396 },
3397 .run => {
3398 tracy.frameMark();
3399 try runOrTest(
3400 comp,
3401 gpa,
3402 arena,
3403 test_exec_args.items,
3404 self_exe_path.?,
3405 arg_mode,
3406 target_info,
3407 watch,
3408 &comp_destroyed,
3409 all_args,
3410 runtime_args_start,
3411 link_libc,
3412 );
3413 },
3414 .update_and_run => {
3415 tracy.frameMark();
3416 if (output_mode == .Exe) {
3494
3495 try comp.update(main_progress_node);
3496 }
3497
3498 try comp.makeBinFileExecutable();
3499 try serveUpdateResults(&server, comp);
3500 },
3501 .run => {
3502 if (child_pid != null) {
3503 @panic("TODO block until the child exits");
3504 }
3505 @panic("TODO call runOrTest");
3506 //try runOrTest(
3507 // comp,
3508 // gpa,
3509 // arena,
3510 // test_exec_args,
3511 // self_exe_path.?,
3512 // arg_mode,
3513 // target_info,
3514 // true,
3515 // &comp_destroyed,
3516 // all_args,
3517 // runtime_args_start,
3518 // link_libc,
3519 //);
3520 },
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) {
34173529 try comp.makeBinFileWritable();
34183530 }
3419 updateModule(gpa, comp, hook) catch |err| switch (err) {
3420 error.SemanticAnalyzeFail => continue,
3421 else => |e| return e,
3422 };
3531 try comp.update(main_progress_node);
34233532 try comp.makeBinFileExecutable();
3424 try runOrTest(
3533 try serveUpdateResults(&server, comp);
3534
3535 child_pid = try runOrTestHotSwap(
34253536 comp,
34263537 gpa,
3427 arena,
3428 test_exec_args.items,
3538 test_exec_args,
34293539 self_exe_path.?,
34303540 arg_mode,
3431 target_info,
3432 watch,
3433 &comp_destroyed,
34343541 all_args,
34353542 runtime_args_start,
3436 link_libc,
34373543 );
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);
34393594 }
3440 } else {
3441 break;
34423595 }
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 });
34433622 }
3444 // Skip resource deallocation in release builds; let the OS do it.
3445 return cleanExit();
34463623}
34473624
34483625const ModuleDepIterator = struct {
......@@ -3530,7 +3707,6 @@ fn runOrTest(
35303707 self_exe_path: []const u8,
35313708 arg_mode: ArgMode,
35323709 target_info: std.zig.system.NativeTargetInfo,
3533 watch: bool,
35343710 comp_destroyed: *bool,
35353711 all_args: []const []const u8,
35363712 runtime_args_start: ?usize,
......@@ -3561,7 +3737,7 @@ fn runOrTest(
35613737
35623738 // We do not execve for tests because if the test fails we want to print
35633739 // 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) {
35653741 // execv releases the locks; no need to destroy the Compilation here.
35663742 const err = std.process.execve(gpa, argv.items, &env_map);
35673743 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);
......@@ -3574,12 +3750,10 @@ fn runOrTest(
35743750 child.stdout_behavior = .Inherit;
35753751 child.stderr_behavior = .Inherit;
35763752
3577 if (!watch) {
3578 // Here we release all the locks associated with the Compilation so
3579 // that whatever this child process wants to do won't deadlock.
3580 comp.destroy();
3581 comp_destroyed.* = true;
3582 }
3753 // Here we release all the locks associated with the Compilation so
3754 // that whatever this child process wants to do won't deadlock.
3755 comp.destroy();
3756 comp_destroyed.* = true;
35833757
35843758 const term = child.spawnAndWait() catch |err| {
35853759 try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc);
......@@ -3591,19 +3765,13 @@ fn runOrTest(
35913765 switch (term) {
35923766 .Exited => |code| {
35933767 if (code == 0) {
3594 if (!watch) return cleanExit();
3595 } else if (watch) {
3596 warn("process exited with code {d}", .{code});
3768 return cleanExit();
35973769 } else {
35983770 process.exit(code);
35993771 }
36003772 },
36013773 else => {
3602 if (watch) {
3603 warn("process aborted abnormally", .{});
3604 } else {
3605 process.exit(1);
3606 }
3774 process.exit(1);
36073775 },
36083776 }
36093777 },
......@@ -3611,7 +3779,7 @@ fn runOrTest(
36113779 switch (term) {
36123780 .Exited => |code| {
36133781 if (code == 0) {
3614 if (!watch) return cleanExit();
3782 return cleanExit();
36153783 } else {
36163784 const cmd = try std.mem.join(arena, " ", argv.items);
36173785 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
......@@ -3631,6 +3799,62 @@ fn runOrTest(
36313799 }
36323800}
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
36343858const AfterUpdateHook = union(enum) {
36353859 none,
36363860 print_emit_bin_dir_path,
......@@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) {
36383862};
36393863
36403864fn 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
36433884 var errors = try comp.getAllErrorsAlloc();
36443885 defer errors.deinit(comp.gpa);
36453886
3646 if (errors.list.len != 0) {
3647 const ttyconf: std.debug.TTY.Config = switch (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 }
3887 if (errors.errorMessageCount() > 0) {
3888 errors.renderToStdErr(renderOptions(comp.color));
36593889 return error.SemanticAnalyzeFail;
36603890 } else switch (hook) {
36613891 .none => {},
......@@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void
36973927 }
36983928}
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 {
37013936 if (!build_options.have_llvm)
37023937 fatal("cannot translate-c: compiler built without LLVM extensions", .{});
37033938
......@@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
37083943
37093944 var man: Cache.Manifest = comp.obtainCObjectCacheManifest();
37103945 man.want_shared_lock = false;
3711 defer if (enable_cache) man.deinit();
3946 defer man.deinit();
37123947
37133948 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
37143949 Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
37153950 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
37163951 };
37173952
3953 if (fancy_output) |p| p.cache_hit = true;
37183954 const digest = if (try man.hit()) man.final() else digest: {
3955 if (fancy_output) |p| p.cache_hit = false;
37193956 var argv = std.ArrayList([]const u8).init(arena);
37203957 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
37664003 error.OutOfMemory => return error.OutOfMemory,
37674004 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", .{}),
37684005 error.SemanticAnalyzeFail => {
4006 // TODO convert these to zig errors
37694007 for (clang_errors) |clang_err| {
37704008 std.debug.print("{s}:{d}:{d}: {s}\n", .{
37714009 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
38104048 break :digest digest;
38114049 };
38124050
3813 if (enable_cache) {
4051 if (fancy_output) |p| {
38144052 const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
38154053 "o", &digest, translated_zig_basename,
38164054 });
3817 try io.getStdOut().writer().print("{s}\n", .{full_zig_path});
3818 return cleanExit();
4055 p.path = full_zig_path;
38194056 } else {
38204057 const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename });
38214058 const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| {
......@@ -4009,6 +4246,8 @@ pub const usage_build =
40094246 \\Options:
40104247 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
40114248 \\ -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
40124251 \\ --build-file [file] Override path to build.zig
40134252 \\ --cache-dir [path] Override path to local Zig cache directory
40144253 \\ --global-cache-dir [path] Override path to global Zig cache directory
......@@ -4021,7 +4260,6 @@ pub const usage_build =
40214260
40224261pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
40234262 var color: Color = .auto;
4024 var prominent_compile_errors: bool = false;
40254263
40264264 // We want to release all the locks before executing the child process, so we make a nice
40274265 // 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
40824320 i += 1;
40834321 override_global_cache_dir = args[i];
40844322 continue;
4085 } else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
4086 prominent_compile_errors = true;
40874323 } else if (mem.eql(u8, arg, "-freference-trace")) {
40884324 try child_argv.append(arg);
40894325 reference_trace = 256;
......@@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42014437 .basename = exe_basename,
42024438 };
42034439 var thread_pool: ThreadPool = undefined;
4204 try thread_pool.init(gpa);
4440 try thread_pool.init(.{ .allocator = gpa });
42054441 defer thread_pool.deinit();
42064442
42074443 var cleanup_build_runner_dir: ?fs.Dir = null;
......@@ -4228,6 +4464,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42284464 .root_src_path = "build_runner.zig",
42294465 };
42304466
4467 var build_pkg: Package = .{
4468 .root_src_directory = build_directory,
4469 .root_src_path = build_zig_basename,
4470 };
42314471 if (!build_options.omit_pkg_fetching_code) {
42324472 var http_client: std.http.Client = .{ .allocator = gpa };
42334473 defer http_client.deinit();
......@@ -4247,9 +4487,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42474487 var all_modules: Package.AllModules = .{};
42484488 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
42504494 // Here we borrow main package's table and will replace it with a fresh
42514495 // one after this process completes.
4252 main_pkg.fetchAndAddDependencies(
4496 const fetch_result = build_pkg.fetchAndAddDependencies(
4497 &main_pkg,
42534498 arena,
42544499 &thread_pool,
42554500 &http_client,
......@@ -4259,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42594504 &dependencies_source,
42604505 &build_roots_source,
42614506 "",
4262 color,
4507 &wip_errors,
42634508 &all_modules,
4264 ) catch |err| switch (err) {
4265 error.PackageFetchFailed => process.exit(1),
4266 else => |e| return e,
4267 };
4509 );
4510 if (wip_errors.root_list.items.len > 0) {
4511 var errors = try wip_errors.toOwnedBundle("");
4512 defer errors.deinit(gpa);
4513 errors.renderToStdErr(renderOptions(color));
4514 process.exit(1);
4515 }
4516 try fetch_result;
42684517
42694518 try dependencies_source.appendSlice("};\npub const build_root = struct {\n");
42704519 try dependencies_source.appendSlice(build_roots_source.items);
......@@ -4280,11 +4529,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42804529 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);
42814530 try main_pkg.add(gpa, "@dependencies", deps_pkg);
42824531 }
4283
4284 var build_pkg: Package = .{
4285 .root_src_directory = build_directory,
4286 .root_src_path = build_zig_basename,
4287 };
42884532 try main_pkg.add(gpa, "@build", &build_pkg);
42894533
42904534 const comp = Compilation.create(gpa, .{
......@@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
43124556 defer comp.destroy();
43134557
43144558 updateModule(gpa, comp, .none) catch |err| switch (err) {
4315 error.SemanticAnalyzeFail => process.exit(1),
4559 error.SemanticAnalyzeFail => process.exit(2),
43164560 else => |e| return e,
43174561 };
43184562 try comp.makeBinFileExecutable();
......@@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
43364580 switch (term) {
43374581 .Exited => |code| {
43384582 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) {
4341 fatal("the build command failed with exit code {d}", .{code});
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 }
4588 const cmd = try std.mem.join(arena, " ", child_argv);
4589 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
43464590 },
43474591 else => {
43484592 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
43564600}
43574601
43584602fn readSourceFileToEndAlloc(
4359 allocator: mem.Allocator,
4603 allocator: Allocator,
43604604 input: *const fs.File,
43614605 size_hint: ?usize,
43624606) ![:0]u8 {
......@@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
45004744 };
45014745 defer tree.deinit(gpa);
45024746
4503 try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color);
4504 var has_ast_error = false;
45054747 if (check_ast_flag) {
4506 const Module = @import("Module.zig");
4507 const AstGen = @import("AstGen.zig");
4508
45094748 var file: Module.File = .{
45104749 .status = .never_loaded,
45114750 .source_loaded = true,
......@@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
45284767 defer file.zir.deinit(gpa);
45294768
45304769 if (file.zir.hasCompileErrors()) {
4531 var arena_instance = std.heap.ArenaAllocator.init(gpa);
4532 defer arena_instance.deinit();
4533 var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa);
4534 defer errors.deinit();
4535
4536 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);
4537 const ttyconf: std.debug.TTY.Config = switch (color) {
4538 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
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;
4770 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
4771 try wip_errors.init(gpa);
4772 defer wip_errors.deinit();
4773 try Compilation.addZirErrorMessages(&wip_errors, &file);
4774 var error_bundle = try wip_errors.toOwnedBundle("");
4775 defer error_bundle.deinit(gpa);
4776 error_bundle.renderToStdErr(renderOptions(color));
4777 process.exit(2);
45464778 }
4547 }
4548 if (tree.errors.len != 0 or has_ast_error) {
4549 process.exit(1);
4779 } else if (tree.errors.len != 0) {
4780 try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
4781 process.exit(2);
45504782 }
45514783 const formatted = try tree.render(gpa);
45524784 defer gpa.free(formatted);
......@@ -4622,6 +4854,7 @@ const FmtError = error{
46224854 ConnectionResetByPeer,
46234855 LockViolation,
46244856 NetNameDeleted,
4857 InvalidArgument,
46254858} || fs.File.OpenError;
46264859
46274860fn 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(
46874920 if (stat.kind == .Directory)
46884921 return error.IsDir;
46894922
4923 const gpa = fmt.gpa;
46904924 const source_code = try readSourceFileToEndAlloc(
4691 fmt.gpa,
4925 gpa,
46924926 &source_file,
46934927 std.math.cast(usize, stat.size) orelse return error.FileTooBig,
46944928 );
4695 defer fmt.gpa.free(source_code);
4929 defer gpa.free(source_code);
46964930
46974931 source_file.close();
46984932 file_closed = true;
......@@ -4700,19 +4934,16 @@ fn fmtPathFile(
47004934 // Add to set after no longer possible to get error.IsDir.
47014935 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
47024936
4703 var tree = try Ast.parse(fmt.gpa, source_code, .zig);
4704 defer tree.deinit(fmt.gpa);
4937 var tree = try Ast.parse(gpa, source_code, .zig);
4938 defer tree.deinit(gpa);
47054939
4706 try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree, file_path, fmt.color);
47074940 if (tree.errors.len != 0) {
4941 try printAstErrorsToStderr(gpa, tree, file_path, fmt.color);
47084942 fmt.any_error = true;
47094943 return;
47104944 }
47114945
47124946 if (fmt.check_ast) {
4713 const Module = @import("Module.zig");
4714 const AstGen = @import("AstGen.zig");
4715
47164947 var file: Module.File = .{
47174948 .status = .never_loaded,
47184949 .source_loaded = true,
......@@ -4731,31 +4962,24 @@ fn fmtPathFile(
47314962 .root_decl = .none,
47324963 };
47334964
4734 file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path);
4735 defer file.pkg.destroy(fmt.gpa);
4965 file.pkg = try Package.create(gpa, null, file.sub_file_path);
4966 defer file.pkg.destroy(gpa);
47364967
47374968 if (stat.size > max_src_size)
47384969 return error.FileTooBig;
47394970
4740 file.zir = try AstGen.generate(fmt.gpa, file.tree);
4971 file.zir = try AstGen.generate(gpa, file.tree);
47414972 file.zir_loaded = true;
4742 defer file.zir.deinit(fmt.gpa);
4973 defer file.zir.deinit(gpa);
47434974
47444975 if (file.zir.hasCompileErrors()) {
4745 var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa);
4746 defer arena_instance.deinit();
4747 var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa);
4748 defer errors.deinit();
4749
4750 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);
4751 const ttyconf: std.debug.TTY.Config = switch (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 }
4976 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
4977 try wip_errors.init(gpa);
4978 defer wip_errors.deinit();
4979 try Compilation.addZirErrorMessages(&wip_errors, &file);
4980 var error_bundle = try wip_errors.toOwnedBundle("");
4981 defer error_bundle.deinit(gpa);
4982 error_bundle.renderToStdErr(renderOptions(fmt.color));
47594983 fmt.any_error = true;
47604984 }
47614985 }
......@@ -4783,100 +5007,50 @@ fn fmtPathFile(
47835007 }
47845008}
47855009
4786pub fn printErrsMsgToStdErr(
4787 gpa: mem.Allocator,
4788 arena: mem.Allocator,
5010fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
5011 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
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,
47895024 tree: Ast,
47905025 path: []const u8,
4791 color: Color,
5026 wip_errors: *std.zig.ErrorBundle.Wip,
47925027) !void {
4793 const parse_errors: []const Ast.Error = tree.errors;
4794 var i: usize = 0;
4795 while (i < parse_errors.len) : (i += 1) {
4796 const parse_error = parse_errors[i];
4797 const lok_token = parse_error.token;
4798 const token_tags = tree.tokens.items(.tag);
4799 const start_loc = tree.tokenLocation(0, lok_token);
4800 const source_line = tree.source[start_loc.line_start..start_loc.line_end];
4801
4802 var text_buf = std.ArrayList(u8).init(gpa);
4803 defer text_buf.deinit();
4804 const writer = text_buf.writer();
4805 try tree.renderError(parse_error, writer);
4806 const text = try arena.dupe(u8, text_buf.items);
4807
4808 var notes_buffer: [2]Compilation.AllErrors.Message = undefined;
4809 var notes_len: usize = 0;
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 }
5028 var file: Module.File = .{
5029 .status = .never_loaded,
5030 .source_loaded = true,
5031 .zir_loaded = false,
5032 .sub_file_path = path,
5033 .source = tree.source,
5034 .stat = .{
5035 .size = 0,
5036 .inode = 0,
5037 .mtime = 0,
5038 },
5039 .tree = tree,
5040 .tree_loaded = true,
5041 .zir = undefined,
5042 .pkg = undefined,
5043 .root_decl = .none,
5044 };
48535045
4854 const extra_offset = tree.errorOffset(parse_error);
4855 const byte_offset = @intCast(u32, start_loc.line_start) + extra_offset;
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 };
5046 file.pkg = try Package.create(gpa, null, path);
5047 defer file.pkg.destroy(gpa);
48715048
4872 const ttyconf: std.debug.TTY.Config = switch (color) {
4873 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
4874 .on => .escape_codes,
4875 .off => .no_color,
4876 };
5049 file.zir = try AstGen.generate(gpa, file.tree);
5050 file.zir_loaded = true;
5051 defer file.zir.deinit(gpa);
48775052
4878 message.renderToStdErr(ttyconf);
4879 }
5053 try Compilation.addZirErrorMessages(wip_errors, &file);
48805054}
48815055
48825056pub const info_zen =
......@@ -5324,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat
53245498 return std.zig.system.NativeTargetInfo.detect(cross_target);
53255499}
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
53405501const usage_ast_check =
53415502 \\Usage: zig ast-check [file]
53425503 \\
......@@ -5359,8 +5520,6 @@ pub fn cmdAstCheck(
53595520 arena: Allocator,
53605521 args: []const []const u8,
53615522) !void {
5362 const Module = @import("Module.zig");
5363 const AstGen = @import("AstGen.zig");
53645523 const Zir = @import("Zir.zig");
53655524
53665525 var color: Color = .auto;
......@@ -5450,26 +5609,18 @@ pub fn cmdAstCheck(
54505609 file.tree_loaded = true;
54515610 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
54585612 file.zir = try AstGen.generate(gpa, file.tree);
54595613 file.zir_loaded = true;
54605614 defer file.zir.deinit(gpa);
54615615
54625616 if (file.zir.hasCompileErrors()) {
5463 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
5464 try Compilation.AllErrors.addZir(arena, &errors, &file);
5465 const ttyconf: std.debug.TTY.Config = switch (color) {
5466 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
5467 .on => .escape_codes,
5468 .off => .no_color,
5469 };
5470 for (errors.items) |full_err_msg| {
5471 full_err_msg.renderToStdErr(ttyconf);
5472 }
5617 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5618 try wip_errors.init(gpa);
5619 defer wip_errors.deinit();
5620 try Compilation.addZirErrorMessages(&wip_errors, &file);
5621 var error_bundle = try wip_errors.toOwnedBundle("");
5622 defer error_bundle.deinit(gpa);
5623 error_bundle.renderToStdErr(renderOptions(color));
54735624 process.exit(1);
54745625 }
54755626
......@@ -5527,8 +5678,7 @@ pub fn cmdChangelist(
55275678 arena: Allocator,
55285679 args: []const []const u8,
55295680) !void {
5530 const Module = @import("Module.zig");
5531 const AstGen = @import("AstGen.zig");
5681 const color: Color = .auto;
55325682 const Zir = @import("Zir.zig");
55335683
55345684 const old_source_file = args[0];
......@@ -5576,22 +5726,18 @@ pub fn cmdChangelist(
55765726 file.tree_loaded = true;
55775727 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
55845729 file.zir = try AstGen.generate(gpa, file.tree);
55855730 file.zir_loaded = true;
55865731 defer file.zir.deinit(gpa);
55875732
55885733 if (file.zir.hasCompileErrors()) {
5589 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
5590 try Compilation.AllErrors.addZir(arena, &errors, &file);
5591 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
5592 for (errors.items) |full_err_msg| {
5593 full_err_msg.renderToStdErr(ttyconf);
5594 }
5734 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5735 try wip_errors.init(gpa);
5736 defer wip_errors.deinit();
5737 try Compilation.addZirErrorMessages(&wip_errors, &file);
5738 var error_bundle = try wip_errors.toOwnedBundle("");
5739 defer error_bundle.deinit(gpa);
5740 error_bundle.renderToStdErr(renderOptions(color));
55955741 process.exit(1);
55965742 }
55975743
......@@ -5613,11 +5759,6 @@ pub fn cmdChangelist(
56135759 var new_tree = try Ast.parse(gpa, new_source, .zig);
56145760 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
56215762 var old_zir = file.zir;
56225763 defer old_zir.deinit(gpa);
56235764 file.zir_loaded = false;
......@@ -5625,12 +5766,13 @@ pub fn cmdChangelist(
56255766 file.zir_loaded = true;
56265767
56275768 if (file.zir.hasCompileErrors()) {
5628 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
5629 try Compilation.AllErrors.addZir(arena, &errors, &file);
5630 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
5631 for (errors.items) |full_err_msg| {
5632 full_err_msg.renderToStdErr(ttyconf);
5633 }
5769 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5770 try wip_errors.init(gpa);
5771 defer wip_errors.deinit();
5772 try Compilation.addZirErrorMessages(&wip_errors, &file);
5773 var error_bundle = try wip_errors.toOwnedBundle("");
5774 defer error_bundle.deinit(gpa);
5775 error_bundle.renderToStdErr(renderOptions(color));
56345776 process.exit(1);
56355777 }
56365778
......@@ -5891,3 +6033,20 @@ const ClangSearchSanitizer = struct {
58916033 iframework: bool = false,
58926034 };
58936035};
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 {
1919 uuid_lib,
2020};
2121
22pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
22pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
2323 if (!build_options.have_llvm) {
2424 return error.ZigCompilerNotBuiltWithLLVMExtensions;
2525 }
......@@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
4141 //"-D_UNICODE",
4242 //"-DWPRFLAG=1",
4343 });
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, &.{
4545 .{
4646 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
4747 "libc", "mingw", "crt", "crtexe.c",
......@@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
6060 "-U__CRTDLL__",
6161 "-D__MSVCRT__",
6262 });
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, &.{
6464 .{
6565 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
6666 "libc", "mingw", "crt", "crtdll.c",
......@@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
100100 .extra_flags = args.items,
101101 };
102102 }
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);
104104 },
105105
106106 .msvcrt_os_lib => {
......@@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
148148 };
149149 }
150150 }
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);
152152 },
153153
154154 .mingwex_lib => {
......@@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
211211 } else {
212212 @panic("unsupported arch");
213213 }
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);
215215 },
216216
217217 .uuid_lib => {
......@@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
244244 .extra_flags = extra_flags,
245245 };
246246 }
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);
248248 },
249249 }
250250}
src/musl.zig+8-8
......@@ -17,7 +17,7 @@ pub const CRTFile = enum {
1717 libc_so,
1818};
1919
20pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
20pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
2121 if (!build_options.have_llvm) {
2222 return error.ZigCompilerNotBuiltWithLLVMExtensions;
2323 }
......@@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
3333 try args.appendSlice(&[_][]const u8{
3434 "-Qunused-arguments",
3535 });
36 return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{
36 return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{
3737 .{
3838 .src_path = try start_asm_path(comp, arena, "crti.s"),
3939 .extra_flags = args.items,
......@@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
4646 try args.appendSlice(&[_][]const u8{
4747 "-Qunused-arguments",
4848 });
49 return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{
49 return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{
5050 .{
5151 .src_path = try start_asm_path(comp, arena, "crtn.s"),
5252 .extra_flags = args.items,
......@@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
6060 "-fno-stack-protector",
6161 "-DCRT",
6262 });
63 return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{
63 return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{
6464 .{
6565 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
6666 "libc", "musl", "crt", "crt1.c",
......@@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
7777 "-fno-stack-protector",
7878 "-DCRT",
7979 });
80 return comp.build_crt_file("rcrt1", .Obj, &[1]Compilation.CSourceFile{
80 return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{
8181 .{
8282 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
8383 "libc", "musl", "crt", "rcrt1.c",
......@@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
9494 "-fno-stack-protector",
9595 "-DCRT",
9696 });
97 return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{
97 return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{
9898 .{
9999 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
100100 "libc", "musl", "crt", "Scrt1.c",
......@@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
187187 .extra_flags = args.items,
188188 };
189189 }
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);
191191 },
192192 .libc_so => {
193193 const target = comp.getTarget();
......@@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
241241 });
242242 defer sub_compilation.destroy();
243243
244 try sub_compilation.updateSubCompilation();
244 try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node);
245245
246246 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
247247
src/objcopy.zig+45-5
......@@ -4,22 +4,25 @@ const fs = std.fs;
44const elf = std.elf;
55const Allocator = std.mem.Allocator;
66const File = std.fs.File;
7const assert = std.debug.assert;
8
79const main = @import("main.zig");
810const fatal = main.fatal;
9const cleanExit = main.cleanExit;
11const Server = std.zig.Server;
12const build_options = @import("build_options");
1013
1114pub fn cmdObjCopy(
1215 gpa: Allocator,
1316 arena: Allocator,
1417 args: []const []const u8,
1518) !void {
16 _ = gpa;
1719 var i: usize = 0;
1820 var opt_out_fmt: ?std.Target.ObjectFormat = null;
1921 var opt_input: ?[]const u8 = null;
2022 var opt_output: ?[]const u8 = null;
2123 var only_section: ?[]const u8 = null;
2224 var pad_to: ?u64 = null;
25 var listen = false;
2326 while (i < args.len) : (i += 1) {
2427 const arg = args[i];
2528 if (!mem.startsWith(u8, arg, "-")) {
......@@ -54,6 +57,8 @@ pub fn cmdObjCopy(
5457 i += 1;
5558 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
5659 only_section = args[i];
60 } else if (mem.eql(u8, arg, "--listen=-")) {
61 listen = true;
5762 } else if (mem.startsWith(u8, arg, "--only-section=")) {
5863 only_section = arg["--output-target=".len..];
5964 } else if (mem.eql(u8, arg, "--pad-to")) {
......@@ -102,10 +107,45 @@ pub fn cmdObjCopy(
102107 .only_section = only_section,
103108 .pad_to = pad_to,
104109 });
105 return cleanExit();
106110 },
107111 else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}),
108112 }
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();
109149}
110150
111151const usage =
......@@ -417,7 +457,7 @@ const HexWriter = struct {
417457 }
418458
419459 fn Address(address: u32) Record {
420 std.debug.assert(address > 0xFFFF);
460 assert(address > 0xFFFF);
421461 const segment = @intCast(u16, address / 0x10000);
422462 if (address > 0xFFFFF) {
423463 return Record{
......@@ -460,7 +500,7 @@ const HexWriter = struct {
460500 const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len;
461501 var outbuf: [BUFSIZE]u8 = undefined;
462502 const payload_bytes = self.getPayloadBytes();
463 std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN);
503 assert(payload_bytes.len <= MAX_PAYLOAD_LEN);
464504
465505 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{
466506 @intCast(u8, payload_bytes.len),
src/print_air.zig+1
......@@ -194,6 +194,7 @@ const Writer = struct {
194194 .c_va_end,
195195 => try w.writeUnOp(s, inst),
196196
197 .trap,
197198 .breakpoint,
198199 .unreach,
199200 .ret_addr,
src/print_zir.zig+2-1
......@@ -196,7 +196,6 @@ const Writer = struct {
196196 .error_name,
197197 .panic,
198198 .panic_comptime,
199 .set_cold,
200199 .set_runtime_safety,
201200 .sqrt,
202201 .sin,
......@@ -411,6 +410,7 @@ const Writer = struct {
411410 .alloc_inferred_comptime_mut,
412411 .ret_ptr,
413412 .ret_type,
413 .trap,
414414 => try self.writeNode(stream, inst),
415415
416416 .error_value,
......@@ -503,6 +503,7 @@ const Writer = struct {
503503 .fence,
504504 .set_float_mode,
505505 .set_align_stack,
506 .set_cold,
506507 .wasm_memory_size,
507508 .error_to_int,
508509 .int_to_error,
src/register_manager.zig+3
......@@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{
1919 /// Can happen when spilling an instruction in codegen runs out of
2020 /// memory, so we propagate that error
2121 OutOfMemory,
22 /// Can happen when spilling an instruction in codegen triggers integer
23 /// overflow, so we propagate that error
24 Overflow,
2225 /// Can happen when spilling an instruction triggers a codegen
2326 /// error, so we propagate that error
2427 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
784784
785785 const qual_type = var_decl.getTypeSourceInfo_getType();
786786 const storage_class = var_decl.getStorageClass();
787 const is_const = qual_type.isConstQualified();
788787 const has_init = var_decl.hasInit();
789788 const decl_init = var_decl.getInit();
789 var is_const = qual_type.isConstQualified();
790790
791791 // In C extern variables with initializers behave like Zig exports.
792792 // extern int foo = 2;
......@@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
843843
844844 // std.mem.zeroes(T)
845845 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;
846860 }
847861
848862 const linksection_string = blk: {
src/translate_c/ast.zig+28
......@@ -158,6 +158,8 @@ pub const Node = extern union {
158158 vector_zero_init,
159159 /// @shuffle(type, a, b, mask)
160160 shuffle,
161 /// @extern(ty, .{ .name = n })
162 builtin_extern,
161163
162164 /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)
163165 macro_arithmetic,
......@@ -373,6 +375,7 @@ pub const Node = extern union {
373375 .field_access => Payload.FieldAccess,
374376 .string_slice => Payload.StringSlice,
375377 .shuffle => Payload.Shuffle,
378 .builtin_extern => Payload.Extern,
376379 .macro_arithmetic => Payload.MacroArithmetic,
377380 };
378381 }
......@@ -718,6 +721,14 @@ pub const Payload = struct {
718721 },
719722 };
720723
724 pub const Extern = struct {
725 base: Payload,
726 data: struct {
727 type: Node,
728 name: Node,
729 },
730 };
731
721732 pub const MacroArithmetic = struct {
722733 base: Payload,
723734 data: struct {
......@@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
14091420 payload.mask_vector,
14101421 });
14111422 },
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 },
14121439 .macro_arithmetic => {
14131440 const payload = node.castTag(.macro_arithmetic).?.data;
14141441 const op = @tagName(payload.op);
......@@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
23482375 .div_exact,
23492376 .offset_of,
23502377 .shuffle,
2378 .builtin_extern,
23512379 .static_local_var,
23522380 .mut_str,
23532381 .macro_arithmetic,
src/value.zig+60-7
......@@ -1365,6 +1365,17 @@ pub const Value = extern union {
13651365 if (val.isDeclRef()) return error.ReinterpretDeclRef;
13661366 return val.writeToMemory(Type.usize, mod, buffer);
13671367 },
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 },
13681379 else => @panic("TODO implement writeToMemory for more types"),
13691380 }
13701381 }
......@@ -1471,6 +1482,17 @@ pub const Value = extern union {
14711482 if (val.isDeclRef()) return error.ReinterpretDeclRef;
14721483 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);
14731484 },
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 },
14741496 else => @panic("TODO implement writeToPackedMemory for more types"),
14751497 }
14761498 }
......@@ -1579,6 +1601,12 @@ pub const Value = extern union {
15791601 assert(!ty.isSlice()); // No well defined layout.
15801602 return readFromMemory(Type.usize, mod, buffer, arena);
15811603 },
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 },
15821610 else => @panic("TODO implement readFromMemory for more types"),
15831611 }
15841612 }
......@@ -1670,6 +1698,12 @@ pub const Value = extern union {
16701698 assert(!ty.isSlice()); // No well defined layout.
16711699 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
16721700 },
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 },
16731707 else => @panic("TODO implement readFromPackedMemory for more types"),
16741708 }
16751709 }
......@@ -3144,13 +3178,32 @@ pub const Value = extern union {
31443178 /// TODO: check for cases such as array that is not marked undef but all the element
31453179 /// values are marked undef, or struct that is not marked undef but all fields are marked
31463180 /// undef, etc.
3147 pub fn anyUndef(self: Value) bool {
3148 if (self.castTag(.aggregate)) |aggregate| {
3149 for (aggregate.data) |val| {
3150 if (val.anyUndef()) return true;
3151 }
3181 pub fn anyUndef(self: Value, mod: *Module) bool {
3182 switch (self.tag()) {
3183 .slice => {
3184 const payload = self.castTag(.slice).?;
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 => {},
31523204 }
3153 return self.isUndef();
3205
3206 return false;
31543207 }
31553208
31563209 /// Asserts the value is not undefined and not unreachable.
......@@ -3319,7 +3372,7 @@ pub const Value = extern union {
33193372 }
33203373 }
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 {
33233376 switch (dest_ty.floatBits(target)) {
33243377 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)),
33253378 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
5959 };
6060}
6161
62pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
62pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {
6363 if (!build_options.have_llvm) {
6464 return error.ZigCompilerNotBuiltWithLLVMExtensions;
6565 }
......@@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
7474 var args = std.ArrayList([]const u8).init(arena);
7575 try addCCArgs(comp, arena, &args, false);
7676 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, &.{
7878 .{
7979 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
8080 "libc", try sanitize(arena, crt1_reactor_src_file),
......@@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
8787 var args = std.ArrayList([]const u8).init(arena);
8888 try addCCArgs(comp, arena, &args, false);
8989 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, &.{
9191 .{
9292 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
9393 "libc", try sanitize(arena, crt1_command_src_file),
......@@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
145145 }
146146 }
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);
149149 },
150150 .libwasi_emulated_process_clocks_a => {
151151 var args = std.ArrayList([]const u8).init(arena);
......@@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
161161 .extra_flags = args.items,
162162 });
163163 }
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);
165165 },
166166 .libwasi_emulated_getpid_a => {
167167 var args = std.ArrayList([]const u8).init(arena);
......@@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
177177 .extra_flags = args.items,
178178 });
179179 }
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);
181181 },
182182 .libwasi_emulated_mman_a => {
183183 var args = std.ArrayList([]const u8).init(arena);
......@@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
193193 .extra_flags = args.items,
194194 });
195195 }
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);
197197 },
198198 .libwasi_emulated_signal_a => {
199199 var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
......@@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
228228 }
229229 }
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);
232232 },
233233 }
234234}
stage1/wasi.c-2
......@@ -497,8 +497,6 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov
497497 size_t read_size = 0;
498498 if (fds[fd].stream != NULL)
499499 read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream);
500 else
501 panic("unimplemented");
502500 size += read_size;
503501 if (read_size < iovs_ptr[i].len) break;
504502 }
stage1/zig.h+1915-904
......@@ -1,8 +1,11 @@
11#undef linux
22
3#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
34#define __STDC_WANT_IEC_60559_TYPES_EXT__
5#endif
46#include <float.h>
57#include <limits.h>
8#include <stdarg.h>
69#include <stddef.h>
710#include <stdint.h>
811
......@@ -34,6 +37,14 @@ typedef char bool;
3437#define zig_has_attribute(attribute) 0
3538#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
3748#if __STDC_VERSION__ >= 201112L
3849#define zig_threadlocal _Thread_local
3950#elif defined(__GNUC__)
......@@ -75,6 +86,32 @@ typedef char bool;
7586#define zig_cold
7687#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
78115#if __STDC_VERSION__ >= 199901L
79116#define zig_restrict restrict
80117#elif defined(__GNUC__)
......@@ -151,16 +188,38 @@ typedef char bool;
151188#define zig_export(sig, symbol, name) __asm(name " = " symbol)
152189#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
154206#if zig_has_builtin(debugtrap)
155207#define zig_breakpoint() __builtin_debugtrap()
156#elif zig_has_builtin(trap) || defined(zig_gnuc)
157#define zig_breakpoint() __builtin_trap()
158208#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
159209#define zig_breakpoint() __debugbreak()
160210#elif defined(__i386__) || defined(__x86_64__)
161211#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");
162216#else
217#include <signal.h>
218#if defined(SIGTRAP)
163219#define zig_breakpoint() raise(SIGTRAP)
220#else
221#define zig_breakpoint() zig_breakpoint_unavailable
222#endif
164223#endif
165224
166225#if zig_has_builtin(return_address) || defined(zig_gnuc)
......@@ -286,701 +345,656 @@ typedef char bool;
286345#endif
287346
288347#if __STDC_VERSION__ >= 201112L
289#define zig_noreturn _Noreturn void
348#define zig_noreturn _Noreturn
290349#elif zig_has_attribute(noreturn) || defined(zig_gnuc)
291#define zig_noreturn __attribute__((noreturn)) void
350#define zig_noreturn __attribute__((noreturn))
292351#elif _MSC_VER
293#define zig_noreturn __declspec(noreturn) void
352#define zig_noreturn __declspec(noreturn)
294353#else
295#define zig_noreturn void
354#define zig_noreturn
296355#endif
297356
298357#define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T))
299358
300typedef uintptr_t zig_usize;
301typedef intptr_t zig_isize;
302typedef signed short int zig_c_short;
303typedef unsigned short int zig_c_ushort;
304typedef signed int zig_c_int;
305typedef unsigned int zig_c_uint;
306typedef signed long int zig_c_long;
307typedef unsigned long int zig_c_ulong;
308typedef signed long long int zig_c_longlong;
309typedef unsigned long long int zig_c_ulonglong;
310
311typedef uint8_t zig_u8;
312typedef int8_t zig_i8;
313typedef uint16_t zig_u16;
314typedef int16_t zig_i16;
315typedef uint32_t zig_u32;
316typedef int32_t zig_i32;
317typedef uint64_t zig_u64;
318typedef int64_t zig_i64;
319
320#define zig_as_u8(val) UINT8_C(val)
321#define zig_as_i8(val) INT8_C(val)
322#define zig_as_u16(val) UINT16_C(val)
323#define zig_as_i16(val) INT16_C(val)
324#define zig_as_u32(val) UINT32_C(val)
325#define zig_as_i32(val) INT32_C(val)
326#define zig_as_u64(val) UINT64_C(val)
327#define zig_as_i64(val) INT64_C(val)
328
329#define zig_minInt_u8 zig_as_u8(0)
330#define zig_maxInt_u8 UINT8_MAX
359#define zig_compiler_rt_abbrev_uint32_t si
360#define zig_compiler_rt_abbrev_int32_t si
361#define zig_compiler_rt_abbrev_uint64_t di
362#define zig_compiler_rt_abbrev_int64_t di
363#define zig_compiler_rt_abbrev_zig_u128 ti
364#define zig_compiler_rt_abbrev_zig_i128 ti
365#define zig_compiler_rt_abbrev_zig_f16 hf
366#define zig_compiler_rt_abbrev_zig_f32 sf
367#define zig_compiler_rt_abbrev_zig_f64 df
368#define zig_compiler_rt_abbrev_zig_f80 xf
369#define zig_compiler_rt_abbrev_zig_f128 tf
370
371zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t);
372zig_extern void *memset (void *, int, size_t);
373
374/* ===================== 8/16/32/64-bit Integer Support ===================== */
375
376#if __STDC_VERSION__ >= 199901L || _MSC_VER
377#include <stdint.h>
378#else
379
380#if SCHAR_MIN == ~0x7F && SCHAR_MAX == 0x7F && UCHAR_MAX == 0xFF
381typedef unsigned char uint8_t;
382typedef signed char int8_t;
383#define INT8_C(c) c
384#define UINT8_C(c) c##U
385#elif SHRT_MIN == ~0x7F && SHRT_MAX == 0x7F && USHRT_MAX == 0xFF
386typedef unsigned short uint8_t;
387typedef signed short int8_t;
388#define INT8_C(c) c
389#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
331505#define zig_minInt_i8 INT8_MIN
332506#define zig_maxInt_i8 INT8_MAX
333#define zig_minInt_u16 zig_as_u16(0)
334#define zig_maxInt_u16 UINT16_MAX
507#define zig_minInt_u8 UINT8_C(0)
508#define zig_maxInt_u8 UINT8_MAX
335509#define zig_minInt_i16 INT16_MIN
336510#define zig_maxInt_i16 INT16_MAX
337#define zig_minInt_u32 zig_as_u32(0)
338#define zig_maxInt_u32 UINT32_MAX
511#define zig_minInt_u16 UINT16_C(0)
512#define zig_maxInt_u16 UINT16_MAX
339513#define zig_minInt_i32 INT32_MIN
340514#define zig_maxInt_i32 INT32_MAX
341#define zig_minInt_u64 zig_as_u64(0)
342#define zig_maxInt_u64 UINT64_MAX
515#define zig_minInt_u32 UINT32_C(0)
516#define zig_maxInt_u32 UINT32_MAX
343517#define zig_minInt_i64 INT64_MIN
344518#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 si
347#define zig_compiler_rt_abbrev_i32 si
348#define zig_compiler_rt_abbrev_u64 di
349#define zig_compiler_rt_abbrev_i64 di
350#define zig_compiler_rt_abbrev_u128 ti
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)
522#define zig_intLimit(s, w, limit, bits) zig_shr_##s##w(zig_##limit##Int_##s##w, w - (bits))
523#define zig_minInt_i(w, bits) zig_intLimit(i, w, min, bits)
524#define zig_maxInt_i(w, bits) zig_intLimit(i, w, max, bits)
525#define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits)
526#define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits)
367527
368528#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) { \
370530 return lhs operator rhs; \
371531 }
372532#define zig_int_basic_operator(Type, operation, operator) \
373 zig_int_operator(Type, Type, operation, operator)
533 zig_int_operator(Type, Type, operation, operator)
374534#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)
376536#define zig_int_helpers(w) \
377 zig_int_basic_operator(u##w, and, &) \
378 zig_int_basic_operator(i##w, and, &) \
379 zig_int_basic_operator(u##w, or, |) \
380 zig_int_basic_operator(i##w, or, |) \
381 zig_int_basic_operator(u##w, xor, ^) \
382 zig_int_basic_operator(i##w, xor, ^) \
383 zig_int_shift_operator(u##w, shl, <<) \
384 zig_int_shift_operator(i##w, shl, <<) \
385 zig_int_shift_operator(u##w, shr, >>) \
537 zig_int_basic_operator(uint##w##_t, and_u##w, &) \
538 zig_int_basic_operator( int##w##_t, and_i##w, &) \
539 zig_int_basic_operator(uint##w##_t, or_u##w, |) \
540 zig_int_basic_operator( int##w##_t, or_i##w, |) \
541 zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \
542 zig_int_basic_operator( int##w##_t, xor_i##w, ^) \
543 zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \
544 zig_int_shift_operator( int##w##_t, shl_i##w, <<) \
545 zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \
386546\
387 static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \
388 zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \
547 static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \
548 int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \
389549 return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \
390550 } \
391551\
392 static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \
393 return val ^ zig_maxInt(u##w, bits); \
552 static inline uint##w##_t zig_not_u##w(uint##w##_t val, uint8_t bits) { \
553 return val ^ zig_maxInt_u(w, bits); \
394554 } \
395555\
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) { \
397557 (void)bits; \
398558 return ~val; \
399559 } \
400560\
401 static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \
402 return val & zig_maxInt(u##w, bits); \
561 static inline uint##w##_t zig_wrap_u##w(uint##w##_t val, uint8_t bits) { \
562 return val & zig_maxInt_u(w, bits); \
403563 } \
404564\
405 static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \
406 return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \
407 ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \
565 static inline int##w##_t zig_wrap_i##w(int##w##_t val, uint8_t bits) { \
566 return (val & UINT##w##_C(1) << (bits - UINT8_C(1))) != 0 \
567 ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \
408568 } \
409569\
410 zig_int_basic_operator(u##w, div_floor, /) \
570 zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \
411571\
412 static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \
413 return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \
572 static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
573 return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \
414574 } \
415575\
416 zig_int_basic_operator(u##w, mod, %) \
576 zig_int_basic_operator(uint##w##_t, mod_u##w, %) \
417577\
418 static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \
419 zig_i##w rem = lhs % rhs; \
420 return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \
578 static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \
579 int##w##_t rem = lhs % rhs; \
580 return rem + (((lhs ^ rhs) & rem) < INT##w##_C(0) ? rhs : INT##w##_C(0)); \
421581 } \
422582\
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) { \
424584 return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \
425585 } \
426586\
427 static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \
428 return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \
587 static inline int##w##_t zig_shlw_i##w(int##w##_t lhs, uint8_t rhs, uint8_t bits) { \
588 return zig_wrap_i##w((int##w##_t)zig_shl_u##w((uint##w##_t)lhs, (uint##w##_t)rhs), bits); \
429589 } \
430590\
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) { \
432592 return zig_wrap_u##w(lhs + rhs, bits); \
433593 } \
434594\
435 static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
436 return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \
595 static inline int##w##_t zig_addw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
596 return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs + (uint##w##_t)rhs), bits); \
437597 } \
438598\
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) { \
440600 return zig_wrap_u##w(lhs - rhs, bits); \
441601 } \
442602\
443 static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
444 return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \
603 static inline int##w##_t zig_subw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
604 return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs - (uint##w##_t)rhs), bits); \
445605 } \
446606\
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) { \
448608 return zig_wrap_u##w(lhs * rhs, bits); \
449609 } \
450610\
451 static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
452 return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \
611 static inline int##w##_t zig_mulw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
612 return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs * (uint##w##_t)rhs), bits); \
453613 }
454614zig_int_helpers(8)
455615zig_int_helpers(16)
456616zig_int_helpers(32)
457617zig_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) {
460620#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
461 zig_u32 full_res;
621 uint32_t full_res;
462622 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
463623 *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);
465625#else
466626 *res = zig_addw_u32(lhs, rhs, bits);
467627 return *res < lhs;
468628#endif
469629}
470630
471static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n,
472 const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 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) {
631zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow);
632static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
479633#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
480 zig_i32 full_res;
634 int32_t full_res;
481635 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
482636#else
483 zig_c_int overflow_int;
484 zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int);
637 int overflow_int;
638 int32_t full_res = __addosi4(lhs, rhs, &overflow_int);
485639 bool overflow = overflow_int != 0;
486640#endif
487641 *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);
489643}
490644
491static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n,
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) {
645static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) {
498646#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
499 zig_u64 full_res;
647 uint64_t full_res;
500648 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
501649 *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);
503651#else
504652 *res = zig_addw_u64(lhs, rhs, bits);
505653 return *res < lhs;
506654#endif
507655}
508656
509static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n,
510 const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 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) {
657zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow);
658static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
517659#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
518 zig_i64 full_res;
660 int64_t full_res;
519661 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
520662#else
521 zig_c_int overflow_int;
522 zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int);
663 int overflow_int;
664 int64_t full_res = __addodi4(lhs, rhs, &overflow_int);
523665 bool overflow = overflow_int != 0;
524666#endif
525667 *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);
527669}
528670
529static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n,
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) {
671static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
536672#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
537 zig_u8 full_res;
673 uint8_t full_res;
538674 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
539675 *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);
541677#else
542 zig_u32 full_res;
678 uint32_t full_res;
543679 bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits);
544 *res = (zig_u8)full_res;
680 *res = (uint8_t)full_res;
545681 return overflow;
546682#endif
547683}
548684
549static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n,
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) {
685static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
556686#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
557 zig_i8 full_res;
687 int8_t full_res;
558688 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
559689 *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);
561691#else
562 zig_i32 full_res;
692 int32_t full_res;
563693 bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits);
564 *res = (zig_i8)full_res;
694 *res = (int8_t)full_res;
565695 return overflow;
566696#endif
567697}
568698
569static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n,
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) {
699static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
576700#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
577 zig_u16 full_res;
701 uint16_t full_res;
578702 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
579703 *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);
581705#else
582 zig_u32 full_res;
706 uint32_t full_res;
583707 bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits);
584 *res = (zig_u16)full_res;
708 *res = (uint16_t)full_res;
585709 return overflow;
586710#endif
587711}
588712
589static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n,
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) {
713static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
596714#if zig_has_builtin(add_overflow) || defined(zig_gnuc)
597 zig_i16 full_res;
715 int16_t full_res;
598716 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
599717 *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);
601719#else
602 zig_i32 full_res;
720 int32_t full_res;
603721 bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits);
604 *res = (zig_i16)full_res;
722 *res = (int16_t)full_res;
605723 return overflow;
606724#endif
607725}
608726
609static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n,
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) {
727static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
616728#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
617 zig_u32 full_res;
729 uint32_t full_res;
618730 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
619731 *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);
621733#else
622734 *res = zig_subw_u32(lhs, rhs, bits);
623735 return *res > lhs;
624736#endif
625737}
626738
627static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n,
628 const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 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) {
739zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow);
740static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
635741#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
636 zig_i32 full_res;
742 int32_t full_res;
637743 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
638744#else
639 zig_c_int overflow_int;
640 zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int);
745 int overflow_int;
746 int32_t full_res = __subosi4(lhs, rhs, &overflow_int);
641747 bool overflow = overflow_int != 0;
642748#endif
643749 *res = zig_wrap_i32(full_res, bits);
644 return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, 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);
750 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
651751}
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) {
654754#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
655 zig_u64 full_res;
755 uint64_t full_res;
656756 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
657757 *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);
659759#else
660760 *res = zig_subw_u64(lhs, rhs, bits);
661761 return *res > lhs;
662762#endif
663763}
664764
665static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n,
666 const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 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) {
765zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow);
766static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
673767#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
674 zig_i64 full_res;
768 int64_t full_res;
675769 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
676770#else
677 zig_c_int overflow_int;
678 zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int);
771 int overflow_int;
772 int64_t full_res = __subodi4(lhs, rhs, &overflow_int);
679773 bool overflow = overflow_int != 0;
680774#endif
681775 *res = zig_wrap_i64(full_res, bits);
682 return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, 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);
776 return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits);
689777}
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) {
692780#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
693 zig_u8 full_res;
781 uint8_t full_res;
694782 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
695783 *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);
697785#else
698 zig_u32 full_res;
786 uint32_t full_res;
699787 bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits);
700 *res = (zig_u8)full_res;
788 *res = (uint8_t)full_res;
701789 return overflow;
702790#endif
703791}
704792
705static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n,
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) {
793static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
712794#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
713 zig_i8 full_res;
795 int8_t full_res;
714796 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
715797 *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);
717799#else
718 zig_i32 full_res;
800 int32_t full_res;
719801 bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits);
720 *res = (zig_i8)full_res;
802 *res = (int8_t)full_res;
721803 return overflow;
722804#endif
723805}
724806
725static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n,
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) {
807static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
733808#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
734 zig_u16 full_res;
809 uint16_t full_res;
735810 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
736811 *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);
738813#else
739 zig_u32 full_res;
814 uint32_t full_res;
740815 bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits);
741 *res = (zig_u16)full_res;
816 *res = (uint16_t)full_res;
742817 return overflow;
743818#endif
744819}
745820
746static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n,
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) {
821static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
754822#if zig_has_builtin(sub_overflow) || defined(zig_gnuc)
755 zig_i16 full_res;
823 int16_t full_res;
756824 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
757825 *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);
759827#else
760 zig_i32 full_res;
828 int32_t full_res;
761829 bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits);
762 *res = (zig_i16)full_res;
830 *res = (int16_t)full_res;
763831 return overflow;
764832#endif
765833}
766834
767static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n,
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) {
835static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) {
774836#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
775 zig_u32 full_res;
837 uint32_t full_res;
776838 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
777839 *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);
779841#else
780842 *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;
782844#endif
783845}
784846
785static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n,
786 const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 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) {
847zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow);
848static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) {
793849#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
794 zig_i32 full_res;
850 int32_t full_res;
795851 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
796852#else
797 zig_c_int overflow_int;
798 zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int);
853 int overflow_int;
854 int32_t full_res = __mulosi4(lhs, rhs, &overflow_int);
799855 bool overflow = overflow_int != 0;
800856#endif
801857 *res = zig_wrap_i32(full_res, bits);
802 return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, 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);
858 return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits);
809859}
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) {
812862#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
813 zig_u64 full_res;
863 uint64_t full_res;
814864 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
815865 *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);
817867#else
818868 *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;
820870#endif
821871}
822872
823static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n,
824 const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 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) {
873zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow);
874static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) {
831875#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
832 zig_i64 full_res;
876 int64_t full_res;
833877 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
834878#else
835 zig_c_int overflow_int;
836 zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int);
879 int overflow_int;
880 int64_t full_res = __mulodi4(lhs, rhs, &overflow_int);
837881 bool overflow = overflow_int != 0;
838882#endif
839883 *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);
841885}
842886
843static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n,
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) {
887static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) {
850888#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
851 zig_u8 full_res;
889 uint8_t full_res;
852890 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
853891 *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);
855893#else
856 zig_u32 full_res;
894 uint32_t full_res;
857895 bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits);
858 *res = (zig_u8)full_res;
896 *res = (uint8_t)full_res;
859897 return overflow;
860898#endif
861899}
862900
863static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n,
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) {
901static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) {
870902#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
871 zig_i8 full_res;
903 int8_t full_res;
872904 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
873905 *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);
875907#else
876 zig_i32 full_res;
908 int32_t full_res;
877909 bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits);
878 *res = (zig_i8)full_res;
910 *res = (int8_t)full_res;
879911 return overflow;
880912#endif
881913}
882914
883static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n,
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) {
915static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) {
890916#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
891 zig_u16 full_res;
917 uint16_t full_res;
892918 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
893919 *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);
895921#else
896 zig_u32 full_res;
922 uint32_t full_res;
897923 bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits);
898 *res = (zig_u16)full_res;
924 *res = (uint16_t)full_res;
899925 return overflow;
900926#endif
901927}
902928
903static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n,
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) {
929static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) {
910930#if zig_has_builtin(mul_overflow) || defined(zig_gnuc)
911 zig_i16 full_res;
931 int16_t full_res;
912932 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
913933 *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);
915935#else
916 zig_i32 full_res;
936 int32_t full_res;
917937 bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits);
918 *res = (zig_i16)full_res;
938 *res = (int16_t)full_res;
919939 return overflow;
920940#endif
921941}
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
929943#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) { \
931945 *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; \
933947 } \
934948\
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) { \
936950 *res = zig_shlw_i##w(lhs, rhs, bits); \
937 zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \
938 return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \
951 int##w##_t mask = (int##w##_t)(UINT##w##_MAX << (bits - rhs - 1)); \
952 return (lhs & mask) != INT##w##_C(0) && (lhs & mask) != mask; \
939953 } \
940954\
941 static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \
942 zig_u##w res; \
943 if (rhs >= bits) return lhs != zig_as_u##w(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; \
955 static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
956 uint##w##_t res; \
957 if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \
958 return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \
945959 } \
946960\
947 static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
948 zig_i##w res; \
949 if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \
950 return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \
961 static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
962 int##w##_t res; \
963 if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \
964 return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \
951965 } \
952966\
953 static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \
954 zig_u##w res; \
955 return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \
967 static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
968 uint##w##_t res; \
969 return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \
956970 } \
957971\
958 static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
959 zig_i##w res; \
972 static inline int##w##_t zig_adds_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
973 int##w##_t res; \
960974 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); \
962976 } \
963977\
964 static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \
965 zig_u##w res; \
966 return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \
978 static inline uint##w##_t zig_subs_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
979 uint##w##_t res; \
980 return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt_u(w, bits) : res; \
967981 } \
968982\
969 static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
970 zig_i##w res; \
983 static inline int##w##_t zig_subs_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
984 int##w##_t res; \
971985 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); \
973987 } \
974988\
975 static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \
976 zig_u##w res; \
977 return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \
989 static inline uint##w##_t zig_muls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \
990 uint##w##_t res; \
991 return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \
978992 } \
979993\
980 static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \
981 zig_i##w res; \
994 static inline int##w##_t zig_muls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \
995 int##w##_t res; \
982996 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); \
984998 }
985999zig_int_builtins(8)
9861000zig_int_builtins(16)
......@@ -988,89 +1002,89 @@ zig_int_builtins(32)
9881002zig_int_builtins(64)
9891003
9901004#define zig_builtin8(name, val) __builtin_##name(val)
991typedef zig_c_uint zig_Builtin8;
1005typedef unsigned int zig_Builtin8;
9921006
9931007#define zig_builtin16(name, val) __builtin_##name(val)
994typedef zig_c_uint zig_Builtin16;
1008typedef unsigned int zig_Builtin16;
9951009
9961010#if INT_MIN <= INT32_MIN
9971011#define zig_builtin32(name, val) __builtin_##name(val)
998typedef zig_c_uint zig_Builtin32;
1012typedef unsigned int zig_Builtin32;
9991013#elif LONG_MIN <= INT32_MIN
10001014#define zig_builtin32(name, val) __builtin_##name##l(val)
1001typedef zig_c_ulong zig_Builtin32;
1015typedef unsigned long zig_Builtin32;
10021016#endif
10031017
10041018#if INT_MIN <= INT64_MIN
10051019#define zig_builtin64(name, val) __builtin_##name(val)
1006typedef zig_c_uint zig_Builtin64;
1020typedef unsigned int zig_Builtin64;
10071021#elif LONG_MIN <= INT64_MIN
10081022#define zig_builtin64(name, val) __builtin_##name##l(val)
1009typedef zig_c_ulong zig_Builtin64;
1023typedef unsigned long zig_Builtin64;
10101024#elif LLONG_MIN <= INT64_MIN
10111025#define zig_builtin64(name, val) __builtin_##name##ll(val)
1012typedef zig_c_ulonglong zig_Builtin64;
1026typedef unsigned long long zig_Builtin64;
10131027#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) {
10161030 return zig_wrap_u8(val >> (8 - bits), bits);
10171031}
10181032
1019static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) {
1020 return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits);
1033static inline int8_t zig_byte_swap_i8(int8_t val, uint8_t bits) {
1034 return zig_wrap_i8((int8_t)zig_byte_swap_u8((uint8_t)val, bits), bits);
10211035}
10221036
1023static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) {
1024 zig_u16 full_res;
1037static inline uint16_t zig_byte_swap_u16(uint16_t val, uint8_t bits) {
1038 uint16_t full_res;
10251039#if zig_has_builtin(bswap16) || defined(zig_gnuc)
10261040 full_res = __builtin_bswap16(val);
10271041#else
1028 full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 |
1029 (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0;
1042 full_res = (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 0), 8) << 8 |
1043 (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 8), 8) >> 0;
10301044#endif
10311045 return zig_wrap_u16(full_res >> (16 - bits), bits);
10321046}
10331047
1034static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) {
1035 return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits);
1048static inline int16_t zig_byte_swap_i16(int16_t val, uint8_t bits) {
1049 return zig_wrap_i16((int16_t)zig_byte_swap_u16((uint16_t)val, bits), bits);
10361050}
10371051
1038static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) {
1039 zig_u32 full_res;
1052static inline uint32_t zig_byte_swap_u32(uint32_t val, uint8_t bits) {
1053 uint32_t full_res;
10401054#if zig_has_builtin(bswap32) || defined(zig_gnuc)
10411055 full_res = __builtin_bswap32(val);
10421056#else
1043 full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 |
1044 (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0;
1057 full_res = (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 0), 16) << 16 |
1058 (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 16), 16) >> 0;
10451059#endif
10461060 return zig_wrap_u32(full_res >> (32 - bits), bits);
10471061}
10481062
1049static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) {
1050 return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits);
1063static inline int32_t zig_byte_swap_i32(int32_t val, uint8_t bits) {
1064 return zig_wrap_i32((int32_t)zig_byte_swap_u32((uint32_t)val, bits), bits);
10511065}
10521066
1053static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) {
1054 zig_u64 full_res;
1067static inline uint64_t zig_byte_swap_u64(uint64_t val, uint8_t bits) {
1068 uint64_t full_res;
10551069#if zig_has_builtin(bswap64) || defined(zig_gnuc)
10561070 full_res = __builtin_bswap64(val);
10571071#else
1058 full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 |
1059 (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0;
1072 full_res = (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 0), 32) << 32 |
1073 (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 32), 32) >> 0;
10601074#endif
10611075 return zig_wrap_u64(full_res >> (64 - bits), bits);
10621076}
10631077
1064static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) {
1065 return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits);
1078static inline int64_t zig_byte_swap_i64(int64_t val, uint8_t bits) {
1079 return zig_wrap_i64((int64_t)zig_byte_swap_u64((uint64_t)val, bits), bits);
10661080}
10671081
1068static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) {
1069 zig_u8 full_res;
1082static inline uint8_t zig_bit_reverse_u8(uint8_t val, uint8_t bits) {
1083 uint8_t full_res;
10701084#if zig_has_builtin(bitreverse8)
10711085 full_res = __builtin_bitreverse8(val);
10721086#else
1073 static zig_u8 const lut[0x10] = {
1087 static uint8_t const lut[0x10] = {
10741088 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
10751089 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
10761090 };
......@@ -1079,62 +1093,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) {
10791093 return zig_wrap_u8(full_res >> (8 - bits), bits);
10801094}
10811095
1082static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) {
1083 return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits);
1096static inline int8_t zig_bit_reverse_i8(int8_t val, uint8_t bits) {
1097 return zig_wrap_i8((int8_t)zig_bit_reverse_u8((uint8_t)val, bits), bits);
10841098}
10851099
1086static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) {
1087 zig_u16 full_res;
1100static inline uint16_t zig_bit_reverse_u16(uint16_t val, uint8_t bits) {
1101 uint16_t full_res;
10881102#if zig_has_builtin(bitreverse16)
10891103 full_res = __builtin_bitreverse16(val);
10901104#else
1091 full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 |
1092 (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0;
1105 full_res = (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 0), 8) << 8 |
1106 (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 8), 8) >> 0;
10931107#endif
10941108 return zig_wrap_u16(full_res >> (16 - bits), bits);
10951109}
10961110
1097static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) {
1098 return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits);
1111static inline int16_t zig_bit_reverse_i16(int16_t val, uint8_t bits) {
1112 return zig_wrap_i16((int16_t)zig_bit_reverse_u16((uint16_t)val, bits), bits);
10991113}
11001114
1101static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) {
1102 zig_u32 full_res;
1115static inline uint32_t zig_bit_reverse_u32(uint32_t val, uint8_t bits) {
1116 uint32_t full_res;
11031117#if zig_has_builtin(bitreverse32)
11041118 full_res = __builtin_bitreverse32(val);
11051119#else
1106 full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 |
1107 (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0;
1120 full_res = (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 0), 16) << 16 |
1121 (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 16), 16) >> 0;
11081122#endif
11091123 return zig_wrap_u32(full_res >> (32 - bits), bits);
11101124}
11111125
1112static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) {
1113 return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits);
1126static inline int32_t zig_bit_reverse_i32(int32_t val, uint8_t bits) {
1127 return zig_wrap_i32((int32_t)zig_bit_reverse_u32((uint32_t)val, bits), bits);
11141128}
11151129
1116static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) {
1117 zig_u64 full_res;
1130static inline uint64_t zig_bit_reverse_u64(uint64_t val, uint8_t bits) {
1131 uint64_t full_res;
11181132#if zig_has_builtin(bitreverse64)
11191133 full_res = __builtin_bitreverse64(val);
11201134#else
1121 full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 |
1122 (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0;
1135 full_res = (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 0), 32) << 32 |
1136 (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 32), 32) >> 0;
11231137#endif
11241138 return zig_wrap_u64(full_res >> (64 - bits), bits);
11251139}
11261140
1127static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) {
1128 return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits);
1141static inline int64_t zig_bit_reverse_i64(int64_t val, uint8_t bits) {
1142 return zig_wrap_i64((int64_t)zig_bit_reverse_u64((uint64_t)val, bits), bits);
11291143}
11301144
11311145#define zig_builtin_popcount_common(w) \
1132 static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \
1133 return zig_popcount_u##w((zig_u##w)val, bits); \
1146 static inline uint8_t zig_popcount_i##w(int##w##_t val, uint8_t bits) { \
1147 return zig_popcount_u##w((uint##w##_t)val, bits); \
11341148 }
11351149#if zig_has_builtin(popcount) || defined(zig_gnuc)
11361150#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) { \
11381152 (void)bits; \
11391153 return zig_builtin##w(popcount, val); \
11401154 } \
......@@ -1142,12 +1156,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) {
11421156 zig_builtin_popcount_common(w)
11431157#else
11441158#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) { \
11461160 (void)bits; \
1147 zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \
1148 temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \
1149 temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \
1150 return temp * (zig_maxInt_u##w / 255) >> (w - 8); \
1161 uint##w##_t temp = val - ((val >> 1) & (UINT##w##_MAX / 3)); \
1162 temp = (temp & (UINT##w##_MAX / 5)) + ((temp >> 2) & (UINT##w##_MAX / 5)); \
1163 temp = (temp + (temp >> 4)) & (UINT##w##_MAX / 17); \
1164 return temp * (UINT##w##_MAX / 255) >> (w - 8); \
11511165 } \
11521166\
11531167 zig_builtin_popcount_common(w)
......@@ -1158,12 +1172,12 @@ zig_builtin_popcount(32)
11581172zig_builtin_popcount(64)
11591173
11601174#define zig_builtin_ctz_common(w) \
1161 static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \
1162 return zig_ctz_u##w((zig_u##w)val, bits); \
1175 static inline uint8_t zig_ctz_i##w(int##w##_t val, uint8_t bits) { \
1176 return zig_ctz_u##w((uint##w##_t)val, bits); \
11631177 }
11641178#if zig_has_builtin(ctz) || defined(zig_gnuc)
11651179#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) { \
11671181 if (val == 0) return bits; \
11681182 return zig_builtin##w(ctz, val); \
11691183 } \
......@@ -1171,7 +1185,7 @@ zig_builtin_popcount(64)
11711185 zig_builtin_ctz_common(w)
11721186#else
11731187#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) { \
11751189 return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \
11761190 } \
11771191\
......@@ -1183,12 +1197,12 @@ zig_builtin_ctz(32)
11831197zig_builtin_ctz(64)
11841198
11851199#define zig_builtin_clz_common(w) \
1186 static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \
1187 return zig_clz_u##w((zig_u##w)val, bits); \
1200 static inline uint8_t zig_clz_i##w(int##w##_t val, uint8_t bits) { \
1201 return zig_clz_u##w((uint##w##_t)val, bits); \
11881202 }
11891203#if zig_has_builtin(clz) || defined(zig_gnuc)
11901204#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) { \
11921206 if (val == 0) return bits; \
11931207 return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \
11941208 } \
......@@ -1196,7 +1210,7 @@ zig_builtin_ctz(64)
11961210 zig_builtin_clz_common(w)
11971211#else
11981212#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) { \
12001214 return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \
12011215 } \
12021216\
......@@ -1207,7 +1221,7 @@ zig_builtin_clz(16)
12071221zig_builtin_clz(32)
12081222zig_builtin_clz(64)
12091223
1210/* ======================== 128-bit Integer Routines ======================== */
1224/* ======================== 128-bit Integer Support ========================= */
12111225
12121226#if !defined(zig_has_int128)
12131227# if defined(__SIZEOF_INT128__)
......@@ -1222,18 +1236,18 @@ zig_builtin_clz(64)
12221236typedef unsigned __int128 zig_u128;
12231237typedef signed __int128 zig_i128;
12241238
1225#define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
1226#define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo))
1227#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo)
1228#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo)
1229#define zig_hi_u128(val) ((zig_u64)((val) >> 64))
1230#define zig_lo_u128(val) ((zig_u64)((val) >> 0))
1231#define zig_hi_i128(val) ((zig_i64)((val) >> 64))
1232#define zig_lo_i128(val) ((zig_u64)((val) >> 0))
1239#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
1240#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo))
1241#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1242#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
1243#define zig_hi_u128(val) ((uint64_t)((val) >> 64))
1244#define zig_lo_u128(val) ((uint64_t)((val) >> 0))
1245#define zig_hi_i128(val) (( int64_t)((val) >> 64))
1246#define zig_lo_i128(val) ((uint64_t)((val) >> 0))
12331247#define zig_bitcast_u128(val) ((zig_u128)(val))
12341248#define zig_bitcast_i128(val) ((zig_i128)(val))
12351249#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) { \
12371251 return (lhs > rhs) - (lhs < rhs); \
12381252 }
12391253#define zig_bit_int128(Type, operation, operator) \
......@@ -1243,32 +1257,32 @@ typedef signed __int128 zig_i128;
12431257
12441258#else /* zig_has_int128 */
12451259
1246#if __LITTLE_ENDIAN__ || _MSC_VER
1247typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128;
1248typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128;
1260#if zig_little_endian
1261typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
1262typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
12491263#else
1250typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128;
1251typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128;
1264typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128;
1265typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
12521266#endif
12531267
1254#define zig_as_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) })
1268#define zig_make_u128(hi, lo) ((zig_u128){ .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_VER
1258#define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1259#define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1260#else
1261#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo)
1262#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo)
1271#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */
1272#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1273#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1274#else /* But non-MSVC doesn't like the unprotected commas */
1275#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1276#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
12631277#endif
12641278#define zig_hi_u128(val) ((val).hi)
12651279#define zig_lo_u128(val) ((val).lo)
12661280#define zig_hi_i128(val) ((val).hi)
12671281#define zig_lo_i128(val) ((val).lo)
1268#define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo)
1269#define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo)
1282#define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo)
1283#define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo)
12701284#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) { \
12721286 return (lhs.hi == rhs.hi) \
12731287 ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \
12741288 : (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;
12801294
12811295#endif /* zig_has_int128 */
12821296
1283#define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64)
1284#define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64)
1285#define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64)
1286#define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64)
1297#define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64)
1298#define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64)
1299#define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64)
1300#define zig_maxInt_i128 zig_make_i128(zig_maxInt_i64, zig_maxInt_u64)
12871301
12881302zig_cmp_int128(u128)
12891303zig_cmp_int128(i128)
......@@ -1297,28 +1311,33 @@ zig_bit_int128(i128, or, |)
12971311zig_bit_int128(u128, xor, ^)
12981312zig_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
13021316#if zig_has_int128
13031317
1304static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) {
1305 return val ^ zig_maxInt(u128, bits);
1318static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) {
1319 return val ^ zig_maxInt_u(128, bits);
13061320}
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) {
13091323 (void)bits;
13101324 return ~val;
13111325}
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) {
13141328 return lhs >> rhs;
13151329}
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) {
13181332 return lhs << rhs;
13191333}
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) {
13221341 return lhs << rhs;
13231342}
13241343
......@@ -1363,40 +1382,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) {
13631382}
13641383
13651384static 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));
13671386}
13681387
13691388static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
13701389 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));
13721391}
13731392
13741393#else /* zig_has_int128 */
13751394
1376static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 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)) };
1395static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) {
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)) };
13781401}
13791402
1380static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) {
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)) };
1403static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) {
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 };
13821407}
13831408
1384static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) {
1385 if (rhs == zig_as_u8(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)) };
1387 return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs };
1409static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) {
1410 if (rhs == UINT8_C(0)) return lhs;
1411 if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 };
1412 return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs };
13881413}
13891414
1390static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) {
1391 if (rhs == zig_as_u8(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 };
1393 return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs };
1415static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) {
1416 if (rhs == UINT8_C(0)) return lhs;
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))) };
1418 return (zig_i128){ .hi = zig_shr_i64(lhs.hi, rhs), .lo = lhs.lo >> rhs | (uint64_t)lhs.hi << (UINT8_C(64) - rhs) };
13941419}
13951420
1396static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) {
1397 if (rhs == zig_as_u8(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 };
1399 return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs };
1421static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) {
1422 if (rhs == UINT8_C(0)) return lhs;
1423 if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 };
1424 return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs };
14001425}
14011426
14021427static 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) {
14241449}
14251450
14261451zig_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
14311452static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) {
14321453 return __multi3(lhs, rhs);
14331454}
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
14351460zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs);
14361461static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) {
14371462 return __udivti3(lhs, rhs);
......@@ -1454,11 +1479,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) {
14541479
14551480static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
14561481 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));
14581483}
14591484
14601485static 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)));
14621487}
14631488
14641489#endif /* zig_has_int128 */
......@@ -1471,326 +1496,1303 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) {
14711496}
14721497
14731498static 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;
14751500}
14761501
14771502static 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;
14791504}
14801505
14811506static 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;
14831508}
14841509
14851510static 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;
14871512}
14881513
1489static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) {
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);
1491 return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask);
1514static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) {
1515 return zig_and_u128(val, zig_maxInt_u(128, bits));
14921516}
14931517
1494static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) {
1495 return zig_and_u128(val, zig_maxInt(u128, bits));
1518static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t 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);
14961522}
14971523
1498static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 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) {
1524static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) {
15031525 return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits);
15041526}
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) {
15071529 return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits);
15081530}
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) {
15111533 return zig_wrap_u128(zig_add_u128(lhs, rhs), bits);
15121534}
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) {
15151537 return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
15161538}
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) {
15191541 return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits);
15201542}
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) {
15231545 return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
15241546}
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) {
15271549 return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits);
15281550}
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) {
15311553 return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
15321554}
15331555
15341556#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) {
15371559#if zig_has_builtin(add_overflow)
15381560 zig_u128 full_res;
15391561 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
15401562 *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);
15421564#else
15431565 *res = zig_addw_u128(lhs, rhs, bits);
15441566 return *res < lhs;
15451567#endif
15461568}
15471569
1548zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);
1549static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
1570zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1571static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
15501572#if zig_has_builtin(add_overflow)
15511573 zig_i128 full_res;
15521574 bool overflow = __builtin_add_overflow(lhs, rhs, &full_res);
15531575#else
1554 zig_c_int overflow_int;
1576 int overflow_int;
15551577 zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int);
15561578 bool overflow = overflow_int != 0;
15571579#endif
15581580 *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);
15601582}
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) {
15631585#if zig_has_builtin(sub_overflow)
15641586 zig_u128 full_res;
15651587 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
15661588 *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);
15681590#else
15691591 *res = zig_subw_u128(lhs, rhs, bits);
15701592 return *res > lhs;
15711593#endif
15721594}
15731595
1574zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);
1575static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
1596zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1597static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
15761598#if zig_has_builtin(sub_overflow)
15771599 zig_i128 full_res;
15781600 bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res);
15791601#else
1580 zig_c_int overflow_int;
1602 int overflow_int;
15811603 zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int);
15821604 bool overflow = overflow_int != 0;
15831605#endif
15841606 *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);
15861608}
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) {
15891611#if zig_has_builtin(mul_overflow)
15901612 zig_u128 full_res;
15911613 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
15921614 *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);
15941616#else
15951617 *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;
15971619#endif
15981620}
15991621
1600zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);
1601static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
1622zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow);
1623static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
16021624#if zig_has_builtin(mul_overflow)
16031625 zig_i128 full_res;
16041626 bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res);
16051627#else
1606 zig_c_int overflow_int;
1628 int overflow_int;
16071629 zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int);
16081630 bool overflow = overflow_int != 0;
16091631#endif
16101632 *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);
16121634}
16131635
16141636#else /* zig_has_int128 */
16151637
1616static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) {
1617 return overflow ||
1618 zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) ||
1619 zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0);
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);
1638static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1639 uint64_t hi;
1640 bool overflow = zig_addo_u64(&hi, lhs.hi, rhs.hi, bits - 64);
1641 return overflow ^ zig_addo_u64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
16261642}
16271643
1628static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {
1629 zig_u128 full_res;
1630 bool overflow =
1631 zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 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);
1644static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1645 int64_t hi;
1646 bool overflow = zig_addo_i64(&hi, lhs.hi, rhs.hi, bits - 64);
1647 return overflow ^ zig_addo_i64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
16351648}
16361649
1637zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);
1638static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
1639 zig_c_int overflow_int;
1640 zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int);
1641 *res = zig_wrap_i128(full_res, bits);
1642 return zig_overflow_i128(overflow_int, full_res, bits);
1650static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
1651 uint64_t hi;
1652 bool overflow = zig_subo_u64(&hi, lhs.hi, rhs.hi, bits - 64);
1653 return overflow ^ zig_subo_u64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
16431654}
16441655
1645static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {
1646 zig_u128 full_res;
1647 bool overflow =
1648 zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 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);
1656static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) {
1657 int64_t hi;
1658 bool overflow = zig_subo_i64(&hi, lhs.hi, rhs.hi, bits - 64);
1659 return overflow ^ zig_subo_i64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64);
16521660}
16531661
1654zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);
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) {
1662static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) {
16631663 *res = zig_mulw_u128(lhs, rhs, bits);
1664 return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) &&
1665 zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > 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_u(128, bits), rhs)) > INT32_C(0);
16661666}
16671667
1668zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow);
1669static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
1670 zig_c_int overflow_int;
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, uint8_t bits) {
1670 int overflow_int;
16711671 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);
16721675 *res = zig_wrap_i128(full_res, bits);
1673 return zig_overflow_i128(overflow_int, full_res, bits);
1676 return overflow;
16741677}
16751678
16761679#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) {
16791682 *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);
16811684}
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) {
16841687 *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)));
1686 return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) &&
1687 zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0);
1688 zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1)));
1689 return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) &&
1690 zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0);
16881691}
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) {
16911694 zig_u128 res;
1692 if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0))
1693 return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs;
1694
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
1695 if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0))
1696 return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs;
1697 return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res;
17001698}
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) {
17031701 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;
1705 return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits);
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;
1703 return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits);
17061704}
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) {
17091707 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;
17111709}
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) {
17141712 zig_i128 res;
17151713 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);
17171715}
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) {
17201718 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;
17221720}
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) {
17251723 zig_i128 res;
17261724 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);
17281726}
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) {
17311729 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;
17331731}
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) {
17361734 zig_i128 res;
17371735 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);
17391737}
17401738
1741static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) {
1742 if (bits <= zig_as_u8(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));
1744 return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64));
1739static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) {
1740 if (bits <= UINT8_C(64)) return zig_clz_u64(zig_lo_u128(val), bits);
1741 if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - UINT8_C(64));
1742 return zig_clz_u64(zig_lo_u128(val), UINT8_C(64)) + (bits - UINT8_C(64));
17451743}
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) {
17481746 return zig_clz_u128(zig_bitcast_u128(val), bits);
17491747}
17501748
1751static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) {
1752 if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64));
1753 return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64);
1749static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) {
1750 if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), UINT8_C(64));
1751 return zig_ctz_u64(zig_hi_u128(val), bits - UINT8_C(64)) + UINT8_C(64);
17541752}
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) {
17571755 return zig_ctz_u128(zig_bitcast_u128(val), bits);
17581756}
17591757
1760static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) {
1761 return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) +
1762 zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64));
1758static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) {
1759 return zig_popcount_u64(zig_hi_u128(val), bits - UINT8_C(64)) +
1760 zig_popcount_u64(zig_lo_u128(val), UINT8_C(64));
17631761}
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) {
17661764 return zig_popcount_u128(zig_bitcast_u128(val), bits);
17671765}
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) {
17701768 zig_u128 full_res;
17711769#if zig_has_builtin(bswap128)
17721770 full_res = __builtin_bswap128(val);
17731771#else
1774 full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)),
1775 zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64)));
1772 full_res = zig_make_u128(zig_byte_swap_u64(zig_lo_u128(val), UINT8_C(64)),
1773 zig_byte_swap_u64(zig_hi_u128(val), UINT8_C(64)));
17761774#endif
1777 return zig_shr_u128(full_res, zig_as_u8(128) - bits);
1775 return zig_shr_u128(full_res, UINT8_C(128) - bits);
17781776}
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) {
17811779 return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits));
17821780}
17831781
1784static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) {
1785 return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)),
1786 zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))),
1787 zig_as_u8(128) - bits);
1782static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) {
1783 return zig_shr_u128(zig_make_u128(zig_bit_reverse_u64(zig_lo_u128(val), UINT8_C(64)),
1784 zig_bit_reverse_u64(zig_hi_u128(val), UINT8_C(64))),
1785 UINT8_C(128) - bits);
17881786}
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) {
17911789 return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));
17921790}
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
17942796/* ========================= Floating Point Support ========================= */
17952797
17962798#if _MSC_VER
......@@ -1810,252 +2812,253 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) {
18102812
18112813#if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc)
18122814#define zig_has_float_builtins 1
1813#define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg)
1814#define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg)
1815#define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg)
1816#define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg)
1817#define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg)
1818#define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg)
2815#define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg)
2816#define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg)
2817#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg)
2818#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg)
2819#define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg)
18192820#else
18202821#define zig_has_float_builtins 0
1821#define zig_as_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)
1823#define zig_as_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)
1825#define zig_as_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)
2822#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr)
2823#define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr)
2824#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)
2825#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)
2826#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr)
18272827#endif
18282828
18292829#define zig_has_f16 1
18302830#define zig_bitSizeOf_f16 16
2831typedef int16_t zig_repr_f16;
18312832#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)
18332834#if FLT_MANT_DIG == 11
18342835typedef float zig_f16;
1835#define zig_as_f16(fp, repr) fp##f
2836#define zig_make_f16(fp, repr) fp##f
18362837#elif DBL_MANT_DIG == 11
18372838typedef double zig_f16;
1838#define zig_as_f16(fp, repr) fp
2839#define zig_make_f16(fp, repr) fp
18392840#elif LDBL_MANT_DIG == 11
18402841#define zig_bitSizeOf_c_longdouble 16
2842#ifndef ZIG_TARGET_ABI_MSVC
2843typedef zig_repr_f16 zig_repr_c_longdouble;
2844#endif
18412845typedef long double zig_f16;
1842#define zig_as_f16(fp, repr) fp##l
2846#define zig_make_f16(fp, repr) fp##l
18432847#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))
18442848typedef _Float16 zig_f16;
1845#define zig_as_f16(fp, repr) fp##f16
2849#define zig_make_f16(fp, repr) fp##f16
18462850#elif defined(__SIZEOF_FP16__)
18472851typedef __fp16 zig_f16;
1848#define zig_as_f16(fp, repr) fp##f16
2852#define zig_make_f16(fp, repr) fp##f16
18492853#else
18502854#undef zig_has_f16
18512855#define zig_has_f16 0
1852#define zig_repr_f16 i16
1853typedef zig_i16 zig_f16;
1854#define zig_as_f16(fp, repr) repr
1855#undef zig_as_special_f16
1856#define zig_as_special_f16(sign, name, arg, repr) repr
1857#undef zig_as_special_constant_f16
1858#define zig_as_special_constant_f16(sign, name, arg, repr) repr
2856#define zig_bitSizeOf_repr_f16 16
2857typedef int16_t zig_f16;
2858#define zig_make_f16(fp, repr) repr
2859#undef zig_make_special_f16
2860#define zig_make_special_f16(sign, name, arg, repr) repr
2861#undef zig_init_special_f16
2862#define zig_init_special_f16(sign, name, arg, repr) repr
18592863#endif
18602864
18612865#define zig_has_f32 1
18622866#define zig_bitSizeOf_f32 32
2867typedef int32_t zig_repr_f32;
18632868#define zig_libc_name_f32(name) name##f
18642869#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, )
18662871#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)
18682873#endif
18692874#if FLT_MANT_DIG == 24
18702875typedef float zig_f32;
1871#define zig_as_f32(fp, repr) fp##f
2876#define zig_make_f32(fp, repr) fp##f
18722877#elif DBL_MANT_DIG == 24
18732878typedef double zig_f32;
1874#define zig_as_f32(fp, repr) fp
2879#define zig_make_f32(fp, repr) fp
18752880#elif LDBL_MANT_DIG == 24
18762881#define zig_bitSizeOf_c_longdouble 32
2882#ifndef ZIG_TARGET_ABI_MSVC
2883typedef zig_repr_f32 zig_repr_c_longdouble;
2884#endif
18772885typedef long double zig_f32;
1878#define zig_as_f32(fp, repr) fp##l
2886#define zig_make_f32(fp, repr) fp##l
18792887#elif FLT32_MANT_DIG == 24
18802888typedef _Float32 zig_f32;
1881#define zig_as_f32(fp, repr) fp##f32
2889#define zig_make_f32(fp, repr) fp##f32
18822890#else
18832891#undef zig_has_f32
18842892#define zig_has_f32 0
1885#define zig_repr_f32 i32
1886typedef zig_i32 zig_f32;
1887#define zig_as_f32(fp, repr) repr
1888#undef zig_as_special_f32
1889#define zig_as_special_f32(sign, name, arg, repr) repr
1890#undef zig_as_special_constant_f32
1891#define zig_as_special_constant_f32(sign, name, arg, repr) repr
2893#define zig_bitSizeOf_repr_f32 32
2894typedef int32_t zig_f32;
2895#define zig_make_f32(fp, repr) repr
2896#undef zig_make_special_f32
2897#define zig_make_special_f32(sign, name, arg, repr) repr
2898#undef zig_init_special_f32
2899#define zig_init_special_f32(sign, name, arg, repr) repr
18922900#endif
18932901
18942902#define zig_has_f64 1
18952903#define zig_bitSizeOf_f64 64
2904typedef int64_t zig_repr_f64;
18962905#define zig_libc_name_f64(name) name
18972906#if _MSC_VER
18982907#ifdef ZIG_TARGET_ABI_MSVC
18992908#define zig_bitSizeOf_c_longdouble 64
2909#ifndef ZIG_TARGET_ABI_MSVC
2910typedef zig_repr_f64 zig_repr_c_longdouble;
2911#endif
19002912#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, )
19022914#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)
19042916#endif /* _MSC_VER */
19052917#if FLT_MANT_DIG == 53
19062918typedef float zig_f64;
1907#define zig_as_f64(fp, repr) fp##f
2919#define zig_make_f64(fp, repr) fp##f
19082920#elif DBL_MANT_DIG == 53
19092921typedef double zig_f64;
1910#define zig_as_f64(fp, repr) fp
2922#define zig_make_f64(fp, repr) fp
19112923#elif LDBL_MANT_DIG == 53
19122924#define zig_bitSizeOf_c_longdouble 64
2925#ifndef ZIG_TARGET_ABI_MSVC
2926typedef zig_repr_f64 zig_repr_c_longdouble;
2927#endif
19132928typedef long double zig_f64;
1914#define zig_as_f64(fp, repr) fp##l
2929#define zig_make_f64(fp, repr) fp##l
19152930#elif FLT64_MANT_DIG == 53
19162931typedef _Float64 zig_f64;
1917#define zig_as_f64(fp, repr) fp##f64
2932#define zig_make_f64(fp, repr) fp##f64
19182933#elif FLT32X_MANT_DIG == 53
19192934typedef _Float32x zig_f64;
1920#define zig_as_f64(fp, repr) fp##f32x
2935#define zig_make_f64(fp, repr) fp##f32x
19212936#else
19222937#undef zig_has_f64
19232938#define zig_has_f64 0
1924#define zig_repr_f64 i64
1925typedef zig_i64 zig_f64;
1926#define zig_as_f64(fp, repr) repr
1927#undef zig_as_special_f64
1928#define zig_as_special_f64(sign, name, arg, repr) repr
1929#undef zig_as_special_constant_f64
1930#define zig_as_special_constant_f64(sign, name, arg, repr) repr
2939#define zig_bitSizeOf_repr_f64 64
2940typedef int64_t zig_f64;
2941#define zig_make_f64(fp, repr) repr
2942#undef zig_make_special_f64
2943#define zig_make_special_f64(sign, name, arg, repr) repr
2944#undef zig_init_special_f64
2945#define zig_init_special_f64(sign, name, arg, repr) repr
19312946#endif
19322947
19332948#define zig_has_f80 1
19342949#define zig_bitSizeOf_f80 80
2950typedef zig_i128 zig_repr_f80;
19352951#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)
19372953#if FLT_MANT_DIG == 64
19382954typedef float zig_f80;
1939#define zig_as_f80(fp, repr) fp##f
2955#define zig_make_f80(fp, repr) fp##f
19402956#elif DBL_MANT_DIG == 64
19412957typedef double zig_f80;
1942#define zig_as_f80(fp, repr) fp
2958#define zig_make_f80(fp, repr) fp
19432959#elif LDBL_MANT_DIG == 64
19442960#define zig_bitSizeOf_c_longdouble 80
2961#ifndef ZIG_TARGET_ABI_MSVC
2962typedef zig_repr_f80 zig_repr_c_longdouble;
2963#endif
19452964typedef long double zig_f80;
1946#define zig_as_f80(fp, repr) fp##l
2965#define zig_make_f80(fp, repr) fp##l
19472966#elif FLT80_MANT_DIG == 64
19482967typedef _Float80 zig_f80;
1949#define zig_as_f80(fp, repr) fp##f80
2968#define zig_make_f80(fp, repr) fp##f80
19502969#elif FLT64X_MANT_DIG == 64
19512970typedef _Float64x zig_f80;
1952#define zig_as_f80(fp, repr) fp##f64x
2971#define zig_make_f80(fp, repr) fp##f64x
19532972#elif defined(__SIZEOF_FLOAT80__)
19542973typedef __float80 zig_f80;
1955#define zig_as_f80(fp, repr) fp##l
2974#define zig_make_f80(fp, repr) fp##l
19562975#else
19572976#undef zig_has_f80
19582977#define zig_has_f80 0
1959#define zig_repr_f80 i128
2978#define zig_bitSizeOf_repr_f80 128
19602979typedef zig_i128 zig_f80;
1961#define zig_as_f80(fp, repr) repr
1962#undef zig_as_special_f80
1963#define zig_as_special_f80(sign, name, arg, repr) repr
1964#undef zig_as_special_constant_f80
1965#define zig_as_special_constant_f80(sign, name, arg, repr) repr
2980#define zig_make_f80(fp, repr) repr
2981#undef zig_make_special_f80
2982#define zig_make_special_f80(sign, name, arg, repr) repr
2983#undef zig_init_special_f80
2984#define zig_init_special_f80(sign, name, arg, repr) repr
19662985#endif
19672986
19682987#define zig_has_f128 1
19692988#define zig_bitSizeOf_f128 128
2989typedef zig_i128 zig_repr_f128;
19702990#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)
19722992#if FLT_MANT_DIG == 113
19732993typedef float zig_f128;
1974#define zig_as_f128(fp, repr) fp##f
2994#define zig_make_f128(fp, repr) fp##f
19752995#elif DBL_MANT_DIG == 113
19762996typedef double zig_f128;
1977#define zig_as_f128(fp, repr) fp
2997#define zig_make_f128(fp, repr) fp
19782998#elif LDBL_MANT_DIG == 113
19792999#define zig_bitSizeOf_c_longdouble 128
3000#ifndef ZIG_TARGET_ABI_MSVC
3001typedef zig_repr_f128 zig_repr_c_longdouble;
3002#endif
19803003typedef long double zig_f128;
1981#define zig_as_f128(fp, repr) fp##l
3004#define zig_make_f128(fp, repr) fp##l
19823005#elif FLT128_MANT_DIG == 113
19833006typedef _Float128 zig_f128;
1984#define zig_as_f128(fp, repr) fp##f128
3007#define zig_make_f128(fp, repr) fp##f128
19853008#elif FLT64X_MANT_DIG == 113
19863009typedef _Float64x zig_f128;
1987#define zig_as_f128(fp, repr) fp##f64x
3010#define zig_make_f128(fp, repr) fp##f64x
19883011#elif defined(__SIZEOF_FLOAT128__)
19893012typedef __float128 zig_f128;
1990#define zig_as_f128(fp, repr) fp##q
1991#undef zig_as_special_f128
1992#define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg)
3013#define zig_make_f128(fp, repr) fp##q
3014#undef zig_make_special_f128
3015#define zig_make_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg)
19933016#else
19943017#undef zig_has_f128
19953018#define zig_has_f128 0
1996#define zig_repr_f128 i128
3019#define zig_bitSizeOf_repr_f128 128
19973020typedef zig_i128 zig_f128;
1998#define zig_as_f128(fp, repr) repr
1999#undef zig_as_special_f128
2000#define zig_as_special_f128(sign, name, arg, repr) repr
2001#undef zig_as_special_constant_f128
2002#define zig_as_special_constant_f128(sign, name, arg, repr) repr
3021#define zig_make_f128(fp, repr) repr
3022#undef zig_make_special_f128
3023#define zig_make_special_f128(sign, name, arg, repr) repr
3024#undef zig_init_special_f128
3025#define zig_init_special_f128(sign, name, arg, repr) repr
20033026#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)
20143028#ifdef zig_bitSizeOf_c_longdouble
20153029
3030#define zig_has_c_longdouble 1
20163031#ifdef ZIG_TARGET_ABI_MSVC
2017typedef double zig_c_longdouble;
20183032#undef zig_bitSizeOf_c_longdouble
20193033#define zig_bitSizeOf_c_longdouble 64
2020#define zig_as_c_longdouble(fp, repr) fp
3034typedef zig_f64 zig_c_longdouble;
3035typedef zig_repr_f64 zig_repr_c_longdouble;
20213036#else
20223037typedef long double zig_c_longdouble;
2023#define zig_as_c_longdouble(fp, repr) fp##l
20243038#endif
20253039
20263040#else /* zig_bitSizeOf_c_longdouble */
20273041
2028#undef zig_has_c_longdouble
20293042#define zig_has_c_longdouble 0
2030#define zig_bitSizeOf_c_longdouble 80
2031#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80
2032#define zig_repr_c_longdouble i128
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
3043#define zig_bitSizeOf_repr_c_longdouble 128
3044typedef zig_f128 zig_c_longdouble;
3045typedef zig_repr_f128 zig_repr_c_longdouble;
20393046
20403047#endif /* zig_bitSizeOf_c_longdouble */
20413048
20423049#if !zig_has_float_builtins
2043#define zig_float_from_repr(Type, ReprType) \
2044 static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \
2045 return *((zig_##Type*)&repr); \
3050#define zig_float_from_repr(Type) \
3051 static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \
3052 zig_##Type result; \
3053 memcpy(&result, &repr, sizeof(result)); \
3054 return result; \
20463055 }
20473056
2048zig_float_from_repr(f16, u16)
2049zig_float_from_repr(f32, u32)
2050zig_float_from_repr(f64, u64)
2051zig_float_from_repr(f80, u128)
2052zig_float_from_repr(f128, u128)
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
3057zig_float_from_repr(f16)
3058zig_float_from_repr(f32)
3059zig_float_from_repr(f64)
3060zig_float_from_repr(f80)
3061zig_float_from_repr(f128)
20593062#endif
20603063
20613064#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
20643067
20653068#if _MSC_VER && !zig_has_f128
20663069#define zig_cast_f80
2067#define zig_cast_c_longdouble
20683070#define zig_cast_f128
20693071#else
20703072#define zig_cast_f80 (zig_f80)
2071#define zig_cast_c_longdouble (zig_c_longdouble)
20723073#define zig_cast_f128 (zig_f128)
20733074#endif
20743075
20753076#define zig_convert_builtin(ResType, operation, ArgType, version) \
2076 zig_extern zig_##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);
2078zig_convert_builtin(f16, trunc, f32, 2)
2079zig_convert_builtin(f16, trunc, f64, 2)
2080zig_convert_builtin(f16, trunc, f80, 2)
2081zig_convert_builtin(f16, trunc, f128, 2)
2082zig_convert_builtin(f32, extend, f16, 2)
2083zig_convert_builtin(f32, trunc, f64, 2)
2084zig_convert_builtin(f32, trunc, f80, 2)
2085zig_convert_builtin(f32, trunc, f128, 2)
2086zig_convert_builtin(f64, extend, f16, 2)
2087zig_convert_builtin(f64, extend, f32, 2)
2088zig_convert_builtin(f64, trunc, f80, 2)
2089zig_convert_builtin(f64, trunc, f128, 2)
2090zig_convert_builtin(f80, extend, f16, 2)
2091zig_convert_builtin(f80, extend, f32, 2)
2092zig_convert_builtin(f80, extend, f64, 2)
2093zig_convert_builtin(f80, trunc, f128, 2)
2094zig_convert_builtin(f128, extend, f16, 2)
2095zig_convert_builtin(f128, extend, f32, 2)
2096zig_convert_builtin(f128, extend, f64, 2)
2097zig_convert_builtin(f128, extend, f80, 2)
3077 zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \
3078 zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType);
3079zig_convert_builtin(zig_f16, trunc, zig_f32, 2)
3080zig_convert_builtin(zig_f16, trunc, zig_f64, 2)
3081zig_convert_builtin(zig_f16, trunc, zig_f80, 2)
3082zig_convert_builtin(zig_f16, trunc, zig_f128, 2)
3083zig_convert_builtin(zig_f32, extend, zig_f16, 2)
3084zig_convert_builtin(zig_f32, trunc, zig_f64, 2)
3085zig_convert_builtin(zig_f32, trunc, zig_f80, 2)
3086zig_convert_builtin(zig_f32, trunc, zig_f128, 2)
3087zig_convert_builtin(zig_f64, extend, zig_f16, 2)
3088zig_convert_builtin(zig_f64, extend, zig_f32, 2)
3089zig_convert_builtin(zig_f64, trunc, zig_f80, 2)
3090zig_convert_builtin(zig_f64, trunc, zig_f128, 2)
3091zig_convert_builtin(zig_f80, extend, zig_f16, 2)
3092zig_convert_builtin(zig_f80, extend, zig_f32, 2)
3093zig_convert_builtin(zig_f80, extend, zig_f64, 2)
3094zig_convert_builtin(zig_f80, trunc, zig_f128, 2)
3095zig_convert_builtin(zig_f128, extend, zig_f16, 2)
3096zig_convert_builtin(zig_f128, extend, zig_f32, 2)
3097zig_convert_builtin(zig_f128, extend, zig_f64, 2)
3098zig_convert_builtin(zig_f128, extend, zig_f80, 2)
20983099
20993100#define zig_float_negate_builtin_0(Type) \
21003101 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 ); \
21023106 }
21033107#define zig_float_negate_builtin_1(Type) \
21043108 static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \
......@@ -2106,28 +3110,28 @@ zig_convert_builtin(f128, extend, f80, 2)
21063110 }
21073111
21083112#define zig_float_less_builtin_0(Type, operation) \
2109 zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \
2110 zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \
2111 static inline zig_i32 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); \
3113 zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \
3114 zig_compiler_rt_abbrev_zig_##Type), 2)(zig_##Type, zig_##Type); \
3115 static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
3116 return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 2)(lhs, rhs); \
21133117 }
21143118#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) { \
21163120 return (!(lhs <= rhs) - (lhs < rhs)); \
21173121 }
21183122
21193123#define zig_float_greater_builtin_0(Type, operation) \
21203124 zig_float_less_builtin_0(Type, operation)
21213125#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) { \
21233127 return ((lhs > rhs) - !(lhs >= rhs)); \
21243128 }
21253129
21263130#define zig_float_binary_builtin_0(Type, operation, operator) \
21273131 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); \
21293133 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); \
21313135 }
21323136#define zig_float_binary_builtin_1(Type, operation, operator) \
21333137 static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \
......@@ -2135,18 +3139,18 @@ zig_convert_builtin(f128, extend, f80, 2)
21353139 }
21363140
21373141#define zig_float_builtins(Type) \
2138 zig_convert_builtin(i32, fix, Type, ) \
2139 zig_convert_builtin(u32, fixuns, Type, ) \
2140 zig_convert_builtin(i64, fix, Type, ) \
2141 zig_convert_builtin(u64, fixuns, Type, ) \
2142 zig_convert_builtin(i128, fix, Type, ) \
2143 zig_convert_builtin(u128, fixuns, Type, ) \
2144 zig_convert_builtin(Type, float, i32, ) \
2145 zig_convert_builtin(Type, floatun, u32, ) \
2146 zig_convert_builtin(Type, float, i64, ) \
2147 zig_convert_builtin(Type, floatun, u64, ) \
2148 zig_convert_builtin(Type, float, i128, ) \
2149 zig_convert_builtin(Type, floatun, u128, ) \
3142 zig_convert_builtin( int32_t, fix, zig_##Type, ) \
3143 zig_convert_builtin(uint32_t, fixuns, zig_##Type, ) \
3144 zig_convert_builtin( int64_t, fix, zig_##Type, ) \
3145 zig_convert_builtin(uint64_t, fixuns, zig_##Type, ) \
3146 zig_convert_builtin(zig_i128, fix, zig_##Type, ) \
3147 zig_convert_builtin(zig_u128, fixuns, zig_##Type, ) \
3148 zig_convert_builtin(zig_##Type, float, int32_t, ) \
3149 zig_convert_builtin(zig_##Type, floatun, uint32_t, ) \
3150 zig_convert_builtin(zig_##Type, float, int64_t, ) \
3151 zig_convert_builtin(zig_##Type, floatun, uint64_t, ) \
3152 zig_convert_builtin(zig_##Type, float, zig_i128, ) \
3153 zig_convert_builtin(zig_##Type, floatun, zig_u128, ) \
21503154 zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \
21513155 zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \
21523156 zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \
......@@ -2194,155 +3198,162 @@ zig_float_builtins(f32)
21943198zig_float_builtins(f64)
21953199zig_float_builtins(f80)
21963200zig_float_builtins(f128)
2197zig_float_builtins(c_longdouble)
21983201
21993202#if _MSC_VER && (_M_IX86 || _M_X64)
22003203
22013204// 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) \
2204 static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \
2205 zig_##Type comparand = *expected; \
2206 zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \
3206#define zig_msvc_atomics(ZigType, Type, suffix) \
3207 static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \
3208 Type comparand = *expected; \
3209 Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \
22073210 bool exchanged = initial == comparand; \
22083211 if (!exchanged) { \
22093212 *expected = initial; \
22103213 } \
22113214 return exchanged; \
22123215 } \
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) { \
22143217 return _InterlockedExchange##suffix(obj, value); \
22153218 } \
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) { \
22173220 return _InterlockedExchangeAdd##suffix(obj, value); \
22183221 } \
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) { \
22203223 bool success = false; \
2221 zig_##Type new; \
2222 zig_##Type prev; \
3224 Type new; \
3225 Type prev; \
22233226 while (!success) { \
22243227 prev = *obj; \
22253228 new = prev - value; \
2226 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \
3229 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
22273230 } \
22283231 return prev; \
22293232 } \
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) { \
22313234 return _InterlockedOr##suffix(obj, value); \
22323235 } \
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) { \
22343237 return _InterlockedXor##suffix(obj, value); \
22353238 } \
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) { \
22373240 return _InterlockedAnd##suffix(obj, value); \
22383241 } \
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) { \
22403243 bool success = false; \
2241 zig_##Type new; \
2242 zig_##Type prev; \
3244 Type new; \
3245 Type prev; \
22433246 while (!success) { \
22443247 prev = *obj; \
22453248 new = ~(prev & value); \
2246 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \
3249 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
22473250 } \
22483251 return prev; \
22493252 } \
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) { \
22513254 bool success = false; \
2252 zig_##Type new; \
2253 zig_##Type prev; \
3255 Type new; \
3256 Type prev; \
22543257 while (!success) { \
22553258 prev = *obj; \
22563259 new = value < prev ? value : prev; \
2257 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \
3260 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
22583261 } \
22593262 return prev; \
22603263 } \
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) { \
22623265 bool success = false; \
2263 zig_##Type new; \
2264 zig_##Type prev; \
3266 Type new; \
3267 Type prev; \
22653268 while (!success) { \
22663269 prev = *obj; \
22673270 new = value > prev ? value : prev; \
2268 success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \
3271 success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \
22693272 } \
22703273 return prev; \
22713274 } \
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) { \
22733276 _InterlockedExchange##suffix(obj, value); \
22743277 } \
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) { \
22763279 return _InterlockedOr##suffix(obj, 0); \
22773280 }
22783281
2279zig_msvc_atomics(u8, 8)
2280zig_msvc_atomics(i8, 8)
2281zig_msvc_atomics(u16, 16)
2282zig_msvc_atomics(i16, 16)
2283zig_msvc_atomics(u32, )
2284zig_msvc_atomics(i32, )
3282zig_msvc_atomics( u8, uint8_t, 8)
3283zig_msvc_atomics( i8, int8_t, 8)
3284zig_msvc_atomics(u16, uint16_t, 16)
3285zig_msvc_atomics(i16, int16_t, 16)
3286zig_msvc_atomics(u32, uint32_t, )
3287zig_msvc_atomics(i32, int32_t, )
22853288
22863289#if _M_X64
2287zig_msvc_atomics(u64, 64)
2288zig_msvc_atomics(i64, 64)
3290zig_msvc_atomics(u64, uint64_t, 64)
3291zig_msvc_atomics(i64, int64_t, 64)
22893292#endif
22903293
22913294#define zig_msvc_flt_atomics(Type, ReprType, suffix) \
22923295 static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \
2293 zig_##ReprType comparand = *((zig_##ReprType*)expected); \
2294 zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \
2295 bool exchanged = initial == comparand; \
2296 if (!exchanged) { \
2297 *expected = *((zig_##Type*)&initial); \
2298 } \
2299 return exchanged; \
3296 ReprType exchange; \
3297 ReprType comparand; \
3298 ReprType initial; \
3299 bool success; \
3300 memcpy(&comparand, expected, sizeof(comparand)); \
3301 memcpy(&exchange, &desired, sizeof(exchange)); \
3302 initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \
3303 success = initial == comparand; \
3304 if (!success) memcpy(expected, &initial, sizeof(*expected)); \
3305 return success; \
23003306 } \
23013307 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)); \
2303 return *((zig_##Type*)&initial); \
3308 ReprType repr; \
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; \
23043315 } \
23053316 static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \
2306 bool success = false; \
2307 zig_##ReprType new; \
2308 zig_##Type prev; \
2309 while (!success) { \
2310 prev = *obj; \
2311 new = prev + value; \
2312 success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \
2313 } \
2314 return prev; \
3317 ReprType repr; \
3318 zig_##Type expected; \
3319 zig_##Type desired; \
3320 repr = *(ReprType volatile*)obj; \
3321 memcpy(&expected, &repr, sizeof(expected)); \
3322 do { \
3323 desired = expected + value; \
3324 } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \
3325 return expected; \
23153326 } \
23163327 static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \
2317 bool success = false; \
2318 zig_##ReprType new; \
2319 zig_##Type prev; \
2320 while (!success) { \
2321 prev = *obj; \
2322 new = prev - value; \
2323 success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \
2324 } \
2325 return prev; \
3328 ReprType repr; \
3329 zig_##Type expected; \
3330 zig_##Type desired; \
3331 repr = *(ReprType volatile*)obj; \
3332 memcpy(&expected, &repr, sizeof(expected)); \
3333 do { \
3334 desired = expected - value; \
3335 } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \
3336 return expected; \
23263337 }
23273338
2328zig_msvc_flt_atomics(f32, u32, )
3339zig_msvc_flt_atomics(f32, uint32_t, )
23293340#if _M_X64
2330zig_msvc_flt_atomics(f64, u64, 64)
3341zig_msvc_flt_atomics(f64, uint64_t, 64)
23313342#endif
23323343
23333344#if _M_IX86
23343345static inline void zig_msvc_atomic_barrier() {
2335 zig_i32 barrier;
3346 int32_t barrier;
23363347 __asm {
23373348 xchg barrier, eax
23383349 }
23393350}
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) {
23423353 return _InterlockedExchangePointer(obj, arg);
23433354}
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) {
23463357 _InterlockedExchangePointer(obj, arg);
23473358}
23483359
......@@ -2360,11 +3371,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir
23603371 return exchanged;
23613372}
23623373#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) {
23643375 return _InterlockedExchangePointer(obj, arg);
23653376}
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) {
23683379 _InterlockedExchangePointer(obj, arg);
23693380}
23703381
......@@ -2383,11 +3394,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir
23833394}
23843395
23853396static 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);
23873398}
23883399
23893400static 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);
23913402}
23923403
23933404#define zig_msvc_atomics_128xchg(Type) \
......@@ -2429,7 +3440,7 @@ zig_msvc_atomics_128op(u128, max)
24293440
24303441#endif /* _MSC_VER && (_M_IX86 || _M_X64) */
24313442
2432/* ========================= Special Case Intrinsics ========================= */
3443/* ======================== Special Case Intrinsics ========================= */
24333444
24343445#if (_MSC_VER && _M_X64) || defined(__x86_64__)
24353446
......@@ -2459,8 +3470,8 @@ static inline void* zig_x86_windows_teb(void) {
24593470
24603471#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) {
2463 zig_u32 cpu_info[4];
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) {
3474 uint32_t cpu_info[4];
24643475#if _MSC_VER
24653476 __cpuidex(cpu_info, leaf_id, subid);
24663477#else
......@@ -2472,12 +3483,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z
24723483 *edx = cpu_info[3];
24733484}
24743485
2475static inline zig_u32 zig_x86_get_xcr0(void) {
3486static inline uint32_t zig_x86_get_xcr0(void) {
24763487#if _MSC_VER
2477 return (zig_u32)_xgetbv(0);
3488 return (uint32_t)_xgetbv(0);
24783489#else
2479 zig_u32 eax;
2480 zig_u32 edx;
3490 uint32_t eax;
3491 uint32_t edx;
24813492 __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
24823493 return eax;
24833494#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 {
141141 _ = @import("behavior/bugs/13664.zig");
142142 _ = @import("behavior/bugs/13714.zig");
143143 _ = @import("behavior/bugs/13785.zig");
144 _ = @import("behavior/bugs/14854.zig");
144145 _ = @import("behavior/byteswap.zig");
145146 _ = @import("behavior/byval_arg_var.zig");
146147 _ = @import("behavior/call.zig");
test/behavior/align.zig-1
......@@ -568,7 +568,6 @@ fn overaligned_fn() align(0x1000) i32 {
568568}
569569
570570test "comptime alloc alignment" {
571 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
572571 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
573572 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
574573 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 {
4848test "array concat with undefined" {
4949 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5050
51 {
52 var array = "hello".* ++ @as([5]u8, undefined);
53 array[5..10].* = "world".*;
54 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
55 }
56 {
57 var array = @as([5]u8, undefined) ++ "world".*;
58 array[0..5].* = "hello".*;
59 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
60 }
51 const S = struct {
52 fn doTheTest() !void {
53 {
54 var array = "hello".* ++ @as([5]u8, undefined);
55 array[5..10].* = "world".*;
56 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
57 }
58 {
59 var array = @as([5]u8, undefined) ++ "world".*;
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();
6168}
6269
6370test "array concat with tuple" {
......@@ -77,6 +84,7 @@ test "array concat with tuple" {
7784}
7885
7986test "array init with concat" {
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8088 const a = 'a';
8189 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };
8290 try expect(std.mem.eql(u8, &i, "abcd"));
......@@ -584,7 +592,6 @@ test "type coercion of anon struct literal to array" {
584592test "type coercion of pointer to anon struct literal to pointer to array" {
585593 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
586594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
587 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
588595 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
589596
590597 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
6363test "sharded table" {
6464 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
6565 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
66 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6766 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
6867
6968 // realistic 16-way sharding
test/behavior/bitcast.zig-3
......@@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" {
3434
3535test "@bitCast iX -> uX exotic integers" {
3636 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
37 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
3837 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
3938 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4039 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
8180
8281test "bitcast uX to bytes" {
8382 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
8583 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8684 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8785 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -368,7 +366,6 @@ test "comptime @bitCast packed struct to int and back" {
368366}
369367
370368test "comptime bitcast with fields following f80" {
371 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
372369 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
373370 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
374371 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
test/behavior/bitreverse.zig-4
......@@ -96,7 +96,6 @@ fn vector8() !void {
9696
9797test "bitReverse vectors u8" {
9898 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
10099 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
101100 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
102101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
......@@ -115,7 +114,6 @@ fn vector16() !void {
115114
116115test "bitReverse vectors u16" {
117116 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
118 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
119117 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
120118 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
121119 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
......@@ -134,7 +132,6 @@ fn vector24() !void {
134132
135133test "bitReverse vectors u24" {
136134 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
137 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
138135 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
139136 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
140137 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
......@@ -153,7 +150,6 @@ fn vector0() !void {
153150
154151test "bitReverse vectors u0" {
155152 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
156 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
157153
158154 comptime try vector0();
159155 try vector0();
test/behavior/bugs/10147.zig-1
......@@ -6,7 +6,6 @@ test "test calling @clz on both vector and scalar inputs" {
66 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
77 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
88 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1110
1211 var x: u32 = 0x1;
test/behavior/bugs/1076.zig-1
......@@ -4,7 +4,6 @@ const mem = std.mem;
44const expect = std.testing.expect;
55
66test "comptime code should not modify constant data" {
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
98 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
109
test/behavior/bugs/11046.zig-1
......@@ -10,7 +10,6 @@ fn bar() !void {
1010}
1111
1212test "fixed" {
13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1413 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1514 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1615 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/bugs/11787.zig-1
......@@ -4,7 +4,6 @@ const testing = std.testing;
44
55test "slicing zero length array field of struct" {
66 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/12000.zig-1
......@@ -7,7 +7,6 @@ const T = struct {
77
88test {
99 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1110 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
1211 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1312 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);
66
77test {
88 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
1110 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1211 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/12169.zig-1
......@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33
44test {
55 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
98 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 {
1010}
1111test {
1212 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1413 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1514
1615 var arr: [8]u3 = undefined;
test/behavior/bugs/13113.zig-1
......@@ -8,7 +8,6 @@ const Foo = extern struct {
88
99test {
1010 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1211 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
1312 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1413 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/1421.zig-1
......@@ -9,7 +9,6 @@ const S = struct {
99};
1010
1111test "functions with return type required to be comptime are generic" {
12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1312 const ti = S.method();
1413 try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct);
1514}
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 {
1313test "slices pointing at the same address as global array." {
1414 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1515 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1716 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1817 try checkAddress(&a);
1918 comptime try checkAddress(&a);
test/behavior/bugs/2622.zig-1
......@@ -4,7 +4,6 @@ var buf: []u8 = undefined;
44
55test "reslice of undefined global var slice" {
66 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/2727.zig-1
......@@ -6,7 +6,6 @@ fn t() bool {
66
77test {
88 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
1110 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1211 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/bugs/3742.zig-1
......@@ -37,7 +37,6 @@ pub const ArgSerializer = struct {
3737test "fixed" {
3838 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3939 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
4140 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4241 if (builtin.zig_backend == .stage2_llvm and
4342 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;
1111const builtin = @import("builtin");
1212
1313test "fixed" {
14 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1514 const x = S{
1615 .x = 3,
1716 .y = E{ .B = 1 },
test/behavior/bugs/421.zig-1
......@@ -2,7 +2,6 @@ const builtin = @import("builtin");
22const expect = @import("std").testing.expect;
33
44test "bitCast to array" {
5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/bugs/5398.zig-1
......@@ -21,7 +21,6 @@ var renderable: Renderable = undefined;
2121test "assignment of field with padding" {
2222 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2323 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2524 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2625
2726 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
1212test "crash" {
1313 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1615 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1716 _ = io.multiWriter(.{writer()});
1817}
test/behavior/bugs/656.zig-1
......@@ -13,7 +13,6 @@ const Value = struct {
1313test "optional if after an if in a switch prong of a switch with 2 prongs in an else" {
1414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1515 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1716 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1817 try foo(false, true);
1918}
test/behavior/bugs/6947.zig-1
......@@ -6,7 +6,6 @@ fn destroy(ptr: *void) void {
66
77test {
88 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
1110 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1211 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 {
6262
6363test "@byteSwap vectors u8" {
6464 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
6665 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6766 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
6867 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
......@@ -81,7 +80,6 @@ fn vector16() !void {
8180
8281test "@byteSwap vectors u16" {
8382 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
8583 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8684 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8785 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
......@@ -100,7 +98,6 @@ fn vector24() !void {
10098
10199test "@byteSwap vectors u24" {
102100 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
104101 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
105102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
106103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
......@@ -119,7 +116,6 @@ fn vector0() !void {
119116
120117test "@byteSwap vectors u0" {
121118 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
122 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
123119
124120 comptime try vector0();
125121 try vector0();
test/behavior/byval_arg_var.zig-1
......@@ -4,7 +4,6 @@ const builtin = @import("builtin");
44var result: []const u8 = "wrong";
55
66test "pass string literal byvalue to a generic var param" {
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
98
109 start();
test/behavior/call.zig-2
......@@ -21,7 +21,6 @@ test "super basic invocations" {
2121test "basic invocations" {
2222 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2323 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2524 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2625 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2726 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -56,7 +55,6 @@ test "basic invocations" {
5655test "tuple parameters" {
5756 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5857 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
59 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
6058 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
6159
6260 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 {
176176
177177test "implicitly cast indirect pointer to maybe-indirect pointer" {
178178 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;
180179 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
181180
182181 const S = struct {
......@@ -318,7 +317,6 @@ test "peer result null and comptime_int" {
318317
319318test "*const ?[*]const T to [*c]const [*c]const T" {
320319 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;
322320 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
323321
324322 var array = [_]u8{ 'o', 'k' };
......@@ -588,7 +586,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {
588586
589587test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
590588 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
591 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
592589 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
593590
594591 const window_name = [1][*]const u8{"window name"};
......@@ -598,7 +595,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
598595
599596test "vector casts" {
600597 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
601 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
602598 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
603599 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
604600 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -928,7 +924,6 @@ test "peer cast *[N:x]T to *[N]T" {
928924
929925test "peer cast [*:x]T to [*]T" {
930926 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
931 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
932927 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
933928
934929 const S = struct {
......@@ -949,7 +944,6 @@ test "peer cast [*:x]T to [*]T" {
949944test "peer cast [:x]T to [*:x]T" {
950945 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
951946 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
952 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
953947 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
954948
955949 const S = struct {
......@@ -990,7 +984,6 @@ test "peer type resolution implicit cast to return type" {
990984
991985test "peer type resolution implicit cast to variable type" {
992986 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
993 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
994987 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
995988
996989 const S = struct {
......@@ -1113,8 +1106,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void {
11131106}
11141107
11151108test "compile time int to ptr of function" {
1116 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1117
11181109 try foobar(FUNCTION_CONSTANT);
11191110}
11201111
......@@ -1506,7 +1497,6 @@ test "implicit cast from [:0]T to [*c]T" {
15061497test "bitcast packed struct with u0" {
15071498 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15081499 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1509 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15101500
15111501 const S = packed struct(u2) { a: u0, b: u2 };
15121502 const s = @bitCast(S, @as(u2, 2));
test/behavior/comptime_memory.zig+8
......@@ -412,3 +412,11 @@ test "bitcast packed union to integer" {
412412 try testing.expectEqual(@as(u2, 2), cast_b);
413413 }
414414}
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 {
5050}
5151
5252test "return variable while defer expression in scope to modify it" {
53 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
5453 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
5554 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
5655 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" {
937937}
938938
939939test "constant enum initialization with differing sizes" {
940 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
941940 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
942941 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
943942 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1137,7 +1136,6 @@ test "tag name functions are unique" {
11371136}
11381137
11391138test "size of enum with only one tag which has explicit integer tag type" {
1140 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
11411139 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11421140 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" {
144144}
145145
146146test "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
149147 entry();
150148 comptime entry();
151149}
152150
153151test "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
156152 entryPtr();
157153 comptime entryPtr();
158154}
......@@ -219,7 +215,6 @@ fn testErrorSetType() !void {
219215}
220216
221217test "explicit error set cast" {
222 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
223218 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
224219
225220 try testExplicitErrorSetCast(Set1.A);
......@@ -238,7 +233,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void {
238233}
239234
240235test "comptime test error for empty error set" {
241 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
242236 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
243237
244238 try testComptimeTestErrorEmptySet(1234);
......@@ -255,8 +249,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
255249}
256250
257251test "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
260252 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
261253 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
262254}
......@@ -426,7 +418,6 @@ test "nested error union function call in optional unwrap" {
426418}
427419
428420test "return function call to error set from error union function" {
429 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
430421 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
431422 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
432423
......@@ -486,7 +477,6 @@ test "nested catch" {
486477}
487478
488479test "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
490480 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
491481
492482 const S = struct {
test/behavior/eval.zig-2
......@@ -138,7 +138,6 @@ test "pointer to type" {
138138}
139139
140140test "a type constructed in a global expression" {
141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
142141 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
143142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
144143
......@@ -1515,7 +1514,6 @@ test "x or true is comptime-known true" {
15151514}
15161515
15171516test "non-optional and optional array elements concatenated" {
1518 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15191517 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15201518 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15211519 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");
33
44test "@fieldParentPtr non-first field" {
55 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
67 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
78 try testParentFieldPtr(&foo.c);
89 comptime try testParentFieldPtr(&foo.c);
......@@ -10,6 +11,7 @@ test "@fieldParentPtr non-first field" {
1011
1112test "@fieldParentPtr first field" {
1213 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1315 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1416 try testParentFieldPtrFirst(&foo.a);
1517 comptime try testParentFieldPtrFirst(&foo.a);
......@@ -47,6 +49,7 @@ fn testParentFieldPtrFirst(a: *const bool) !void {
4749
4850test "@fieldParentPtr untagged union" {
4951 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
52 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
5053 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5154 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5255
......@@ -73,6 +76,7 @@ fn testFieldParentPtrUnion(c: *const i32) !void {
7376
7477test "@fieldParentPtr tagged union" {
7578 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
79 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7680 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7781 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7882
......@@ -99,6 +103,7 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void {
99103
100104test "@fieldParentPtr extern union" {
101105 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
106 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
102107 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
103108 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 {
2121
2222test "floating point comparisons" {
2323 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2524 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2625
2726 try testFloatComparisons();
......@@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void {
9190
9291test "negative f128 floatToInt at compile-time" {
9392 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
94 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
9593 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9694 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9795
......@@ -141,7 +139,6 @@ fn testSqrt() !void {
141139
142140test "@sqrt with vectors" {
143141 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
144 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
145142 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
146143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
147144 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -234,7 +231,6 @@ fn testSin() !void {
234231
235232test "@sin with vectors" {
236233 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
237 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
238234 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
239235 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
240236 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -275,7 +271,6 @@ fn testCos() !void {
275271
276272test "@cos with vectors" {
277273 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
278 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
279274 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
280275 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
281276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -315,7 +310,6 @@ fn testExp() !void {
315310
316311test "@exp with vectors" {
317312 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
319313 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
320314 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
321315 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -335,7 +329,6 @@ fn testExpWithVectors() !void {
335329
336330test "@exp2" {
337331 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
338 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
339332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
340333 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
341334 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -355,7 +348,6 @@ fn testExp2() !void {
355348
356349test "@exp2" {
357350 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
358 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
359351 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
360352 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
361353 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -409,7 +401,6 @@ test "@log with @vectors" {
409401 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
410402 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
411403 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
412 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
413404 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
414405
415406 {
......@@ -447,7 +438,6 @@ test "@log2 with vectors" {
447438 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
448439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
449440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
450 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
451441 // https://github.com/ziglang/zig/issues/13681
452442 if (builtin.zig_backend == .stage2_llvm and
453443 builtin.cpu.arch == .aarch64 and
......@@ -491,7 +481,6 @@ test "@log10 with vectors" {
491481 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
492482 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
493483 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
494 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
495484
496485 comptime try testLog10WithVectors();
497486 try testLog10WithVectors();
......@@ -508,7 +497,6 @@ fn testLog10WithVectors() !void {
508497
509498test "@fabs" {
510499 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
511 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
512500 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
513501 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
514502 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -537,7 +525,6 @@ fn testFabs() !void {
537525
538526test "@fabs with vectors" {
539527 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
540 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
541528 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
542529 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
543530 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" {
636623
637624test "@floor" {
638625 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
639 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
640626 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
641627 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
642628 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -660,7 +646,6 @@ fn testFloor() !void {
660646
661647test "@floor with vectors" {
662648 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
663 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
664649 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
665650 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
666651 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -730,7 +715,6 @@ fn testFloorLegacy(comptime T: type, x: T) !void {
730715
731716test "@ceil" {
732717 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
733 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
734718 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
735719 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
736720 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -754,7 +738,6 @@ fn testCeil() !void {
754738
755739test "@ceil with vectors" {
756740 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
757 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
758741 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
759742 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
760743 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -824,7 +807,6 @@ fn testCeilLegacy(comptime T: type, x: T) !void {
824807
825808test "@trunc" {
826809 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
827 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
828810 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
829811 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
830812 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -848,7 +830,6 @@ fn testTrunc() !void {
848830
849831test "@trunc with vectors" {
850832 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
851 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
852833 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
853834 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
854835 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" {
275275}
276276
277277test "void parameters" {
278 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
279278 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
280279 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
281280
......@@ -300,7 +299,6 @@ fn acceptsString(foo: []u8) void {
300299}
301300
302301test "function pointers" {
303 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
304302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
305303 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
306304 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/for.zig-8
......@@ -110,7 +110,6 @@ test "basic for loop" {
110110}
111111
112112test "for with null and T peer types and inferred result location type" {
113 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
114113 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
115114 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
116115 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -152,7 +151,6 @@ test "2 break statements and an else" {
152151}
153152
154153test "for loop with pointer elem var" {
155 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
156154 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
157155 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
158156 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -198,7 +196,6 @@ test "for copies its payload" {
198196}
199197
200198test "for on slice with allowzero ptr" {
201 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
202199 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
203200 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
204201 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -253,7 +250,6 @@ test "for loop with else branch" {
253250test "count over fixed range" {
254251 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
255252 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
256 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
257253
258254 var sum: usize = 0;
259255 for (0..6) |i| {
......@@ -266,7 +262,6 @@ test "count over fixed range" {
266262test "two counters" {
267263 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
268264 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
269 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
270265
271266 var sum: usize = 0;
272267 for (0..10, 10..20) |i, j| {
......@@ -280,7 +275,6 @@ test "two counters" {
280275test "1-based counter and ptr to array" {
281276 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
282277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
283 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
284278
285279 var ok: usize = 0;
286280
......@@ -401,7 +395,6 @@ test "raw pointer and counter" {
401395test "inline for with slice as the comptime-known" {
402396 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
403397 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
404 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
405398 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
406399
407400 const comptime_slice = "hello";
......@@ -432,7 +425,6 @@ test "inline for with slice as the comptime-known" {
432425test "inline for with counter as the comptime-known" {
433426 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
434427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
435 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
436428 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
437429
438430 var runtime_slice = "hello";
test/behavior/generics.zig-3
......@@ -56,7 +56,6 @@ fn sameButWithFloats(a: f64, b: f64) f64 {
5656}
5757
5858test "fn with comptime args" {
59 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6059 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
6160 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
6261 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -67,7 +66,6 @@ test "fn with comptime args" {
6766}
6867
6968test "anytype params" {
70 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7169 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7270 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7371 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -93,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 {
9391}
9492
9593test "type constructed by comptime function call" {
96 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
9794 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9895 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" {
7878}
7979
8080test "if copies its payload" {
81 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8281 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8382 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 };
4646test "inline switch unions" {
4747 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4848 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
5049 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5150
5251 var x: U = .a;
test/behavior/int_div.zig+19
......@@ -91,3 +91,22 @@ fn mod(comptime T: type, a: T, b: T) T {
9191fn rem(comptime T: type, a: T, b: T) T {
9292 return @rem(a, b);
9393}
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" {
100100 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
101101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
102102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
104103 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
105104
106105 try testClzVectors();
......@@ -163,7 +162,6 @@ test "@ctz vectors" {
163162 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
164163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
165164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
166 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
167165 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
168166
169167 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
......@@ -1526,7 +1524,6 @@ fn testNanEqNan(comptime F: type) !void {
15261524}
15271525
15281526test "vector comparison" {
1529 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
15301527 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
15311528 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15321529 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -1563,6 +1560,12 @@ test "signed zeros are represented properly" {
15631560 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15641561 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
15661569 const S = struct {
15671570 fn doTheTest() !void {
15681571 try testOne(f16);
test/behavior/maximum_minimum.zig-2
......@@ -25,7 +25,6 @@ test "@max" {
2525test "@max on vectors" {
2626 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2727 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
2928 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3029 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3130 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -75,7 +74,6 @@ test "@min for vectors" {
7574 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7675 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7776 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
78 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7977 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8078
8179 const S = struct {
test/behavior/member_func.zig-2
......@@ -27,7 +27,6 @@ const HasFuncs = struct {
2727};
2828
2929test "standard field calls" {
30 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
3130 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3231 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3332 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -71,7 +70,6 @@ test "standard field calls" {
7170}
7271
7372test "@field field calls" {
74 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7573 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7674 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7775 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/muladd.zig+7-5
......@@ -100,7 +100,6 @@ fn vector16() !void {
100100}
101101
102102test "vector f16" {
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
104103 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
105104 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
106105 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -124,7 +123,6 @@ fn vector32() !void {
124123}
125124
126125test "vector f32" {
127 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
128126 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
129127 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
130128 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -148,7 +146,6 @@ fn vector64() !void {
148146}
149147
150148test "vector f64" {
151 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
152149 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
153150 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
154151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -171,7 +168,6 @@ fn vector80() !void {
171168}
172169
173170test "vector f80" {
174 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
175171 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
176172 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
177173 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -195,13 +191,19 @@ fn vector128() !void {
195191}
196192
197193test "vector f128" {
198 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
199194 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
200195 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
201196 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
202197 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
203198 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
205207 comptime try vector128();
206208 try vector128();
207209}
test/behavior/null.zig-3
......@@ -50,7 +50,6 @@ test "rhs maybe unwrap return" {
5050}
5151
5252test "maybe return" {
53 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
5453 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
5554 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
5655 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -71,7 +70,6 @@ fn foo(x: ?i32) ?bool {
7170}
7271
7372test "test null runtime" {
74 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7573 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7674 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7775
......@@ -184,7 +182,6 @@ const SillyStruct = struct {
184182const here_is_a_null_literal = SillyStruct{ .context = null };
185183
186184test "unwrap optional which is field of global var" {
187 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
188185 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
189186 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
190187 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" {
274274}
275275
276276test "0-bit child type coerced to optional" {
277 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
278277 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
279278
280279 const S = struct {
test/behavior/pointers.zig-2
......@@ -190,7 +190,6 @@ test "compare equality of optional and non-optional pointer" {
190190test "allowzero pointer and slice" {
191191 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
192192 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
193 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
194193 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
195194 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
196195
......@@ -510,7 +509,6 @@ test "ptrToInt on a generic function" {
510509 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
511510 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
512511 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
514512
515513 const S = struct {
516514 fn generic(i: anytype) @TypeOf(i) {
test/behavior/popcount.zig-1
......@@ -67,7 +67,6 @@ fn testPopCountIntegers() !void {
6767}
6868
6969test "@popCount vectors" {
70 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7170 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7271 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7372 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/ptrcast.zig+2-1
......@@ -53,7 +53,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void {
5353}
5454
5555test "reinterpret bytes of an array into an extern struct" {
56 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
5756 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5857 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5958
......@@ -171,6 +170,7 @@ test "lower reinterpreted comptime field ptr" {
171170
172171test "reinterpret struct field at comptime" {
173172 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
173 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
174174
175175 const numNative = comptime Bytes.init(0x12345678);
176176 if (native_endian != .Little) {
......@@ -233,6 +233,7 @@ test "ptrcast of const integer has the correct object size" {
233233test "implicit optional pointer to optional anyopaque pointer" {
234234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
235235 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
236 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
236237
237238 var buf: [4]u8 = "aoeu".*;
238239 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;
55
66var ok: bool = false;
77test "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;
98 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
109 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1110 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 {
2727
2828test "reflection: @field" {
2929 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
30 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
3130 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3231
3332 var f = Foo{
test/behavior/select.zig-2
......@@ -4,7 +4,6 @@ const mem = std.mem;
44const expect = std.testing.expect;
55
66test "@select vectors" {
7 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -33,7 +32,6 @@ fn selectVectors() !void {
3332}
3433
3534test "@select arrays" {
36 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
3735 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
3836 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
3937 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/shuffle.zig+1-4
......@@ -8,7 +8,6 @@ test "@shuffle int" {
88 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1010 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1211 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1312
1413 const S = struct {
......@@ -50,7 +49,6 @@ test "@shuffle bool 1" {
5049 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
5150 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5251 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
53 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
5452 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5553
5654 const S = struct {
......@@ -71,7 +69,6 @@ test "@shuffle bool 2" {
7169 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7270 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7371 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
74 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7572 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7673
7774 if (builtin.zig_backend == .stage2_llvm) {
......@@ -83,7 +80,7 @@ test "@shuffle bool 2" {
8380 fn doTheTest() !void {
8481 var x: @Vector(3, bool) = [3]bool{ false, true, false };
8582 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 };
8784 var res = @shuffle(bool, x, v, mask);
8885 try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));
8986 }
test/behavior/sizeof_and_typeof.zig-3
......@@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" {
1818}
1919
2020test "@TypeOf() with multiple arguments" {
21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2221 {
2322 var var_1: u32 = undefined;
2423 var var_2: u8 = undefined;
......@@ -138,7 +137,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {
138137}
139138
140139test "@TypeOf() has no runtime side effects" {
141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
142140 const S = struct {
143141 fn foo(comptime T: type, ptr: *T) T {
144142 ptr.* += 1;
......@@ -153,7 +151,6 @@ test "@TypeOf() has no runtime side effects" {
153151
154152test "branching logic inside @TypeOf" {
155153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
156 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
157154 const S = struct {
158155 var data: i32 = 0;
159156 fn foo() anyerror!i32 {
test/behavior/slice.zig+21-9
......@@ -119,7 +119,6 @@ test "slice of type" {
119119}
120120
121121test "generic malloc free" {
122 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
123122 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
124123
125124 const a = memAlloc(u8, 10) catch unreachable;
......@@ -171,7 +170,6 @@ test "comptime pointer cast array and then slice" {
171170
172171test "slicing zero length array" {
173172 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
174 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
175173 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
176174
177175 const s1 = ""[0..];
......@@ -185,8 +183,6 @@ test "slicing zero length array" {
185183const x = @intToPtr([*]i32, 0x1000)[0..0x500];
186184const y = x[0x100..];
187185test "compile time slice of pointer to hard coded address" {
188 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
189
190186 try expect(@ptrToInt(x) == 0x1000);
191187 try expect(x.len == 0x500);
192188
......@@ -231,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
231227
232228test "C pointer" {
233229 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
230 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
234231
235232 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
236233 var len: u32 = 10;
......@@ -342,7 +339,6 @@ test "@ptrCast slice to pointer" {
342339}
343340
344341test "slice syntax resulting in pointer-to-array" {
345 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
346342 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
347343 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
348344 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -477,7 +473,6 @@ test "slice syntax resulting in pointer-to-array" {
477473}
478474
479475test "slice pointer-to-array null terminated" {
480 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
481476 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
482477 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
483478
......@@ -497,7 +492,6 @@ test "slice pointer-to-array null terminated" {
497492}
498493
499494test "slice pointer-to-array zero length" {
500 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
501495 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
502496
503497 comptime {
......@@ -530,7 +524,6 @@ test "slice pointer-to-array zero length" {
530524}
531525
532526test "type coercion of pointer to anon struct literal to pointer to slice" {
533 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
534527 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
535528 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
536529 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -730,7 +723,6 @@ test "slice with dereferenced value" {
730723
731724test "empty slice ptr is non null" {
732725 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
735727 const empty_slice: []u8 = &[_]u8{};
736728 const p: [*]u8 = empty_slice.ptr + 0;
......@@ -747,3 +739,23 @@ test "slice decays to many pointer" {
747739 const p: [*:0]const u8 = buf[0..7 :0];
748740 try expectEqualStrings(buf[0..7], std.mem.span(p));
749741}
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 {
342342test "self-referencing struct via array member" {
343343 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
344344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
345 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
346345 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
347346
348347 const T = struct {
......@@ -536,7 +535,6 @@ test "implicit cast packed struct field to const ptr" {
536535}
537536
538537test "zero-bit field in packed struct" {
539 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
540538 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
541539
542540 const S = packed struct {
......@@ -719,7 +717,6 @@ test "pointer to packed struct member in a stack variable" {
719717}
720718
721719test "packed struct with u0 field access" {
722 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
723720 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
724721
725722 const S = packed struct {
......@@ -1018,7 +1015,6 @@ test "type coercion of anon struct literal to struct" {
10181015}
10191016
10201017test "type coercion of pointer to anon struct literal to pointer to struct" {
1021 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10221018 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10231019 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10241020 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1088,7 +1084,6 @@ test "packed struct with undefined initializers" {
10881084}
10891085
10901086test "for loop over pointers to struct, getting field from struct pointer" {
1091 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10921087 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10931088 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");
44
55test "struct contains null pointer which contains original struct" {
66 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
98
109 var x: ?*NodeLineComment = null;
test/behavior/switch.zig-2
......@@ -403,7 +403,6 @@ fn return_a_number() anyerror!i32 {
403403}
404404
405405test "switch on integer with else capturing expr" {
406 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
407406 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
408407
409408 const S = struct {
......@@ -577,7 +576,6 @@ test "switch prongs with cases with identical payload types" {
577576}
578577
579578test "switch on pointer type" {
580 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
581579 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
582580 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;
44
55test "thread local variable" {
66 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
......@@ -40,7 +39,6 @@ threadlocal var buffer: [11]u8 = undefined;
4039
4140test "reference a global threadlocal variable" {
4241 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4442 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4543 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4644 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" {
7979
8080test "casting or calling a value with a paren-surrounded macro" {
8181 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
82 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8382 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
8483 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8584 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/truncate.zig-1
......@@ -60,7 +60,6 @@ test "truncate on comptime integer" {
6060}
6161
6262test "truncate on vectors" {
63 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
6463 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
6564 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6665 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
test/behavior/tuple.zig+19-1
......@@ -126,7 +126,6 @@ test "tuple initializer for var" {
126126}
127127
128128test "array-like initializer for tuple types" {
129 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
130129 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
131130 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
132131
......@@ -397,3 +396,22 @@ test "nested runtime conditionals in tuple initializer" {
397396 };
398397 try expectEqualStrings("up", x[0]);
399398}
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" {
449449}
450450
451451test "Type.Union from Type.Enum" {
452 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
453
454452 const Tag = @Type(.{
455453 .Enum = .{
456454 .tag_type = u0,
......@@ -475,8 +473,6 @@ test "Type.Union from Type.Enum" {
475473}
476474
477475test "Type.Union from regular enum" {
478 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
479
480476 const E = enum { working_as_expected };
481477 const T = @Type(.{
482478 .Union = .{
test/behavior/type_info.zig-2
......@@ -509,7 +509,6 @@ test "type info for async frames" {
509509}
510510
511511test "Declarations are returned in declaration order" {
512 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
513512 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
514513
515514 const S = struct {
......@@ -532,7 +531,6 @@ test "Struct.is_tuple for anon list literal" {
532531}
533532
534533test "Struct.is_tuple for anon struct literal" {
535 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
536534 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
537535
538536 const info = @typeInfo(@TypeOf(.{ .a = 0 }));
test/behavior/union.zig-11
......@@ -11,7 +11,6 @@ const FooWithFloats = union {
1111};
1212
1313test "basic unions with floats" {
14 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1514 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1615 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1716 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -215,7 +214,6 @@ const Payload = union(Letter) {
215214};
216215
217216test "union with specified enum tag" {
218 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
219217 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
220218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
221219 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -225,7 +223,6 @@ test "union with specified enum tag" {
225223}
226224
227225test "packed union generates correctly aligned type" {
228 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
229226 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
230227 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
231228 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" {
379376}
380377
381378test "tagged union initialization with runtime void" {
382 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
383379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
384380 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
385381 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -400,7 +396,6 @@ fn testTaggedUnionInit(x: anytype) bool {
400396pub const UnionEnumNoPayloads = union(enum) { A, B };
401397
402398test "tagged union with no payloads" {
403 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
404399 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
405400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
406401 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -466,7 +461,6 @@ pub const FooUnion = union(enum) {
466461var glbl_array: [2]FooUnion = undefined;
467462
468463test "initialize global array of union" {
469 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
470464 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
471465 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
472466 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -494,7 +488,6 @@ test "update the tag value for zero-sized unions" {
494488
495489test "union initializer generates padding only if needed" {
496490 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
497 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
498491 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
499492 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
500493 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -561,7 +554,6 @@ const FooNoVoid = union(enum) {
561554const Baz = enum { A, B, C, D };
562555
563556test "tagged union type" {
564 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
565557 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
566558
567559 const foo1 = TaggedFoo{ .One = 13 };
......@@ -598,7 +590,6 @@ fn returnAnInt(x: i32) TaggedFoo {
598590}
599591
600592test "tagged union with all void fields but a meaningful tag" {
601 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
602593 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
603594 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
604595 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -777,7 +768,6 @@ fn Setter(comptime attr: Attribute) type {
777768}
778769
779770test "return union init with void payload" {
780 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
781771 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
782772 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
783773 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
......@@ -913,7 +903,6 @@ test "extern union doesn't trigger field check at comptime" {
913903test "anonymous union literal syntax" {
914904 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
915905 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
916 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
917906 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
918907
919908 const S = struct {
test/behavior/union_with_members.zig-1
......@@ -18,7 +18,6 @@ const ET = union(enum) {
1818
1919test "enum with members" {
2020 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2322 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2423 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" {
2525
2626test "vector wrap operators" {
2727 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
2928 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
3029 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3130 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -48,7 +47,6 @@ test "vector wrap operators" {
4847
4948test "vector bin compares with mem.eql" {
5049 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
5250 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
5351 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5452 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -93,12 +91,18 @@ test "vector int operators" {
9391
9492test "vector float operators" {
9593 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
96 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
9794 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
9895 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9996 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10097 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
102106 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
103107 const S = struct {
104108 fn doTheTest() !void {
......@@ -117,7 +121,6 @@ test "vector float operators" {
117121
118122test "vector bit operators" {
119123 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
120 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
121124 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
122125 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
123126 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -237,7 +240,6 @@ test "vector casts of sizes not divisible by 8" {
237240}
238241
239242test "vector @splat" {
240 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
241243 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
242244 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
243245 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -380,7 +382,6 @@ test "store vector elements via runtime index" {
380382}
381383
382384test "initialize vector which is a struct field" {
383 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
384385 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
385386 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
386387 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -403,7 +404,6 @@ test "initialize vector which is a struct field" {
403404
404405test "vector comparison operators" {
405406 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
406 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
407407 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
408408 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
409409 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -444,7 +444,6 @@ test "vector comparison operators" {
444444
445445test "vector division operators" {
446446 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
447 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
448447 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
449448 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
450449 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -527,7 +526,6 @@ test "vector division operators" {
527526
528527test "vector bitwise not operator" {
529528 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
530 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
531529 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
532530 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
533531 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -559,7 +557,6 @@ test "vector bitwise not operator" {
559557
560558test "vector shift operators" {
561559 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
562 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
563560 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
564561 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
565562 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -653,7 +650,6 @@ test "vector shift operators" {
653650
654651test "vector reduce operation" {
655652 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
656 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
657653 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
658654 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
659655 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -709,7 +705,7 @@ test "vector reduce operation" {
709705
710706 // LLVM 11 ERROR: Cannot select type
711707 // 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) {
713709 try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386));
714710 try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9));
715711 }
......@@ -727,7 +723,7 @@ test "vector reduce operation" {
727723
728724 // LLVM 11 ERROR: Cannot select type
729725 // 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) {
731727 try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567));
732728 try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999));
733729 }
......@@ -775,14 +771,14 @@ test "vector reduce operation" {
775771
776772 // LLVM 11 ERROR: Cannot select type
777773 // https://github.com/ziglang/zig/issues/7138
778 if (false) {
779 try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
780 try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);
781 try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);
782
783 try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
784 try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan);
785 try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan);
774 if (builtin.zig_backend != .stage2_llvm) {
775 try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, -1.9));
776 try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, -1.9));
777 try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, -1.9));
778
779 try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, 100.0));
780 try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, 100.0));
781 try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, 100.0));
786782 }
787783
788784 try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan);
......@@ -797,7 +793,6 @@ test "vector reduce operation" {
797793
798794test "vector @reduce comptime" {
799795 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
800 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
801796 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
802797 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
803798 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -813,7 +808,6 @@ test "vector @reduce comptime" {
813808
814809test "mask parameter of @shuffle is comptime scope" {
815810 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
816 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
817811 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
818812 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
819813 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -833,7 +827,6 @@ test "mask parameter of @shuffle is comptime scope" {
833827
834828test "saturating add" {
835829 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
836 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
837830 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
838831 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
839832 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -865,7 +858,6 @@ test "saturating add" {
865858
866859test "saturating subtraction" {
867860 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
868 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
869861 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
870862 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
871863 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -888,7 +880,6 @@ test "saturating subtraction" {
888880
889881test "saturating multiplication" {
890882 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
891 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
892883 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
893884 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
894885 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -915,7 +906,6 @@ test "saturating multiplication" {
915906
916907test "saturating shift-left" {
917908 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
918 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
919909 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
920910 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
921911 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -1049,7 +1039,6 @@ test "@mulWithOverflow" {
10491039}
10501040
10511041test "@shlWithOverflow" {
1052 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10531042 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10541043 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10551044 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -1078,7 +1067,6 @@ test "alignment of vectors" {
10781067
10791068test "loading the second vector from a slice of vectors" {
10801069 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1081 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10821070 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10831071 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10841072 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1131,7 +1119,6 @@ test "byte vector initialized in inline function" {
11311119}
11321120
11331121test "byte vector initialized in inline function" {
1134 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11351122 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11361123 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11371124 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -1160,7 +1147,6 @@ test "byte vector initialized in inline function" {
11601147
11611148test "zero divisor" {
11621149 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1163 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11641150 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11651151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11661152 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1181,7 +1167,6 @@ test "zero divisor" {
11811167
11821168test "zero multiplicand" {
11831169 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1184 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11851170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11861171 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11871172 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1204,7 +1189,6 @@ test "zero multiplicand" {
12041189
12051190test "@intCast to u0" {
12061191 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1207 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12081192 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12091193 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12101194 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -1228,7 +1212,6 @@ test "modRem with zero divisor" {
12281212
12291213test "array operands to shuffle are coerced to vectors" {
12301214 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1231 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12321215 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12331216 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12341217 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -1246,8 +1229,6 @@ test "load packed vector element" {
12461229 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12471230 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12481231 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
12521233 var x: @Vector(2, u15) = .{ 1, 4 };
12531234 try expect((&x[0]).* == 1);
......@@ -1260,7 +1241,6 @@ test "store packed vector element" {
12601241 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12611242 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
12621243 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1263 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12641244
12651245 var v = @Vector(4, u1){ 1, 1, 1, 1 };
12661246 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 {
3333}
3434
3535test "void optional" {
36 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
3736 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3837 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3938 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/while.zig-8
......@@ -104,7 +104,6 @@ fn testBreakOuter() void {
104104}
105105
106106test "while copies its payload" {
107 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
108107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
109108 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
110109
......@@ -143,7 +142,6 @@ fn runContinueAndBreakTest() !void {
143142}
144143
145144test "while with optional as condition" {
146 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
147145 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
148146 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
149147
......@@ -156,7 +154,6 @@ test "while with optional as condition" {
156154}
157155
158156test "while with optional as condition with else" {
159 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
160157 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
161158 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
162159
......@@ -174,7 +171,6 @@ test "while with optional as condition with else" {
174171}
175172
176173test "while with error union condition" {
177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
178174 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
179175
180176 numbers_left = 10;
......@@ -205,7 +201,6 @@ test "while on bool with else result follow break prong" {
205201}
206202
207203test "while on optional with else result follow else prong" {
208 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
209204 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
210205 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
211206 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -217,7 +212,6 @@ test "while on optional with else result follow else prong" {
217212}
218213
219214test "while on optional with else result follow break prong" {
220 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
221215 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
222216 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
223217 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -290,7 +284,6 @@ test "while bool 2 break statements and an else" {
290284}
291285
292286test "while optional 2 break statements and an else" {
293 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
294287 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
295288 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
296289 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -310,7 +303,6 @@ test "while optional 2 break statements and an else" {
310303}
311304
312305test "while error 2 break statements and an else" {
313 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
314306 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
315307 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
316308 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" {
2828}
2929
3030test "implicit unsigned integer to signed integer" {
31 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
3231 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3332 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3433
test/cases.zig+5-5
......@@ -1,8 +1,8 @@
11const std = @import("std");
2const TestContext = @import("../src/test.zig").TestContext;
2const Cases = @import("src/Cases.zig");
33
4pub fn addCases(ctx: *TestContext) !void {
5 try @import("compile_errors.zig").addCases(ctx);
6 try @import("stage2/cbe.zig").addCases(ctx);
7 try @import("stage2/nvptx.zig").addCases(ctx);
4pub fn addCases(cases: *Cases) !void {
5 try @import("compile_errors.zig").addCases(cases);
6 try @import("cbe.zig").addCases(cases);
7 try @import("nvptx.zig").addCases(cases);
88}
test/cases/compile_errors/access_inactive_union_field_comptime.zig+1
......@@ -21,3 +21,4 @@ pub export fn entry1() void {
2121// :9:15: error: access of union field 'a' while field 'b' is active
2222// :2:21: note: union declared here
2323// :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",);
44// backend=stage2
55// target=native
66//
7// :1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound
7// 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 {
1515// target=native
1616//
1717// :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 {
1717//
1818// :5:5: error: found compile log statement
1919// :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 @@
11export fn entry() void {
2 @compileLog(@ptrCast(*const anyopaque, &entry));
2 @compileLog(@as(*align(1) const anyopaque, @ptrCast(*const anyopaque, &entry)));
33}
44
55// error
......@@ -7,3 +7,6 @@ export fn entry() void {
77// target=native
88//
99// :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 {
1212// target=native
1313//
1414// :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 {
1313//
1414// :7:39: error: found compile log statement
1515// :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 {
4545// :22:13: error: unable to resolve comptime value
4646// :22:13: note: condition in comptime switch must be comptime-known
4747// :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
4850// :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 {
3232// :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs
3333// :1:11: note: opaque declared here
3434// :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions
35// :1:11: note: opaque declared here
3536// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs
3637// :18:22: note: opaque declared here
3738// :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; }
1212// backend=stage2
1313// target=native
1414//
15// :1:15: error: comptime parameters not allowed in function with calling convention 'C'
1615// :5:30: error: comptime parameters not allowed in function with calling convention 'C'
1716// :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 {
2727// :1:17: note: opaque declared here
2828// :8:28: error: parameter of type '@TypeOf(null)' not allowed
2929// :12:8: error: parameter of opaque type 'tmp.FooType' not allowed
30// :1:17: note: opaque declared here
3031// :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 {
2424// :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set'
2525// :7:17: note: function cannot return an error
2626// :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
2827// :11:15: note: cannot convert error union to payload type
2928// :11:15: note: consider using 'try', 'catch', or 'if'
29// :10:17: note: function cannot return an error
3030// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
3131// :15:14: note: cannot convert error union to payload type
3232// :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 {
33 var good = {};
44 _ = {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-block_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-comptime_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = comptime {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-comptime_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 comptime ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-defer.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 defer ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-for_expression.zig+3
......@@ -3,7 +3,10 @@ export fn entry() void {
33 var good = {};
44 _ = for(foo()) |_| {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
9fn foo() void {}
710
811// error
912// backend=stage2
test/cases/compile_errors/implicit_semicolon-for_statement.zig+3
......@@ -3,7 +3,10 @@ export fn entry() void {
33 var good = {};
44 for(foo()) |_| ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
9fn foo() void {}
710
811// error
912// backend=stage2
test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = if(true) {} else if(true) {} else {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 if(true) ({}) else if(true) ({}) else ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = if(true) {} else if(true) {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 if(true) ({}) else if(true) ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if-else_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = if(true) {} else {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if-else_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 if(true) ({}) else ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = if(true) {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-if_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 if(true) ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-test_expression.zig+3
......@@ -3,7 +3,10 @@ export fn entry() void {
33 var good = {};
44 _ = if (foo()) |_| {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
9fn foo() void {}
710
811// error
912// backend=stage2
test/cases/compile_errors/implicit_semicolon-test_statement.zig+3
......@@ -3,7 +3,10 @@ export fn entry() void {
33 var good = {};
44 if (foo()) |_| ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
9fn foo() void {}
710
811// error
912// backend=stage2
test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = while(true):({}) {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 while(true):({}) ({})
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-while_expression.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 _ = while(true) {}
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/implicit_semicolon-while_statement.zig+2
......@@ -3,6 +3,8 @@ export fn entry() void {
33 var good = {};
44 while(true) 1
55 var bad = {};
6 _ = good;
7 _ = bad;
68}
79
810// error
test/cases/compile_errors/invalid_member_of_builtin_enum.zig+1-1
......@@ -9,4 +9,4 @@ export fn entry() void {
99// target=native
1010//
1111// :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86'
12// :?:18: note: enum declared here
12// : 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 {
7373//
7474// :6:19: error: value stored in comptime field does not match the default value of the field
7575// :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
7776// :19:38: error: value stored in comptime field does not match the default value of the field
7877// :31:19: error: value stored in comptime field does not match the default value of the field
7978// :25:29: note: default value set here
8079// :41:16: error: value stored in comptime field does not match the default value of the field
8180// :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
8282// :66:43: error: value stored in comptime field does not match the default value of the field
8383// :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 {
2525// :4:7: error: no field named 'foo' in struct 'tmp.A'
2626// :1:11: note: struct declared here
2727// :10:17: error: no field named 'bar' in struct 'tmp.A'
28// :1:11: note: struct declared here
2829// :18:45: error: no field named 'f' in struct 'tmp.e.B'
2930// :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 {
22 x: i32,
33
44 fn init(x: i32) Foo {
5 return Foo {
5 return Foo{
66 .x = x,
77 };
88 }
......@@ -20,3 +20,4 @@ export fn f() void {
2020//
2121// :14:9: error: no field or member function named 'init' in 'tmp.Foo'
2222// :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 {
2929//
3030// :23:6: error: no field or member function named 'init' in 'tmp.List'
3131// :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 @@
55// target=x86_64-linux
66// output_mode=Exe
77//
8// :?:?: error: root struct of file 'tmp' has no member named 'main'
9// :?:?: note: called from here
8// : error: root struct of file 'tmp' has no member named 'main'
9// : 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 {}
55// target=x86_64-linux
66// output_mode=Exe
77//
8// :?:?: error: 'main' is not marked 'pub'
8// : error: 'main' is not marked 'pub'
99// :1:1: note: declared here
10// :?:?: note: called from here
10// : 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 {
1111 },
1212 });
1313}
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
1527// error
1628// backend=stage2
......@@ -18,3 +30,4 @@ comptime {
1830//
1931// :2:9: error: use of undefined value here causes undefined behavior
2032// :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 {
1515// target=native
1616//
1717// :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 field
19// :?:21: note: types are not available at runtime
18// : note: struct requires comptime because of this field
19// : 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 {
1313// target=native
1414//
1515// :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar'
16// :1:13: note: struct declared here
1716// :2:13: note: struct declared here
17// :1:13: note: struct declared here
1818// :4:18: note: parameter type declared here
test/cases/compile_errors/undefined_as_field_type_is_rejected.zig+8-4
......@@ -1,9 +1,13 @@
1export fn a() void {
2 b();
1const Foo = struct {
2 a: undefined,
3};
4export fn entry1() void {
5 const foo: Foo = undefined;
6 _ = foo;
37}
48
59// error
6// backend=stage2
10// backend=stage1
711// target=native
812//
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 {
2828// target=native
2929//
3030// :9:1: error: union initializer must initialize one field
31// :1:12: note: union declared here
3132// :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field
3233// :14:31: note: additional initializer here
34// :1:12: note: union declared here
3335// :18:21: error: union initializer must initialize one field
3436// :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field
3537// :22:31: note: additional initializer here
36// :1:12: note: union declared here
3738// :5:12: note: union declared here
test/cases/compile_log.0.zig+5
......@@ -15,3 +15,8 @@ fn x() void {}
1515// error
1616//
1717// :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 {}
1414//
1515// :9:5: error: found compile log statement
1616// :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 {
99// run
1010// backend=llvm
1111// target=x86_64-linux-gnu
12// link_libc=1
1213//
1314// f64: 2.000000
1415// 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 {
1414
1515// run
1616// is_test=1
17// backend=llvm
1718//
test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig+1-1
......@@ -5,7 +5,7 @@ pub fn main() void {
55 _ = entry;
66}
77
8// error
8// compile
99// output_mode=Exe
1010// backend=llvm
1111// 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 {
55 _ = entry;
66}
77
8// error
8// compile
99// output_mode=Exe
1010// backend=stage2,llvm
1111// 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 {
66 _ = entry;
77}
88
9// error
9// compile
1010// output_mode=Exe
1111// backend=llvm
1212// 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 {
66 _ = entry;
77}
88
9// error
9// compile
1010// output_mode=Exe
1111// backend=stage2,llvm
1212// 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 {
55 _ = entry;
66}
77
8// error
8// compile
99// output_mode=Exe
1010// backend=stage2,llvm
1111// target=x86_64-linux,x86_64-macos
test/cases/llvm/hello_world.zig+1
......@@ -7,6 +7,7 @@ pub fn main() void {
77// run
88// backend=llvm
99// target=x86_64-linux,x86_64-macos
10// link_libc=1
1011//
1112// hello world!
1213//
test/cases/llvm/pointer_keeps_address_space.zig+1-1
......@@ -5,7 +5,7 @@ pub fn main() void {
55 _ = entry;
66}
77
8// error
8// compile
99// output_mode=Exe
1010// backend=stage2,llvm
1111// 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 {
55 _ = entry;
66}
77
8// error
8// compile
99// output_mode=Exe
1010// backend=stage2,llvm
1111// 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 {
55 _ = entry;
66}
77
8// error
8// compile
99// output_mode=Exe
1010// backend=stage2,llvm
1111// 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 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const TestContext = @import("../src/test.zig").TestContext;
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 });
3const Cases = @import("src/Cases.zig");
1404
5pub fn addCases(ctx: *Cases) !void {
1416 {
1427 const case = ctx.obj("multiline error messages", .{});
143 case.backend = .stage2;
1448
1459 case.addError(
14610 \\comptime {
......@@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void {
17640
17741 {
17842 const case = ctx.obj("isolated carriage return in multiline string literal", .{});
179 case.backend = .stage2;
18043
18144 case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{
18245 ":1:19: error: expected ';' after declaration",
......@@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void {
19558
19659 {
19760 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
20962 case.addError(
21063 \\pub export fn entry() void {
......@@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void {
21669 ":3:12: note: argument to function being called at comptime must be comptime-known",
21770 ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type",
21871 });
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 );
21980 }
22081
22182 {
22283 const case = ctx.obj("astgen failure in file struct", .{});
223 case.backend = .stage2;
224
225 case.addSourceFile("b.zig",
226 \\+
227 );
22884
22985 case.addError(
23086 \\pub export fn entry() void {
......@@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void {
23389 , &[_][]const u8{
23490 ":1:1: error: expected type expression, found '+'",
23591 });
92 case.addSourceFile("b.zig",
93 \\+
94 );
23695 }
23796
23897 {
23998 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
252100 case.addError(
253101 \\const a = @import("a.zig");
......@@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void {
259107 ":4:23: error: value stored in comptime field does not match the default value of the field",
260108 ":2:25: note: default value set here",
261109 });
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 );
262119 }
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
292121 {
293122 const case = ctx.obj("file in multiple modules", .{});
294 case.backend = .stage2;
295
296 case.addSourceFile("foo.zig",
297 \\const dummy = 0;
298 );
299
300123 case.addDepModule("foo", "foo.zig");
301124
302125 case.addError(
......@@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void {
309132 ":1:1: note: root of module root.foo",
310133 ":3:17: note: imported from module root",
311134 });
135 case.addSourceFile("foo.zig",
136 \\const dummy = 0;
137 );
312138 }
313139}
test/link.zig+172-213
......@@ -1,213 +1,172 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const tests = @import("tests.zig");
4
5pub fn addCases(cases: *tests.StandaloneContext) void {
6 cases.addBuildFile("test/link/bss/build.zig", .{
7 .build_modes = false, // we only guarantee zerofill for undefined in Debug
8 });
9
10 cases.addBuildFile("test/link/common_symbols/build.zig", .{
11 .build_modes = true,
12 });
13
14 cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{
15 .build_modes = true,
16 });
17
18 cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{
19 .build_modes = true,
20 });
21
22 cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{
23 .build_modes = true,
24 });
25
26 addWasmCases(cases);
27 addMachOCases(cases);
28}
29
30fn addWasmCases(cases: *tests.StandaloneContext) void {
31 cases.addBuildFile("test/link/wasm/archive/build.zig", .{
32 .build_modes = true,
33 .requires_stage2 = true,
34 });
35
36 cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{
37 .requires_stage2 = true,
38 });
39
40 cases.addBuildFile("test/link/wasm/bss/build.zig", .{
41 .build_modes = false,
42 .requires_stage2 = true,
43 });
44
45 cases.addBuildFile("test/link/wasm/export/build.zig", .{
46 .build_modes = true,
47 .requires_stage2 = true,
48 });
49
50 // TODO: Fix open handle in wasm-linker refraining rename from working on Windows.
51 if (builtin.os.tag != .windows) {
52 cases.addBuildFile("test/link/wasm/export-data/build.zig", .{});
53 }
54
55 cases.addBuildFile("test/link/wasm/extern/build.zig", .{
56 .build_modes = true,
57 .requires_stage2 = true,
58 .use_emulation = true,
59 });
60
61 cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{
62 .build_modes = true,
63 .requires_stage2 = true,
64 });
65
66 cases.addBuildFile("test/link/wasm/function-table/build.zig", .{
67 .build_modes = true,
68 .requires_stage2 = true,
69 });
70
71 cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{
72 .requires_stage2 = true,
73 });
74
75 cases.addBuildFile("test/link/wasm/producers/build.zig", .{
76 .build_modes = true,
77 .requires_stage2 = true,
78 });
79
80 cases.addBuildFile("test/link/wasm/segments/build.zig", .{
81 .build_modes = true,
82 .requires_stage2 = true,
83 });
84
85 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{
86 .build_modes = true,
87 .requires_stage2 = true,
88 });
89
90 cases.addBuildFile("test/link/wasm/type/build.zig", .{
91 .build_modes = true,
92 .requires_stage2 = true,
93 });
94}
95
96fn addMachOCases(cases: *tests.StandaloneContext) void {
97 cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{
98 .build_modes = true,
99 .requires_macos_sdk = true,
100 .requires_symlinks = true,
101 });
102
103 cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{
104 .build_modes = true,
105 .requires_symlinks = true,
106 });
107
108 cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{
109 .build_modes = false,
110 .requires_symlinks = true,
111 });
112
113 cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{
114 .build_modes = true,
115 .requires_macos_sdk = true,
116 .requires_symlinks = true,
117 });
118
119 cases.addBuildFile("test/link/macho/dylib/build.zig", .{
120 .build_modes = true,
121 .requires_symlinks = true,
122 });
123
124 cases.addBuildFile("test/link/macho/empty/build.zig", .{
125 .build_modes = true,
126 .requires_symlinks = true,
127 });
128
129 cases.addBuildFile("test/link/macho/entry/build.zig", .{
130 .build_modes = true,
131 .requires_symlinks = true,
132 });
133
134 cases.addBuildFile("test/link/macho/headerpad/build.zig", .{
135 .build_modes = true,
136 .requires_macos_sdk = true,
137 .requires_symlinks = true,
138 });
139
140 cases.addBuildFile("test/link/macho/linksection/build.zig", .{
141 .build_modes = true,
142 .requires_symlinks = true,
143 });
144
145 cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{
146 .build_modes = true,
147 .requires_macos_sdk = true,
148 .requires_symlinks = true,
149 });
150
151 cases.addBuildFile("test/link/macho/needed_library/build.zig", .{
152 .build_modes = true,
153 .requires_symlinks = true,
154 });
155
156 cases.addBuildFile("test/link/macho/objc/build.zig", .{
157 .build_modes = true,
158 .requires_macos_sdk = true,
159 .requires_symlinks = true,
160 });
161
162 cases.addBuildFile("test/link/macho/objcpp/build.zig", .{
163 .build_modes = true,
164 .requires_macos_sdk = true,
165 .requires_symlinks = true,
166 });
167
168 cases.addBuildFile("test/link/macho/pagezero/build.zig", .{
169 .build_modes = false,
170 .requires_symlinks = true,
171 });
172
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}
1pub const Case = struct {
2 build_root: []const u8,
3 import: type,
4};
5
6pub const cases = [_]Case{
7 .{
8 .build_root = "test/link/bss",
9 .import = @import("link/bss/build.zig"),
10 },
11 .{
12 .build_root = "test/link/common_symbols",
13 .import = @import("link/common_symbols/build.zig"),
14 },
15 .{
16 .build_root = "test/link/common_symbols_alignment",
17 .import = @import("link/common_symbols_alignment/build.zig"),
18 },
19 .{
20 .build_root = "test/link/interdependent_static_c_libs",
21 .import = @import("link/interdependent_static_c_libs/build.zig"),
22 },
23
24 // WASM Cases
25 .{
26 .build_root = "test/link/wasm/archive",
27 .import = @import("link/wasm/archive/build.zig"),
28 },
29 .{
30 .build_root = "test/link/wasm/basic-features",
31 .import = @import("link/wasm/basic-features/build.zig"),
32 },
33 .{
34 .build_root = "test/link/wasm/bss",
35 .import = @import("link/wasm/bss/build.zig"),
36 },
37 .{
38 .build_root = "test/link/wasm/export",
39 .import = @import("link/wasm/export/build.zig"),
40 },
41 .{
42 .build_root = "test/link/wasm/export-data",
43 .import = @import("link/wasm/export-data/build.zig"),
44 },
45 .{
46 .build_root = "test/link/wasm/extern",
47 .import = @import("link/wasm/extern/build.zig"),
48 },
49 .{
50 .build_root = "test/link/wasm/extern-mangle",
51 .import = @import("link/wasm/extern-mangle/build.zig"),
52 },
53 .{
54 .build_root = "test/link/wasm/function-table",
55 .import = @import("link/wasm/function-table/build.zig"),
56 },
57 .{
58 .build_root = "test/link/wasm/infer-features",
59 .import = @import("link/wasm/infer-features/build.zig"),
60 },
61 .{
62 .build_root = "test/link/wasm/producers",
63 .import = @import("link/wasm/producers/build.zig"),
64 },
65 .{
66 .build_root = "test/link/wasm/segments",
67 .import = @import("link/wasm/segments/build.zig"),
68 },
69 .{
70 .build_root = "test/link/wasm/stack_pointer",
71 .import = @import("link/wasm/stack_pointer/build.zig"),
72 },
73 .{
74 .build_root = "test/link/wasm/type",
75 .import = @import("link/wasm/type/build.zig"),
76 },
77
78 // Mach-O Cases
79 .{
80 .build_root = "test/link/macho/bugs/13056",
81 .import = @import("link/macho/bugs/13056/build.zig"),
82 },
83 .{
84 .build_root = "test/link/macho/bugs/13457",
85 .import = @import("link/macho/bugs/13457/build.zig"),
86 },
87 .{
88 .build_root = "test/link/macho/dead_strip",
89 .import = @import("link/macho/dead_strip/build.zig"),
90 },
91 .{
92 .build_root = "test/link/macho/dead_strip_dylibs",
93 .import = @import("link/macho/dead_strip_dylibs/build.zig"),
94 },
95 .{
96 .build_root = "test/link/macho/dylib",
97 .import = @import("link/macho/dylib/build.zig"),
98 },
99 .{
100 .build_root = "test/link/macho/empty",
101 .import = @import("link/macho/empty/build.zig"),
102 },
103 .{
104 .build_root = "test/link/macho/entry",
105 .import = @import("link/macho/entry/build.zig"),
106 },
107 .{
108 .build_root = "test/link/macho/headerpad",
109 .import = @import("link/macho/headerpad/build.zig"),
110 },
111 .{
112 .build_root = "test/link/macho/linksection",
113 .import = @import("link/macho/linksection/build.zig"),
114 },
115 .{
116 .build_root = "test/link/macho/needed_framework",
117 .import = @import("link/macho/needed_framework/build.zig"),
118 },
119 .{
120 .build_root = "test/link/macho/needed_library",
121 .import = @import("link/macho/needed_library/build.zig"),
122 },
123 .{
124 .build_root = "test/link/macho/objc",
125 .import = @import("link/macho/objc/build.zig"),
126 },
127 .{
128 .build_root = "test/link/macho/objcpp",
129 .import = @import("link/macho/objcpp/build.zig"),
130 },
131 .{
132 .build_root = "test/link/macho/pagezero",
133 .import = @import("link/macho/pagezero/build.zig"),
134 },
135 .{
136 .build_root = "test/link/macho/search_strategy",
137 .import = @import("link/macho/search_strategy/build.zig"),
138 },
139 .{
140 .build_root = "test/link/macho/stack_size",
141 .import = @import("link/macho/stack_size/build.zig"),
142 },
143 .{
144 .build_root = "test/link/macho/strict_validation",
145 .import = @import("link/macho/strict_validation/build.zig"),
146 },
147 .{
148 .build_root = "test/link/macho/tls",
149 .import = @import("link/macho/tls/build.zig"),
150 },
151 .{
152 .build_root = "test/link/macho/unwind_info",
153 .import = @import("link/macho/unwind_info/build.zig"),
154 },
155 // TODO: re-enable this test. It currently has some incompatibilities with
156 // the new build system API. In particular, it depends on installing the build
157 // artifacts, which should be unnecessary, and it has a custom build step that
158 // prints directly to stderr instead of failing the step with an error message.
159 //.{
160 // .build_root = "test/link/macho/uuid",
161 // .import = @import("link/macho/uuid/build.zig"),
162 //},
163
164 .{
165 .build_root = "test/link/macho/weak_library",
166 .import = @import("link/macho/weak_library/build.zig"),
167 },
168 .{
169 .build_root = "test/link/macho/weak_framework",
170 .import = @import("link/macho/weak_framework/build.zig"),
171 },
172};
test/link/bss/build.zig+3-3
......@@ -1,17 +1,17 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
54 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
66
77 const exe = b.addExecutable(.{
88 .name = "bss",
99 .root_source_file = .{ .path = "main.zig" },
10 .optimize = optimize,
10 .optimize = .Debug,
1111 });
12 b.default_step.dependOn(&exe.step);
1312
1413 const run = exe.run();
1514 run.expectStdOutEqual("0, 1, 0\n");
15
1616 test_step.dependOn(&run.step);
1717}
test/link/bss/main.zig+1-1
......@@ -1,7 +1,7 @@
11const std = @import("std");
22
33// Stress test zerofill layout
4var buffer: [0x1000000]u64 = undefined;
4var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000;
55
66pub fn main() anyerror!void {
77 buffer[0x10] = 1;
test/link/common_symbols/build.zig+10-3
......@@ -1,8 +1,16 @@
11const std = @import("std");
22
33pub 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 {
614 const lib_a = b.addStaticLibrary(.{
715 .name = "a",
816 .optimize = optimize,
......@@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void {
1624 });
1725 test_exe.linkLibrary(lib_a);
1826
19 const test_step = b.step("test", "Test it");
20 test_step.dependOn(&test_exe.step);
27 test_step.dependOn(&test_exe.run().step);
2128}
test/link/common_symbols_alignment/build.zig+11-6
......@@ -1,23 +1,28 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
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}
612
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
714 const lib_a = b.addStaticLibrary(.{
815 .name = "a",
916 .optimize = optimize,
10 .target = target,
17 .target = .{},
1118 });
1219 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});
1320
1421 const test_exe = b.addTest(.{
1522 .root_source_file = .{ .path = "main.zig" },
1623 .optimize = optimize,
17 .target = target,
1824 });
1925 test_exe.linkLibrary(lib_a);
2026
21 const test_step = b.step("test", "Test it");
22 test_step.dependOn(&test_exe.step);
27 test_step.dependOn(&test_exe.run().step);
2328}
test/link/interdependent_static_c_libs/build.zig+12-7
......@@ -1,13 +1,20 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
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}
612
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
714 const lib_a = b.addStaticLibrary(.{
815 .name = "a",
916 .optimize = optimize,
10 .target = target,
17 .target = .{},
1118 });
1219 lib_a.addCSourceFile("a.c", &[_][]const u8{});
1320 lib_a.addIncludePath(".");
......@@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void {
1522 const lib_b = b.addStaticLibrary(.{
1623 .name = "b",
1724 .optimize = optimize,
18 .target = target,
25 .target = .{},
1926 });
2027 lib_b.addCSourceFile("b.c", &[_][]const u8{});
2128 lib_b.addIncludePath(".");
......@@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void {
2330 const test_exe = b.addTest(.{
2431 .root_source_file = .{ .path = "main.zig" },
2532 .optimize = optimize,
26 .target = target,
2733 });
2834 test_exe.linkLibrary(lib_a);
2935 test_exe.linkLibrary(lib_b);
3036 test_exe.addIncludePath(".");
3137
32 const test_step = b.step("test", "Test it");
33 test_step.dependOn(&test_exe.step);
38 test_step.dependOn(&test_exe.run().step);
3439}
test/link/macho/bugs/13056/build.zig+12-4
......@@ -1,20 +1,28 @@
11const std = @import("std");
22
3pub const requires_macos_sdk = true;
4pub const requires_symlinks = true;
5
36pub 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 {
617 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
718 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
819 const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse
920 @panic("macOS SDK is required to run the test");
1021
11 const test_step = b.step("test", "Test the program");
12
1322 const exe = b.addExecutable(.{
1423 .name = "test",
1524 .optimize = optimize,
1625 });
17 b.default_step.dependOn(&exe.step);
1826 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);
1927 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);
2028 exe.addCSourceFile("test.cpp", &.{
test/link/macho/bugs/13457/build.zig+16-4
......@@ -1,10 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
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}
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
918 const exe = b.addExecutable(.{
1019 .name = "test",
......@@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void {
1322 .target = target,
1423 });
1524
16 const run = exe.runEmulatable();
25 const run = b.addRunArtifact(exe);
26 run.skip_foreign_checks = true;
27 run.expectStdOutEqual("");
28
1729 test_step.dependOn(&run.step);
1830}
test/link/macho/dead_strip/build.zig+10-7
......@@ -1,17 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
6 const optimize: std.builtin.OptimizeMode = .Debug;
57 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
68
79 const test_step = b.step("test", "Test the program");
8 test_step.dependOn(b.getInstallStep());
10 b.default_step = test_step;
911
1012 {
1113 // 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();
1517 check.checkInSymtab();
1618 check.checkNext("{*} (__TEXT,__text) external _iAmUnused");
1719
......@@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void {
2224
2325 {
2426 // 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");
2628 exe.link_gc_sections = true;
2729
28 const check = exe.checkObject(.macho);
30 const check = exe.checkObject();
2931 check.checkInSymtab();
3032 check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused");
3133
......@@ -39,9 +41,10 @@ fn createScenario(
3941 b: *std.Build,
4042 optimize: std.builtin.OptimizeMode,
4143 target: std.zig.CrossTarget,
44 name: []const u8,
4245) *std.Build.CompileStep {
4346 const exe = b.addExecutable(.{
44 .name = "test",
47 .name = name,
4548 .optimize = optimize,
4649 .target = target,
4750 });
test/link/macho/dead_strip_dylibs/build.zig+22-10
......@@ -1,16 +1,24 @@
11const std = @import("std");
22
3pub const requires_macos_sdk = true;
4pub const requires_symlinks = true;
5
36pub 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");
7 test_step.dependOn(b.getInstallStep());
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}
815
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
917 {
1018 // 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();
1422 check.checkStart("cmd LOAD_DYLIB");
1523 check.checkNext("name {*}Cocoa");
1624
......@@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void {
2533
2634 {
2735 // 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");
2937 exe.dead_strip_dylibs = true;
3038
31 const run_cmd = exe.run();
32 run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail
39 const run_cmd = b.addRunArtifact(exe);
40 run_cmd.expectExitCode(@bitCast(u8, @as(i8, -2))); // should fail
3341 test_step.dependOn(&run_cmd.step);
3442 }
3543}
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 {
3850 const exe = b.addExecutable(.{
39 .name = "test",
51 .name = name,
4052 .optimize = optimize,
4153 });
4254 exe.addCSourceFile("main.c", &[0][]const u8{});
test/link/macho/dylib/build.zig+20-11
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
1018 const dylib = b.addSharedLibrary(.{
1119 .name = "a",
......@@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void {
1523 });
1624 dylib.addCSourceFile("a.c", &.{});
1725 dylib.linkLibC();
18 dylib.install();
1926
20 const check_dylib = dylib.checkObject(.macho);
27 const check_dylib = dylib.checkObject();
2128 check_dylib.checkStart("cmd ID_DYLIB");
2229 check_dylib.checkNext("name @rpath/liba.dylib");
2330 check_dylib.checkNext("timestamp 2");
......@@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void {
3340 });
3441 exe.addCSourceFile("main.c", &.{});
3542 exe.linkSystemLibrary("a");
43 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
44 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
3645 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();
4148 check_exe.checkStart("cmd LOAD_DYLIB");
4249 check_exe.checkNext("name @rpath/liba.dylib");
4350 check_exe.checkNext("timestamp 2");
......@@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void {
4552 check_exe.checkNext("compatibility version 10000");
4653
4754 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
5060 const run = check_exe.runAndCompare();
51 run.cwd = b.pathFromRoot(".");
5261 run.expectStdOutEqual("Hello world");
5362 test_step.dependOn(&run.step);
5463}
test/link/macho/empty/build.zig+14-5
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test the program");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
1018 const exe = b.addExecutable(.{
1119 .name = "test",
......@@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void {
1624 exe.addCSourceFile("empty.c", &[0][]const u8{});
1725 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;
2029 run_cmd.expectStdOutEqual("Hello!\n");
2130 test_step.dependOn(&run_cmd.step);
2231}
test/link/macho/entry/build.zig+11-4
......@@ -1,11 +1,18 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub 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");
7 test_step.dependOn(b.getInstallStep());
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}
814
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
916 const exe = b.addExecutable(.{
1017 .name = "main",
1118 .optimize = optimize,
......@@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void {
1522 exe.linkLibC();
1623 exe.entry_symbol_name = "_non_main";
1724
18 const check_exe = exe.checkObject(.macho);
25 const check_exe = exe.checkObject();
1926
2027 check_exe.checkStart("segname __TEXT");
2128 check_exe.checkNext("vmaddr {vmaddr}");
test/link/macho/headerpad/build.zig+25-13
......@@ -1,18 +1,26 @@
11const std = @import("std");
22const builtin = @import("builtin");
33
4pub const requires_symlinks = true;
5pub const requires_macos_sdk = true;
6
47pub 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");
8 test_step.dependOn(b.getInstallStep());
11 add(b, test_step, .Debug);
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 {
1018 {
1119 // Test -headerpad_max_install_names
12 const exe = simpleExe(b, optimize);
20 const exe = simpleExe(b, optimize, "headerpad_max_install_names");
1321 exe.headerpad_max_install_names = true;
1422
15 const check = exe.checkObject(.macho);
23 const check = exe.checkObject();
1624 check.checkStart("sectname __text");
1725 check.checkNext("offset {offset}");
1826
......@@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void {
3442
3543 {
3644 // Test -headerpad
37 const exe = simpleExe(b, optimize);
45 const exe = simpleExe(b, optimize, "headerpad");
3846 exe.headerpad_size = 0x10000;
3947
40 const check = exe.checkObject(.macho);
48 const check = exe.checkObject();
4149 check.checkStart("sectname __text");
4250 check.checkNext("offset {offset}");
4351 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
......@@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void {
5058
5159 {
5260 // Test both flags with -headerpad overriding -headerpad_max_install_names
53 const exe = simpleExe(b, optimize);
61 const exe = simpleExe(b, optimize, "headerpad_overriding");
5462 exe.headerpad_max_install_names = true;
5563 exe.headerpad_size = 0x10000;
5664
57 const check = exe.checkObject(.macho);
65 const check = exe.checkObject();
5866 check.checkStart("sectname __text");
5967 check.checkNext("offset {offset}");
6068 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
......@@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void {
6775
6876 {
6977 // 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");
7179 exe.headerpad_size = 0x1000;
7280 exe.headerpad_max_install_names = true;
7381
74 const check = exe.checkObject(.macho);
82 const check = exe.checkObject();
7583 check.checkStart("sectname __text");
7684 check.checkNext("offset {offset}");
7785
......@@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void {
92100 }
93101}
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 {
96108 const exe = b.addExecutable(.{
97 .name = "main",
109 .name = name,
98110 .optimize = optimize,
99111 });
100112 exe.addCSourceFile("main.c", &.{});
test/link/macho/linksection/build.zig+13-5
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = std.zig.CrossTarget{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target = std.zig.CrossTarget{ .os_tag = .macos };
917
1018 const obj = b.addObject(.{
1119 .name = "test",
......@@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void {
1422 .target = target,
1523 });
1624
17 const check = obj.checkObject(.macho);
25 const check = obj.checkObject();
1826
1927 check.checkInSymtab();
2028 check.checkNext("{*} (__DATA,__TestGlobal) external _test_global");
test/link/macho/needed_framework/build.zig+12-4
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
36pub 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");
7 test_step.dependOn(b.getInstallStep());
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}
815
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
917 // -dead_strip_dylibs
1018 // -needed_framework Cocoa
1119 const exe = b.addExecutable(.{
......@@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void {
1725 exe.linkFrameworkNeeded("Cocoa");
1826 exe.dead_strip_dylibs = true;
1927
20 const check = exe.checkObject(.macho);
28 const check = exe.checkObject();
2129 check.checkStart("cmd LOAD_DYLIB");
2230 check.checkNext("name {*}Cocoa");
2331 test_step.dependOn(&check.step);
test/link/macho/needed_library/build.zig+16-8
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test the program");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
1018 const dylib = b.addSharedLibrary(.{
1119 .name = "a",
......@@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void {
1523 });
1624 dylib.addCSourceFile("a.c", &.{});
1725 dylib.linkLibC();
18 dylib.install();
1926
2027 // -dead_strip_dylibs
2128 // -needed-la
......@@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void {
2734 exe.addCSourceFile("main.c", &[0][]const u8{});
2835 exe.linkLibC();
2936 exe.linkSystemLibraryNeeded("a");
30 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
31 exe.addRPath(b.pathFromRoot("zig-out/lib"));
37 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
38 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
3239 exe.dead_strip_dylibs = true;
3340
34 const check = exe.checkObject(.macho);
41 const check = exe.checkObject();
3542 check.checkStart("cmd LOAD_DYLIB");
3643 check.checkNext("name @rpath/liba.dylib");
3744
3845 const run_cmd = check.runAndCompare();
46 run_cmd.expectStdOutEqual("");
3947 test_step.dependOn(&run_cmd.step);
4048}
test/link/macho/objc/build.zig+14-3
......@@ -1,10 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
36pub 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 {
817 const exe = b.addExecutable(.{
918 .name = "test",
1019 .optimize = optimize,
......@@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void {
1726 // populate paths to the sysroot here.
1827 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("");
2132 test_step.dependOn(&run_cmd.step);
2233}
test/link/macho/objcpp/build.zig+11-2
......@@ -1,10 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
36pub 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 {
817 const exe = b.addExecutable(.{
918 .name = "test",
1019 .optimize = optimize,
test/link/macho/pagezero/build.zig+8-6
......@@ -1,11 +1,13 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
9 const optimize: std.builtin.OptimizeMode = .Debug;
10 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
911
1012 {
1113 const exe = b.addExecutable(.{
......@@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void {
1719 exe.linkLibC();
1820 exe.pagezero_size = 0x4000;
1921
20 const check = exe.checkObject(.macho);
22 const check = exe.checkObject();
2123 check.checkStart("LC 0");
2224 check.checkNext("segname __PAGEZERO");
2325 check.checkNext("vmaddr 0");
......@@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void {
3941 exe.linkLibC();
4042 exe.pagezero_size = 0;
4143
42 const check = exe.checkObject(.macho);
44 const check = exe.checkObject();
4345 check.checkStart("LC 0");
4446 check.checkNext("segname __TEXT");
4547 check.checkNext("vmaddr 0");
test/link/macho/search_strategy/build.zig+26-20
......@@ -1,34 +1,41 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
1018 {
1119 // -search_dylibs_first
12 const exe = createScenario(b, optimize, target);
20 const exe = createScenario(b, optimize, target, "search_dylibs_first");
1321 exe.search_strategy = .dylibs_first;
1422
15 const check = exe.checkObject(.macho);
23 const check = exe.checkObject();
1624 check.checkStart("cmd LOAD_DYLIB");
17 check.checkNext("name @rpath/liba.dylib");
25 check.checkNext("name @rpath/libsearch_dylibs_first.dylib");
1826
1927 const run = check.runAndCompare();
20 run.cwd = b.pathFromRoot(".");
2128 run.expectStdOutEqual("Hello world");
2229 test_step.dependOn(&run.step);
2330 }
2431
2532 {
2633 // -search_paths_first
27 const exe = createScenario(b, optimize, target);
34 const exe = createScenario(b, optimize, target, "search_paths_first");
2835 exe.search_strategy = .paths_first;
2936
30 const run = std.Build.EmulatableRunStep.create(b, "run", exe);
31 run.cwd = b.pathFromRoot(".");
37 const run = b.addRunArtifact(exe);
38 run.skip_foreign_checks = true;
3239 run.expectStdOutEqual("Hello world");
3340 test_step.dependOn(&run.step);
3441 }
......@@ -38,9 +45,10 @@ fn createScenario(
3845 b: *std.Build,
3946 optimize: std.builtin.OptimizeMode,
4047 target: std.zig.CrossTarget,
48 name: []const u8,
4149) *std.Build.CompileStep {
4250 const static = b.addStaticLibrary(.{
43 .name = "a",
51 .name = name,
4452 .optimize = optimize,
4553 .target = target,
4654 });
......@@ -49,10 +57,9 @@ fn createScenario(
4957 static.override_dest_dir = std.Build.InstallDir{
5058 .custom = "static",
5159 };
52 static.install();
5360
5461 const dylib = b.addSharedLibrary(.{
55 .name = "a",
62 .name = name,
5663 .version = .{ .major = 1, .minor = 0 },
5764 .optimize = optimize,
5865 .target = target,
......@@ -62,18 +69,17 @@ fn createScenario(
6269 dylib.override_dest_dir = std.Build.InstallDir{
6370 .custom = "dynamic",
6471 };
65 dylib.install();
6672
6773 const exe = b.addExecutable(.{
68 .name = "main",
74 .name = name,
6975 .optimize = optimize,
7076 .target = target,
7177 });
7278 exe.addCSourceFile("main.c", &.{});
73 exe.linkSystemLibraryName("a");
79 exe.linkSystemLibraryName(name);
7480 exe.linkLibC();
75 exe.addLibraryPath(b.pathFromRoot("zig-out/static"));
76 exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic"));
77 exe.addRPath(b.pathFromRoot("zig-out/dynamic"));
81 exe.addLibraryPathDirectorySource(static.getOutputDirectorySource());
82 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
83 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
7884 return exe;
7985}
test/link/macho/stack_size/build.zig+14-5
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
1018 const exe = b.addExecutable(.{
1119 .name = "main",
......@@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void {
1624 exe.linkLibC();
1725 exe.stack_size = 0x100000000;
1826
19 const check_exe = exe.checkObject(.macho);
27 const check_exe = exe.checkObject();
2028 check_exe.checkStart("cmd MAIN");
2129 check_exe.checkNext("stacksize 100000000");
2230
2331 const run = check_exe.runAndCompare();
32 run.expectStdOutEqual("");
2433 test_step.dependOn(&run.step);
2534}
test/link/macho/strict_validation/build.zig+13-5
......@@ -1,12 +1,20 @@
11const std = @import("std");
22const builtin = @import("builtin");
33
4pub const requires_symlinks = true;
5
46pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
79
8 const test_step = b.step("test", "Test");
9 test_step.dependOn(b.getInstallStep());
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 };
1018
1119 const exe = b.addExecutable(.{
1220 .name = "main",
......@@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void {
1624 });
1725 exe.linkLibC();
1826
19 const check_exe = exe.checkObject(.macho);
27 const check_exe = exe.checkObject();
2028
2129 check_exe.checkStart("cmd SEGMENT_64");
2230 check_exe.checkNext("segname __LINKEDIT");
test/link/macho/tls/build.zig+16-3
......@@ -1,7 +1,18 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub 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 {
516 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
617
718 const lib = b.addSharedLibrary(.{
......@@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void {
2132 test_exe.linkLibrary(lib);
2233 test_exe.linkLibC();
2334
24 const test_step = b.step("test", "Test it");
25 test_step.dependOn(&test_exe.step);
35 const run = test_exe.run();
36 run.skip_foreign_checks = true;
37
38 test_step.dependOn(&run.step);
2639}
test/link/macho/unwind_info/build.zig+19-8
......@@ -1,14 +1,23 @@
11const std = @import("std");
22const builtin = @import("builtin");
33
4pub const requires_symlinks = true;
5
46pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
7 const test_step = b.step("test", "Test it");
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);
11 testUnwindInfo(b, test_step, optimize, target, true);
19 testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip");
20 testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip");
1221}
1322
1423fn testUnwindInfo(
......@@ -17,11 +26,12 @@ fn testUnwindInfo(
1726 optimize: std.builtin.OptimizeMode,
1827 target: std.zig.CrossTarget,
1928 dead_strip: bool,
29 name: []const u8,
2030) void {
21 const exe = createScenario(b, optimize, target);
31 const exe = createScenario(b, optimize, target, name);
2232 exe.link_gc_sections = dead_strip;
2333
24 const check = exe.checkObject(.macho);
34 const check = exe.checkObject();
2535 check.checkStart("segname __TEXT");
2636 check.checkNext("sectname __gcc_except_tab");
2737 check.checkNext("sectname __unwind_info");
......@@ -54,9 +64,10 @@ fn createScenario(
5464 b: *std.Build,
5565 optimize: std.builtin.OptimizeMode,
5666 target: std.zig.CrossTarget,
67 name: []const u8,
5768) *std.Build.CompileStep {
5869 const exe = b.addExecutable(.{
59 .name = "test",
70 .name = name,
6071 .optimize = optimize,
6172 .target = target,
6273 });
test/link/macho/uuid/build.zig+23-62
......@@ -1,14 +1,16 @@
11const std = @import("std");
2const Builder = std.Build.Builder;
32const CompileStep = std.Build.CompileStep;
43const FileSource = std.Build.FileSource;
54const Step = std.Build.Step;
65
6pub const requires_symlinks = true;
7
78pub fn build(b: *std.Build) void {
89 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.
1214 const aarch64_macos = std.zig.CrossTarget{
1315 .cpu_arch = .aarch64,
1416 .os_tag = .macos,
......@@ -38,13 +40,15 @@ fn testUuid(
3840 // stay the same across builds.
3941 {
4042 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";
4245 install_step.step.dependOn(&dylib.step);
4346 }
4447 {
4548 const dylib = simpleDylib(b, optimize, target);
4649 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";
4852 install_step.step.dependOn(&dylib.step);
4953 }
5054
......@@ -68,86 +72,43 @@ fn simpleDylib(
6872 return dylib;
6973}
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
11475const CompareUuid = struct {
11576 pub const base_id = .custom;
11677
11778 step: Step,
118 builder: *Builder,
11979 lhs: []const u8,
12080 rhs: []const u8,
12181
122 pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid {
123 const self = builder.allocator.create(CompareUuid) catch @panic("OOM");
82 pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid {
83 const self = owner.allocator.create(CompareUuid) catch @panic("OOM");
12484 self.* = CompareUuid{
125 .builder = builder,
126 .step = Step.init(
127 .custom,
128 builder.fmt("compare uuid: {s} and {s}", .{
85 .step = Step.init(.{
86 .id = base_id,
87 .name = owner.fmt("compare uuid: {s} and {s}", .{
12988 lhs,
13089 rhs,
13190 }),
132 builder.allocator,
133 make,
134 ),
91 .owner = owner,
92 .makeFn = make,
93 }),
13594 .lhs = lhs,
13695 .rhs = rhs,
13796 };
13897 return self;
13998 }
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;
142103 const self = @fieldParentPtr(CompareUuid, "step", step);
143 const gpa = self.builder.allocator;
104 const gpa = b.allocator;
144105
145106 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);
147108 try parseUuid(gpa, lhs_path, &lhs_uuid);
148109
149110 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);
151112 try parseUuid(gpa, rhs_path, &rhs_uuid);
152113
153114 try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid);
test/link/macho/weak_framework/build.zig+12-4
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4pub const requires_macos_sdk = true;
5
36pub 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");
7 test_step.dependOn(b.getInstallStep());
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}
815
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
917 const exe = b.addExecutable(.{
1018 .name = "test",
1119 .optimize = optimize,
......@@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void {
1422 exe.linkLibC();
1523 exe.linkFrameworkWeak("Cocoa");
1624
17 const check = exe.checkObject(.macho);
25 const check = exe.checkObject();
1826 check.checkStart("cmd LOAD_WEAK_DYLIB");
1927 check.checkNext("name {*}Cocoa");
2028 test_step.dependOn(&check.step);
test/link/macho/weak_library/build.zig+15-7
......@@ -1,11 +1,19 @@
11const std = @import("std");
22
3pub const requires_symlinks = true;
4
35pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
68
7 const test_step = b.step("test", "Test the program");
8 test_step.dependOn(b.getInstallStep());
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 {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
917
1018 const dylib = b.addSharedLibrary(.{
1119 .name = "a",
......@@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void {
2533 exe.addCSourceFile("main.c", &[0][]const u8{});
2634 exe.linkLibC();
2735 exe.linkSystemLibraryWeak("a");
28 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
29 exe.addRPath(b.pathFromRoot("zig-out/lib"));
36 exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
37 exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
3038
31 const check = exe.checkObject(.macho);
39 const check = exe.checkObject();
3240 check.checkStart("cmd LOAD_WEAK_DYLIB");
3341 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 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());
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}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
716 // The code in question will pull-in compiler-rt,
817 // and therefore link with its archive file.
918 const lib = b.addSharedLibrary(.{
1019 .name = "main",
1120 .root_source_file = .{ .path = "main.zig" },
12 .optimize = b.standardOptimizeOption(.{}),
21 .optimize = optimize,
1322 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
1423 });
1524 lib.use_llvm = false;
1625 lib.use_lld = false;
1726 lib.strip = false;
1827
19 const check = lib.checkObject(.wasm);
28 const check = lib.checkObject();
2029 check.checkStart("Section custom");
2130 check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved
2231
test/link/wasm/basic-features/build.zig+5-2
......@@ -1,11 +1,13 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
46 // Library with explicitly set cpu features
57 const lib = b.addSharedLibrary(.{
68 .name = "lib",
79 .root_source_file = .{ .path = "main.zig" },
8 .optimize = b.standardOptimizeOption(.{}),
10 .optimize = .Debug,
911 .target = .{
1012 .cpu_arch = .wasm32,
1113 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
......@@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void {
1719 lib.use_lld = false;
1820
1921 // 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();
2123 check.checkStart("name target_features");
2224 check.checkNext("features 1");
2325 check.checkNext("+ atomics");
2426
2527 const test_step = b.step("test", "Run linker test");
2628 test_step.dependOn(&check.step);
29 b.default_step = test_step;
2730}
test/link/wasm/bss/build.zig+6-3
......@@ -1,14 +1,16 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
46 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());
7 b.default_step = test_step;
68
79 const lib = b.addSharedLibrary(.{
810 .name = "lib",
911 .root_source_file = .{ .path = "lib.zig" },
1012 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
13 .optimize = .Debug,
1214 });
1315 lib.use_llvm = false;
1416 lib.use_lld = false;
......@@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void {
1719 lib.import_memory = true;
1820 lib.install();
1921
20 const check_lib = lib.checkObject(.wasm);
22 const check_lib = lib.checkObject();
2123
2224 // since we import memory, make sure it exists with the correct naming
2325 check_lib.checkStart("Section import");
......@@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void {
3638 check_lib.checkNext("name .rodata");
3739 check_lib.checkNext("index 1"); // bss section always last
3840 check_lib.checkNext("name .bss");
41
3942 test_step.dependOn(&check_lib.step);
4043}
test/link/wasm/export-data/build.zig+7-2
......@@ -2,7 +2,12 @@ const std = @import("std");
22
33pub fn build(b: *std.Build) void {
44 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
712 const lib = b.addSharedLibrary(.{
813 .name = "lib",
......@@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void {
1419 lib.export_symbol_names = &.{ "foo", "bar" };
1520 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
1924 check_lib.checkStart("Section global");
2025 check_lib.checkNext("entries 3");
test/link/wasm/export/build.zig+14-5
......@@ -1,8 +1,18 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub 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 {
616 const no_export = b.addSharedLibrary(.{
717 .name = "no-export",
818 .root_source_file = .{ .path = "main.zig" },
......@@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void {
3242 force_export.use_llvm = false;
3343 force_export.use_lld = false;
3444
35 const check_no_export = no_export.checkObject(.wasm);
45 const check_no_export = no_export.checkObject();
3646 check_no_export.checkStart("Section export");
3747 check_no_export.checkNext("entries 1");
3848 check_no_export.checkNext("name memory");
3949 check_no_export.checkNext("kind memory");
4050
41 const check_dynamic_export = dynamic_export.checkObject(.wasm);
51 const check_dynamic_export = dynamic_export.checkObject();
4252 check_dynamic_export.checkStart("Section export");
4353 check_dynamic_export.checkNext("entries 2");
4454 check_dynamic_export.checkNext("name foo");
4555 check_dynamic_export.checkNext("kind function");
4656
47 const check_force_export = force_export.checkObject(.wasm);
57 const check_force_export = force_export.checkObject();
4858 check_force_export.checkStart("Section export");
4959 check_force_export.checkNext("entries 2");
5060 check_force_export.checkNext("name foo");
5161 check_force_export.checkNext("kind function");
5262
53 const test_step = b.step("test", "Run linker test");
5463 test_step.dependOn(&check_no_export.step);
5564 test_step.dependOn(&check_dynamic_export.step);
5665 test_step.dependOn(&check_force_export.step);
test/link/wasm/extern-mangle/build.zig+11-5
......@@ -1,20 +1,26 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());
4 const test_step = b.step("test", "Test it");
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 {
714 const lib = b.addSharedLibrary(.{
815 .name = "lib",
916 .root_source_file = .{ .path = "lib.zig" },
1017 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
18 .optimize = optimize,
1219 });
1320 lib.import_symbols = true; // import `a` and `b`
1421 lib.rdynamic = true; // export `foo`
15 lib.install();
1622
17 const check_lib = lib.checkObject(.wasm);
23 const check_lib = lib.checkObject();
1824 check_lib.checkStart("Section import");
1925 check_lib.checkNext("entries 2"); // a.hello & b.hello
2026 check_lib.checkNext("module a");
test/link/wasm/extern/build.zig+15-3
......@@ -1,19 +1,31 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub 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 {
416 const exe = b.addExecutable(.{
517 .name = "extern",
618 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
19 .optimize = optimize,
820 .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },
921 });
1022 exe.addCSourceFile("foo.c", &.{});
1123 exe.use_llvm = false;
1224 exe.use_lld = false;
1325
14 const run = exe.runEmulatable();
26 const run = b.addRunArtifact(exe);
27 run.skip_foreign_checks = true;
1528 run.expectStdOutEqual("Result: 30");
1629
17 const test_step = b.step("test", "Run linker test");
1830 test_step.dependOn(&run.step);
1931}
test/link/wasm/function-table/build.zig+16-9
......@@ -1,13 +1,20 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub 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");
7 test_step.dependOn(b.getInstallStep());
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}
814
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
916 const import_table = b.addSharedLibrary(.{
10 .name = "lib",
17 .name = "import_table",
1118 .root_source_file = .{ .path = "lib.zig" },
1219 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
1320 .optimize = optimize,
......@@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void {
1724 import_table.import_table = true;
1825
1926 const export_table = b.addSharedLibrary(.{
20 .name = "lib",
27 .name = "export_table",
2128 .root_source_file = .{ .path = "lib.zig" },
2229 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
2330 .optimize = optimize,
......@@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void {
2734 export_table.export_table = true;
2835
2936 const regular_table = b.addSharedLibrary(.{
30 .name = "lib",
37 .name = "regular_table",
3138 .root_source_file = .{ .path = "lib.zig" },
3239 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
3340 .optimize = optimize,
......@@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void {
3542 regular_table.use_llvm = false;
3643 regular_table.use_lld = false;
3744
38 const check_import = import_table.checkObject(.wasm);
39 const check_export = export_table.checkObject(.wasm);
40 const check_regular = regular_table.checkObject(.wasm);
45 const check_import = import_table.checkObject();
46 const check_export = export_table.checkObject();
47 const check_regular = regular_table.checkObject();
4148
4249 check_import.checkStart("Section import");
4350 check_import.checkNext("entries 1");
test/link/wasm/infer-features/build.zig+6-5
......@@ -1,12 +1,12 @@
11const std = @import("std");
22
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
3pub const requires_stage2 = true;
54
5pub fn build(b: *std.Build) void {
66 // Wasm Object file which we will use to infer the features from
77 const c_obj = b.addObject(.{
88 .name = "c_obj",
9 .optimize = optimize,
9 .optimize = .Debug,
1010 .target = .{
1111 .cpu_arch = .wasm32,
1212 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
......@@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void {
2020 const lib = b.addSharedLibrary(.{
2121 .name = "lib",
2222 .root_source_file = .{ .path = "main.zig" },
23 .optimize = optimize,
23 .optimize = .Debug,
2424 .target = .{
2525 .cpu_arch = .wasm32,
2626 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
......@@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void {
3232 lib.addObject(c_obj);
3333
3434 // Verify the result contains the features from the C Object file.
35 const check = lib.checkObject(.wasm);
35 const check = lib.checkObject();
3636 check.checkStart("name target_features");
3737 check.checkNext("features 7");
3838 check.checkNext("+ atomics");
......@@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void {
4545
4646 const test_step = b.step("test", "Run linker test");
4747 test_step.dependOn(&check.step);
48 b.default_step = test_step;
4849}
test/link/wasm/producers/build.zig+14-7
......@@ -1,26 +1,33 @@
11const std = @import("std");
22const builtin = @import("builtin");
33
4pub const requires_stage2 = true;
5
46pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test");
6 test_step.dependOn(b.getInstallStep());
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}
715
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
817 const lib = b.addSharedLibrary(.{
918 .name = "lib",
1019 .root_source_file = .{ .path = "lib.zig" },
1120 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 .optimize = b.standardOptimizeOption(.{}),
21 .optimize = optimize,
1322 });
1423 lib.use_llvm = false;
1524 lib.use_lld = false;
1625 lib.strip = false;
1726 lib.install();
1827
19 const zig_version = builtin.zig_version;
20 var version_buf: [100]u8 = undefined;
21 const version_fmt = std.fmt.bufPrint(&version_buf, "version {}", .{zig_version}) catch unreachable;
28 const version_fmt = "version " ++ builtin.zig_version_string;
2229
23 const check_lib = lib.checkObject(.wasm);
30 const check_lib = lib.checkObject();
2431 check_lib.checkStart("name producers");
2532 check_lib.checkNext("fields 2");
2633 check_lib.checkNext("field_name language");
test/link/wasm/segments/build.zig+13-4
......@@ -1,21 +1,30 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());
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}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
716 const lib = b.addSharedLibrary(.{
817 .name = "lib",
918 .root_source_file = .{ .path = "lib.zig" },
1019 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
20 .optimize = optimize,
1221 });
1322 lib.use_llvm = false;
1423 lib.use_lld = false;
1524 lib.strip = false;
1625 lib.install();
1726
18 const check_lib = lib.checkObject(.wasm);
27 const check_lib = lib.checkObject();
1928 check_lib.checkStart("Section data");
2029 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 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());
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}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
716 const lib = b.addSharedLibrary(.{
817 .name = "lib",
918 .root_source_file = .{ .path = "lib.zig" },
1019 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
20 .optimize = optimize,
1221 });
1322 lib.use_llvm = false;
1423 lib.use_lld = false;
......@@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void {
1625 lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size
1726 lib.install();
1827
19 const check_lib = lib.checkObject(.wasm);
28 const check_lib = lib.checkObject();
2029
2130 // ensure global exists and its initial value is equal to explitic stack size
2231 check_lib.checkStart("Section global");
test/link/wasm/type/build.zig+13-4
......@@ -1,21 +1,30 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 test_step.dependOn(b.getInstallStep());
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}
614
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
716 const lib = b.addSharedLibrary(.{
817 .name = "lib",
918 .root_source_file = .{ .path = "lib.zig" },
1019 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
20 .optimize = optimize,
1221 });
1322 lib.use_llvm = false;
1423 lib.use_lld = false;
1524 lib.strip = false;
1625 lib.install();
1726
18 const check_lib = lib.checkObject(.wasm);
27 const check_lib = lib.checkObject();
1928 check_lib.checkStart("Section type");
2029 // only 2 entries, although we have 3 functions.
2130 // 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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const tests = @import("tests.zig");
1pub const SimpleCase = struct {
2 src_path: []const u8,
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 {
6 cases.add("test/standalone/hello_world/hello.zig");
7 cases.addC("test/standalone/hello_world/hello_libc.zig");
12pub const BuildCase = struct {
13 build_root: []const u8,
14 import: type,
15};
816
9 cases.addBuildFile("test/standalone/options/build.zig", .{
10 .extra_argv = &.{
11 "-Dbool_true",
12 "-Dbool_false=false",
13 "-Dint=1234",
14 "-De=two",
15 "-Dstring=hello",
17pub const simple_cases = [_]SimpleCase{
18 .{
19 .src_path = "test/standalone/hello_world/hello.zig",
20 .all_modes = true,
21 },
22 .{
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,
1643 },
17 });
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 }
44 },
6945
70 if (builtin.os.tag == .windows) {
71 cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{});
72 }
46 .{ .src_path = "test/standalone/issue_12471/main.zig" },
47 .{ .src_path = "test/standalone/guess_number/main.zig" },
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", .{
75 .build_modes = true,
76 .cross_targets = true,
77 });
78
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 }
53 .{
54 .src_path = "test/standalone/issue_9402/main.zig",
55 .os_filter = .windows,
56 .link_libc = true,
57 },
9058
9159 // Ensure the development tools are buildable. Alphabetically sorted.
9260 // No need to build `tools/spirv/grammar.zig`.
93 cases.add("tools/extract-grammar.zig");
94 cases.add("tools/gen_outline_atomics.zig");
95 cases.add("tools/gen_spirv_spec.zig");
96 cases.add("tools/gen_stubs.zig");
97 cases.add("tools/generate_linux_syscalls.zig");
98 cases.add("tools/process_headers.zig");
99 cases.add("tools/update-license-headers.zig");
100 cases.add("tools/update-linux-headers.zig");
101 cases.add("tools/update_clang_options.zig");
102 cases.add("tools/update_cpu_features.zig");
103 cases.add("tools/update_glibc.zig");
104 cases.add("tools/update_spirv_features.zig");
61 .{ .src_path = "tools/extract-grammar.zig" },
62 .{ .src_path = "tools/gen_outline_atomics.zig" },
63 .{ .src_path = "tools/gen_spirv_spec.zig" },
64 .{ .src_path = "tools/gen_stubs.zig" },
65 .{ .src_path = "tools/generate_linux_syscalls.zig" },
66 .{ .src_path = "tools/process_headers.zig" },
67 .{ .src_path = "tools/update-license-headers.zig" },
68 .{ .src_path = "tools/update-linux-headers.zig" },
69 .{ .src_path = "tools/update_clang_options.zig" },
70 .{ .src_path = "tools/update_cpu_features.zig" },
71 .{ .src_path = "tools/update_glibc.zig" },
72 .{ .src_path = "tools/update_spirv_features.zig" },
73};
10574
106 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });
107 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
108 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
109 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
75pub const build_cases = [_]BuildCase{
76 .{
77 .build_root = "test/standalone/test_runner_path",
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", .{});
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}
218const std = @import("std");
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 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;
43
5// TODO integrate this with the std.Build executor API
6fn isRunnableTarget(t: CrossTarget) bool {
7 if (t.isNative()) return true;
4pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test it");
6 b.default_step = test_step;
87
9 return (t.getOsTag() == builtin.os.tag and
10 t.getCpuArch() == builtin.cpu.arch);
8 add(b, test_step, .Debug);
9 add(b, test_step, .ReleaseFast);
10 add(b, test_step, .ReleaseSmall);
11 add(b, test_step, .ReleaseSafe);
1112}
1213
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
15 const target = b.standardTargetOptions(.{});
16
17 const test_step = b.step("test", "Test the program");
14fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
15 const target: std.zig.CrossTarget = .{};
1816
1917 const exe_c = b.addExecutable(.{
2018 .name = "test_c",
2119 .optimize = optimize,
2220 .target = target,
2321 });
24 b.default_step.dependOn(&exe_c.step);
2522 exe_c.addCSourceFile("test.c", &[0][]const u8{});
2623 exe_c.linkLibC();
2724
......@@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void {
4744 else => {},
4845 }
4946
50 if (isRunnableTarget(target)) {
51 const run_c_cmd = exe_c.run();
52 test_step.dependOn(&run_c_cmd.step);
53 const run_cpp_cmd = exe_cpp.run();
54 test_step.dependOn(&run_cpp_cmd.step);
55 } else {
56 test_step.dependOn(&exe_c.step);
57 test_step.dependOn(&exe_cpp.step);
58 }
47 const run_c_cmd = b.addRunArtifact(exe_c);
48 run_c_cmd.expectExitCode(0);
49 run_c_cmd.skip_foreign_checks = true;
50 test_step.dependOn(&run_c_cmd.step);
51
52 const run_cpp_cmd = b.addRunArtifact(exe_cpp);
53 run_cpp_cmd.expectExitCode(0);
54 run_cpp_cmd.skip_foreign_checks = true;
55 test_step.dependOn(&run_cpp_cmd.step);
5956}
test/standalone/dep_diamond/build.zig+4-2
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const shared = b.createModule(.{
710 .source_file = .{ .path = "shared.zig" },
......@@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void {
2326
2427 const run = exe.run();
2528
26 const test_step = b.step("test", "Test it");
2729 test_step.dependOn(&run.step);
2830}
test/standalone/dep_mutually_recursive/build.zig+4-2
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const foo = b.createModule(.{
710 .source_file = .{ .path = "foo.zig" },
......@@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void {
2124
2225 const run = exe.run();
2326
24 const test_step = b.step("test", "Test it");
2527 test_step.dependOn(&run.step);
2628}
test/standalone/dep_recursive/build.zig+4-2
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const foo = b.createModule(.{
710 .source_file = .{ .path = "foo.zig" },
......@@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void {
1720
1821 const run = exe.run();
1922
20 const test_step = b.step("test", "Test it");
2123 test_step.dependOn(&run.step);
2224}
test/standalone/dep_shared_builtin/build.zig+4-2
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const exe = b.addExecutable(.{
710 .name = "test",
......@@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void {
1417
1518 const run = exe.run();
1619
17 const test_step = b.step("test", "Test it");
1820 test_step.dependOn(&run.step);
1921}
test/standalone/dep_triangle/build.zig+4-2
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const shared = b.createModule(.{
710 .source_file = .{ .path = "shared.zig" },
......@@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void {
2023
2124 const run = exe.run();
2225
23 const test_step = b.step("test", "Test it");
2426 test_step.dependOn(&run.step);
2527}
test/standalone/embed_generated_file/build.zig+3-5
......@@ -1,8 +1,8 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
66
77 const bootloader = b.addExecutable(.{
88 .name = "bootloader",
......@@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void {
1616
1717 const exe = b.addTest(.{
1818 .root_source_file = .{ .path = "main.zig" },
19 .target = target,
20 .optimize = optimize,
19 .optimize = .Debug,
2120 });
2221 exe.addAnonymousModule("bootloader.elf", .{
2322 .source_file = bootloader.getOutputSource(),
2423 });
2524
26 const test_step = b.step("test", "Test the program");
2725 test_step.dependOn(&exe.step);
2826}
test/standalone/emit_asm_and_bin/build.zig+4-2
......@@ -1,6 +1,9 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
47 const main = b.addTest(.{
58 .root_source_file = .{ .path = "main.zig" },
69 .optimize = b.standardOptimizeOption(.{}),
......@@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void {
811 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
912 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
1013
11 const test_step = b.step("test", "Run test");
12 test_step.dependOn(&main.step);
14 test_step.dependOn(&main.run().step);
1315}
test/standalone/empty_env/build.zig+13-3
......@@ -1,15 +1,25 @@
11const std = @import("std");
2const builtin = @import("builtin");
23
34pub 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
415 const main = b.addExecutable(.{
516 .name = "main",
617 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
18 .optimize = optimize,
819 });
920
10 const run = main.run();
21 const run = b.addRunArtifact(main);
1122 run.clearEnvironment();
1223
13 const test_step = b.step("test", "Test it");
1424 test_step.dependOn(&run.step);
1525}
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 @@
11const std = @import("std");
22
33pub 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
610 const obj1 = b.addStaticLibrary(.{
711 .name = "obj1",
812 .root_source_file = .{ .path = "obj1.zig" },
913 .optimize = optimize,
10 .target = .{},
14 .target = target,
1115 });
1216
1317 const obj2 = b.addStaticLibrary(.{
1418 .name = "obj2",
1519 .root_source_file = .{ .path = "obj2.zig" },
1620 .optimize = optimize,
17 .target = .{},
21 .target = target,
1822 });
1923
2024 const main = b.addTest(.{
......@@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void {
2428 main.linkLibrary(obj1);
2529 main.linkLibrary(obj2);
2630
27 const test_step = b.step("test", "Test it");
28 test_step.dependOn(&main.step);
31 test_step.dependOn(&main.run().step);
2932}
test/standalone/install_raw_hex/build.zig+3-3
......@@ -3,8 +3,8 @@ const std = @import("std");
33const CheckFileStep = std.Build.CheckFileStep;
44
55pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test the program");
7 b.default_step.dependOn(test_step);
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
88
99 const target = .{
1010 .cpu_arch = .thumb,
......@@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
1313 .abi = .gnueabihf,
1414 };
1515
16 const optimize = b.standardOptimizeOption(.{});
16 const optimize: std.builtin.OptimizeMode = .Debug;
1717
1818 const elf = b.addExecutable(.{
1919 .name = "zig-nrf52-blink.elf",
test/standalone/issue_11595/build.zig+14-17
......@@ -1,18 +1,17 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;
43
5// TODO integrate this with the std.Build executor API
6fn isRunnableTarget(t: CrossTarget) bool {
7 if (t.isNative()) return true;
4pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test it");
6 b.default_step = test_step;
87
9 return (t.getOsTag() == builtin.os.tag and
10 t.getCpuArch() == builtin.cpu.arch);
11}
8 const optimize: std.builtin.OptimizeMode = .Debug;
9 const target: std.zig.CrossTarget = .{};
1210
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
15 const target = b.standardTargetOptions(.{});
11 if (builtin.os.tag == .windows) {
12 // https://github.com/ziglang/zig/issues/12419
13 return;
14 }
1615
1716 const exe = b.addExecutable(.{
1817 .name = "zigtest",
......@@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void {
4443
4544 b.default_step.dependOn(&exe.step);
4645
47 const test_step = b.step("test", "Test the program");
48 if (isRunnableTarget(target)) {
49 const run_cmd = exe.run();
50 test_step.dependOn(&run_cmd.step);
51 } else {
52 test_step.dependOn(&exe.step);
53 }
46 const run_cmd = b.addRunArtifact(exe);
47 run_cmd.skip_foreign_checks = true;
48 run_cmd.expectExitCode(0);
49
50 test_step.dependOn(&run_cmd.step);
5451}
test/standalone/issue_12588/build.zig+5-3
......@@ -1,8 +1,11 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
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 = .{};
69
710 const obj = b.addObject(.{
811 .name = "main",
......@@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void {
1518 obj.emit_bin = .no_emit;
1619 b.default_step.dependOn(&obj.step);
1720
18 const test_step = b.step("test", "Test the program");
1921 test_step.dependOn(&obj.step);
2022}
test/standalone/issue_12706/build.zig+9-21
......@@ -2,17 +2,12 @@ const std = @import("std");
22const builtin = @import("builtin");
33const 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
135pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
15 const target = b.standardTargetOptions(.{});
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 const optimize: std.builtin.OptimizeMode = .Debug;
10 const target: std.zig.CrossTarget = .{};
1611
1712 const exe = b.addExecutable(.{
1813 .name = "main",
......@@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void {
2015 .optimize = optimize,
2116 .target = target,
2217 });
23 exe.install();
2418
2519 const c_sources = [_][]const u8{
2620 "test.c",
2721 };
28
2922 exe.addCSourceFiles(&c_sources, &.{});
3023 exe.linkLibC();
3124
32 b.default_step.dependOn(&exe.step);
33
34 const test_step = b.step("test", "Test the program");
35 if (isRunnableTarget(target)) {
36 const run_cmd = exe.run();
37 test_step.dependOn(&run_cmd.step);
38 } else {
39 test_step.dependOn(&exe.step);
40 }
25 const run_cmd = b.addRunArtifact(exe);
26 run_cmd.expectExitCode(0);
27 run_cmd.skip_foreign_checks = true;
28 test_step.dependOn(&run_cmd.step);
4129}
test/standalone/issue_13030/build.zig+10-5
......@@ -3,17 +3,22 @@ const builtin = @import("builtin");
33const CrossTarget = std.zig.CrossTarget;
44
55pub fn build(b: *std.Build) void {
6 const optimize = b.standardOptimizeOption(.{});
7 const target = b.standardTargetOptions(.{});
6 const test_step = b.step("test", "Test it");
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 {
916 const obj = b.addObject(.{
1017 .name = "main",
1118 .root_source_file = .{ .path = "main.zig" },
1219 .optimize = optimize,
13 .target = target,
20 .target = .{},
1421 });
15 b.default_step.dependOn(&obj.step);
1622
17 const test_step = b.step("test", "Test the program");
1823 test_step.dependOn(&obj.step);
1924}
test/standalone/issue_13970/build.zig+6-4
......@@ -1,6 +1,9 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
47 const test1 = b.addTest(.{
58 .root_source_file = .{ .path = "test_root/empty.zig" },
69 });
......@@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void {
1417 test2.setTestRunner("src/main.zig");
1518 test3.setTestRunner("src/main.zig");
1619
17 const test_step = b.step("test", "Test package path resolution of custom test runner");
18 test_step.dependOn(&test1.step);
19 test_step.dependOn(&test2.step);
20 test_step.dependOn(&test3.step);
20 test_step.dependOn(&test1.run().step);
21 test_step.dependOn(&test2.run().step);
22 test_step.dependOn(&test3.run().step);
2123}
test/standalone/issue_339/build.zig+8-3
......@@ -1,13 +1,18 @@
11const std = @import("std");
22
33pub 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
410 const obj = b.addObject(.{
511 .name = "test",
612 .root_source_file = .{ .path = "test.zig" },
7 .target = b.standardTargetOptions(.{}),
8 .optimize = b.standardOptimizeOption(.{}),
13 .target = target,
14 .optimize = optimize,
915 });
1016
11 const test_step = b.step("test", "Test the program");
1217 test_step.dependOn(&obj.step);
1318}
test/standalone/issue_5825/build.zig+4-2
......@@ -1,12 +1,15 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
47 const target = .{
58 .cpu_arch = .x86_64,
69 .os_tag = .windows,
710 .abi = .msvc,
811 };
9 const optimize = b.standardOptimizeOption(.{});
12 const optimize: std.builtin.OptimizeMode = .Debug;
1013 const obj = b.addObject(.{
1114 .name = "issue_5825",
1215 .root_source_file = .{ .path = "main.zig" },
......@@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void {
2427 exe.linkSystemLibrary("ntdll");
2528 exe.addObject(obj);
2629
27 const test_step = b.step("test", "Test the program");
2830 test_step.dependOn(&exe.step);
2931}
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 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
47 const test_artifact = b.addTest(.{
58 .root_source_file = .{ .path = "main.zig" },
69 });
710 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");
1212 test_step.dependOn(&test_artifact.step);
1313}
test/standalone/issue_8550/build.zig+5-2
......@@ -1,6 +1,10 @@
11const std = @import("std");
22
33pub 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;
48 const target = std.zig.CrossTarget{
59 .os_tag = .freestanding,
610 .cpu_arch = .arm,
......@@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void {
812 .explicit = &std.Target.arm.cpu.arm1176jz_s,
913 },
1014 };
11 const optimize = b.standardOptimizeOption(.{});
15
1216 const kernel = b.addExecutable(.{
1317 .name = "kernel",
1418 .root_source_file = .{ .path = "./main.zig" },
......@@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void {
1923 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });
2024 kernel.install();
2125
22 const test_step = b.step("test", "Test it");
2326 test_step.dependOn(&kernel.step);
2427}
test/standalone/issue_9812/build.zig+5-2
......@@ -1,7 +1,11 @@
11const std = @import("std");
22
33pub 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
59 const zip_add = b.addTest(.{
610 .root_source_file = .{ .path = "main.zig" },
711 .optimize = optimize,
......@@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void {
1317 zip_add.addIncludePath("vendor/kuba-zip");
1418 zip_add.linkLibC();
1519
16 const test_step = b.step("test", "Test it");
1720 test_step.dependOn(&zip_add.step);
1821}
test/standalone/load_dynamic_library/build.zig+16-4
......@@ -1,8 +1,19 @@
11const std = @import("std");
2const builtin = @import("builtin");
23
34pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 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 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
718 const lib = b.addSharedLibrary(.{
819 .name = "add",
......@@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void {
1930 .target = target,
2031 });
2132
22 const run = main.run();
33 const run = b.addRunArtifact(main);
2334 run.addArtifactArg(lib);
35 run.skip_foreign_checks = true;
36 run.expectExitCode(0);
2437
25 const test_step = b.step("test", "Test the program");
2638 test_step.dependOn(&run.step);
2739}
test/standalone/load_dynamic_library/main.zig+1-5
......@@ -11,11 +11,7 @@ pub fn main() !void {
1111 var lib = try std.DynLib.open(dynlib_name);
1212 defer lib.close();
1313
14 const Add = switch (@import("builtin").zig_backend) {
15 .stage1 => fn (i32, i32) callconv(.C) i32,
16 else => *const fn (i32, i32) callconv(.C) i32,
17 };
18
14 const Add = *const fn (i32, i32) callconv(.C) i32;
1915 const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound;
2016
2117 const result = addFn(12, 34);
test/standalone/main_pkg_path/build.zig+4-2
......@@ -1,11 +1,13 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
47 const test_exe = b.addTest(.{
58 .root_source_file = .{ .path = "a/test.zig" },
69 });
710 test_exe.setMainPkgPath(".");
811
9 const test_step = b.step("test", "Test the program");
10 test_step.dependOn(&test_exe.step);
12 test_step.dependOn(&test_exe.run().step);
1113}
test/standalone/mix_c_files/build.zig+13-19
......@@ -1,34 +1,28 @@
11const std = @import("std");
2const builtin = @import("builtin");
3const CrossTarget = std.zig.CrossTarget;
42
5// TODO integrate this with the std.Build executor API
6fn isRunnableTarget(t: CrossTarget) bool {
7 if (t.isNative()) return true;
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
86
9 return (t.getOsTag() == builtin.os.tag and
10 t.getCpuArch() == builtin.cpu.arch);
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
1111}
1212
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
15 const target = b.standardTargetOptions(.{});
16
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
1714 const exe = b.addExecutable(.{
1815 .name = "test",
1916 .root_source_file = .{ .path = "main.zig" },
2017 .optimize = optimize,
21 .target = target,
2218 });
2319 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});
2420 exe.linkLibC();
2521 b.default_step.dependOn(&exe.step);
2622
27 const test_step = b.step("test", "Test the program");
28 if (isRunnableTarget(target)) {
29 const run_cmd = exe.run();
30 test_step.dependOn(&run_cmd.step);
31 } else {
32 test_step.dependOn(&exe.step);
33 }
23 const run_cmd = b.addRunArtifact(exe);
24 run_cmd.skip_foreign_checks = true;
25 run_cmd.expectExitCode(0);
26
27 test_step.dependOn(&run_cmd.step);
3428}
test/standalone/mix_o_files/build.zig+7-3
......@@ -1,18 +1,23 @@
11const std = @import("std");
22
33pub 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
610 const obj = b.addObject(.{
711 .name = "base64",
812 .root_source_file = .{ .path = "base64.zig" },
913 .optimize = optimize,
10 .target = .{},
14 .target = target,
1115 });
1216
1317 const exe = b.addExecutable(.{
1418 .name = "test",
1519 .optimize = optimize,
20 .target = target,
1621 });
1722 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1823 exe.addObject(obj);
......@@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void {
2227
2328 const run_cmd = exe.run();
2429
25 const test_step = b.step("test", "Test the program");
2630 test_step.dependOn(&run_cmd.step);
2731}
test/standalone/options/build.zig+1-1
......@@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void {
2020 options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?);
2121
2222 const test_step = b.step("test", "Run unit tests");
23 test_step.dependOn(&main.step);
23 test_step.dependOn(&main.run().step);
2424}
test/standalone/pie/build.zig+14-4
......@@ -1,14 +1,24 @@
11const std = @import("std");
22
33pub 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
413 const main = b.addTest(.{
514 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
15 .optimize = optimize,
16 .target = target,
717 });
818 main.pie = true;
919
10 const test_step = b.step("test", "Test the program");
11 test_step.dependOn(&main.step);
20 const run = main.run();
21 run.skip_foreign_checks = true;
1222
13 b.default_step.dependOn(test_step);
23 test_step.dependOn(&run.step);
1424}
test/standalone/pkg_import/build.zig+4-2
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const exe = b.addExecutable(.{
710 .name = "test",
......@@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void {
1215
1316 const run = exe.run();
1417
15 const test_step = b.step("test", "Test it");
1618 test_step.dependOn(&run.step);
1719}
test/standalone/shared_library/build.zig+5-5
......@@ -1,8 +1,11 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
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 = .{};
69 const lib = b.addSharedLibrary(.{
710 .name = "mathtest",
811 .root_source_file = .{ .path = "mathtest.zig" },
......@@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void {
2023 exe.linkLibrary(lib);
2124 exe.linkSystemLibrary("c");
2225
23 b.default_step.dependOn(&exe.step);
24
2526 const run_cmd = exe.run();
2627
27 const test_step = b.step("test", "Test the program");
2828 test_step.dependOn(&run_cmd.step);
2929}
test/standalone/sigpipe/build.zig+14-5
......@@ -2,7 +2,16 @@ const std = @import("std");
22const os = std.os;
33
44pub 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
716 // This test runs "breakpipe" as a child process and that process
817 // depends on inheriting a SIGPIPE disposition of "default".
......@@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void {
2332 .root_source_file = .{ .path = "breakpipe.zig" },
2433 });
2534 exe.addOptions("build_options", options);
26 const run = exe.run();
35 const run = b.addRunArtifact(exe);
2736 if (keep_sigpipe) {
28 run.expected_term = .{ .Signal = std.os.SIG.PIPE };
37 run.addCheck(.{ .expect_term = .{ .Signal = std.os.SIG.PIPE } });
2938 } else {
30 run.stdout_action = .{ .expect_exact = "BrokenPipe\n" };
31 run.expected_term = .{ .Exited = 123 };
39 run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" });
40 run.addCheck(.{ .expect_term = .{ .Exited = 123 } });
3241 }
3342 test_step.dependOn(&run.step);
3443 }
test/standalone/static_c_lib/build.zig+5-3
......@@ -1,7 +1,10 @@
11const std = @import("std");
22
33pub 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
69 const foo = b.addStaticLibrary(.{
710 .name = "foo",
......@@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void {
1821 test_exe.linkLibrary(foo);
1922 test_exe.addIncludePath(".");
2023
21 const test_step = b.step("test", "Test it");
22 test_step.dependOn(&test_exe.step);
24 test_step.dependOn(&test_exe.run().step);
2325}
test/standalone/test_runner_module_imports/build.zig+1-1
......@@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void {
1515 t.addModule("module2", module2);
1616
1717 const test_step = b.step("test", "Run unit tests");
18 test_step.dependOn(&t.step);
18 test_step.dependOn(&t.run().step);
1919}
test/standalone/test_runner_path/build.zig+5-2
......@@ -1,14 +1,17 @@
11const std = @import("std");
22
3pub const requires_stage2 = true;
4
35pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test the program");
7 b.default_step = test_step;
8
49 const test_exe = b.addTest(.{
510 .root_source_file = .{ .path = "test.zig" },
6 .kind = .test_exe,
711 });
812 test_exe.test_runner = "test_runner.zig";
913
1014 const test_run = test_exe.run();
1115
12 const test_step = b.step("test", "Test the program");
1316 test_step.dependOn(&test_run.step);
1417}
test/standalone/test_runner_path/test_runner.zig+4-36
......@@ -1,51 +1,19 @@
11const std = @import("std");
2const io = std.io;
32const builtin = @import("builtin");
43
5pub const io_mode: io.Mode = builtin.test_io_mode;
6
74pub fn main() void {
8 const test_fn_list = builtin.test_functions;
95 var ok_count: usize = 0;
106 var skip_count: usize = 0;
117 var fail_count: usize = 0;
128
13 var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined;
14 // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly
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) |_| {
9 for (builtin.test_functions) |test_fn| {
10 if (test_fn.func()) |_| {
3411 ok_count += 1;
3512 } else |err| switch (err) {
36 error.SkipZigTest => {
37 skip_count += 1;
38 },
39 else => {
40 fail_count += 1;
41 },
13 error.SkipZigTest => skip_count += 1,
14 else => fail_count += 1,
4215 }
4316 }
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 }
4917 if (ok_count != 1 or skip_count != 1 or fail_count != 1) {
5018 std.process.exit(1);
5119 }
test/standalone/use_alias/build.zig+7-3
......@@ -1,12 +1,16 @@
11const std = @import("std");
22
33pub 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
49 const main = b.addTest(.{
510 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
11 .optimize = optimize,
712 });
813 main.addIncludePath(".");
914
10 const test_step = b.step("test", "Test it");
11 test_step.dependOn(&main.step);
15 test_step.dependOn(&main.run().step);
1216}
test/standalone/windows_spawn/build.zig+13-3
......@@ -1,23 +1,33 @@
11const std = @import("std");
2const builtin = @import("builtin");
23
34pub 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
613 const hello = b.addExecutable(.{
714 .name = "hello",
815 .root_source_file = .{ .path = "hello.zig" },
916 .optimize = optimize,
17 .target = target,
1018 });
1119
1220 const main = b.addExecutable(.{
1321 .name = "main",
1422 .root_source_file = .{ .path = "main.zig" },
1523 .optimize = optimize,
24 .target = target,
1625 });
1726
18 const run = main.run();
27 const run = b.addRunArtifact(main);
1928 run.addArtifactArg(hello);
29 run.expectExitCode(0);
30 run.skip_foreign_checks = true;
2031
21 const test_step = b.step("test", "Test it");
2232 test_step.dependOn(&run.step);
2333}
test/tests.zig+449-745
......@@ -1,16 +1,9 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const debug = std.debug;
3const assert = std.debug.assert;
44const CrossTarget = std.zig.CrossTarget;
5const io = std.io;
6const fs = std.fs;
75const mem = std.mem;
8const fmt = std.fmt;
9const ArrayList = std.ArrayList;
106const OptimizeMode = std.builtin.OptimizeMode;
11const CompileStep = std.Build.CompileStep;
12const Allocator = mem.Allocator;
13const ExecError = std.Build.ExecError;
147const Step = std.Build.Step;
158
169// Cases
......@@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig");
2013const assemble_and_link = @import("assemble_and_link.zig");
2114const translate_c = @import("translate_c.zig");
2215const run_translated_c = @import("run_translated_c.zig");
23const gen_h = @import("gen_h.zig");
2416const link = @import("link.zig");
2517
2618// Implementations
2719pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext;
2820pub 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
3124const TestTarget = struct {
3225 target: CrossTarget = @as(CrossTarget, .{}),
......@@ -460,10 +453,71 @@ const test_targets = blk: {
460453 };
461454};
462455
463const max_stdout_size = 1 * 1024 * 1024; // 1 MB
456const 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 {
466 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
515pub fn addCompareOutputTests(
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");
467521 cases.* = CompareOutputContext{
468522 .b = b,
469523 .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
477531 return cases.step;
478532}
479533
480pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
481 const cases = b.allocator.create(StackTracesContext) catch unreachable;
482 cases.* = StackTracesContext{
534pub fn addStackTraceTests(
535 b: *std.Build,
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.* = .{
483548 .b = b,
484549 .step = b.step("test-stack-traces", "Run the stack trace tests"),
485550 .test_index = 0,
486551 .test_filter = test_filter,
487552 .optimize_modes = optimize_modes,
553 .check_exe = check_exe,
488554 };
489555
490556 stack_traces.addCases(cases);
......@@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode
494560
495561pub fn addStandaloneTests(
496562 b: *std.Build,
497 test_filter: ?[]const u8,
498563 optimize_modes: []const OptimizeMode,
499 skip_non_native: bool,
500564 enable_macos_sdk: bool,
501 target: std.zig.CrossTarget,
502565 omit_stage2: bool,
503 enable_darling: bool,
504 enable_qemu: bool,
505 enable_rosetta: bool,
506 enable_wasmtime: bool,
507 enable_wine: bool,
508566 enable_symlinks_windows: bool,
509567) *Step {
510 const cases = b.allocator.create(StandaloneContext) catch unreachable;
511 cases.* = StandaloneContext{
512 .b = b,
513 .step = b.step("test-standalone", "Run the standalone tests"),
514 .test_index = 0,
515 .test_filter = test_filter,
516 .optimize_modes = optimize_modes,
517 .skip_non_native = skip_non_native,
518 .enable_macos_sdk = enable_macos_sdk,
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 };
568 const step = b.step("test-standalone", "Run the standalone tests");
569 const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows;
570
571 for (standalone.simple_cases) |case| {
572 for (optimize_modes) |optimize| {
573 if (!case.all_modes and optimize != .Debug) continue;
574 if (case.os_filter) |os_tag| {
575 if (os_tag != builtin.os.tag) continue;
576 }
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;
532626}
533627
534628pub fn addLinkTests(
535629 b: *std.Build,
536 test_filter: ?[]const u8,
537 optimize_modes: []const OptimizeMode,
538630 enable_macos_sdk: bool,
539631 omit_stage2: bool,
540632 enable_symlinks_windows: bool,
541633) *Step {
542 const cases = b.allocator.create(StandaloneContext) catch unreachable;
543 cases.* = StandaloneContext{
544 .b = b,
545 .step = b.step("test-link", "Run the linker tests"),
546 .test_index = 0,
547 .test_filter = test_filter,
548 .optimize_modes = optimize_modes,
549 .skip_non_native = true,
550 .enable_macos_sdk = enable_macos_sdk,
551 .target = .{},
552 .omit_stage2 = omit_stage2,
553 .enable_symlinks_windows = enable_symlinks_windows,
554 };
555 link.addCases(cases);
556 return cases.step;
634 const step = b.step("test-link", "Run the linker tests");
635 const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows;
636
637 inline for (link.cases) |case| {
638 const requires_stage2 = @hasDecl(case.import, "requires_stage2") and
639 case.import.requires_stage2;
640 const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and
641 case.import.requires_symlinks;
642 const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and
643 case.import.requires_macos_sdk;
644 const bad =
645 (requires_stage2 and omit_stage2) or
646 (requires_symlinks and omit_symlinks) or
647 (requires_macos_sdk and !enable_macos_sdk);
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;
557659}
558660
559pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
560 _ = test_filter;
561 _ = optimize_modes;
661pub fn addCliTests(b: *std.Build) *Step {
562662 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(.{
565 .name = "test-cli",
566 .root_source_file = .{ .path = "test/cli.zig" },
567 .target = .{},
568 .optimize = .Debug,
569 });
570 const run_cmd = exe.run();
571 run_cmd.addArgs(&[_][]const u8{
572 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,
573 b.pathFromRoot(b.cache_root.path orelse "."),
574 });
689 {
690 // Test `zig init-exe`.
691 const tmp_path = b.makeTempPath();
692 const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" });
693 init_exe.cwd = tmp_path;
694 init_exe.setName("zig init-exe");
695 init_exe.expectStdOutEqual("");
696 init_exe.expectStdErrEqual("info: Created build.zig\n" ++
697 "info: Created src" ++ s ++ "main.zig\n" ++
698 "info: Next, try `zig build --help` or `zig build run`\n");
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);
577854 return step;
578855}
579856
580857pub 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");
582859 cases.* = CompareOutputContext{
583860 .b = b,
584861 .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
593870}
594871
595872pub 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");
597874 cases.* = TranslateCContext{
598875 .b = b,
599876 .step = b.step("test-translate-c", "Run the C translation tests"),
......@@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests(
611888 test_filter: ?[]const u8,
612889 target: std.zig.CrossTarget,
613890) *Step {
614 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
891 const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM");
615892 cases.* = .{
616893 .b = b,
617894 .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"),
......@@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests(
625902 return cases.step;
626903}
627904
628pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {
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,
905const ModuleTestOptions = struct {
644906 test_filter: ?[]const u8,
645907 root_src: []const u8,
646908 name: []const u8,
......@@ -651,14 +913,17 @@ pub fn addPkgTests(
651913 skip_libc: bool,
652914 skip_stage1: bool,
653915 skip_stage2: bool,
654) *Step {
655 const step = b.step(b.fmt("test-{s}", .{name}), desc);
916 max_rss: usize = 0,
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
657922 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())
659924 continue;
660925
661 if (skip_libc and test_target.link_libc)
926 if (options.skip_libc and test_target.link_libc)
662927 continue;
663928
664929 if (test_target.link_libc and test_target.target.getOs().requiresLibC()) {
......@@ -666,7 +931,7 @@ pub fn addPkgTests(
666931 continue;
667932 }
668933
669 if (skip_single_threaded and test_target.single_threaded)
934 if (options.skip_single_threaded and test_target.single_threaded)
670935 continue;
671936
672937 if (test_target.disable_native and
......@@ -677,12 +942,12 @@ pub fn addPkgTests(
677942 }
678943
679944 if (test_target.backend) |backend| switch (backend) {
680 .stage1 => if (skip_stage1) continue,
945 .stage1 => if (options.skip_stage1) continue,
681946 .stage2_llvm => {},
682 else => if (skip_stage2) continue,
947 else => if (options.skip_stage2) continue,
683948 };
684949
685 const want_this_mode = for (optimize_modes) |m| {
950 const want_this_mode = for (options.optimize_modes) |m| {
686951 if (m == test_target.optimize_mode) break true;
687952 } else false;
688953 if (!want_this_mode) continue;
......@@ -694,25 +959,24 @@ pub fn addPkgTests(
694959 else
695960 "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
699970 const these_tests = b.addTest(.{
700 .root_source_file = .{ .path = root_src },
971 .root_source_file = .{ .path = options.root_src },
701972 .optimize = test_target.optimize_mode,
702973 .target = test_target.target,
974 .max_rss = max_rss,
703975 });
704976 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
705977 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 }));
714978 these_tests.single_threaded = test_target.single_threaded;
715 these_tests.setFilter(test_filter);
979 these_tests.setFilter(options.test_filter);
716980 if (test_target.link_libc) {
717981 these_tests.linkSystemLibrary("c");
718982 }
......@@ -736,654 +1000,94 @@ pub fn addPkgTests(
7361000 },
7371001 };
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);
7401015 }
7411016 return step;
7421017}
7431018
744pub const StackTracesContext = struct {
745 b: *std.Build,
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 }
1019pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
1020 const step = b.step("test-c-abi", "Run the C ABI tests");
10821021
1083 if (features.use_emulation) {
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 }
1022 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };
11001023
1101 const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug};
1102 for (optimize_modes) |optimize_mode| {
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 }
1024 for (optimize_modes) |optimize_mode| {
1025 if (optimize_mode != .Debug and skip_release) continue;
11211026
1122 pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void {
1123 const b = self.b;
1027 for (c_abi_targets) |c_abi_target| {
1028 if (skip_non_native and !c_abi_target.isNative()) continue;
11241029
1125 for (self.optimize_modes) |optimize| {
1126 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{
1127 root_src,
1128 @tagName(optimize),
1129 }) catch unreachable;
1130 if (self.test_filter) |filter| {
1131 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
1030 if (c_abi_target.isWindows() and c_abi_target.getCpuArch() == .aarch64) {
1031 // https://github.com/ziglang/zig/issues/14908
1032 continue;
11321033 }
11331034
1134 const exe = b.addExecutable(.{
1135 .name = "test",
1136 .root_source_file = .{ .path = root_src },
1137 .optimize = optimize,
1138 .target = .{},
1035 const test_step = b.addTest(.{
1036 .root_source_file = .{ .path = "test/c_abi/main.zig" },
1037 .optimize = optimize_mode,
1038 .target = c_abi_target,
11391039 });
1140 if (link_libc) {
1141 exe.linkSystemLibrary("c");
1040 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {
1041 // TODO NativeTargetInfo insists on dynamically linking musl
1042 // for some reason?
1043 test_step.target_info.dynamic_linker.max_byte = null;
11421044 }
1143
1144 const log_step = b.addLog("PASS {s}", .{annotated_case_name});
1145 log_step.step.dependOn(&exe.step);
1146
1147 self.step.dependOn(&log_step.step);
1148 }
1149 }
1150};
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 }
1045 test_step.linkLibC();
1046 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});
1047
1048 // test-c-abi should test both with LTO on and with LTO off. Only
1049 // some combinations are passing currently:
1050 // https://github.com/ziglang/zig/issues/14908
1051 if (c_abi_target.isWindows()) {
1052 test_step.want_lto = false;
12301053 }
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 {
1263 const b = self.b;
1055 const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM");
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;
1266 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable;
1267 if (self.test_filter) |filter| {
1268 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
1060 const run = test_step.run();
1061 run.skip_foreign_checks = true;
1062 step.dependOn(&run.step);
12691063 }
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);
12821064 }
1283};
1284
1285fn printInvocation(args: []const []const u8) void {
1286 for (args) |arg| {
1287 std.debug.print("{s} ", .{arg});
1288 }
1289 std.debug.print("\n", .{});
1065 return step;
12901066}
12911067
1292const c_abi_targets = [_]CrossTarget{
1293 .{},
1294 .{
1295 .cpu_arch = .x86_64,
1296 .os_tag = .linux,
1297 .abi = .musl,
1298 },
1299 .{
1300 .cpu_arch = .x86,
1301 .os_tag = .linux,
1302 .abi = .musl,
1303 },
1304 .{
1305 .cpu_arch = .aarch64,
1306 .os_tag = .linux,
1307 .abi = .musl,
1308 },
1309 .{
1310 .cpu_arch = .arm,
1311 .os_tag = .linux,
1312 .abi = .musleabihf,
1313 },
1314 .{
1315 .cpu_arch = .mips,
1316 .os_tag = .linux,
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;
1068pub fn addCases(
1069 b: *std.Build,
1070 parent_step: *Step,
1071 opt_test_filter: ?[]const u8,
1072 check_case_exe: *std.Build.CompileStep,
1073) !void {
1074 const arena = b.allocator;
1075 const gpa = b.allocator;
1076
1077 var cases = @import("src/Cases.zig").init(gpa, arena);
1078
1079 var dir = try b.build_root.handle.openIterableDir("test/cases", .{});
1080 defer dir.close();
1081
1082 cases.addFromDir(dir);
1083 try @import("cases.zig").addCases(&cases);
1084
1085 const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" });
1086 cases.lowerToBuildSteps(
1087 b,
1088 parent_step,
1089 opt_test_filter,
1090 cases_dir_path,
1091 check_case_exe,
1092 );
13891093}
test/translate_c.zig+8
......@@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
39483948 \\}
39493949 });
39503950 }
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 });
39513959}
tools/lldb_pretty_printers.py+38-5
......@@ -1,6 +1,6 @@
11# pretty printing for the zig language, zig standard library, and zig stage 2 compiler.
22# put commands in ~/.lldbinit to run them automatically when starting lldb
3# `command script /path/to/stage2_lldb_pretty_printers.py` to import this file
3# `command script import /path/to/zig/tools/lldb_pretty_printers.py` to import this file
44# `type category enable zig` to enable pretty printing for the zig language
55# `type category enable zig.std` to enable pretty printing for the zig standard library
66# `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler
......@@ -201,7 +201,7 @@ class std_MultiArrayList_SynthProvider:
201201
202202 value_type = self.value.type
203203 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()
205205 if ptr_self_type.GetPointeeType() == value_type: break
206206 else: return
207207
......@@ -221,12 +221,44 @@ class std_MultiArrayList_SynthProvider:
221221 offset = 0
222222 data = lldb.SBData()
223223 for field in self.entry_type.fields:
224 ptr_field_type = field.type
225 field_size = ptr_field_type.GetPointeeType().size
226 data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, ptr_field_type).address_of.data)
224 field_type = field.type.GetPointeeType()
225 field_size = field_type.size
226 data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, field_type).address_of.data)
227227 offset += self.capacity * field_size
228228 return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type)
229229 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
231263class std_HashMapUnmanaged_SynthProvider:
232264 def __init__(self, value, _=None): self.value = value
......@@ -556,6 +588,7 @@ def __lldb_init_module(debugger, _=None):
556588 add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')
557589 add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')
558590 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}')
559592 add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)
560593 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)
561594 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)