| author | |
| committer | |
| log | 1ed569e0b23c4432cd00604dcae89a17edc852a9 |
| tree | 090e0b3817a0caa4f3e7b99ec1d4d965f2bc7438 |
| parent | 778ca2ae6bf025edb6babeec08c957be1fbb37a5 |
| parent | b4d58e93ea4d0bbfe674f80d301279d302fe8fc8 |
399 files changed, 27595 insertions(+), 19646 deletions(-)
CMakeLists.txt+14-5| ... | @@ -296,14 +296,12 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -296,14 +296,12 @@ set(ZIG_STAGE2_SOURCES |
| 296 | "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig" | 296 | "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig" |
| 297 | "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig" | 297 | "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig" |
| 298 | "${CMAKE_SOURCE_DIR}/lib/std/os.zig" | 298 | "${CMAKE_SOURCE_DIR}/lib/std/os.zig" |
| 299 | "${CMAKE_SOURCE_DIR}/lib/std/os/darwin.zig" | ||
| 300 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" | 299 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" |
| 301 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig" | 300 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig" |
| 302 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" | 301 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" |
| 303 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" | 302 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" |
| 304 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig" | 303 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/io_uring.zig" |
| 305 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" | 304 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" |
| 306 | "${CMAKE_SOURCE_DIR}/lib/std/os/posix_spawn.zig" | ||
| 307 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig" | 305 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows.zig" |
| 308 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig" | 306 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows/ntstatus.zig" |
| 309 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig" | 307 | "${CMAKE_SOURCE_DIR}/lib/std/os/windows/win32error.zig" |
| ... | @@ -507,7 +505,9 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -507,7 +505,9 @@ set(ZIG_STAGE2_SOURCES |
| 507 | "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" | 505 | "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" |
| 508 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" | 506 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" |
| 509 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" | 507 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" |
| 508 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Pool.zig" | ||
| 510 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" | 509 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" |
| 510 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/WaitGroup.zig" | ||
| 511 | "${CMAKE_SOURCE_DIR}/lib/std/time.zig" | 511 | "${CMAKE_SOURCE_DIR}/lib/std/time.zig" |
| 512 | "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" | 512 | "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" |
| 513 | "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" | 513 | "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" |
| ... | @@ -517,6 +517,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -517,6 +517,7 @@ set(ZIG_STAGE2_SOURCES |
| 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" | 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" |
| 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" | 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" |
| 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" | 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" |
| 520 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig" | ||
| 520 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" | 521 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" |
| 521 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" | 522 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| 522 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" | 523 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" |
| ... | @@ -531,9 +532,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -531,9 +532,7 @@ set(ZIG_STAGE2_SOURCES |
| 531 | "${CMAKE_SOURCE_DIR}/src/Package.zig" | 532 | "${CMAKE_SOURCE_DIR}/src/Package.zig" |
| 532 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" | 533 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" |
| 533 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" | 534 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" |
| 534 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" | ||
| 535 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" | 535 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 536 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" | ||
| 537 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" | 536 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" |
| 538 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig" | 537 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig" |
| 539 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" | 538 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" |
| ... | @@ -560,9 +559,12 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -560,9 +559,12 @@ set(ZIG_STAGE2_SOURCES |
| 560 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" | 559 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" |
| 561 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig" | 560 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig" |
| 562 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" | 561 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" |
| 562 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig" | ||
| 563 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" | 563 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" |
| 564 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" | ||
| 565 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig" | 564 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig" |
| 565 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" | ||
| 566 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/encoder.zig" | ||
| 567 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/encodings.zig" | ||
| 566 | "${CMAKE_SOURCE_DIR}/src/clang.zig" | 568 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 567 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" | 569 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 568 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" | 570 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| ... | @@ -827,6 +829,12 @@ else() | ... | @@ -827,6 +829,12 @@ else() |
| 827 | set(ZIG_STATIC_ARG "") | 829 | set(ZIG_STATIC_ARG "") |
| 828 | endif() | 830 | endif() |
| 829 | 831 | ||
| 832 | if(CMAKE_POSITION_INDEPENDENT_CODE) | ||
| 833 | set(ZIG_PIE_ARG="-Dpie") | ||
| 834 | else() | ||
| 835 | set(ZIG_PIE_ARG="") | ||
| 836 | endif() | ||
| 837 | |||
| 830 | set(ZIG_BUILD_ARGS | 838 | set(ZIG_BUILD_ARGS |
| 831 | --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" | 839 | --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" |
| 832 | "-Dconfig_h=${ZIG_CONFIG_H_OUT}" | 840 | "-Dconfig_h=${ZIG_CONFIG_H_OUT}" |
| ... | @@ -835,6 +843,7 @@ set(ZIG_BUILD_ARGS | ... | @@ -835,6 +843,7 @@ set(ZIG_BUILD_ARGS |
| 835 | ${ZIG_STATIC_ARG} | 843 | ${ZIG_STATIC_ARG} |
| 836 | ${ZIG_NO_LIB_ARG} | 844 | ${ZIG_NO_LIB_ARG} |
| 837 | ${ZIG_SINGLE_THREADED_ARG} | 845 | ${ZIG_SINGLE_THREADED_ARG} |
| 846 | ${ZIG_PIE_ARG} | ||
| 838 | "-Dtarget=${ZIG_TARGET_TRIPLE}" | 847 | "-Dtarget=${ZIG_TARGET_TRIPLE}" |
| 839 | "-Dcpu=${ZIG_TARGET_MCPU}" | 848 | "-Dcpu=${ZIG_TARGET_MCPU}" |
| 840 | "-Dversion-string=${RESOLVED_ZIG_VERSION}" | 849 | "-Dversion-string=${RESOLVED_ZIG_VERSION}" |
build.zig+114-108| ... | @@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void { | ... | @@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void { |
| 31 | const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false; | 31 | const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false; |
| 32 | 32 | ||
| 33 | const test_step = b.step("test", "Run all the tests"); | 33 | const test_step = b.step("test", "Run all the tests"); |
| 34 | const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false; | ||
| 35 | if (deprecated_skip_install_lib_files) { | ||
| 36 | std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{}); | ||
| 37 | } | ||
| 38 | const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files and langref to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files; | ||
| 34 | 39 | ||
| 35 | const docgen_exe = b.addExecutable(.{ | 40 | const docgen_exe = b.addExecutable(.{ |
| 36 | .name = "docgen", | 41 | .name = "docgen", |
| ... | @@ -40,28 +45,35 @@ pub fn build(b: *std.Build) !void { | ... | @@ -40,28 +45,35 @@ pub fn build(b: *std.Build) !void { |
| 40 | }); | 45 | }); |
| 41 | docgen_exe.single_threaded = single_threaded; | 46 | docgen_exe.single_threaded = single_threaded; |
| 42 | 47 | ||
| 43 | const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"}); | 48 | const docgen_cmd = b.addRunArtifact(docgen_exe); |
| 44 | const docgen_cmd = docgen_exe.run(); | 49 | docgen_cmd.addArgs(&.{ "--zig", b.zig_exe }); |
| 45 | docgen_cmd.addArgs(&[_][]const u8{ | 50 | if (b.zig_lib_dir) |p| { |
| 46 | "--zig", | 51 | docgen_cmd.addArgs(&.{ "--zig-lib-dir", p }); |
| 47 | b.zig_exe, | 52 | } |
| 48 | "doc" ++ fs.path.sep_str ++ "langref.html.in", | 53 | docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" }); |
| 49 | langref_out_path, | 54 | const langref_file = docgen_cmd.addOutputFileArg("langref.html"); |
| 50 | }); | 55 | const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html"); |
| 51 | docgen_cmd.step.dependOn(&docgen_exe.step); | 56 | if (!skip_install_lib_files) { |
| 57 | b.getInstallStep().dependOn(&install_langref.step); | ||
| 58 | } | ||
| 52 | 59 | ||
| 53 | const docs_step = b.step("docs", "Build documentation"); | 60 | const docs_step = b.step("docs", "Build documentation"); |
| 54 | docs_step.dependOn(&docgen_cmd.step); | 61 | docs_step.dependOn(&docgen_cmd.step); |
| 55 | 62 | ||
| 56 | const test_cases = b.addTest(.{ | 63 | // This is for legacy reasons, to be removed after our CI scripts are upgraded to use |
| 57 | .root_source_file = .{ .path = "src/test.zig" }, | 64 | // the file from the install prefix instead. |
| 65 | const legacy_write_to_cache = b.addWriteFiles(); | ||
| 66 | legacy_write_to_cache.addCopyFileToSource(langref_file, "zig-cache/langref.html"); | ||
| 67 | docs_step.dependOn(&legacy_write_to_cache.step); | ||
| 68 | |||
| 69 | const check_case_exe = b.addExecutable(.{ | ||
| 70 | .name = "check-case", | ||
| 71 | .root_source_file = .{ .path = "test/src/Cases.zig" }, | ||
| 58 | .optimize = optimize, | 72 | .optimize = optimize, |
| 59 | }); | 73 | }); |
| 60 | test_cases.main_pkg_path = "."; | 74 | check_case_exe.main_pkg_path = "."; |
| 61 | test_cases.stack_size = stack_size; | 75 | check_case_exe.stack_size = stack_size; |
| 62 | test_cases.single_threaded = single_threaded; | 76 | check_case_exe.single_threaded = single_threaded; |
| 63 | |||
| 64 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); | ||
| 65 | 77 | ||
| 66 | const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; | 78 | const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; |
| 67 | const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; | 79 | const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; |
| ... | @@ -74,11 +86,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -74,11 +86,6 @@ pub fn build(b: *std.Build) !void { |
| 74 | const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; | 86 | const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; |
| 75 | const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; | 87 | const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; |
| 76 | const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; | 88 | const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; |
| 77 | const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false; | ||
| 78 | if (deprecated_skip_install_lib_files) { | ||
| 79 | std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{}); | ||
| 80 | } | ||
| 81 | const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files; | ||
| 82 | 89 | ||
| 83 | const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; | 90 | const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; |
| 84 | 91 | ||
| ... | @@ -157,6 +164,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -157,6 +164,7 @@ pub fn build(b: *std.Build) !void { |
| 157 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c); | 164 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c); |
| 158 | const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false; | 165 | const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false; |
| 159 | const strip = b.option(bool, "strip", "Omit debug information"); | 166 | const strip = b.option(bool, "strip", "Omit debug information"); |
| 167 | const pie = b.option(bool, "pie", "Produce a Position Independent Executable"); | ||
| 160 | const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false; | 168 | const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false; |
| 161 | 169 | ||
| 162 | const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { | 170 | const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { |
| ... | @@ -167,6 +175,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -167,6 +175,7 @@ pub fn build(b: *std.Build) !void { |
| 167 | 175 | ||
| 168 | const exe = addCompilerStep(b, optimize, target); | 176 | const exe = addCompilerStep(b, optimize, target); |
| 169 | exe.strip = strip; | 177 | exe.strip = strip; |
| 178 | exe.pie = pie; | ||
| 170 | exe.sanitize_thread = sanitize_thread; | 179 | exe.sanitize_thread = sanitize_thread; |
| 171 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; | 180 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; |
| 172 | exe.install(); | 181 | exe.install(); |
| ... | @@ -178,13 +187,12 @@ pub fn build(b: *std.Build) !void { | ... | @@ -178,13 +187,12 @@ pub fn build(b: *std.Build) !void { |
| 178 | test_step.dependOn(&exe.step); | 187 | test_step.dependOn(&exe.step); |
| 179 | } | 188 | } |
| 180 | 189 | ||
| 181 | b.default_step.dependOn(&exe.step); | ||
| 182 | exe.single_threaded = single_threaded; | 190 | exe.single_threaded = single_threaded; |
| 183 | 191 | ||
| 184 | if (target.isWindows() and target.getAbi() == .gnu) { | 192 | if (target.isWindows() and target.getAbi() == .gnu) { |
| 185 | // LTO is currently broken on mingw, this can be removed when it's fixed. | 193 | // LTO is currently broken on mingw, this can be removed when it's fixed. |
| 186 | exe.want_lto = false; | 194 | exe.want_lto = false; |
| 187 | test_cases.want_lto = false; | 195 | check_case_exe.want_lto = false; |
| 188 | } | 196 | } |
| 189 | 197 | ||
| 190 | const exe_options = b.addOptions(); | 198 | const exe_options = b.addOptions(); |
| ... | @@ -199,11 +207,11 @@ pub fn build(b: *std.Build) !void { | ... | @@ -199,11 +207,11 @@ pub fn build(b: *std.Build) !void { |
| 199 | exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa); | 207 | exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa); |
| 200 | exe_options.addOption(bool, "force_gpa", force_gpa); | 208 | exe_options.addOption(bool, "force_gpa", force_gpa); |
| 201 | exe_options.addOption(bool, "only_c", only_c); | 209 | exe_options.addOption(bool, "only_c", only_c); |
| 202 | exe_options.addOption(bool, "omit_pkg_fetching_code", false); | 210 | exe_options.addOption(bool, "omit_pkg_fetching_code", only_c); |
| 203 | 211 | ||
| 204 | if (link_libc) { | 212 | if (link_libc) { |
| 205 | exe.linkLibC(); | 213 | exe.linkLibC(); |
| 206 | test_cases.linkLibC(); | 214 | check_case_exe.linkLibC(); |
| 207 | } | 215 | } |
| 208 | 216 | ||
| 209 | const is_debug = optimize == .Debug; | 217 | const is_debug = optimize == .Debug; |
| ... | @@ -289,14 +297,14 @@ pub fn build(b: *std.Build) !void { | ... | @@ -289,14 +297,14 @@ pub fn build(b: *std.Build) !void { |
| 289 | } | 297 | } |
| 290 | 298 | ||
| 291 | try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); | 299 | try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); |
| 292 | try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx); | 300 | try addCmakeCfgOptionsToExe(b, cfg, check_case_exe, use_zig_libcxx); |
| 293 | } else { | 301 | } else { |
| 294 | // Here we are -Denable-llvm but no cmake integration. | 302 | // Here we are -Denable-llvm but no cmake integration. |
| 295 | try addStaticLlvmOptionsToExe(exe); | 303 | try addStaticLlvmOptionsToExe(exe); |
| 296 | try addStaticLlvmOptionsToExe(test_cases); | 304 | try addStaticLlvmOptionsToExe(check_case_exe); |
| 297 | } | 305 | } |
| 298 | if (target.isWindows()) { | 306 | if (target.isWindows()) { |
| 299 | inline for (.{ exe, test_cases }) |artifact| { | 307 | inline for (.{ exe, check_case_exe }) |artifact| { |
| 300 | artifact.linkSystemLibrary("version"); | 308 | artifact.linkSystemLibrary("version"); |
| 301 | artifact.linkSystemLibrary("uuid"); | 309 | artifact.linkSystemLibrary("uuid"); |
| 302 | artifact.linkSystemLibrary("ole32"); | 310 | artifact.linkSystemLibrary("ole32"); |
| ... | @@ -341,8 +349,9 @@ pub fn build(b: *std.Build) !void { | ... | @@ -341,8 +349,9 @@ pub fn build(b: *std.Build) !void { |
| 341 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); | 349 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); |
| 342 | 350 | ||
| 343 | const test_cases_options = b.addOptions(); | 351 | const test_cases_options = b.addOptions(); |
| 344 | test_cases.addOptions("build_options", test_cases_options); | 352 | check_case_exe.addOptions("build_options", test_cases_options); |
| 345 | 353 | ||
| 354 | test_cases_options.addOption(bool, "enable_tracy", false); | ||
| 346 | test_cases_options.addOption(bool, "enable_logging", enable_logging); | 355 | test_cases_options.addOption(bool, "enable_logging", enable_logging); |
| 347 | test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); | 356 | test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); |
| 348 | test_cases_options.addOption(bool, "skip_non_native", skip_non_native); | 357 | test_cases_options.addOption(bool, "skip_non_native", skip_non_native); |
| ... | @@ -366,12 +375,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -366,12 +375,6 @@ pub fn build(b: *std.Build) !void { |
| 366 | test_cases_options.addOption(std.SemanticVersion, "semver", semver); | 375 | test_cases_options.addOption(std.SemanticVersion, "semver", semver); |
| 367 | test_cases_options.addOption(?[]const u8, "test_filter", test_filter); | 376 | test_cases_options.addOption(?[]const u8, "test_filter", test_filter); |
| 368 | 377 | ||
| 369 | const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); | ||
| 370 | test_cases_step.dependOn(&test_cases.step); | ||
| 371 | if (!skip_stage2_tests) { | ||
| 372 | test_step.dependOn(test_cases_step); | ||
| 373 | } | ||
| 374 | |||
| 375 | var chosen_opt_modes_buf: [4]builtin.Mode = undefined; | 378 | var chosen_opt_modes_buf: [4]builtin.Mode = undefined; |
| 376 | var chosen_mode_index: usize = 0; | 379 | var chosen_mode_index: usize = 0; |
| 377 | if (!skip_debug) { | 380 | if (!skip_debug) { |
| ... | @@ -392,96 +395,101 @@ pub fn build(b: *std.Build) !void { | ... | @@ -392,96 +395,101 @@ pub fn build(b: *std.Build) !void { |
| 392 | } | 395 | } |
| 393 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; | 396 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; |
| 394 | 397 | ||
| 395 | // run stage1 `zig fmt` on this build.zig file just to make sure it works | 398 | const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; |
| 396 | test_step.dependOn(&fmt_build_zig.step); | 399 | const fmt_exclude_paths = &.{"test/cases"}; |
| 397 | const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); | 400 | const do_fmt = b.addFmt(.{ |
| 398 | fmt_step.dependOn(&fmt_build_zig.step); | 401 | .paths = fmt_include_paths, |
| 399 | 402 | .exclude_paths = fmt_exclude_paths, | |
| 400 | test_step.dependOn(tests.addPkgTests( | 403 | }); |
| 401 | b, | ||
| 402 | test_filter, | ||
| 403 | "test/behavior.zig", | ||
| 404 | "behavior", | ||
| 405 | "Run the behavior tests", | ||
| 406 | optimization_modes, | ||
| 407 | skip_single_threaded, | ||
| 408 | skip_non_native, | ||
| 409 | skip_libc, | ||
| 410 | skip_stage1, | ||
| 411 | skip_stage2_tests, | ||
| 412 | )); | ||
| 413 | 404 | ||
| 414 | test_step.dependOn(tests.addPkgTests( | 405 | b.step("test-fmt", "Check source files having conforming formatting").dependOn(&b.addFmt(.{ |
| 415 | b, | 406 | .paths = fmt_include_paths, |
| 416 | test_filter, | 407 | .exclude_paths = fmt_exclude_paths, |
| 417 | "lib/compiler_rt.zig", | 408 | .check = true, |
| 418 | "compiler-rt", | 409 | }).step); |
| 419 | "Run the compiler_rt tests", | ||
| 420 | optimization_modes, | ||
| 421 | true, // skip_single_threaded | ||
| 422 | skip_non_native, | ||
| 423 | true, // skip_libc | ||
| 424 | skip_stage1, | ||
| 425 | skip_stage2_tests or true, // TODO get these all passing | ||
| 426 | )); | ||
| 427 | 410 | ||
| 428 | test_step.dependOn(tests.addPkgTests( | 411 | const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); |
| 429 | b, | 412 | try tests.addCases(b, test_cases_step, test_filter, check_case_exe); |
| 430 | test_filter, | 413 | if (!skip_stage2_tests) test_step.dependOn(test_cases_step); |
| 431 | "lib/c.zig", | 414 | |
| 432 | "universal-libc", | 415 | test_step.dependOn(tests.addModuleTests(b, .{ |
| 433 | "Run the universal libc tests", | 416 | .test_filter = test_filter, |
| 434 | optimization_modes, | 417 | .root_src = "test/behavior.zig", |
| 435 | true, // skip_single_threaded | 418 | .name = "behavior", |
| 436 | skip_non_native, | 419 | .desc = "Run the behavior tests", |
| 437 | true, // skip_libc | 420 | .optimize_modes = optimization_modes, |
| 438 | skip_stage1, | 421 | .skip_single_threaded = skip_single_threaded, |
| 439 | skip_stage2_tests or true, // TODO get these all passing | 422 | .skip_non_native = skip_non_native, |
| 440 | )); | 423 | .skip_libc = skip_libc, |
| 424 | .skip_stage1 = skip_stage1, | ||
| 425 | .skip_stage2 = skip_stage2_tests, | ||
| 426 | .max_rss = 1 * 1024 * 1024 * 1024, | ||
| 427 | })); | ||
| 428 | |||
| 429 | test_step.dependOn(tests.addModuleTests(b, .{ | ||
| 430 | .test_filter = test_filter, | ||
| 431 | .root_src = "lib/compiler_rt.zig", | ||
| 432 | .name = "compiler-rt", | ||
| 433 | .desc = "Run the compiler_rt tests", | ||
| 434 | .optimize_modes = optimization_modes, | ||
| 435 | .skip_single_threaded = true, | ||
| 436 | .skip_non_native = skip_non_native, | ||
| 437 | .skip_libc = true, | ||
| 438 | .skip_stage1 = skip_stage1, | ||
| 439 | .skip_stage2 = true, // TODO get all these passing | ||
| 440 | })); | ||
| 441 | |||
| 442 | test_step.dependOn(tests.addModuleTests(b, .{ | ||
| 443 | .test_filter = test_filter, | ||
| 444 | .root_src = "lib/c.zig", | ||
| 445 | .name = "universal-libc", | ||
| 446 | .desc = "Run the universal libc tests", | ||
| 447 | .optimize_modes = optimization_modes, | ||
| 448 | .skip_single_threaded = true, | ||
| 449 | .skip_non_native = skip_non_native, | ||
| 450 | .skip_libc = true, | ||
| 451 | .skip_stage1 = skip_stage1, | ||
| 452 | .skip_stage2 = true, // TODO get all these passing | ||
| 453 | })); | ||
| 441 | 454 | ||
| 442 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); | 455 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); |
| 443 | test_step.dependOn(tests.addStandaloneTests( | 456 | test_step.dependOn(tests.addStandaloneTests( |
| 444 | b, | 457 | b, |
| 445 | test_filter, | ||
| 446 | optimization_modes, | 458 | optimization_modes, |
| 447 | skip_non_native, | ||
| 448 | enable_macos_sdk, | 459 | enable_macos_sdk, |
| 449 | target, | ||
| 450 | skip_stage2_tests, | 460 | skip_stage2_tests, |
| 451 | b.enable_darling, | ||
| 452 | b.enable_qemu, | ||
| 453 | b.enable_rosetta, | ||
| 454 | b.enable_wasmtime, | ||
| 455 | b.enable_wine, | ||
| 456 | enable_symlinks_windows, | 461 | enable_symlinks_windows, |
| 457 | )); | 462 | )); |
| 458 | test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); | 463 | test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); |
| 459 | test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); | 464 | test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); |
| 460 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); | 465 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); |
| 461 | test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); | 466 | test_step.dependOn(tests.addCliTests(b)); |
| 462 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); | 467 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); |
| 463 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); | 468 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); |
| 464 | if (!skip_run_translated_c) { | 469 | if (!skip_run_translated_c) { |
| 465 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); | 470 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); |
| 466 | } | 471 | } |
| 467 | // tests for this feature are disabled until we have the self-hosted compiler available | ||
| 468 | // test_step.dependOn(tests.addGenHTests(b, test_filter)); | ||
| 469 | 472 | ||
| 470 | test_step.dependOn(tests.addPkgTests( | 473 | test_step.dependOn(tests.addModuleTests(b, .{ |
| 471 | b, | 474 | .test_filter = test_filter, |
| 472 | test_filter, | 475 | .root_src = "lib/std/std.zig", |
| 473 | "lib/std/std.zig", | 476 | .name = "std", |
| 474 | "std", | 477 | .desc = "Run the standard library tests", |
| 475 | "Run the standard library tests", | 478 | .optimize_modes = optimization_modes, |
| 476 | optimization_modes, | 479 | .skip_single_threaded = skip_single_threaded, |
| 477 | skip_single_threaded, | 480 | .skip_non_native = skip_non_native, |
| 478 | skip_non_native, | 481 | .skip_libc = skip_libc, |
| 479 | skip_libc, | 482 | .skip_stage1 = skip_stage1, |
| 480 | skip_stage1, | 483 | .skip_stage2 = true, // TODO get all these passing |
| 481 | true, // TODO get these all passing | 484 | // I observed a value of 3398275072 on my M1, and multiplied by 1.1 to |
| 482 | )); | 485 | // get this amount: |
| 486 | .max_rss = 3738102579, | ||
| 487 | })); | ||
| 483 | 488 | ||
| 484 | try addWasiUpdateStep(b, version); | 489 | try addWasiUpdateStep(b, version); |
| 490 | |||
| 491 | b.step("fmt", "Modify source files in place to have conforming formatting") | ||
| 492 | .dependOn(&do_fmt.step); | ||
| 485 | } | 493 | } |
| 486 | 494 | ||
| 487 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | 495 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
| ... | @@ -510,6 +518,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | ... | @@ -510,6 +518,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
| 510 | exe_options.addOption(bool, "enable_tracy_callstack", false); | 518 | exe_options.addOption(bool, "enable_tracy_callstack", false); |
| 511 | exe_options.addOption(bool, "enable_tracy_allocation", false); | 519 | exe_options.addOption(bool, "enable_tracy_allocation", false); |
| 512 | exe_options.addOption(bool, "value_tracing", false); | 520 | exe_options.addOption(bool, "value_tracing", false); |
| 521 | exe_options.addOption(bool, "omit_pkg_fetching_code", true); | ||
| 513 | 522 | ||
| 514 | const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" }); | 523 | const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" }); |
| 515 | run_opt.addArtifactArg(exe); | 524 | run_opt.addArtifactArg(exe); |
| ... | @@ -681,10 +690,7 @@ fn addCxxKnownPath( | ... | @@ -681,10 +690,7 @@ fn addCxxKnownPath( |
| 681 | ) !void { | 690 | ) !void { |
| 682 | if (!std.process.can_spawn) | 691 | if (!std.process.can_spawn) |
| 683 | return error.RequiredLibraryNotFound; | 692 | return error.RequiredLibraryNotFound; |
| 684 | const path_padded = try b.exec(&[_][]const u8{ | 693 | const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) }); |
| 685 | ctx.cxx_compiler, | ||
| 686 | b.fmt("-print-file-name={s}", .{objname}), | ||
| 687 | }); | ||
| 688 | var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); | 694 | var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); |
| 689 | const path_unpadded = tokenizer.next().?; | 695 | const path_unpadded = tokenizer.next().?; |
| 690 | if (mem.eql(u8, path_unpadded, objname)) { | 696 | if (mem.eql(u8, path_unpadded, objname)) { |
ci/aarch64-linux-debug.sh+7-1| ... | @@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \ | ... | @@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \ |
| 67 | --zig-lib-dir "$(pwd)/../lib" | 67 | --zig-lib-dir "$(pwd)/../lib" |
| 68 | 68 | ||
| 69 | # Look for HTML errors. | 69 | # Look for HTML errors. |
| 70 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 70 | tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html" |
| 71 | 71 | ||
| 72 | # Produce the experimental std lib documentation. | 72 | # Produce the experimental std lib documentation. |
| 73 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 73 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| ... | @@ -98,3 +98,9 @@ unset CXX | ... | @@ -98,3 +98,9 @@ unset CXX |
| 98 | ninja install | 98 | ninja install |
| 99 | 99 | ||
| 100 | stage3/bin/zig test ../test/behavior.zig -I../test | 100 | stage3/bin/zig test ../test/behavior.zig -I../test |
| 101 | stage3/bin/zig build -p stage4 \ | ||
| 102 | -Dstatic-llvm \ | ||
| 103 | -Dtarget=native-native-musl \ | ||
| 104 | --search-prefix "$PREFIX" \ | ||
| 105 | --zig-lib-dir "$(pwd)/../lib" | ||
| 106 | stage4/bin/zig test ../test/behavior.zig -I../test |
ci/aarch64-linux-release.sh+7-1| ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ | ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ |
| 67 | --zig-lib-dir "$(pwd)/../lib" | 67 | --zig-lib-dir "$(pwd)/../lib" |
| 68 | 68 | ||
| 69 | # Look for HTML errors. | 69 | # Look for HTML errors. |
| 70 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 70 | tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html" |
| 71 | 71 | ||
| 72 | # Produce the experimental std lib documentation. | 72 | # Produce the experimental std lib documentation. |
| 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| ... | @@ -98,3 +98,9 @@ unset CXX | ... | @@ -98,3 +98,9 @@ unset CXX |
| 98 | ninja install | 98 | ninja install |
| 99 | 99 | ||
| 100 | stage3/bin/zig test ../test/behavior.zig -I../test | 100 | stage3/bin/zig test ../test/behavior.zig -I../test |
| 101 | stage3/bin/zig build -p stage4 \ | ||
| 102 | -Dstatic-llvm \ | ||
| 103 | -Dtarget=native-native-musl \ | ||
| 104 | --search-prefix "$PREFIX" \ | ||
| 105 | --zig-lib-dir "$(pwd)/../lib" | ||
| 106 | stage4/bin/zig test ../test/behavior.zig -I../test |
ci/x86_64-linux-debug.sh+7-1| ... | @@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \ | ... | @@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \ |
| 66 | --zig-lib-dir "$(pwd)/../lib" | 66 | --zig-lib-dir "$(pwd)/../lib" |
| 67 | 67 | ||
| 68 | # Look for HTML errors. | 68 | # Look for HTML errors. |
| 69 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 69 | tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html" |
| 70 | 70 | ||
| 71 | # Produce the experimental std lib documentation. | 71 | # Produce the experimental std lib documentation. |
| 72 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 72 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| ... | @@ -97,3 +97,9 @@ unset CXX | ... | @@ -97,3 +97,9 @@ unset CXX |
| 97 | ninja install | 97 | ninja install |
| 98 | 98 | ||
| 99 | stage3/bin/zig test ../test/behavior.zig -I../test | 99 | stage3/bin/zig test ../test/behavior.zig -I../test |
| 100 | stage3/bin/zig build -p stage4 \ | ||
| 101 | -Dstatic-llvm \ | ||
| 102 | -Dtarget=native-native-musl \ | ||
| 103 | --search-prefix "$PREFIX" \ | ||
| 104 | --zig-lib-dir "$(pwd)/../lib" | ||
| 105 | stage4/bin/zig test ../test/behavior.zig -I../test |
ci/x86_64-linux-release.sh+7-1| ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ | ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ |
| 67 | --zig-lib-dir "$(pwd)/../lib" | 67 | --zig-lib-dir "$(pwd)/../lib" |
| 68 | 68 | ||
| 69 | # Look for HTML errors. | 69 | # Look for HTML errors. |
| 70 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 70 | tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html" |
| 71 | 71 | ||
| 72 | # Produce the experimental std lib documentation. | 72 | # Produce the experimental std lib documentation. |
| 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| ... | @@ -114,3 +114,9 @@ unset CXX | ... | @@ -114,3 +114,9 @@ unset CXX |
| 114 | ninja install | 114 | ninja install |
| 115 | 115 | ||
| 116 | stage3/bin/zig test ../test/behavior.zig -I../test | 116 | stage3/bin/zig test ../test/behavior.zig -I../test |
| 117 | stage3/bin/zig build -p stage4 \ | ||
| 118 | -Dstatic-llvm \ | ||
| 119 | -Dtarget=native-native-musl \ | ||
| 120 | --search-prefix "$PREFIX" \ | ||
| 121 | --zig-lib-dir "$(pwd)/../lib" | ||
| 122 | stage4/bin/zig test ../test/behavior.zig -I../test |
doc/docgen.zig+62-27| ... | @@ -28,10 +28,10 @@ const usage = | ... | @@ -28,10 +28,10 @@ const usage = |
| 28 | \\ | 28 | \\ |
| 29 | ; | 29 | ; |
| 30 | 30 | ||
| 31 | fn errorf(comptime format: []const u8, args: anytype) noreturn { | 31 | fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 32 | const stderr = io.getStdErr().writer(); | 32 | const stderr = io.getStdErr().writer(); |
| 33 | 33 | ||
| 34 | stderr.print("error: " ++ format, args) catch {}; | 34 | stderr.print("error: " ++ format ++ "\n", args) catch {}; |
| 35 | process.exit(1); | 35 | process.exit(1); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| ... | @@ -45,6 +45,7 @@ pub fn main() !void { | ... | @@ -45,6 +45,7 @@ pub fn main() !void { |
| 45 | if (!args_it.skip()) @panic("expected self arg"); | 45 | if (!args_it.skip()) @panic("expected self arg"); |
| 46 | 46 | ||
| 47 | var zig_exe: []const u8 = "zig"; | 47 | var zig_exe: []const u8 = "zig"; |
| 48 | var opt_zig_lib_dir: ?[]const u8 = null; | ||
| 48 | var do_code_tests = true; | 49 | var do_code_tests = true; |
| 49 | var files = [_][]const u8{ "", "" }; | 50 | var files = [_][]const u8{ "", "" }; |
| 50 | 51 | ||
| ... | @@ -59,24 +60,29 @@ pub fn main() !void { | ... | @@ -59,24 +60,29 @@ pub fn main() !void { |
| 59 | if (args_it.next()) |param| { | 60 | if (args_it.next()) |param| { |
| 60 | zig_exe = param; | 61 | zig_exe = param; |
| 61 | } else { | 62 | } else { |
| 62 | errorf("expected parameter after --zig\n", .{}); | 63 | fatal("expected parameter after --zig", .{}); |
| 64 | } | ||
| 65 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { | ||
| 66 | if (args_it.next()) |param| { | ||
| 67 | opt_zig_lib_dir = param; | ||
| 68 | } else { | ||
| 69 | fatal("expected parameter after --zig-lib-dir", .{}); | ||
| 63 | } | 70 | } |
| 64 | } else if (mem.eql(u8, arg, "--skip-code-tests")) { | 71 | } else if (mem.eql(u8, arg, "--skip-code-tests")) { |
| 65 | do_code_tests = false; | 72 | do_code_tests = false; |
| 66 | } else { | 73 | } else { |
| 67 | errorf("unrecognized option: '{s}'\n", .{arg}); | 74 | fatal("unrecognized option: '{s}'", .{arg}); |
| 68 | } | 75 | } |
| 69 | } else { | 76 | } else { |
| 70 | if (i > 1) { | 77 | if (i > 1) { |
| 71 | errorf("too many arguments\n", .{}); | 78 | fatal("too many arguments", .{}); |
| 72 | } | 79 | } |
| 73 | files[i] = arg; | 80 | files[i] = arg; |
| 74 | i += 1; | 81 | i += 1; |
| 75 | } | 82 | } |
| 76 | } | 83 | } |
| 77 | if (i < 2) { | 84 | if (i < 2) { |
| 78 | errorf("not enough arguments\n", .{}); | 85 | fatal("not enough arguments", .{}); |
| 79 | process.exit(1); | ||
| 80 | } | 86 | } |
| 81 | 87 | ||
| 82 | var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only }); | 88 | var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only }); |
| ... | @@ -95,7 +101,7 @@ pub fn main() !void { | ... | @@ -95,7 +101,7 @@ pub fn main() !void { |
| 95 | try fs.cwd().makePath(tmp_dir_name); | 101 | try fs.cwd().makePath(tmp_dir_name); |
| 96 | defer fs.cwd().deleteTree(tmp_dir_name) catch {}; | 102 | defer fs.cwd().deleteTree(tmp_dir_name) catch {}; |
| 97 | 103 | ||
| 98 | try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, do_code_tests); | 104 | try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, opt_zig_lib_dir, do_code_tests); |
| 99 | try buffered_writer.flush(); | 105 | try buffered_writer.flush(); |
| 100 | } | 106 | } |
| 101 | 107 | ||
| ... | @@ -1268,9 +1274,10 @@ fn genHtml( | ... | @@ -1268,9 +1274,10 @@ fn genHtml( |
| 1268 | toc: *Toc, | 1274 | toc: *Toc, |
| 1269 | out: anytype, | 1275 | out: anytype, |
| 1270 | zig_exe: []const u8, | 1276 | zig_exe: []const u8, |
| 1277 | opt_zig_lib_dir: ?[]const u8, | ||
| 1271 | do_code_tests: bool, | 1278 | do_code_tests: bool, |
| 1272 | ) !void { | 1279 | ) !void { |
| 1273 | var progress = Progress{}; | 1280 | var progress = Progress{ .dont_print_on_dumb = true }; |
| 1274 | const root_node = progress.start("Generating docgen examples", toc.nodes.len); | 1281 | const root_node = progress.start("Generating docgen examples", toc.nodes.len); |
| 1275 | defer root_node.end(); | 1282 | defer root_node.end(); |
| 1276 | 1283 | ||
| ... | @@ -1278,7 +1285,7 @@ fn genHtml( | ... | @@ -1278,7 +1285,7 @@ fn genHtml( |
| 1278 | try env_map.put("ZIG_DEBUG_COLOR", "1"); | 1285 | try env_map.put("ZIG_DEBUG_COLOR", "1"); |
| 1279 | 1286 | ||
| 1280 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | 1287 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); |
| 1281 | const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe); | 1288 | const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe, opt_zig_lib_dir); |
| 1282 | 1289 | ||
| 1283 | for (toc.nodes) |node| { | 1290 | for (toc.nodes) |node| { |
| 1284 | defer root_node.completeOne(); | 1291 | defer root_node.completeOne(); |
| ... | @@ -1370,6 +1377,9 @@ fn genHtml( | ... | @@ -1370,6 +1377,9 @@ fn genHtml( |
| 1370 | "--color", "on", | 1377 | "--color", "on", |
| 1371 | "--enable-cache", tmp_source_file_name, | 1378 | "--enable-cache", tmp_source_file_name, |
| 1372 | }); | 1379 | }); |
| 1380 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1381 | try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); | ||
| 1382 | } | ||
| 1373 | 1383 | ||
| 1374 | try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext}); | 1384 | try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext}); |
| 1375 | 1385 | ||
| ... | @@ -1512,8 +1522,12 @@ fn genHtml( | ... | @@ -1512,8 +1522,12 @@ fn genHtml( |
| 1512 | defer test_args.deinit(); | 1522 | defer test_args.deinit(); |
| 1513 | 1523 | ||
| 1514 | try test_args.appendSlice(&[_][]const u8{ | 1524 | try test_args.appendSlice(&[_][]const u8{ |
| 1515 | zig_exe, "test", tmp_source_file_name, | 1525 | zig_exe, "test", |
| 1526 | tmp_source_file_name, | ||
| 1516 | }); | 1527 | }); |
| 1528 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1529 | try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); | ||
| 1530 | } | ||
| 1517 | try shell_out.print("$ zig test {s}.zig ", .{code.name}); | 1531 | try shell_out.print("$ zig test {s}.zig ", .{code.name}); |
| 1518 | 1532 | ||
| 1519 | switch (code.mode) { | 1533 | switch (code.mode) { |
| ... | @@ -1564,12 +1578,13 @@ fn genHtml( | ... | @@ -1564,12 +1578,13 @@ fn genHtml( |
| 1564 | defer test_args.deinit(); | 1578 | defer test_args.deinit(); |
| 1565 | 1579 | ||
| 1566 | try test_args.appendSlice(&[_][]const u8{ | 1580 | try test_args.appendSlice(&[_][]const u8{ |
| 1567 | zig_exe, | 1581 | zig_exe, "test", |
| 1568 | "test", | 1582 | "--color", "on", |
| 1569 | "--color", | ||
| 1570 | "on", | ||
| 1571 | tmp_source_file_name, | 1583 | tmp_source_file_name, |
| 1572 | }); | 1584 | }); |
| 1585 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1586 | try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); | ||
| 1587 | } | ||
| 1573 | try shell_out.print("$ zig test {s}.zig ", .{code.name}); | 1588 | try shell_out.print("$ zig test {s}.zig ", .{code.name}); |
| 1574 | 1589 | ||
| 1575 | switch (code.mode) { | 1590 | switch (code.mode) { |
| ... | @@ -1624,8 +1639,12 @@ fn genHtml( | ... | @@ -1624,8 +1639,12 @@ fn genHtml( |
| 1624 | defer test_args.deinit(); | 1639 | defer test_args.deinit(); |
| 1625 | 1640 | ||
| 1626 | try test_args.appendSlice(&[_][]const u8{ | 1641 | try test_args.appendSlice(&[_][]const u8{ |
| 1627 | zig_exe, "test", tmp_source_file_name, | 1642 | zig_exe, "test", |
| 1643 | tmp_source_file_name, | ||
| 1628 | }); | 1644 | }); |
| 1645 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1646 | try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); | ||
| 1647 | } | ||
| 1629 | var mode_arg: []const u8 = ""; | 1648 | var mode_arg: []const u8 = ""; |
| 1630 | switch (code.mode) { | 1649 | switch (code.mode) { |
| 1631 | .Debug => {}, | 1650 | .Debug => {}, |
| ... | @@ -1684,17 +1703,17 @@ fn genHtml( | ... | @@ -1684,17 +1703,17 @@ fn genHtml( |
| 1684 | defer build_args.deinit(); | 1703 | defer build_args.deinit(); |
| 1685 | 1704 | ||
| 1686 | try build_args.appendSlice(&[_][]const u8{ | 1705 | try build_args.appendSlice(&[_][]const u8{ |
| 1687 | zig_exe, | 1706 | zig_exe, "build-obj", |
| 1688 | "build-obj", | 1707 | "--color", "on", |
| 1708 | "--name", code.name, | ||
| 1689 | tmp_source_file_name, | 1709 | tmp_source_file_name, |
| 1690 | "--color", | ||
| 1691 | "on", | ||
| 1692 | "--name", | ||
| 1693 | code.name, | ||
| 1694 | try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{ | 1710 | try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{ |
| 1695 | tmp_dir_name, fs.path.sep, name_plus_obj_ext, | 1711 | tmp_dir_name, fs.path.sep, name_plus_obj_ext, |
| 1696 | }), | 1712 | }), |
| 1697 | }); | 1713 | }); |
| 1714 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1715 | try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); | ||
| 1716 | } | ||
| 1698 | 1717 | ||
| 1699 | try shell_out.print("$ zig build-obj {s}.zig ", .{code.name}); | 1718 | try shell_out.print("$ zig build-obj {s}.zig ", .{code.name}); |
| 1700 | 1719 | ||
| ... | @@ -1758,13 +1777,15 @@ fn genHtml( | ... | @@ -1758,13 +1777,15 @@ fn genHtml( |
| 1758 | defer test_args.deinit(); | 1777 | defer test_args.deinit(); |
| 1759 | 1778 | ||
| 1760 | try test_args.appendSlice(&[_][]const u8{ | 1779 | try test_args.appendSlice(&[_][]const u8{ |
| 1761 | zig_exe, | 1780 | zig_exe, "build-lib", |
| 1762 | "build-lib", | ||
| 1763 | tmp_source_file_name, | 1781 | tmp_source_file_name, |
| 1764 | try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{ | 1782 | try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{ |
| 1765 | tmp_dir_name, fs.path.sep_str, bin_basename, | 1783 | tmp_dir_name, fs.path.sep_str, bin_basename, |
| 1766 | }), | 1784 | }), |
| 1767 | }); | 1785 | }); |
| 1786 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1787 | try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir }); | ||
| 1788 | } | ||
| 1768 | try shell_out.print("$ zig build-lib {s}.zig ", .{code.name}); | 1789 | try shell_out.print("$ zig build-lib {s}.zig ", .{code.name}); |
| 1769 | 1790 | ||
| 1770 | switch (code.mode) { | 1791 | switch (code.mode) { |
| ... | @@ -1829,9 +1850,23 @@ fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8 | ... | @@ -1829,9 +1850,23 @@ fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8 |
| 1829 | return result; | 1850 | return result; |
| 1830 | } | 1851 | } |
| 1831 | 1852 | ||
| 1832 | fn getBuiltinCode(allocator: Allocator, env_map: *process.EnvMap, zig_exe: []const u8) ![]const u8 { | 1853 | fn getBuiltinCode( |
| 1833 | const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" }); | 1854 | allocator: Allocator, |
| 1834 | return result.stdout; | 1855 | env_map: *process.EnvMap, |
| 1856 | zig_exe: []const u8, | ||
| 1857 | opt_zig_lib_dir: ?[]const u8, | ||
| 1858 | ) ![]const u8 { | ||
| 1859 | if (opt_zig_lib_dir) |zig_lib_dir| { | ||
| 1860 | const result = try exec(allocator, env_map, &.{ | ||
| 1861 | zig_exe, "build-obj", "--show-builtin", "--zig-lib-dir", zig_lib_dir, | ||
| 1862 | }); | ||
| 1863 | return result.stdout; | ||
| 1864 | } else { | ||
| 1865 | const result = try exec(allocator, env_map, &.{ | ||
| 1866 | zig_exe, "build-obj", "--show-builtin", | ||
| 1867 | }); | ||
| 1868 | return result.stdout; | ||
| 1869 | } | ||
| 1835 | } | 1870 | } |
| 1836 | 1871 | ||
| 1837 | fn dumpArgs(args: []const []const u8) void { | 1872 | fn dumpArgs(args: []const []const u8) void { |
doc/langref.html.in+28-12| ... | @@ -7457,20 +7457,20 @@ pub const STDOUT_FILENO = 1; | ... | @@ -7457,20 +7457,20 @@ pub const STDOUT_FILENO = 1; |
| 7457 | 7457 | ||
| 7458 | pub fn syscall1(number: usize, arg1: usize) usize { | 7458 | pub fn syscall1(number: usize, arg1: usize) usize { |
| 7459 | return asm volatile ("syscall" | 7459 | return asm volatile ("syscall" |
| 7460 | : [ret] "={rax}" (-> usize) | 7460 | : [ret] "={rax}" (-> usize), |
| 7461 | : [number] "{rax}" (number), | 7461 | : [number] "{rax}" (number), |
| 7462 | [arg1] "{rdi}" (arg1) | 7462 | [arg1] "{rdi}" (arg1), |
| 7463 | : "rcx", "r11" | 7463 | : "rcx", "r11" |
| 7464 | ); | 7464 | ); |
| 7465 | } | 7465 | } |
| 7466 | 7466 | ||
| 7467 | pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { | 7467 | pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { |
| 7468 | return asm volatile ("syscall" | 7468 | return asm volatile ("syscall" |
| 7469 | : [ret] "={rax}" (-> usize) | 7469 | : [ret] "={rax}" (-> usize), |
| 7470 | : [number] "{rax}" (number), | 7470 | : [number] "{rax}" (number), |
| 7471 | [arg1] "{rdi}" (arg1), | 7471 | [arg1] "{rdi}" (arg1), |
| 7472 | [arg2] "{rsi}" (arg2), | 7472 | [arg2] "{rsi}" (arg2), |
| 7473 | [arg3] "{rdx}" (arg3) | 7473 | [arg3] "{rdx}" (arg3), |
| 7474 | : "rcx", "r11" | 7474 | : "rcx", "r11" |
| 7475 | ); | 7475 | ); |
| 7476 | } | 7476 | } |
| ... | @@ -7519,14 +7519,14 @@ pub fn syscall1(number: usize, arg1: usize) usize { | ... | @@ -7519,14 +7519,14 @@ pub fn syscall1(number: usize, arg1: usize) usize { |
| 7519 | // type is the result type of the inline assembly expression. | 7519 | // type is the result type of the inline assembly expression. |
| 7520 | // If it is a value binding, then `%[ret]` syntax would be used | 7520 | // If it is a value binding, then `%[ret]` syntax would be used |
| 7521 | // to refer to the register bound to the value. | 7521 | // to refer to the register bound to the value. |
| 7522 | (-> usize) | 7522 | (-> usize), |
| 7523 | // Next is the list of inputs. | 7523 | // Next is the list of inputs. |
| 7524 | // The constraint for these inputs means, "when the assembly code is | 7524 | // The constraint for these inputs means, "when the assembly code is |
| 7525 | // executed, $rax shall have the value of `number` and $rdi shall have | 7525 | // executed, $rax shall have the value of `number` and $rdi shall have |
| 7526 | // the value of `arg1`". Any number of input parameters is allowed, | 7526 | // the value of `arg1`". Any number of input parameters is allowed, |
| 7527 | // including none. | 7527 | // including none. |
| 7528 | : [number] "{rax}" (number), | 7528 | : [number] "{rax}" (number), |
| 7529 | [arg1] "{rdi}" (arg1) | 7529 | [arg1] "{rdi}" (arg1), |
| 7530 | // Next is the list of clobbers. These declare a set of registers whose | 7530 | // Next is the list of clobbers. These declare a set of registers whose |
| 7531 | // values will not be preserved by the execution of this assembly code. | 7531 | // values will not be preserved by the execution of this assembly code. |
| 7532 | // These do not include output or input registers. The special clobber | 7532 | // These do not include output or input registers. The special clobber |
| ... | @@ -7818,12 +7818,14 @@ comptime { | ... | @@ -7818,12 +7818,14 @@ comptime { |
| 7818 | <p> | 7818 | <p> |
| 7819 | This function inserts a platform-specific debug trap instruction which causes | 7819 | This function inserts a platform-specific debug trap instruction which causes |
| 7820 | debuggers to break there. | 7820 | debuggers to break there. |
| 7821 | Unlike for {#syntax#}@trap(){#endsyntax#}, execution may continue after this point if the program is resumed. | ||
| 7821 | </p> | 7822 | </p> |
| 7822 | <p> | 7823 | <p> |
| 7823 | This function is only valid within function scope. | 7824 | This function is only valid within function scope. |
| 7824 | </p> | 7825 | </p> |
| 7825 | 7826 | {#see_also|@trap#} | |
| 7826 | {#header_close#} | 7827 | {#header_close#} |
| 7828 | |||
| 7827 | {#header_open|@mulAdd#} | 7829 | {#header_open|@mulAdd#} |
| 7828 | <pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre> | 7830 | <pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre> |
| 7829 | <p> | 7831 | <p> |
| ... | @@ -9393,6 +9395,19 @@ fn List(comptime T: type) type { | ... | @@ -9393,6 +9395,19 @@ fn List(comptime T: type) type { |
| 9393 | </p> | 9395 | </p> |
| 9394 | {#header_close#} | 9396 | {#header_close#} |
| 9395 | 9397 | ||
| 9398 | {#header_open|@trap#} | ||
| 9399 | <pre>{#syntax#}@trap() noreturn{#endsyntax#}</pre> | ||
| 9400 | <p> | ||
| 9401 | This function inserts a platform-specific trap/jam instruction which can be used to exit the program abnormally. | ||
| 9402 | This may be implemented by explicitly emitting an invalid instruction which may cause an illegal instruction exception of some sort. | ||
| 9403 | Unlike for {#syntax#}@breakpoint(){#endsyntax#}, execution does not continue after this point. | ||
| 9404 | </p> | ||
| 9405 | <p> | ||
| 9406 | Outside function scope, this builtin causes a compile error. | ||
| 9407 | </p> | ||
| 9408 | {#see_also|@breakpoint#} | ||
| 9409 | {#header_close#} | ||
| 9410 | |||
| 9396 | {#header_open|@truncate#} | 9411 | {#header_open|@truncate#} |
| 9397 | <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre> | 9412 | <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre> |
| 9398 | <p> | 9413 | <p> |
| ... | @@ -9565,9 +9580,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -9565,9 +9580,10 @@ pub fn build(b: *std.Build) void { |
| 9565 | This causes these options to be available: | 9580 | This causes these options to be available: |
| 9566 | </p> | 9581 | </p> |
| 9567 | <dl> | 9582 | <dl> |
| 9568 | <dt><kbd>-Drelease-safe=[bool]</kbd></dt><dd>Optimizations on and safety on</dd> | 9583 | <dt><kbd>-Doptimize=Debug</kbd></dt><dd>Optimizations off and safety on (default)</dd> |
| 9569 | <dt><kbd>-Drelease-fast=[bool]</kbd></dt><dd>Optimizations on and safety off</dd> | 9584 | <dt><kbd>-Doptimize=ReleaseSafe</kbd></dt><dd>Optimizations on and safety on</dd> |
| 9570 | <dt><kbd>-Drelease-small=[bool]</kbd></dt><dd>Size optimizations on and safety off</dd> | 9585 | <dt><kbd>-Doptimize=ReleaseFast</kbd></dt><dd>Optimizations on and safety off</dd> |
| 9586 | <dt><kbd>-Doptimize=ReleaseSmall</kbd></dt><dd>Size optimizations on and safety off</dd> | ||
| 9571 | </dl> | 9587 | </dl> |
| 9572 | {#header_open|Debug#} | 9588 | {#header_open|Debug#} |
| 9573 | {#shell_samp#}$ zig build-exe example.zig{#end_shell_samp#} | 9589 | {#shell_samp#}$ zig build-exe example.zig{#end_shell_samp#} |
| ... | @@ -9788,8 +9804,8 @@ pub fn main() !void { | ... | @@ -9788,8 +9804,8 @@ pub fn main() !void { |
| 9788 | {#header_close#} | 9804 | {#header_close#} |
| 9789 | {#header_open|Builtin Overflow Functions#} | 9805 | {#header_open|Builtin Overflow Functions#} |
| 9790 | <p> | 9806 | <p> |
| 9791 | These builtins return a {#syntax#}bool{#endsyntax#} of whether or not overflow | 9807 | These builtins return a tuple containing whether there was an overflow |
| 9792 | occurred, as well as returning the overflowed bits: | 9808 | (as a {#syntax#}u1{#endsyntax#}) and the possibly overflowed bits of the operation: |
| 9793 | </p> | 9809 | </p> |
| 9794 | <ul> | 9810 | <ul> |
| 9795 | <li>{#link|@addWithOverflow#}</li> | 9811 | <li>{#link|@addWithOverflow#}</li> |
lib/build_runner.zig+702-53| ... | @@ -1,12 +1,14 @@ | ... | @@ -1,12 +1,14 @@ |
| 1 | const root = @import("@build"); | 1 | const root = @import("@build"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const assert = std.debug.assert; | ||
| 4 | const io = std.io; | 5 | const io = std.io; |
| 5 | const fmt = std.fmt; | 6 | const fmt = std.fmt; |
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| 7 | const process = std.process; | 8 | const process = std.process; |
| 8 | const ArrayList = std.ArrayList; | 9 | const ArrayList = std.ArrayList; |
| 9 | const File = std.fs.File; | 10 | const File = std.fs.File; |
| 11 | const Step = std.Build.Step; | ||
| 10 | 12 | ||
| 11 | pub const dependencies = @import("@dependencies"); | 13 | pub const dependencies = @import("@dependencies"); |
| 12 | 14 | ||
| ... | @@ -14,12 +16,15 @@ pub fn main() !void { | ... | @@ -14,12 +16,15 @@ pub fn main() !void { |
| 14 | // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, | 16 | // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, |
| 15 | // one shot program. We don't need to waste time freeing memory and finding places to squish | 17 | // one shot program. We don't need to waste time freeing memory and finding places to squish |
| 16 | // bytes into. So we free everything all at once at the very end. | 18 | // bytes into. So we free everything all at once at the very end. |
| 17 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 19 | var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 18 | defer arena.deinit(); | 20 | defer single_threaded_arena.deinit(); |
| 19 | 21 | ||
| 20 | const allocator = arena.allocator(); | 22 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ |
| 21 | var args = try process.argsAlloc(allocator); | 23 | .child_allocator = single_threaded_arena.allocator(), |
| 22 | defer process.argsFree(allocator, args); | 24 | }; |
| 25 | const arena = thread_safe_arena.allocator(); | ||
| 26 | |||
| 27 | var args = try process.argsAlloc(arena); | ||
| 23 | 28 | ||
| 24 | // skip my own exe name | 29 | // skip my own exe name |
| 25 | var arg_idx: usize = 1; | 30 | var arg_idx: usize = 1; |
| ... | @@ -59,18 +64,17 @@ pub fn main() !void { | ... | @@ -59,18 +64,17 @@ pub fn main() !void { |
| 59 | }; | 64 | }; |
| 60 | 65 | ||
| 61 | var cache: std.Build.Cache = .{ | 66 | var cache: std.Build.Cache = .{ |
| 62 | .gpa = allocator, | 67 | .gpa = arena, |
| 63 | .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), | 68 | .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), |
| 64 | }; | 69 | }; |
| 65 | cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); | 70 | cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); |
| 66 | cache.addPrefix(build_root_directory); | 71 | cache.addPrefix(build_root_directory); |
| 67 | cache.addPrefix(local_cache_directory); | 72 | cache.addPrefix(local_cache_directory); |
| 68 | cache.addPrefix(global_cache_directory); | 73 | cache.addPrefix(global_cache_directory); |
| 69 | 74 | cache.hash.addBytes(builtin.zig_version_string); | |
| 70 | //cache.hash.addBytes(builtin.zig_version); | ||
| 71 | 75 | ||
| 72 | const builder = try std.Build.create( | 76 | const builder = try std.Build.create( |
| 73 | allocator, | 77 | arena, |
| 74 | zig_exe, | 78 | zig_exe, |
| 75 | build_root_directory, | 79 | build_root_directory, |
| 76 | local_cache_directory, | 80 | local_cache_directory, |
| ... | @@ -80,35 +84,34 @@ pub fn main() !void { | ... | @@ -80,35 +84,34 @@ pub fn main() !void { |
| 80 | ); | 84 | ); |
| 81 | defer builder.destroy(); | 85 | defer builder.destroy(); |
| 82 | 86 | ||
| 83 | var targets = ArrayList([]const u8).init(allocator); | 87 | var targets = ArrayList([]const u8).init(arena); |
| 84 | var debug_log_scopes = ArrayList([]const u8).init(allocator); | 88 | var debug_log_scopes = ArrayList([]const u8).init(arena); |
| 85 | 89 | var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; | |
| 86 | const stderr_stream = io.getStdErr().writer(); | ||
| 87 | const stdout_stream = io.getStdOut().writer(); | ||
| 88 | 90 | ||
| 89 | var install_prefix: ?[]const u8 = null; | 91 | var install_prefix: ?[]const u8 = null; |
| 90 | var dir_list = std.Build.DirList{}; | 92 | var dir_list = std.Build.DirList{}; |
| 93 | var enable_summary: ?bool = null; | ||
| 94 | var max_rss: usize = 0; | ||
| 95 | var color: Color = .auto; | ||
| 91 | 96 | ||
| 92 | // before arg parsing, check for the NO_COLOR environment variable | 97 | const stderr_stream = io.getStdErr().writer(); |
| 93 | // if it exists, default the color setting to .off | 98 | const stdout_stream = io.getStdOut().writer(); |
| 94 | // explicit --color arguments will still override this setting. | ||
| 95 | builder.color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto; | ||
| 96 | 99 | ||
| 97 | while (nextArg(args, &arg_idx)) |arg| { | 100 | while (nextArg(args, &arg_idx)) |arg| { |
| 98 | if (mem.startsWith(u8, arg, "-D")) { | 101 | if (mem.startsWith(u8, arg, "-D")) { |
| 99 | const option_contents = arg[2..]; | 102 | const option_contents = arg[2..]; |
| 100 | if (option_contents.len == 0) { | 103 | if (option_contents.len == 0) { |
| 101 | std.debug.print("Expected option name after '-D'\n\n", .{}); | 104 | std.debug.print("Expected option name after '-D'\n\n", .{}); |
| 102 | return usageAndErr(builder, false, stderr_stream); | 105 | usageAndErr(builder, false, stderr_stream); |
| 103 | } | 106 | } |
| 104 | if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { | 107 | if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { |
| 105 | const option_name = option_contents[0..name_end]; | 108 | const option_name = option_contents[0..name_end]; |
| 106 | const option_value = option_contents[name_end + 1 ..]; | 109 | const option_value = option_contents[name_end + 1 ..]; |
| 107 | if (try builder.addUserInputOption(option_name, option_value)) | 110 | if (try builder.addUserInputOption(option_name, option_value)) |
| 108 | return usageAndErr(builder, false, stderr_stream); | 111 | usageAndErr(builder, false, stderr_stream); |
| 109 | } else { | 112 | } else { |
| 110 | if (try builder.addUserInputFlag(option_contents)) | 113 | if (try builder.addUserInputFlag(option_contents)) |
| 111 | return usageAndErr(builder, false, stderr_stream); | 114 | usageAndErr(builder, false, stderr_stream); |
| 112 | } | 115 | } |
| 113 | } else if (mem.startsWith(u8, arg, "-")) { | 116 | } else if (mem.startsWith(u8, arg, "-")) { |
| 114 | if (mem.eql(u8, arg, "--verbose")) { | 117 | if (mem.eql(u8, arg, "--verbose")) { |
| ... | @@ -118,69 +121,83 @@ pub fn main() !void { | ... | @@ -118,69 +121,83 @@ pub fn main() !void { |
| 118 | } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { | 121 | } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { |
| 119 | install_prefix = nextArg(args, &arg_idx) orelse { | 122 | install_prefix = nextArg(args, &arg_idx) orelse { |
| 120 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 123 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 121 | return usageAndErr(builder, false, stderr_stream); | 124 | usageAndErr(builder, false, stderr_stream); |
| 122 | }; | 125 | }; |
| 123 | } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { | 126 | } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { |
| 124 | return steps(builder, false, stdout_stream); | 127 | return steps(builder, false, stdout_stream); |
| 125 | } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { | 128 | } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { |
| 126 | dir_list.lib_dir = nextArg(args, &arg_idx) orelse { | 129 | dir_list.lib_dir = nextArg(args, &arg_idx) orelse { |
| 127 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 130 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 128 | return usageAndErr(builder, false, stderr_stream); | 131 | usageAndErr(builder, false, stderr_stream); |
| 129 | }; | 132 | }; |
| 130 | } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { | 133 | } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { |
| 131 | dir_list.exe_dir = nextArg(args, &arg_idx) orelse { | 134 | dir_list.exe_dir = nextArg(args, &arg_idx) orelse { |
| 132 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 135 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 133 | return usageAndErr(builder, false, stderr_stream); | 136 | usageAndErr(builder, false, stderr_stream); |
| 134 | }; | 137 | }; |
| 135 | } else if (mem.eql(u8, arg, "--prefix-include-dir")) { | 138 | } else if (mem.eql(u8, arg, "--prefix-include-dir")) { |
| 136 | dir_list.include_dir = nextArg(args, &arg_idx) orelse { | 139 | dir_list.include_dir = nextArg(args, &arg_idx) orelse { |
| 137 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 140 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 138 | return usageAndErr(builder, false, stderr_stream); | 141 | usageAndErr(builder, false, stderr_stream); |
| 139 | }; | 142 | }; |
| 140 | } else if (mem.eql(u8, arg, "--sysroot")) { | 143 | } else if (mem.eql(u8, arg, "--sysroot")) { |
| 141 | const sysroot = nextArg(args, &arg_idx) orelse { | 144 | const sysroot = nextArg(args, &arg_idx) orelse { |
| 142 | std.debug.print("Expected argument after --sysroot\n\n", .{}); | 145 | std.debug.print("Expected argument after --sysroot\n\n", .{}); |
| 143 | return usageAndErr(builder, false, stderr_stream); | 146 | usageAndErr(builder, false, stderr_stream); |
| 144 | }; | 147 | }; |
| 145 | builder.sysroot = sysroot; | 148 | builder.sysroot = sysroot; |
| 149 | } else if (mem.eql(u8, arg, "--maxrss")) { | ||
| 150 | const max_rss_text = nextArg(args, &arg_idx) orelse { | ||
| 151 | std.debug.print("Expected argument after --sysroot\n\n", .{}); | ||
| 152 | usageAndErr(builder, false, stderr_stream); | ||
| 153 | }; | ||
| 154 | // TODO: support shorthand such as "2GiB", "2GB", or "2G" | ||
| 155 | max_rss = std.fmt.parseInt(usize, max_rss_text, 10) catch |err| { | ||
| 156 | std.debug.print("invalid byte size: '{s}': {s}\n", .{ | ||
| 157 | max_rss_text, @errorName(err), | ||
| 158 | }); | ||
| 159 | process.exit(1); | ||
| 160 | }; | ||
| 146 | } else if (mem.eql(u8, arg, "--search-prefix")) { | 161 | } else if (mem.eql(u8, arg, "--search-prefix")) { |
| 147 | const search_prefix = nextArg(args, &arg_idx) orelse { | 162 | const search_prefix = nextArg(args, &arg_idx) orelse { |
| 148 | std.debug.print("Expected argument after --search-prefix\n\n", .{}); | 163 | std.debug.print("Expected argument after --search-prefix\n\n", .{}); |
| 149 | return usageAndErr(builder, false, stderr_stream); | 164 | usageAndErr(builder, false, stderr_stream); |
| 150 | }; | 165 | }; |
| 151 | builder.addSearchPrefix(search_prefix); | 166 | builder.addSearchPrefix(search_prefix); |
| 152 | } else if (mem.eql(u8, arg, "--libc")) { | 167 | } else if (mem.eql(u8, arg, "--libc")) { |
| 153 | const libc_file = nextArg(args, &arg_idx) orelse { | 168 | const libc_file = nextArg(args, &arg_idx) orelse { |
| 154 | std.debug.print("Expected argument after --libc\n\n", .{}); | 169 | std.debug.print("Expected argument after --libc\n\n", .{}); |
| 155 | return usageAndErr(builder, false, stderr_stream); | 170 | usageAndErr(builder, false, stderr_stream); |
| 156 | }; | 171 | }; |
| 157 | builder.libc_file = libc_file; | 172 | builder.libc_file = libc_file; |
| 158 | } else if (mem.eql(u8, arg, "--color")) { | 173 | } else if (mem.eql(u8, arg, "--color")) { |
| 159 | const next_arg = nextArg(args, &arg_idx) orelse { | 174 | const next_arg = nextArg(args, &arg_idx) orelse { |
| 160 | std.debug.print("expected [auto|on|off] after --color", .{}); | 175 | std.debug.print("expected [auto|on|off] after --color", .{}); |
| 161 | return usageAndErr(builder, false, stderr_stream); | 176 | usageAndErr(builder, false, stderr_stream); |
| 162 | }; | 177 | }; |
| 163 | builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse { | 178 | color = std.meta.stringToEnum(Color, next_arg) orelse { |
| 164 | std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); | 179 | std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); |
| 165 | return usageAndErr(builder, false, stderr_stream); | 180 | usageAndErr(builder, false, stderr_stream); |
| 166 | }; | 181 | }; |
| 167 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { | 182 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { |
| 168 | builder.zig_lib_dir = nextArg(args, &arg_idx) orelse { | 183 | builder.zig_lib_dir = nextArg(args, &arg_idx) orelse { |
| 169 | std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); | 184 | std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); |
| 170 | return usageAndErr(builder, false, stderr_stream); | 185 | usageAndErr(builder, false, stderr_stream); |
| 171 | }; | 186 | }; |
| 172 | } else if (mem.eql(u8, arg, "--debug-log")) { | 187 | } else if (mem.eql(u8, arg, "--debug-log")) { |
| 173 | const next_arg = nextArg(args, &arg_idx) orelse { | 188 | const next_arg = nextArg(args, &arg_idx) orelse { |
| 174 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 189 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 175 | return usageAndErr(builder, false, stderr_stream); | 190 | usageAndErr(builder, false, stderr_stream); |
| 176 | }; | 191 | }; |
| 177 | try debug_log_scopes.append(next_arg); | 192 | try debug_log_scopes.append(next_arg); |
| 193 | } else if (mem.eql(u8, arg, "--debug-pkg-config")) { | ||
| 194 | builder.debug_pkg_config = true; | ||
| 178 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { | 195 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { |
| 179 | builder.debug_compile_errors = true; | 196 | builder.debug_compile_errors = true; |
| 180 | } else if (mem.eql(u8, arg, "--glibc-runtimes")) { | 197 | } else if (mem.eql(u8, arg, "--glibc-runtimes")) { |
| 181 | builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse { | 198 | builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse { |
| 182 | std.debug.print("Expected argument after --glibc-runtimes\n\n", .{}); | 199 | std.debug.print("Expected argument after --glibc-runtimes\n\n", .{}); |
| 183 | return usageAndErr(builder, false, stderr_stream); | 200 | usageAndErr(builder, false, stderr_stream); |
| 184 | }; | 201 | }; |
| 185 | } else if (mem.eql(u8, arg, "--verbose-link")) { | 202 | } else if (mem.eql(u8, arg, "--verbose-link")) { |
| 186 | builder.verbose_link = true; | 203 | builder.verbose_link = true; |
| ... | @@ -194,8 +211,6 @@ pub fn main() !void { | ... | @@ -194,8 +211,6 @@ pub fn main() !void { |
| 194 | builder.verbose_cc = true; | 211 | builder.verbose_cc = true; |
| 195 | } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { | 212 | } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { |
| 196 | builder.verbose_llvm_cpu_features = true; | 213 | builder.verbose_llvm_cpu_features = true; |
| 197 | } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { | ||
| 198 | builder.prominent_compile_errors = true; | ||
| 199 | } else if (mem.eql(u8, arg, "-fwine")) { | 214 | } else if (mem.eql(u8, arg, "-fwine")) { |
| 200 | builder.enable_wine = true; | 215 | builder.enable_wine = true; |
| 201 | } else if (mem.eql(u8, arg, "-fno-wine")) { | 216 | } else if (mem.eql(u8, arg, "-fno-wine")) { |
| ... | @@ -216,6 +231,10 @@ pub fn main() !void { | ... | @@ -216,6 +231,10 @@ pub fn main() !void { |
| 216 | builder.enable_darling = true; | 231 | builder.enable_darling = true; |
| 217 | } else if (mem.eql(u8, arg, "-fno-darling")) { | 232 | } else if (mem.eql(u8, arg, "-fno-darling")) { |
| 218 | builder.enable_darling = false; | 233 | builder.enable_darling = false; |
| 234 | } else if (mem.eql(u8, arg, "-fsummary")) { | ||
| 235 | enable_summary = true; | ||
| 236 | } else if (mem.eql(u8, arg, "-fno-summary")) { | ||
| 237 | enable_summary = false; | ||
| 219 | } else if (mem.eql(u8, arg, "-freference-trace")) { | 238 | } else if (mem.eql(u8, arg, "-freference-trace")) { |
| 220 | builder.reference_trace = 256; | 239 | builder.reference_trace = 256; |
| 221 | } else if (mem.startsWith(u8, arg, "-freference-trace=")) { | 240 | } else if (mem.startsWith(u8, arg, "-freference-trace=")) { |
| ... | @@ -226,39 +245,639 @@ pub fn main() !void { | ... | @@ -226,39 +245,639 @@ pub fn main() !void { |
| 226 | }; | 245 | }; |
| 227 | } else if (mem.eql(u8, arg, "-fno-reference-trace")) { | 246 | } else if (mem.eql(u8, arg, "-fno-reference-trace")) { |
| 228 | builder.reference_trace = null; | 247 | builder.reference_trace = null; |
| 248 | } else if (mem.startsWith(u8, arg, "-j")) { | ||
| 249 | const num = arg["-j".len..]; | ||
| 250 | const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| { | ||
| 251 | std.debug.print("unable to parse jobs count '{s}': {s}", .{ | ||
| 252 | num, @errorName(err), | ||
| 253 | }); | ||
| 254 | process.exit(1); | ||
| 255 | }; | ||
| 256 | if (n_jobs < 1) { | ||
| 257 | std.debug.print("number of jobs must be at least 1\n", .{}); | ||
| 258 | process.exit(1); | ||
| 259 | } | ||
| 260 | thread_pool_options.n_jobs = n_jobs; | ||
| 229 | } else if (mem.eql(u8, arg, "--")) { | 261 | } else if (mem.eql(u8, arg, "--")) { |
| 230 | builder.args = argsRest(args, arg_idx); | 262 | builder.args = argsRest(args, arg_idx); |
| 231 | break; | 263 | break; |
| 232 | } else { | 264 | } else { |
| 233 | std.debug.print("Unrecognized argument: {s}\n\n", .{arg}); | 265 | std.debug.print("Unrecognized argument: {s}\n\n", .{arg}); |
| 234 | return usageAndErr(builder, false, stderr_stream); | 266 | usageAndErr(builder, false, stderr_stream); |
| 235 | } | 267 | } |
| 236 | } else { | 268 | } else { |
| 237 | try targets.append(arg); | 269 | try targets.append(arg); |
| 238 | } | 270 | } |
| 239 | } | 271 | } |
| 240 | 272 | ||
| 273 | const stderr = std.io.getStdErr(); | ||
| 274 | const ttyconf = get_tty_conf(color, stderr); | ||
| 275 | switch (ttyconf) { | ||
| 276 | .no_color => try builder.env_map.put("NO_COLOR", "1"), | ||
| 277 | .escape_codes => try builder.env_map.put("ZIG_DEBUG_COLOR", "1"), | ||
| 278 | .windows_api => {}, | ||
| 279 | } | ||
| 280 | |||
| 281 | var progress: std.Progress = .{ .dont_print_on_dumb = true }; | ||
| 282 | const main_progress_node = progress.start("", 0); | ||
| 283 | |||
| 241 | builder.debug_log_scopes = debug_log_scopes.items; | 284 | builder.debug_log_scopes = debug_log_scopes.items; |
| 242 | builder.resolveInstallPrefix(install_prefix, dir_list); | 285 | builder.resolveInstallPrefix(install_prefix, dir_list); |
| 243 | try builder.runBuild(root); | 286 | { |
| 287 | var prog_node = main_progress_node.start("user build.zig logic", 0); | ||
| 288 | defer prog_node.end(); | ||
| 289 | try builder.runBuild(root); | ||
| 290 | } | ||
| 244 | 291 | ||
| 245 | if (builder.validateUserInputDidItFail()) | 292 | if (builder.validateUserInputDidItFail()) |
| 246 | return usageAndErr(builder, true, stderr_stream); | 293 | usageAndErr(builder, true, stderr_stream); |
| 247 | 294 | ||
| 248 | builder.make(targets.items) catch |err| { | 295 | var run: Run = .{ |
| 249 | switch (err) { | 296 | .max_rss = max_rss, |
| 250 | error.InvalidStepName => { | 297 | .max_rss_is_default = false, |
| 251 | return usageAndErr(builder, true, stderr_stream); | 298 | .max_rss_mutex = .{}, |
| 299 | .memory_blocked_steps = std.ArrayList(*Step).init(arena), | ||
| 300 | |||
| 301 | .claimed_rss = 0, | ||
| 302 | .enable_summary = enable_summary, | ||
| 303 | .ttyconf = ttyconf, | ||
| 304 | .stderr = stderr, | ||
| 305 | }; | ||
| 306 | |||
| 307 | if (run.max_rss == 0) { | ||
| 308 | run.max_rss = process.totalSystemMemory() catch std.math.maxInt(usize); | ||
| 309 | run.max_rss_is_default = true; | ||
| 310 | } | ||
| 311 | |||
| 312 | runStepNames( | ||
| 313 | arena, | ||
| 314 | builder, | ||
| 315 | targets.items, | ||
| 316 | main_progress_node, | ||
| 317 | thread_pool_options, | ||
| 318 | &run, | ||
| 319 | ) catch |err| switch (err) { | ||
| 320 | error.UncleanExit => process.exit(1), | ||
| 321 | else => return err, | ||
| 322 | }; | ||
| 323 | } | ||
| 324 | |||
| 325 | const 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 | |||
| 337 | fn runStepNames( | ||
| 338 | arena: std.mem.Allocator, | ||
| 339 | b: *std.Build, | ||
| 340 | step_names: []const []const u8, | ||
| 341 | parent_prog_node: *std.Progress.Node, | ||
| 342 | thread_pool_options: std.Thread.Pool.Options, | ||
| 343 | run: *Run, | ||
| 344 | ) !void { | ||
| 345 | const gpa = b.allocator; | ||
| 346 | var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; | ||
| 347 | defer step_stack.deinit(gpa); | ||
| 348 | |||
| 349 | if (step_names.len == 0) { | ||
| 350 | try step_stack.put(gpa, b.default_step, {}); | ||
| 351 | } else { | ||
| 352 | try step_stack.ensureUnusedCapacity(gpa, step_names.len); | ||
| 353 | for (0..step_names.len) |i| { | ||
| 354 | const step_name = step_names[step_names.len - i - 1]; | ||
| 355 | const s = b.top_level_steps.get(step_name) orelse { | ||
| 356 | std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); | ||
| 357 | process.exit(1); | ||
| 358 | }; | ||
| 359 | step_stack.putAssumeCapacity(&s.step, {}); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | const starting_steps = try arena.dupe(*Step, step_stack.keys()); | ||
| 364 | for (starting_steps) |s| { | ||
| 365 | checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) { | ||
| 366 | error.DependencyLoopDetected => return error.UncleanExit, | ||
| 367 | else => |e| return e, | ||
| 368 | }; | ||
| 369 | } | ||
| 370 | |||
| 371 | { | ||
| 372 | // Check that we have enough memory to complete the build. | ||
| 373 | var any_problems = false; | ||
| 374 | for (step_stack.keys()) |s| { | ||
| 375 | if (s.max_rss == 0) continue; | ||
| 376 | if (s.max_rss > run.max_rss) { | ||
| 377 | std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{ | ||
| 378 | s.owner.dep_prefix, s.name, s.max_rss, run.max_rss, | ||
| 379 | }); | ||
| 380 | any_problems = true; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | if (any_problems) { | ||
| 384 | if (run.max_rss_is_default) { | ||
| 385 | std.debug.print("note: use --maxrss to override the default", .{}); | ||
| 386 | } | ||
| 387 | return error.UncleanExit; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | var thread_pool: std.Thread.Pool = undefined; | ||
| 392 | try thread_pool.init(thread_pool_options); | ||
| 393 | defer thread_pool.deinit(); | ||
| 394 | |||
| 395 | { | ||
| 396 | defer parent_prog_node.end(); | ||
| 397 | |||
| 398 | var step_prog = parent_prog_node.start("steps", step_stack.count()); | ||
| 399 | defer step_prog.end(); | ||
| 400 | |||
| 401 | var wait_group: std.Thread.WaitGroup = .{}; | ||
| 402 | defer wait_group.wait(); | ||
| 403 | |||
| 404 | // Here we spawn the initial set of tasks with a nice heuristic - | ||
| 405 | // dependency order. Each worker when it finishes a step will then | ||
| 406 | // check whether it should run any dependants. | ||
| 407 | const steps_slice = step_stack.keys(); | ||
| 408 | for (0..steps_slice.len) |i| { | ||
| 409 | const step = steps_slice[steps_slice.len - i - 1]; | ||
| 410 | |||
| 411 | wait_group.start(); | ||
| 412 | thread_pool.spawn(workerMakeOneStep, .{ | ||
| 413 | &wait_group, &thread_pool, b, step, &step_prog, run, | ||
| 414 | }) catch @panic("OOM"); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | assert(run.memory_blocked_steps.items.len == 0); | ||
| 418 | |||
| 419 | var test_skip_count: usize = 0; | ||
| 420 | var test_fail_count: usize = 0; | ||
| 421 | var test_pass_count: usize = 0; | ||
| 422 | var test_leak_count: usize = 0; | ||
| 423 | var test_count: usize = 0; | ||
| 424 | |||
| 425 | var success_count: usize = 0; | ||
| 426 | var skipped_count: usize = 0; | ||
| 427 | var failure_count: usize = 0; | ||
| 428 | var pending_count: usize = 0; | ||
| 429 | var total_compile_errors: usize = 0; | ||
| 430 | var compile_error_steps: std.ArrayListUnmanaged(*Step) = .{}; | ||
| 431 | defer compile_error_steps.deinit(gpa); | ||
| 432 | |||
| 433 | for (step_stack.keys()) |s| { | ||
| 434 | test_fail_count += s.test_results.fail_count; | ||
| 435 | test_skip_count += s.test_results.skip_count; | ||
| 436 | test_leak_count += s.test_results.leak_count; | ||
| 437 | test_pass_count += s.test_results.passCount(); | ||
| 438 | test_count += s.test_results.test_count; | ||
| 439 | |||
| 440 | switch (s.state) { | ||
| 441 | .precheck_unstarted => unreachable, | ||
| 442 | .precheck_started => unreachable, | ||
| 443 | .running => unreachable, | ||
| 444 | .precheck_done => { | ||
| 445 | // precheck_done is equivalent to dependency_failure in the case of | ||
| 446 | // transitive dependencies. For example: | ||
| 447 | // A -> B -> C (failure) | ||
| 448 | // B will be marked as dependency_failure, while A may never be queued, and thus | ||
| 449 | // remain in the initial state of precheck_done. | ||
| 450 | s.state = .dependency_failure; | ||
| 451 | pending_count += 1; | ||
| 452 | }, | ||
| 453 | .dependency_failure => pending_count += 1, | ||
| 454 | .success => success_count += 1, | ||
| 455 | .skipped => skipped_count += 1, | ||
| 456 | .failure => { | ||
| 457 | failure_count += 1; | ||
| 458 | const compile_errors_len = s.result_error_bundle.errorMessageCount(); | ||
| 459 | if (compile_errors_len > 0) { | ||
| 460 | total_compile_errors += compile_errors_len; | ||
| 461 | try compile_error_steps.append(gpa, s); | ||
| 462 | } | ||
| 252 | }, | 463 | }, |
| 253 | error.UncleanExit => process.exit(1), | ||
| 254 | // This error is intended to indicate that the step has already | ||
| 255 | // logged an error message and so printing the error return trace | ||
| 256 | // here would be unwanted extra information, unless the user opts | ||
| 257 | // into it with a debug flag. | ||
| 258 | error.StepFailed => process.exit(1), | ||
| 259 | else => return err, | ||
| 260 | } | 464 | } |
| 261 | }; | 465 | } |
| 466 | |||
| 467 | // A proper command line application defaults to silently succeeding. | ||
| 468 | // The user may request verbose mode if they have a different preference. | ||
| 469 | if (failure_count == 0 and run.enable_summary != true) return cleanExit(); | ||
| 470 | |||
| 471 | const ttyconf = run.ttyconf; | ||
| 472 | const stderr = run.stderr; | ||
| 473 | |||
| 474 | if (run.enable_summary != false) { | ||
| 475 | const total_count = success_count + failure_count + pending_count + skipped_count; | ||
| 476 | ttyconf.setColor(stderr, .Cyan) catch {}; | ||
| 477 | stderr.writeAll("Build Summary:") catch {}; | ||
| 478 | ttyconf.setColor(stderr, .Reset) catch {}; | ||
| 479 | stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; | ||
| 480 | if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; | ||
| 481 | if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; | ||
| 482 | |||
| 483 | if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; | ||
| 484 | if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; | ||
| 485 | if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; | ||
| 486 | if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; | ||
| 487 | |||
| 488 | if (run.enable_summary == null) { | ||
| 489 | ttyconf.setColor(stderr, .Dim) catch {}; | ||
| 490 | stderr.writeAll(" (disable with -fno-summary)") catch {}; | ||
| 491 | ttyconf.setColor(stderr, .Reset) catch {}; | ||
| 492 | } | ||
| 493 | stderr.writeAll("\n") catch {}; | ||
| 494 | |||
| 495 | // Print a fancy tree with build results. | ||
| 496 | var print_node: PrintNode = .{ .parent = null }; | ||
| 497 | if (step_names.len == 0) { | ||
| 498 | print_node.last = true; | ||
| 499 | printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {}; | ||
| 500 | } else { | ||
| 501 | for (step_names, 0..) |step_name, i| { | ||
| 502 | const tls = b.top_level_steps.get(step_name).?; | ||
| 503 | print_node.last = i + 1 == b.top_level_steps.count(); | ||
| 504 | printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {}; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | if (failure_count == 0) return cleanExit(); | ||
| 510 | |||
| 511 | // Finally, render compile errors at the bottom of the terminal. | ||
| 512 | // We use a separate compile_error_steps array list because step_stack is destructively | ||
| 513 | // mutated in printTreeStep above. | ||
| 514 | if (total_compile_errors > 0) { | ||
| 515 | for (compile_error_steps.items) |s| { | ||
| 516 | if (s.result_error_bundle.errorMessageCount() > 0) { | ||
| 517 | s.result_error_bundle.renderToStdErr(renderOptions(ttyconf)); | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | // Signal to parent process that we have printed compile errors. The | ||
| 522 | // parent process may choose to omit the "following command failed" | ||
| 523 | // line in this case. | ||
| 524 | process.exit(2); | ||
| 525 | } | ||
| 526 | |||
| 527 | process.exit(1); | ||
| 528 | } | ||
| 529 | |||
| 530 | const PrintNode = struct { | ||
| 531 | parent: ?*PrintNode, | ||
| 532 | last: bool = false, | ||
| 533 | }; | ||
| 534 | |||
| 535 | fn 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 | |||
| 549 | fn 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 | |||
| 707 | fn 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 | |||
| 745 | fn workerMakeOneStep( | ||
| 746 | wg: *std.Thread.WaitGroup, | ||
| 747 | thread_pool: *std.Thread.Pool, | ||
| 748 | b: *std.Build, | ||
| 749 | s: *Step, | ||
| 750 | prog_node: *std.Progress.Node, | ||
| 751 | run: *Run, | ||
| 752 | ) void { | ||
| 753 | defer wg.finish(); | ||
| 754 | |||
| 755 | // First, check the conditions for running this step. If they are not met, | ||
| 756 | // then we return without doing the step, relying on another worker to | ||
| 757 | // queue this step up again when dependencies are met. | ||
| 758 | for (s.dependencies.items) |dep| { | ||
| 759 | switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { | ||
| 760 | .success, .skipped => continue, | ||
| 761 | .failure, .dependency_failure => { | ||
| 762 | @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); | ||
| 763 | return; | ||
| 764 | }, | ||
| 765 | .precheck_done, .running => { | ||
| 766 | // dependency is not finished yet. | ||
| 767 | return; | ||
| 768 | }, | ||
| 769 | .precheck_unstarted => unreachable, | ||
| 770 | .precheck_started => unreachable, | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | if (s.max_rss != 0) { | ||
| 775 | run.max_rss_mutex.lock(); | ||
| 776 | defer run.max_rss_mutex.unlock(); | ||
| 777 | |||
| 778 | // Avoid running steps twice. | ||
| 779 | if (s.state != .precheck_done) { | ||
| 780 | // Another worker got the job. | ||
| 781 | return; | ||
| 782 | } | ||
| 783 | |||
| 784 | const new_claimed_rss = run.claimed_rss + s.max_rss; | ||
| 785 | if (new_claimed_rss > run.max_rss) { | ||
| 786 | // Running this step right now could possibly exceed the allotted RSS. | ||
| 787 | // Add this step to the queue of memory-blocked steps. | ||
| 788 | run.memory_blocked_steps.append(s) catch @panic("OOM"); | ||
| 789 | return; | ||
| 790 | } | ||
| 791 | |||
| 792 | run.claimed_rss = new_claimed_rss; | ||
| 793 | s.state = .running; | ||
| 794 | } else { | ||
| 795 | // Avoid running steps twice. | ||
| 796 | if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) { | ||
| 797 | // Another worker got the job. | ||
| 798 | return; | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 802 | var sub_prog_node = prog_node.start(s.name, 0); | ||
| 803 | sub_prog_node.activate(); | ||
| 804 | defer sub_prog_node.end(); | ||
| 805 | |||
| 806 | const make_result = s.make(&sub_prog_node); | ||
| 807 | |||
| 808 | // No matter the result, we want to display error/warning messages. | ||
| 809 | if (s.result_error_msgs.items.len > 0) { | ||
| 810 | sub_prog_node.context.lock_stderr(); | ||
| 811 | defer sub_prog_node.context.unlock_stderr(); | ||
| 812 | |||
| 813 | const stderr = run.stderr; | ||
| 814 | const ttyconf = run.ttyconf; | ||
| 815 | |||
| 816 | for (s.result_error_msgs.items) |msg| { | ||
| 817 | // Sometimes it feels like you just can't catch a break. Finally, | ||
| 818 | // with Zig, you can. | ||
| 819 | ttyconf.setColor(stderr, .Bold) catch break; | ||
| 820 | stderr.writeAll(s.owner.dep_prefix) catch break; | ||
| 821 | stderr.writeAll(s.name) catch break; | ||
| 822 | stderr.writeAll(": ") catch break; | ||
| 823 | ttyconf.setColor(stderr, .Red) catch break; | ||
| 824 | stderr.writeAll("error: ") catch break; | ||
| 825 | ttyconf.setColor(stderr, .Reset) catch break; | ||
| 826 | stderr.writeAll(msg) catch break; | ||
| 827 | stderr.writeAll("\n") catch break; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | |||
| 831 | handle_result: { | ||
| 832 | if (make_result) |_| { | ||
| 833 | @atomicStore(Step.State, &s.state, .success, .SeqCst); | ||
| 834 | } else |err| switch (err) { | ||
| 835 | error.MakeFailed => { | ||
| 836 | @atomicStore(Step.State, &s.state, .failure, .SeqCst); | ||
| 837 | break :handle_result; | ||
| 838 | }, | ||
| 839 | error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst), | ||
| 840 | } | ||
| 841 | |||
| 842 | // Successful completion of a step, so we queue up its dependants as well. | ||
| 843 | for (s.dependants.items) |dep| { | ||
| 844 | wg.start(); | ||
| 845 | thread_pool.spawn(workerMakeOneStep, .{ | ||
| 846 | wg, thread_pool, b, dep, prog_node, run, | ||
| 847 | }) catch @panic("OOM"); | ||
| 848 | } | ||
| 849 | } | ||
| 850 | |||
| 851 | // If this is a step that claims resources, we must now queue up other | ||
| 852 | // steps that are waiting for resources. | ||
| 853 | if (s.max_rss != 0) { | ||
| 854 | run.max_rss_mutex.lock(); | ||
| 855 | defer run.max_rss_mutex.unlock(); | ||
| 856 | |||
| 857 | // Give the memory back to the scheduler. | ||
| 858 | run.claimed_rss -= s.max_rss; | ||
| 859 | // Avoid kicking off too many tasks that we already know will not have | ||
| 860 | // enough resources. | ||
| 861 | var remaining = run.max_rss - run.claimed_rss; | ||
| 862 | var i: usize = 0; | ||
| 863 | var j: usize = 0; | ||
| 864 | while (j < run.memory_blocked_steps.items.len) : (j += 1) { | ||
| 865 | const dep = run.memory_blocked_steps.items[j]; | ||
| 866 | assert(dep.max_rss != 0); | ||
| 867 | if (dep.max_rss <= remaining) { | ||
| 868 | remaining -= dep.max_rss; | ||
| 869 | |||
| 870 | wg.start(); | ||
| 871 | thread_pool.spawn(workerMakeOneStep, .{ | ||
| 872 | wg, thread_pool, b, dep, prog_node, run, | ||
| 873 | }) catch @panic("OOM"); | ||
| 874 | } else { | ||
| 875 | run.memory_blocked_steps.items[i] = dep; | ||
| 876 | i += 1; | ||
| 877 | } | ||
| 878 | } | ||
| 879 | run.memory_blocked_steps.shrinkRetainingCapacity(i); | ||
| 880 | } | ||
| 262 | } | 881 | } |
| 263 | 882 | ||
| 264 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { | 883 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { |
| ... | @@ -269,7 +888,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -269,7 +888,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 269 | } | 888 | } |
| 270 | 889 | ||
| 271 | const allocator = builder.allocator; | 890 | const allocator = builder.allocator; |
| 272 | for (builder.top_level_steps.items) |top_level_step| { | 891 | for (builder.top_level_steps.values()) |top_level_step| { |
| 273 | const name = if (&top_level_step.step == builder.default_step) | 892 | const name = if (&top_level_step.step == builder.default_step) |
| 274 | try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) | 893 | try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) |
| 275 | else | 894 | else |
| ... | @@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 327 | \\ --verbose Print commands before executing them | 946 | \\ --verbose Print commands before executing them |
| 328 | \\ --color [auto|off|on] Enable or disable colored error messages | 947 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 329 | \\ --prominent-compile-errors Output compile errors formatted for a human to read | 948 | \\ --prominent-compile-errors Output compile errors formatted for a human to read |
| 949 | \\ -fsummary Print the build summary, even on success | ||
| 950 | \\ -fno-summary Omit the build summary, even on failure | ||
| 951 | \\ -j<N> Limit concurrent jobs (default is to use all CPU cores) | ||
| 952 | \\ --maxrss <bytes> Limit memory usage (default is to use available memory) | ||
| 330 | \\ | 953 | \\ |
| 331 | \\Project-Specific Options: | 954 | \\Project-Specific Options: |
| 332 | \\ | 955 | \\ |
| ... | @@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 364 | \\ --zig-lib-dir [arg] Override path to Zig lib directory | 987 | \\ --zig-lib-dir [arg] Override path to Zig lib directory |
| 365 | \\ --build-runner [file] Override path to build runner | 988 | \\ --build-runner [file] Override path to build runner |
| 366 | \\ --debug-log [scope] Enable debugging the compiler | 989 | \\ --debug-log [scope] Enable debugging the compiler |
| 990 | \\ --debug-pkg-config Fail if unknown pkg-config flags encountered | ||
| 367 | \\ --verbose-link Enable compiler debug output for linking | 991 | \\ --verbose-link Enable compiler debug output for linking |
| 368 | \\ --verbose-air Enable compiler debug output for Zig AIR | 992 | \\ --verbose-air Enable compiler debug output for Zig AIR |
| 369 | \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR | 993 | \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR |
| ... | @@ -374,7 +998,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -374,7 +998,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 374 | ); | 998 | ); |
| 375 | } | 999 | } |
| 376 | 1000 | ||
| 377 | fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void { | 1001 | fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) noreturn { |
| 378 | usage(builder, already_ran_build, out_stream) catch {}; | 1002 | usage(builder, already_ran_build, out_stream) catch {}; |
| 379 | process.exit(1); | 1003 | process.exit(1); |
| 380 | } | 1004 | } |
| ... | @@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { | ... | @@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { |
| 389 | if (idx >= args.len) return null; | 1013 | if (idx >= args.len) return null; |
| 390 | return args[idx..]; | 1014 | return args[idx..]; |
| 391 | } | 1015 | } |
| 1016 | |||
| 1017 | fn 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 | |||
| 1024 | const Color = enum { auto, off, on }; | ||
| 1025 | |||
| 1026 | fn 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 | |||
| 1034 | fn renderOptions(ttyconf: std.debug.TTY.Config) std.zig.ErrorBundle.RenderOptions { | ||
| 1035 | return .{ | ||
| 1036 | .ttyconf = ttyconf, | ||
| 1037 | .include_source_line = ttyconf != .no_color, | ||
| 1038 | .include_reference_trace = ttyconf != .no_color, | ||
| 1039 | }; | ||
| 1040 | } |
lib/compiler_rt/udivmodei4.zig+6-6| ... | @@ -79,16 +79,16 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { | ... | @@ -79,16 +79,16 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { |
| 79 | } | 79 | } |
| 80 | break; | 80 | break; |
| 81 | } | 81 | } |
| 82 | var carry: u64 = 0; | 82 | var carry: i64 = 0; |
| 83 | i = 0; | 83 | i = 0; |
| 84 | while (i <= n) : (i += 1) { | 84 | while (i <= n) : (i += 1) { |
| 85 | const p = qhat * limb(&vn, i); | 85 | const p = qhat * limb(&vn, i); |
| 86 | const t = limb(&un, i + j) - carry - @truncate(u32, p); | 86 | const t = limb(&un, i + j) - carry - @truncate(u32, p); |
| 87 | limb_set(&un, i + j, @truncate(u32, t)); | 87 | limb_set(&un, i + j, @truncate(u32, @bitCast(u64, t))); |
| 88 | carry = @intCast(u64, p >> 32) - @intCast(u64, t >> 32); | 88 | carry = @intCast(i64, p >> 32) - @intCast(i64, t >> 32); |
| 89 | } | 89 | } |
| 90 | const t = limb(&un, j + n + 1) - carry; | 90 | const t = limb(&un, j + n + 1) -% carry; |
| 91 | limb_set(&un, j + n + 1, @truncate(u32, t)); | 91 | limb_set(&un, j + n + 1, @truncate(u32, @bitCast(u64, t))); |
| 92 | if (q) |q_| limb_set(q_, j, @truncate(u32, qhat)); | 92 | if (q) |q_| limb_set(q_, j, @truncate(u32, qhat)); |
| 93 | if (t < 0) { | 93 | if (t < 0) { |
| 94 | if (q) |q_| limb_set(q_, j, limb(q_, j) - 1); | 94 | if (q) |q_| limb_set(q_, j, limb(q_, j) - 1); |
| ... | @@ -99,7 +99,7 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { | ... | @@ -99,7 +99,7 @@ fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { |
| 99 | limb_set(&un, i + j, @truncate(u32, t2)); | 99 | limb_set(&un, i + j, @truncate(u32, t2)); |
| 100 | carry2 = t2 >> 32; | 100 | carry2 = t2 >> 32; |
| 101 | } | 101 | } |
| 102 | limb_set(un, j + n + 1, @truncate(u32, limb(&un, j + n + 1) + carry2)); | 102 | limb_set(&un, j + n + 1, @truncate(u32, limb(&un, j + n + 1) + carry2)); |
| 103 | } | 103 | } |
| 104 | if (j == 0) break; | 104 | if (j == 0) break; |
| 105 | } | 105 | } |
lib/docs/main.js-4| ... | @@ -1187,10 +1187,6 @@ const NAV_MODES = { | ... | @@ -1187,10 +1187,6 @@ const NAV_MODES = { |
| 1187 | payloadHtml += "panic"; | 1187 | payloadHtml += "panic"; |
| 1188 | break; | 1188 | break; |
| 1189 | } | 1189 | } |
| 1190 | case "set_cold": { | ||
| 1191 | payloadHtml += "setCold"; | ||
| 1192 | break; | ||
| 1193 | } | ||
| 1194 | case "set_runtime_safety": { | 1190 | case "set_runtime_safety": { |
| 1195 | payloadHtml += "setRuntimeSafety"; | 1191 | payloadHtml += "setRuntimeSafety"; |
| 1196 | break; | 1192 | break; |
lib/std/Build.zig+243-245| ... | @@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig"); | ... | @@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig"); |
| 32 | pub const CheckFileStep = @import("Build/CheckFileStep.zig"); | 32 | pub const CheckFileStep = @import("Build/CheckFileStep.zig"); |
| 33 | pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); | 33 | pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); |
| 34 | pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); | 34 | pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); |
| 35 | pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig"); | ||
| 36 | pub const FmtStep = @import("Build/FmtStep.zig"); | 35 | pub const FmtStep = @import("Build/FmtStep.zig"); |
| 37 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); | 36 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); |
| 38 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); | 37 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); |
| 39 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); | 38 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); |
| 40 | pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); | 39 | pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); |
| 41 | pub const CompileStep = @import("Build/CompileStep.zig"); | 40 | pub const CompileStep = @import("Build/CompileStep.zig"); |
| 42 | pub const LogStep = @import("Build/LogStep.zig"); | ||
| 43 | pub const OptionsStep = @import("Build/OptionsStep.zig"); | 41 | pub const OptionsStep = @import("Build/OptionsStep.zig"); |
| 44 | pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); | 42 | pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); |
| 45 | pub const RunStep = @import("Build/RunStep.zig"); | 43 | pub const RunStep = @import("Build/RunStep.zig"); |
| ... | @@ -59,15 +57,12 @@ verbose_air: bool, | ... | @@ -59,15 +57,12 @@ verbose_air: bool, |
| 59 | verbose_llvm_ir: bool, | 57 | verbose_llvm_ir: bool, |
| 60 | verbose_cimport: bool, | 58 | verbose_cimport: bool, |
| 61 | verbose_llvm_cpu_features: bool, | 59 | verbose_llvm_cpu_features: bool, |
| 62 | /// The purpose of executing the command is for a human to read compile errors from the terminal | ||
| 63 | prominent_compile_errors: bool, | ||
| 64 | color: enum { auto, on, off } = .auto, | ||
| 65 | reference_trace: ?u32 = null, | 60 | reference_trace: ?u32 = null, |
| 66 | invalid_user_input: bool, | 61 | invalid_user_input: bool, |
| 67 | zig_exe: []const u8, | 62 | zig_exe: []const u8, |
| 68 | default_step: *Step, | 63 | default_step: *Step, |
| 69 | env_map: *EnvMap, | 64 | env_map: *EnvMap, |
| 70 | top_level_steps: ArrayList(*TopLevelStep), | 65 | top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep), |
| 71 | install_prefix: []const u8, | 66 | install_prefix: []const u8, |
| 72 | dest_dir: ?[]const u8, | 67 | dest_dir: ?[]const u8, |
| 73 | lib_dir: []const u8, | 68 | lib_dir: []const u8, |
| ... | @@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, | ... | @@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, |
| 90 | args: ?[][]const u8 = null, | 85 | args: ?[][]const u8 = null, |
| 91 | debug_log_scopes: []const []const u8 = &.{}, | 86 | debug_log_scopes: []const []const u8 = &.{}, |
| 92 | debug_compile_errors: bool = false, | 87 | debug_compile_errors: bool = false, |
| 88 | debug_pkg_config: bool = false, | ||
| 93 | 89 | ||
| 94 | /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. | 90 | /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. |
| 95 | enable_darling: bool = false, | 91 | enable_darling: bool = false, |
| ... | @@ -198,7 +194,7 @@ pub fn create( | ... | @@ -198,7 +194,7 @@ pub fn create( |
| 198 | env_map.* = try process.getEnvMap(allocator); | 194 | env_map.* = try process.getEnvMap(allocator); |
| 199 | 195 | ||
| 200 | const self = try allocator.create(Build); | 196 | const self = try allocator.create(Build); |
| 201 | self.* = Build{ | 197 | self.* = .{ |
| 202 | .zig_exe = zig_exe, | 198 | .zig_exe = zig_exe, |
| 203 | .build_root = build_root, | 199 | .build_root = build_root, |
| 204 | .cache_root = cache_root, | 200 | .cache_root = cache_root, |
| ... | @@ -211,13 +207,12 @@ pub fn create( | ... | @@ -211,13 +207,12 @@ pub fn create( |
| 211 | .verbose_llvm_ir = false, | 207 | .verbose_llvm_ir = false, |
| 212 | .verbose_cimport = false, | 208 | .verbose_cimport = false, |
| 213 | .verbose_llvm_cpu_features = false, | 209 | .verbose_llvm_cpu_features = false, |
| 214 | .prominent_compile_errors = false, | ||
| 215 | .invalid_user_input = false, | 210 | .invalid_user_input = false, |
| 216 | .allocator = allocator, | 211 | .allocator = allocator, |
| 217 | .user_input_options = UserInputOptionsMap.init(allocator), | 212 | .user_input_options = UserInputOptionsMap.init(allocator), |
| 218 | .available_options_map = AvailableOptionsMap.init(allocator), | 213 | .available_options_map = AvailableOptionsMap.init(allocator), |
| 219 | .available_options_list = ArrayList(AvailableOption).init(allocator), | 214 | .available_options_list = ArrayList(AvailableOption).init(allocator), |
| 220 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | 215 | .top_level_steps = .{}, |
| 221 | .default_step = undefined, | 216 | .default_step = undefined, |
| 222 | .env_map = env_map, | 217 | .env_map = env_map, |
| 223 | .search_prefixes = ArrayList([]const u8).init(allocator), | 218 | .search_prefixes = ArrayList([]const u8).init(allocator), |
| ... | @@ -227,12 +222,21 @@ pub fn create( | ... | @@ -227,12 +222,21 @@ pub fn create( |
| 227 | .h_dir = undefined, | 222 | .h_dir = undefined, |
| 228 | .dest_dir = env_map.get("DESTDIR"), | 223 | .dest_dir = env_map.get("DESTDIR"), |
| 229 | .installed_files = ArrayList(InstalledFile).init(allocator), | 224 | .installed_files = ArrayList(InstalledFile).init(allocator), |
| 230 | .install_tls = TopLevelStep{ | 225 | .install_tls = .{ |
| 231 | .step = Step.initNoOp(.top_level, "install", allocator), | 226 | .step = Step.init(.{ |
| 227 | .id = .top_level, | ||
| 228 | .name = "install", | ||
| 229 | .owner = self, | ||
| 230 | }), | ||
| 232 | .description = "Copy build artifacts to prefix path", | 231 | .description = "Copy build artifacts to prefix path", |
| 233 | }, | 232 | }, |
| 234 | .uninstall_tls = TopLevelStep{ | 233 | .uninstall_tls = .{ |
| 235 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | 234 | .step = Step.init(.{ |
| 235 | .id = .top_level, | ||
| 236 | .name = "uninstall", | ||
| 237 | .owner = self, | ||
| 238 | .makeFn = makeUninstall, | ||
| 239 | }), | ||
| 236 | .description = "Remove build artifacts from prefix path", | 240 | .description = "Remove build artifacts from prefix path", |
| 237 | }, | 241 | }, |
| 238 | .zig_lib_dir = null, | 242 | .zig_lib_dir = null, |
| ... | @@ -241,8 +245,8 @@ pub fn create( | ... | @@ -241,8 +245,8 @@ pub fn create( |
| 241 | .host = host, | 245 | .host = host, |
| 242 | .modules = std.StringArrayHashMap(*Module).init(allocator), | 246 | .modules = std.StringArrayHashMap(*Module).init(allocator), |
| 243 | }; | 247 | }; |
| 244 | try self.top_level_steps.append(&self.install_tls); | 248 | try self.top_level_steps.put(allocator, self.install_tls.step.name, &self.install_tls); |
| 245 | try self.top_level_steps.append(&self.uninstall_tls); | 249 | try self.top_level_steps.put(allocator, self.uninstall_tls.step.name, &self.uninstall_tls); |
| 246 | self.default_step = &self.install_tls.step; | 250 | self.default_step = &self.install_tls.step; |
| 247 | return self; | 251 | return self; |
| 248 | } | 252 | } |
| ... | @@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 264 | child.* = .{ | 268 | child.* = .{ |
| 265 | .allocator = allocator, | 269 | .allocator = allocator, |
| 266 | .install_tls = .{ | 270 | .install_tls = .{ |
| 267 | .step = Step.initNoOp(.top_level, "install", allocator), | 271 | .step = Step.init(.{ |
| 272 | .id = .top_level, | ||
| 273 | .name = "install", | ||
| 274 | .owner = child, | ||
| 275 | }), | ||
| 268 | .description = "Copy build artifacts to prefix path", | 276 | .description = "Copy build artifacts to prefix path", |
| 269 | }, | 277 | }, |
| 270 | .uninstall_tls = .{ | 278 | .uninstall_tls = .{ |
| 271 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | 279 | .step = Step.init(.{ |
| 280 | .id = .top_level, | ||
| 281 | .name = "uninstall", | ||
| 282 | .owner = child, | ||
| 283 | .makeFn = makeUninstall, | ||
| 284 | }), | ||
| 272 | .description = "Remove build artifacts from prefix path", | 285 | .description = "Remove build artifacts from prefix path", |
| 273 | }, | 286 | }, |
| 274 | .user_input_options = UserInputOptionsMap.init(allocator), | 287 | .user_input_options = UserInputOptionsMap.init(allocator), |
| ... | @@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 281 | .verbose_llvm_ir = parent.verbose_llvm_ir, | 294 | .verbose_llvm_ir = parent.verbose_llvm_ir, |
| 282 | .verbose_cimport = parent.verbose_cimport, | 295 | .verbose_cimport = parent.verbose_cimport, |
| 283 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, | 296 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, |
| 284 | .prominent_compile_errors = parent.prominent_compile_errors, | ||
| 285 | .color = parent.color, | ||
| 286 | .reference_trace = parent.reference_trace, | 297 | .reference_trace = parent.reference_trace, |
| 287 | .invalid_user_input = false, | 298 | .invalid_user_input = false, |
| 288 | .zig_exe = parent.zig_exe, | 299 | .zig_exe = parent.zig_exe, |
| 289 | .default_step = undefined, | 300 | .default_step = undefined, |
| 290 | .env_map = parent.env_map, | 301 | .env_map = parent.env_map, |
| 291 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | 302 | .top_level_steps = .{}, |
| 292 | .install_prefix = undefined, | 303 | .install_prefix = undefined, |
| 293 | .dest_dir = parent.dest_dir, | 304 | .dest_dir = parent.dest_dir, |
| 294 | .lib_dir = parent.lib_dir, | 305 | .lib_dir = parent.lib_dir, |
| ... | @@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 306 | .zig_lib_dir = parent.zig_lib_dir, | 317 | .zig_lib_dir = parent.zig_lib_dir, |
| 307 | .debug_log_scopes = parent.debug_log_scopes, | 318 | .debug_log_scopes = parent.debug_log_scopes, |
| 308 | .debug_compile_errors = parent.debug_compile_errors, | 319 | .debug_compile_errors = parent.debug_compile_errors, |
| 320 | .debug_pkg_config = parent.debug_pkg_config, | ||
| 309 | .enable_darling = parent.enable_darling, | 321 | .enable_darling = parent.enable_darling, |
| 310 | .enable_qemu = parent.enable_qemu, | 322 | .enable_qemu = parent.enable_qemu, |
| 311 | .enable_rosetta = parent.enable_rosetta, | 323 | .enable_rosetta = parent.enable_rosetta, |
| ... | @@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 316 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), | 328 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), |
| 317 | .modules = std.StringArrayHashMap(*Module).init(allocator), | 329 | .modules = std.StringArrayHashMap(*Module).init(allocator), |
| 318 | }; | 330 | }; |
| 319 | try child.top_level_steps.append(&child.install_tls); | 331 | try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls); |
| 320 | try child.top_level_steps.append(&child.uninstall_tls); | 332 | try child.top_level_steps.put(allocator, child.uninstall_tls.step.name, &child.uninstall_tls); |
| 321 | child.default_step = &child.install_tls.step; | 333 | child.default_step = &child.install_tls.step; |
| 322 | return child; | 334 | return child; |
| 323 | } | 335 | } |
| ... | @@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void { | ... | @@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void { |
| 372 | }, | 384 | }, |
| 373 | } | 385 | } |
| 374 | } | 386 | } |
| 375 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | 387 | |
| 388 | // Create an installation directory local to this package. This will be used when | ||
| 389 | // dependant packages require a standard prefix, such as include directories for C headers. | ||
| 390 | var hash = b.cache.hash; | ||
| 376 | // Random bytes to make unique. Refresh this with new random bytes when | 391 | // Random bytes to make unique. Refresh this with new random bytes when |
| 377 | // implementation is modified in a non-backwards-compatible way. | 392 | // implementation is modified in a non-backwards-compatible way. |
| 378 | var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); | 393 | hash.add(@as(u32, 0xd8cb0055)); |
| 379 | hash.update(b.dep_prefix); | 394 | hash.addBytes(b.dep_prefix); |
| 380 | // TODO additionally update the hash with `args`. | 395 | // TODO additionally update the hash with `args`. |
| 381 | 396 | const digest = hash.final(); | |
| 382 | var digest: [16]u8 = undefined; | 397 | const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest }); |
| 383 | hash.final(&digest); | ||
| 384 | var hash_basename: [digest.len * 2]u8 = undefined; | ||
| 385 | _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch | ||
| 386 | unreachable; | ||
| 387 | |||
| 388 | const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename }); | ||
| 389 | b.resolveInstallPrefix(install_prefix, .{}); | 398 | b.resolveInstallPrefix(install_prefix, .{}); |
| 390 | } | 399 | } |
| 391 | 400 | ||
| 392 | pub fn destroy(self: *Build) void { | 401 | pub fn destroy(b: *Build) void { |
| 393 | self.env_map.deinit(); | 402 | b.env_map.deinit(); |
| 394 | self.top_level_steps.deinit(); | 403 | b.top_level_steps.deinit(b.allocator); |
| 395 | self.allocator.destroy(self); | 404 | b.allocator.destroy(b); |
| 396 | } | 405 | } |
| 397 | 406 | ||
| 398 | /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. | 407 | /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. |
| ... | @@ -441,6 +450,7 @@ pub const ExecutableOptions = struct { | ... | @@ -441,6 +450,7 @@ pub const ExecutableOptions = struct { |
| 441 | target: CrossTarget = .{}, | 450 | target: CrossTarget = .{}, |
| 442 | optimize: std.builtin.Mode = .Debug, | 451 | optimize: std.builtin.Mode = .Debug, |
| 443 | linkage: ?CompileStep.Linkage = null, | 452 | linkage: ?CompileStep.Linkage = null, |
| 453 | max_rss: usize = 0, | ||
| 444 | }; | 454 | }; |
| 445 | 455 | ||
| 446 | pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { | 456 | pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { |
| ... | @@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { | ... | @@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { |
| 452 | .optimize = options.optimize, | 462 | .optimize = options.optimize, |
| 453 | .kind = .exe, | 463 | .kind = .exe, |
| 454 | .linkage = options.linkage, | 464 | .linkage = options.linkage, |
| 465 | .max_rss = options.max_rss, | ||
| 455 | }); | 466 | }); |
| 456 | } | 467 | } |
| 457 | 468 | ||
| ... | @@ -460,6 +471,7 @@ pub const ObjectOptions = struct { | ... | @@ -460,6 +471,7 @@ pub const ObjectOptions = struct { |
| 460 | root_source_file: ?FileSource = null, | 471 | root_source_file: ?FileSource = null, |
| 461 | target: CrossTarget, | 472 | target: CrossTarget, |
| 462 | optimize: std.builtin.Mode, | 473 | optimize: std.builtin.Mode, |
| 474 | max_rss: usize = 0, | ||
| 463 | }; | 475 | }; |
| 464 | 476 | ||
| 465 | pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { | 477 | pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { |
| ... | @@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { | ... | @@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { |
| 469 | .target = options.target, | 481 | .target = options.target, |
| 470 | .optimize = options.optimize, | 482 | .optimize = options.optimize, |
| 471 | .kind = .obj, | 483 | .kind = .obj, |
| 484 | .max_rss = options.max_rss, | ||
| 472 | }); | 485 | }); |
| 473 | } | 486 | } |
| 474 | 487 | ||
| ... | @@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct { | ... | @@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct { |
| 478 | version: ?std.builtin.Version = null, | 491 | version: ?std.builtin.Version = null, |
| 479 | target: CrossTarget, | 492 | target: CrossTarget, |
| 480 | optimize: std.builtin.Mode, | 493 | optimize: std.builtin.Mode, |
| 494 | max_rss: usize = 0, | ||
| 481 | }; | 495 | }; |
| 482 | 496 | ||
| 483 | pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { | 497 | pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { |
| ... | @@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { | ... | @@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { |
| 489 | .version = options.version, | 503 | .version = options.version, |
| 490 | .target = options.target, | 504 | .target = options.target, |
| 491 | .optimize = options.optimize, | 505 | .optimize = options.optimize, |
| 506 | .max_rss = options.max_rss, | ||
| 492 | }); | 507 | }); |
| 493 | } | 508 | } |
| 494 | 509 | ||
| ... | @@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct { | ... | @@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct { |
| 498 | target: CrossTarget, | 513 | target: CrossTarget, |
| 499 | optimize: std.builtin.Mode, | 514 | optimize: std.builtin.Mode, |
| 500 | version: ?std.builtin.Version = null, | 515 | version: ?std.builtin.Version = null, |
| 516 | max_rss: usize = 0, | ||
| 501 | }; | 517 | }; |
| 502 | 518 | ||
| 503 | pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { | 519 | pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { |
| ... | @@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { | ... | @@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { |
| 509 | .version = options.version, | 525 | .version = options.version, |
| 510 | .target = options.target, | 526 | .target = options.target, |
| 511 | .optimize = options.optimize, | 527 | .optimize = options.optimize, |
| 528 | .max_rss = options.max_rss, | ||
| 512 | }); | 529 | }); |
| 513 | } | 530 | } |
| 514 | 531 | ||
| 515 | pub const TestOptions = struct { | 532 | pub const TestOptions = struct { |
| 516 | name: []const u8 = "test", | 533 | name: []const u8 = "test", |
| 517 | kind: CompileStep.Kind = .@"test", | ||
| 518 | root_source_file: FileSource, | 534 | root_source_file: FileSource, |
| 519 | target: CrossTarget = .{}, | 535 | target: CrossTarget = .{}, |
| 520 | optimize: std.builtin.Mode = .Debug, | 536 | optimize: std.builtin.Mode = .Debug, |
| 521 | version: ?std.builtin.Version = null, | 537 | version: ?std.builtin.Version = null, |
| 538 | max_rss: usize = 0, | ||
| 522 | }; | 539 | }; |
| 523 | 540 | ||
| 524 | pub fn addTest(b: *Build, options: TestOptions) *CompileStep { | 541 | pub fn addTest(b: *Build, options: TestOptions) *CompileStep { |
| 525 | return CompileStep.create(b, .{ | 542 | return CompileStep.create(b, .{ |
| 526 | .name = options.name, | 543 | .name = options.name, |
| 527 | .kind = options.kind, | 544 | .kind = .@"test", |
| 528 | .root_source_file = options.root_source_file, | 545 | .root_source_file = options.root_source_file, |
| 529 | .target = options.target, | 546 | .target = options.target, |
| 530 | .optimize = options.optimize, | 547 | .optimize = options.optimize, |
| 548 | .max_rss = options.max_rss, | ||
| 531 | }); | 549 | }); |
| 532 | } | 550 | } |
| 533 | 551 | ||
| ... | @@ -536,6 +554,7 @@ pub const AssemblyOptions = struct { | ... | @@ -536,6 +554,7 @@ pub const AssemblyOptions = struct { |
| 536 | source_file: FileSource, | 554 | source_file: FileSource, |
| 537 | target: CrossTarget, | 555 | target: CrossTarget, |
| 538 | optimize: std.builtin.Mode, | 556 | optimize: std.builtin.Mode, |
| 557 | max_rss: usize = 0, | ||
| 539 | }; | 558 | }; |
| 540 | 559 | ||
| 541 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { | 560 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { |
| ... | @@ -545,22 +564,19 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { | ... | @@ -545,22 +564,19 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { |
| 545 | .root_source_file = null, | 564 | .root_source_file = null, |
| 546 | .target = options.target, | 565 | .target = options.target, |
| 547 | .optimize = options.optimize, | 566 | .optimize = options.optimize, |
| 567 | .max_rss = options.max_rss, | ||
| 548 | }); | 568 | }); |
| 549 | obj_step.addAssemblyFileSource(options.source_file.dupe(b)); | 569 | obj_step.addAssemblyFileSource(options.source_file.dupe(b)); |
| 550 | return obj_step; | 570 | return obj_step; |
| 551 | } | 571 | } |
| 552 | 572 | ||
| 553 | pub const AddModuleOptions = struct { | 573 | /// This function creates a module and adds it to the package's module set, making |
| 554 | name: []const u8, | 574 | /// it available to other packages which depend on this one. |
| 555 | source_file: FileSource, | 575 | /// `createModule` can be used instead to create a private module. |
| 556 | dependencies: []const ModuleDependency = &.{}, | 576 | pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module { |
| 557 | }; | 577 | const module = b.createModule(options); |
| 558 | 578 | b.modules.put(b.dupe(name), module) catch @panic("OOM"); | |
| 559 | pub fn addModule(b: *Build, options: AddModuleOptions) void { | 579 | return module; |
| 560 | b.modules.put(b.dupe(options.name), b.createModule(.{ | ||
| 561 | .source_file = options.source_file, | ||
| 562 | .dependencies = options.dependencies, | ||
| 563 | })) catch @panic("OOM"); | ||
| 564 | } | 580 | } |
| 565 | 581 | ||
| 566 | pub const ModuleDependency = struct { | 582 | pub const ModuleDependency = struct { |
| ... | @@ -573,8 +589,9 @@ pub const CreateModuleOptions = struct { | ... | @@ -573,8 +589,9 @@ pub const CreateModuleOptions = struct { |
| 573 | dependencies: []const ModuleDependency = &.{}, | 589 | dependencies: []const ModuleDependency = &.{}, |
| 574 | }; | 590 | }; |
| 575 | 591 | ||
| 576 | /// Prefer to use `addModule` which will make the module available to other | 592 | /// This function creates a private module, to be used by the current package, |
| 577 | /// packages which depend on this package. | 593 | /// but not exposed to other packages depending on this one. |
| 594 | /// `addModule` can be used instead to create a public module. | ||
| 578 | pub fn createModule(b: *Build, options: CreateModuleOptions) *Module { | 595 | pub fn createModule(b: *Build, options: CreateModuleOptions) *Module { |
| 579 | const module = b.allocator.create(Module) catch @panic("OOM"); | 596 | const module = b.allocator.create(Module) catch @panic("OOM"); |
| 580 | module.* = .{ | 597 | module.* = .{ |
| ... | @@ -608,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { | ... | @@ -608,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { |
| 608 | /// Creates a `RunStep` with an executable built with `addExecutable`. | 625 | /// Creates a `RunStep` with an executable built with `addExecutable`. |
| 609 | /// Add command line arguments with methods of `RunStep`. | 626 | /// Add command line arguments with methods of `RunStep`. |
| 610 | pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { | 627 | pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { |
| 611 | assert(exe.kind == .exe or exe.kind == .test_exe); | ||
| 612 | |||
| 613 | // It doesn't have to be native. We catch that if you actually try to run it. | 628 | // It doesn't have to be native. We catch that if you actually try to run it. |
| 614 | // Consider that this is declarative; the run step may not be run unless a user | 629 | // Consider that this is declarative; the run step may not be run unless a user |
| 615 | // option is supplied. | 630 | // option is supplied. |
| 616 | const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name})); | 631 | const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name})); |
| 617 | run_step.addArtifactArg(exe); | 632 | run_step.addArtifactArg(exe); |
| 618 | 633 | ||
| 619 | if (exe.kind == .test_exe) { | 634 | if (exe.kind == .@"test") { |
| 620 | run_step.addArg(b.zig_exe); | 635 | run_step.stdio = .zig_test; |
| 636 | run_step.addArgs(&.{"--listen=-"}); | ||
| 621 | } | 637 | } |
| 622 | 638 | ||
| 623 | if (exe.vcpkg_bin_path) |path| { | 639 | if (exe.vcpkg_bin_path) |path| { |
| ... | @@ -637,7 +653,11 @@ pub fn addConfigHeader( | ... | @@ -637,7 +653,11 @@ pub fn addConfigHeader( |
| 637 | options: ConfigHeaderStep.Options, | 653 | options: ConfigHeaderStep.Options, |
| 638 | values: anytype, | 654 | values: anytype, |
| 639 | ) *ConfigHeaderStep { | 655 | ) *ConfigHeaderStep { |
| 640 | const config_header_step = ConfigHeaderStep.create(b, options); | 656 | var options_copy = options; |
| 657 | if (options_copy.first_ret_addr == null) | ||
| 658 | options_copy.first_ret_addr = @returnAddress(); | ||
| 659 | |||
| 660 | const config_header_step = ConfigHeaderStep.create(b, options_copy); | ||
| 641 | config_header_step.addValues(values); | 661 | config_header_step.addValues(values); |
| 642 | return config_header_step; | 662 | return config_header_step; |
| 643 | } | 663 | } |
| ... | @@ -674,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ | ... | @@ -674,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ |
| 674 | return write_file_step; | 694 | return write_file_step; |
| 675 | } | 695 | } |
| 676 | 696 | ||
| 677 | pub fn addWriteFiles(self: *Build) *WriteFileStep { | 697 | pub fn addWriteFiles(b: *Build) *WriteFileStep { |
| 678 | const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM"); | 698 | return WriteFileStep.create(b); |
| 679 | write_file_step.* = WriteFileStep.init(self); | ||
| 680 | return write_file_step; | ||
| 681 | } | ||
| 682 | |||
| 683 | pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep { | ||
| 684 | const data = self.fmt(format, args); | ||
| 685 | const log_step = self.allocator.create(LogStep) catch @panic("OOM"); | ||
| 686 | log_step.* = LogStep.init(self, data); | ||
| 687 | return log_step; | ||
| 688 | } | 699 | } |
| 689 | 700 | ||
| 690 | pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { | 701 | pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { |
| ... | @@ -693,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { | ... | @@ -693,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { |
| 693 | return remove_dir_step; | 704 | return remove_dir_step; |
| 694 | } | 705 | } |
| 695 | 706 | ||
| 696 | pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep { | 707 | pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep { |
| 697 | return FmtStep.create(self, paths); | 708 | return FmtStep.create(b, options); |
| 698 | } | 709 | } |
| 699 | 710 | ||
| 700 | pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { | 711 | pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { |
| 701 | return TranslateCStep.create(self, options); | 712 | return TranslateCStep.create(self, options); |
| 702 | } | 713 | } |
| 703 | 714 | ||
| 704 | pub 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 | |||
| 722 | pub fn getInstallStep(self: *Build) *Step { | 715 | pub fn getInstallStep(self: *Build) *Step { |
| 723 | return &self.install_tls.step; | 716 | return &self.install_tls.step; |
| 724 | } | 717 | } |
| ... | @@ -727,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step { | ... | @@ -727,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step { |
| 727 | return &self.uninstall_tls.step; | 720 | return &self.uninstall_tls.step; |
| 728 | } | 721 | } |
| 729 | 722 | ||
| 730 | fn makeUninstall(uninstall_step: *Step) anyerror!void { | 723 | fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void { |
| 724 | _ = prog_node; | ||
| 731 | const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); | 725 | const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); |
| 732 | const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); | 726 | const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); |
| 733 | 727 | ||
| ... | @@ -742,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void { | ... | @@ -742,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void { |
| 742 | // TODO remove empty directories | 736 | // TODO remove empty directories |
| 743 | } | 737 | } |
| 744 | 738 | ||
| 745 | fn 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 | |||
| 766 | fn 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 | |||
| 776 | pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { | 739 | pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { |
| 777 | const name = self.dupe(name_raw); | 740 | const name = self.dupe(name_raw); |
| 778 | const description = self.dupe(description_raw); | 741 | const description = self.dupe(description_raw); |
| ... | @@ -909,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ | ... | @@ -909,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ |
| 909 | 872 | ||
| 910 | pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { | 873 | pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { |
| 911 | const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); | 874 | const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); |
| 912 | step_info.* = TopLevelStep{ | 875 | step_info.* = .{ |
| 913 | .step = Step.initNoOp(.top_level, name, self.allocator), | 876 | .step = Step.init(.{ |
| 877 | .id = .top_level, | ||
| 878 | .name = name, | ||
| 879 | .owner = self, | ||
| 880 | }), | ||
| 914 | .description = self.dupe(description), | 881 | .description = self.dupe(description), |
| 915 | }; | 882 | }; |
| 916 | self.top_level_steps.append(step_info) catch @panic("OOM"); | 883 | self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM"); |
| 917 | return &step_info.step; | 884 | return &step_info.step; |
| 918 | } | 885 | } |
| 919 | 886 | ||
| ... | @@ -1181,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool { | ... | @@ -1181,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool { |
| 1181 | return self.invalid_user_input; | 1148 | return self.invalid_user_input; |
| 1182 | } | 1149 | } |
| 1183 | 1150 | ||
| 1184 | pub fn spawnChild(self: *Build, argv: []const []const u8) !void { | 1151 | fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { |
| 1185 | return self.spawnChildEnvMap(null, self.env_map, argv); | 1152 | var buf = ArrayList(u8).init(ally); |
| 1186 | } | 1153 | if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd}); |
| 1187 | |||
| 1188 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | ||
| 1189 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | ||
| 1190 | for (argv) |arg| { | 1154 | for (argv) |arg| { |
| 1191 | std.debug.print("{s} ", .{arg}); | 1155 | try buf.writer().print("{s} ", .{arg}); |
| 1192 | } | 1156 | } |
| 1193 | std.debug.print("\n", .{}); | 1157 | return buf.toOwnedSlice(); |
| 1194 | } | 1158 | } |
| 1195 | 1159 | ||
| 1196 | pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { | 1160 | fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void { |
| 1197 | if (self.verbose) { | 1161 | const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM"); |
| 1198 | printCmd(cwd, argv); | 1162 | std.debug.print("{s}\n", .{text}); |
| 1199 | } | ||
| 1200 | |||
| 1201 | if (!std.process.can_spawn) | ||
| 1202 | return error.ExecNotSupported; | ||
| 1203 | |||
| 1204 | var child = std.ChildProcess.init(argv, self.allocator); | ||
| 1205 | child.cwd = cwd; | ||
| 1206 | child.env_map = env_map; | ||
| 1207 | |||
| 1208 | const term = child.spawnAndWait() catch |err| { | ||
| 1209 | log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); | ||
| 1210 | return err; | ||
| 1211 | }; | ||
| 1212 | |||
| 1213 | switch (term) { | ||
| 1214 | .Exited => |code| { | ||
| 1215 | if (code != 0) { | ||
| 1216 | log.err("The following command exited with error code {}:", .{code}); | ||
| 1217 | printCmd(cwd, argv); | ||
| 1218 | return error.UncleanExit; | ||
| 1219 | } | ||
| 1220 | }, | ||
| 1221 | else => { | ||
| 1222 | log.err("The following command terminated unexpectedly:", .{}); | ||
| 1223 | printCmd(cwd, argv); | ||
| 1224 | |||
| 1225 | return error.UncleanExit; | ||
| 1226 | }, | ||
| 1227 | } | ||
| 1228 | } | 1163 | } |
| 1229 | 1164 | ||
| 1230 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { | 1165 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { |
| ... | @@ -1283,12 +1218,7 @@ pub fn addInstallFileWithDir( | ... | @@ -1283,12 +1218,7 @@ pub fn addInstallFileWithDir( |
| 1283 | install_dir: InstallDir, | 1218 | install_dir: InstallDir, |
| 1284 | dest_rel_path: []const u8, | 1219 | dest_rel_path: []const u8, |
| 1285 | ) *InstallFileStep { | 1220 | ) *InstallFileStep { |
| 1286 | if (dest_rel_path.len == 0) { | 1221 | return InstallFileStep.create(self, source.dupe(self), install_dir, dest_rel_path); |
| 1287 | panic("dest_rel_path must be non-empty", .{}); | ||
| 1288 | } | ||
| 1289 | const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM"); | ||
| 1290 | install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); | ||
| 1291 | return install_step; | ||
| 1292 | } | 1222 | } |
| 1293 | 1223 | ||
| 1294 | pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { | 1224 | pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { |
| ... | @@ -1297,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst | ... | @@ -1297,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst |
| 1297 | return install_step; | 1227 | return install_step; |
| 1298 | } | 1228 | } |
| 1299 | 1229 | ||
| 1230 | pub 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 | |||
| 1300 | pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { | 1238 | pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { |
| 1301 | const file = InstalledFile{ | 1239 | const file = InstalledFile{ |
| 1302 | .dir = dir, | 1240 | .dir = dir, |
| ... | @@ -1305,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u | ... | @@ -1305,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u |
| 1305 | self.installed_files.append(file.dupe(self)) catch @panic("OOM"); | 1243 | self.installed_files.append(file.dupe(self)) catch @panic("OOM"); |
| 1306 | } | 1244 | } |
| 1307 | 1245 | ||
| 1308 | pub 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 | |||
| 1320 | pub fn truncateFile(self: *Build, dest_path: []const u8) !void { | 1246 | pub fn truncateFile(self: *Build, dest_path: []const u8) !void { |
| 1321 | if (self.verbose) { | 1247 | if (self.verbose) { |
| 1322 | log.info("truncate {s}", .{dest_path}); | 1248 | log.info("truncate {s}", .{dest_path}); |
| ... | @@ -1400,7 +1326,7 @@ pub fn execAllowFail( | ... | @@ -1400,7 +1326,7 @@ pub fn execAllowFail( |
| 1400 | ) ExecError![]u8 { | 1326 | ) ExecError![]u8 { |
| 1401 | assert(argv.len != 0); | 1327 | assert(argv.len != 0); |
| 1402 | 1328 | ||
| 1403 | if (!std.process.can_spawn) | 1329 | if (!process.can_spawn) |
| 1404 | return error.ExecNotSupported; | 1330 | return error.ExecNotSupported; |
| 1405 | 1331 | ||
| 1406 | const max_output_size = 400 * 1024; | 1332 | const max_output_size = 400 * 1024; |
| ... | @@ -1433,59 +1359,27 @@ pub fn execAllowFail( | ... | @@ -1433,59 +1359,27 @@ pub fn execAllowFail( |
| 1433 | } | 1359 | } |
| 1434 | } | 1360 | } |
| 1435 | 1361 | ||
| 1436 | pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 { | 1362 | /// This is a helper function to be called from build.zig scripts, *not* from |
| 1437 | assert(argv.len != 0); | 1363 | /// inside step make() functions. If any errors occur, it fails the build with |
| 1438 | 1364 | /// a helpful message. | |
| 1439 | if (self.verbose) { | 1365 | pub fn exec(b: *Build, argv: []const []const u8) []u8 { |
| 1440 | printCmd(null, argv); | 1366 | if (!process.can_spawn) { |
| 1441 | } | 1367 | std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{ |
| 1442 | 1368 | try allocPrintCmd(b.allocator, null, argv), | |
| 1443 | if (!std.process.can_spawn) { | 1369 | }); |
| 1444 | if (src_step) |s| log.err("{s}...", .{s.name}); | 1370 | process.exit(1); |
| 1445 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | ||
| 1446 | printCmd(null, argv); | ||
| 1447 | std.os.abort(); | ||
| 1448 | } | 1371 | } |
| 1449 | 1372 | ||
| 1450 | var code: u8 = undefined; | 1373 | var code: u8 = undefined; |
| 1451 | return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { | 1374 | return b.execAllowFail(argv, &code, .Inherit) catch |err| { |
| 1452 | error.ExecNotSupported => { | 1375 | const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM"); |
| 1453 | if (src_step) |s| log.err("{s}...", .{s.name}); | 1376 | std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{ |
| 1454 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | 1377 | @errorName(err), printed_cmd, |
| 1455 | printCmd(null, argv); | 1378 | }); |
| 1456 | std.os.abort(); | 1379 | process.exit(1); |
| 1457 | }, | ||
| 1458 | error.FileNotFound => { | ||
| 1459 | if (src_step) |s| log.err("{s}...", .{s.name}); | ||
| 1460 | log.err("Unable to spawn the following command: file not found", .{}); | ||
| 1461 | printCmd(null, argv); | ||
| 1462 | std.os.exit(@truncate(u8, code)); | ||
| 1463 | }, | ||
| 1464 | error.ExitCodeFailure => { | ||
| 1465 | if (src_step) |s| log.err("{s}...", .{s.name}); | ||
| 1466 | if (self.prominent_compile_errors) { | ||
| 1467 | log.err("The step exited with error code {d}", .{code}); | ||
| 1468 | } else { | ||
| 1469 | log.err("The following command exited with error code {d}:", .{code}); | ||
| 1470 | printCmd(null, argv); | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | std.os.exit(@truncate(u8, code)); | ||
| 1474 | }, | ||
| 1475 | error.ProcessTerminated => { | ||
| 1476 | if (src_step) |s| log.err("{s}...", .{s.name}); | ||
| 1477 | log.err("The following command terminated unexpectedly:", .{}); | ||
| 1478 | printCmd(null, argv); | ||
| 1479 | std.os.exit(@truncate(u8, code)); | ||
| 1480 | }, | ||
| 1481 | else => |e| return e, | ||
| 1482 | }; | 1380 | }; |
| 1483 | } | 1381 | } |
| 1484 | 1382 | ||
| 1485 | pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { | ||
| 1486 | return self.execFromStep(argv, null); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { | 1383 | pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { |
| 1490 | self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); | 1384 | self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); |
| 1491 | } | 1385 | } |
| ... | @@ -1550,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { | ... | @@ -1550,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { |
| 1550 | 1444 | ||
| 1551 | const full_path = b.pathFromRoot("build.zig.zon"); | 1445 | const full_path = b.pathFromRoot("build.zig.zon"); |
| 1552 | std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path }); | 1446 | std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path }); |
| 1553 | std.process.exit(1); | 1447 | process.exit(1); |
| 1448 | } | ||
| 1449 | |||
| 1450 | pub fn anonymousDependency( | ||
| 1451 | b: *Build, | ||
| 1452 | /// The path to the directory containing the dependency's build.zig file, | ||
| 1453 | /// relative to the current package's build.zig. | ||
| 1454 | relative_build_root: []const u8, | ||
| 1455 | /// A direct `@import` of the build.zig of the dependency. | ||
| 1456 | comptime build_zig: type, | ||
| 1457 | args: anytype, | ||
| 1458 | ) *Dependency { | ||
| 1459 | const arena = b.allocator; | ||
| 1460 | const build_root = b.build_root.join(arena, &.{relative_build_root}) catch @panic("OOM"); | ||
| 1461 | const name = arena.dupe(u8, relative_build_root) catch @panic("OOM"); | ||
| 1462 | for (name) |*byte| switch (byte.*) { | ||
| 1463 | '/', '\\' => byte.* = '.', | ||
| 1464 | else => continue, | ||
| 1465 | }; | ||
| 1466 | return dependencyInner(b, name, build_root, build_zig, args); | ||
| 1554 | } | 1467 | } |
| 1555 | 1468 | ||
| 1556 | fn dependencyInner( | 1469 | pub fn dependencyInner( |
| 1557 | b: *Build, | 1470 | b: *Build, |
| 1558 | name: []const u8, | 1471 | name: []const u8, |
| 1559 | build_root_string: []const u8, | 1472 | build_root_string: []const u8, |
| ... | @@ -1566,7 +1479,7 @@ fn dependencyInner( | ... | @@ -1566,7 +1479,7 @@ fn dependencyInner( |
| 1566 | std.debug.print("unable to open '{s}': {s}\n", .{ | 1479 | std.debug.print("unable to open '{s}': {s}\n", .{ |
| 1567 | build_root_string, @errorName(err), | 1480 | build_root_string, @errorName(err), |
| 1568 | }); | 1481 | }); |
| 1569 | std.process.exit(1); | 1482 | process.exit(1); |
| 1570 | }, | 1483 | }, |
| 1571 | }; | 1484 | }; |
| 1572 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); | 1485 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); |
| ... | @@ -1610,7 +1523,7 @@ pub const GeneratedFile = struct { | ... | @@ -1610,7 +1523,7 @@ pub const GeneratedFile = struct { |
| 1610 | 1523 | ||
| 1611 | pub fn getPath(self: GeneratedFile) []const u8 { | 1524 | pub fn getPath(self: GeneratedFile) []const u8 { |
| 1612 | return self.path orelse std.debug.panic( | 1525 | return self.path orelse std.debug.panic( |
| 1613 | "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", | 1526 | "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?", |
| 1614 | .{self.step.name}, | 1527 | .{self.step.name}, |
| 1615 | ); | 1528 | ); |
| 1616 | } | 1529 | } |
| ... | @@ -1650,12 +1563,23 @@ pub const FileSource = union(enum) { | ... | @@ -1650,12 +1563,23 @@ pub const FileSource = union(enum) { |
| 1650 | } | 1563 | } |
| 1651 | 1564 | ||
| 1652 | /// Should only be called during make(), returns a path relative to the build root or absolute. | 1565 | /// Should only be called during make(), returns a path relative to the build root or absolute. |
| 1653 | pub fn getPath(self: FileSource, builder: *Build) []const u8 { | 1566 | pub fn getPath(self: FileSource, src_builder: *Build) []const u8 { |
| 1654 | const path = switch (self) { | 1567 | return getPath2(self, src_builder, null); |
| 1655 | .path => |p| builder.pathFromRoot(p), | 1568 | } |
| 1656 | .generated => |gen| gen.getPath(), | 1569 | |
| 1657 | }; | 1570 | /// Should only be called during make(), returns a path relative to the build root or absolute. |
| 1658 | return path; | 1571 | /// asking_step is only used for debugging purposes; it's the step being run that is asking for |
| 1572 | /// the path. | ||
| 1573 | pub fn getPath2(self: FileSource, src_builder: *Build, asking_step: ?*Step) []const u8 { | ||
| 1574 | switch (self) { | ||
| 1575 | .path => |p| return src_builder.pathFromRoot(p), | ||
| 1576 | .generated => |gen| return gen.path orelse { | ||
| 1577 | std.debug.getStderrMutex().lock(); | ||
| 1578 | const stderr = std.io.getStdErr(); | ||
| 1579 | dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; | ||
| 1580 | @panic("misconfigured build script"); | ||
| 1581 | }, | ||
| 1582 | } | ||
| 1659 | } | 1583 | } |
| 1660 | 1584 | ||
| 1661 | /// Duplicates the file source for a given builder. | 1585 | /// Duplicates the file source for a given builder. |
| ... | @@ -1667,6 +1591,54 @@ pub const FileSource = union(enum) { | ... | @@ -1667,6 +1591,54 @@ pub const FileSource = union(enum) { |
| 1667 | } | 1591 | } |
| 1668 | }; | 1592 | }; |
| 1669 | 1593 | ||
| 1594 | /// In this function the stderr mutex has already been locked. | ||
| 1595 | fn dumpBadGetPathHelp( | ||
| 1596 | s: *Step, | ||
| 1597 | stderr: fs.File, | ||
| 1598 | src_builder: *Build, | ||
| 1599 | asking_step: ?*Step, | ||
| 1600 | ) anyerror!void { | ||
| 1601 | const w = stderr.writer(); | ||
| 1602 | try w.print( | ||
| 1603 | \\getPath() was called on a GeneratedFile that wasn't built yet. | ||
| 1604 | \\ source package path: {s} | ||
| 1605 | \\ Is there a missing Step dependency on step '{s}'? | ||
| 1606 | \\ | ||
| 1607 | , .{ | ||
| 1608 | src_builder.build_root.path orelse ".", | ||
| 1609 | s.name, | ||
| 1610 | }); | ||
| 1611 | |||
| 1612 | const tty_config = std.debug.detectTTYConfig(stderr); | ||
| 1613 | tty_config.setColor(w, .Red) catch {}; | ||
| 1614 | try stderr.writeAll(" The step was created by this stack trace:\n"); | ||
| 1615 | tty_config.setColor(w, .Reset) catch {}; | ||
| 1616 | |||
| 1617 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { | ||
| 1618 | try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); | ||
| 1619 | return; | ||
| 1620 | }; | ||
| 1621 | const ally = debug_info.allocator; | ||
| 1622 | std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { | ||
| 1623 | try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); | ||
| 1624 | return; | ||
| 1625 | }; | ||
| 1626 | if (asking_step) |as| { | ||
| 1627 | tty_config.setColor(w, .Red) catch {}; | ||
| 1628 | try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n"); | ||
| 1629 | tty_config.setColor(w, .Reset) catch {}; | ||
| 1630 | |||
| 1631 | std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { | ||
| 1632 | try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); | ||
| 1633 | return; | ||
| 1634 | }; | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | tty_config.setColor(w, .Red) catch {}; | ||
| 1638 | try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); | ||
| 1639 | tty_config.setColor(w, .Reset) catch {}; | ||
| 1640 | } | ||
| 1641 | |||
| 1670 | /// Allocates a new string for assigning a value to a named macro. | 1642 | /// Allocates a new string for assigning a value to a named macro. |
| 1671 | /// If the value is omitted, it is set to 1. | 1643 | /// If the value is omitted, it is set to 1. |
| 1672 | /// `name` and `value` need not live longer than the function call. | 1644 | /// `name` and `value` need not live longer than the function call. |
| ... | @@ -1706,9 +1678,7 @@ pub const InstallDir = union(enum) { | ... | @@ -1706,9 +1678,7 @@ pub const InstallDir = union(enum) { |
| 1706 | /// Duplicates the install directory including the path if set to custom. | 1678 | /// Duplicates the install directory including the path if set to custom. |
| 1707 | pub fn dupe(self: InstallDir, builder: *Build) InstallDir { | 1679 | pub fn dupe(self: InstallDir, builder: *Build) InstallDir { |
| 1708 | if (self == .custom) { | 1680 | if (self == .custom) { |
| 1709 | // Written with this temporary to avoid RLS problems | 1681 | return .{ .custom = builder.dupe(self.custom) }; |
| 1710 | const duped_path = builder.dupe(self.custom); | ||
| 1711 | return .{ .custom = duped_path }; | ||
| 1712 | } else { | 1682 | } else { |
| 1713 | return self; | 1683 | return self; |
| 1714 | } | 1684 | } |
| ... | @@ -1756,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { | ... | @@ -1756,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { |
| 1756 | } | 1726 | } |
| 1757 | } | 1727 | } |
| 1758 | 1728 | ||
| 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. | ||
| 1733 | pub 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. | ||
| 1747 | fn 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 | |||
| 1759 | test { | 1759 | test { |
| 1760 | _ = CheckFileStep; | 1760 | _ = CheckFileStep; |
| 1761 | _ = CheckObjectStep; | 1761 | _ = CheckObjectStep; |
| 1762 | _ = EmulatableRunStep; | ||
| 1763 | _ = FmtStep; | 1762 | _ = FmtStep; |
| 1764 | _ = InstallArtifactStep; | 1763 | _ = InstallArtifactStep; |
| 1765 | _ = InstallDirStep; | 1764 | _ = InstallDirStep; |
| 1766 | _ = InstallFileStep; | 1765 | _ = InstallFileStep; |
| 1767 | _ = ObjCopyStep; | 1766 | _ = ObjCopyStep; |
| 1768 | _ = CompileStep; | 1767 | _ = CompileStep; |
| 1769 | _ = LogStep; | ||
| 1770 | _ = OptionsStep; | 1768 | _ = OptionsStep; |
| 1771 | _ = RemoveDirStep; | 1769 | _ = RemoveDirStep; |
| 1772 | _ = RunStep; | 1770 | _ = RunStep; |
lib/std/Build/Cache.zig+74-61| ... | @@ -7,27 +7,27 @@ pub const Directory = struct { | ... | @@ -7,27 +7,27 @@ pub const Directory = struct { |
| 7 | /// directly, but it is needed when passing the directory to a child process. | 7 | /// directly, but it is needed when passing the directory to a child process. |
| 8 | /// `null` means cwd. | 8 | /// `null` means cwd. |
| 9 | path: ?[]const u8, | 9 | path: ?[]const u8, |
| 10 | handle: std.fs.Dir, | 10 | handle: fs.Dir, |
| 11 | 11 | ||
| 12 | pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { | 12 | pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 13 | if (self.path) |p| { | 13 | if (self.path) |p| { |
| 14 | // TODO clean way to do this with only 1 allocation | 14 | // TODO clean way to do this with only 1 allocation |
| 15 | const part2 = try std.fs.path.join(allocator, paths); | 15 | const part2 = try fs.path.join(allocator, paths); |
| 16 | defer allocator.free(part2); | 16 | defer allocator.free(part2); |
| 17 | return std.fs.path.join(allocator, &[_][]const u8{ p, part2 }); | 17 | return fs.path.join(allocator, &[_][]const u8{ p, part2 }); |
| 18 | } else { | 18 | } else { |
| 19 | return std.fs.path.join(allocator, paths); | 19 | return fs.path.join(allocator, paths); |
| 20 | } | 20 | } |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { | 23 | pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { |
| 24 | if (self.path) |p| { | 24 | if (self.path) |p| { |
| 25 | // TODO clean way to do this with only 1 allocation | 25 | // TODO clean way to do this with only 1 allocation |
| 26 | const part2 = try std.fs.path.join(allocator, paths); | 26 | const part2 = try fs.path.join(allocator, paths); |
| 27 | defer allocator.free(part2); | 27 | defer allocator.free(part2); |
| 28 | return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); | 28 | return fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); |
| 29 | } else { | 29 | } else { |
| 30 | return std.fs.path.joinZ(allocator, paths); | 30 | return fs.path.joinZ(allocator, paths); |
| 31 | } | 31 | } |
| 32 | } | 32 | } |
| 33 | 33 | ||
| ... | @@ -39,6 +39,20 @@ pub const Directory = struct { | ... | @@ -39,6 +39,20 @@ pub const Directory = struct { |
| 39 | if (self.path) |p| gpa.free(p); | 39 | if (self.path) |p| gpa.free(p); |
| 40 | self.* = undefined; | 40 | self.* = undefined; |
| 41 | } | 41 | } |
| 42 | |||
| 43 | pub fn format( | ||
| 44 | self: Directory, | ||
| 45 | comptime fmt_string: []const u8, | ||
| 46 | options: fmt.FormatOptions, | ||
| 47 | writer: anytype, | ||
| 48 | ) !void { | ||
| 49 | _ = options; | ||
| 50 | if (fmt_string.len != 0) fmt.invalidFmtError(fmt, self); | ||
| 51 | if (self.path) |p| { | ||
| 52 | try writer.writeAll(p); | ||
| 53 | try writer.writeAll(fs.path.sep_str); | ||
| 54 | } | ||
| 55 | } | ||
| 42 | }; | 56 | }; |
| 43 | 57 | ||
| 44 | gpa: Allocator, | 58 | gpa: Allocator, |
| ... | @@ -243,10 +257,10 @@ pub const HashHelper = struct { | ... | @@ -243,10 +257,10 @@ pub const HashHelper = struct { |
| 243 | hh.hasher.final(&bin_digest); | 257 | hh.hasher.final(&bin_digest); |
| 244 | 258 | ||
| 245 | var out_digest: [hex_digest_len]u8 = undefined; | 259 | var out_digest: [hex_digest_len]u8 = undefined; |
| 246 | _ = std.fmt.bufPrint( | 260 | _ = fmt.bufPrint( |
| 247 | &out_digest, | 261 | &out_digest, |
| 248 | "{s}", | 262 | "{s}", |
| 249 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | 263 | .{fmt.fmtSliceHexLower(&bin_digest)}, |
| 250 | ) catch unreachable; | 264 | ) catch unreachable; |
| 251 | return out_digest; | 265 | return out_digest; |
| 252 | } | 266 | } |
| ... | @@ -365,10 +379,10 @@ pub const Manifest = struct { | ... | @@ -365,10 +379,10 @@ pub const Manifest = struct { |
| 365 | var bin_digest: BinDigest = undefined; | 379 | var bin_digest: BinDigest = undefined; |
| 366 | self.hash.hasher.final(&bin_digest); | 380 | self.hash.hasher.final(&bin_digest); |
| 367 | 381 | ||
| 368 | _ = std.fmt.bufPrint( | 382 | _ = fmt.bufPrint( |
| 369 | &self.hex_digest, | 383 | &self.hex_digest, |
| 370 | "{s}", | 384 | "{s}", |
| 371 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | 385 | .{fmt.fmtSliceHexLower(&bin_digest)}, |
| 372 | ) catch unreachable; | 386 | ) catch unreachable; |
| 373 | 387 | ||
| 374 | self.hash.hasher = hasher_init; | 388 | self.hash.hasher = hasher_init; |
| ... | @@ -408,7 +422,11 @@ pub const Manifest = struct { | ... | @@ -408,7 +422,11 @@ pub const Manifest = struct { |
| 408 | self.have_exclusive_lock = true; | 422 | self.have_exclusive_lock = true; |
| 409 | return false; // cache miss; exclusive lock already held | 423 | return false; // cache miss; exclusive lock already held |
| 410 | } else |err| switch (err) { | 424 | } else |err| switch (err) { |
| 411 | error.WouldBlock => continue, | 425 | // There are no dir components, so you would think |
| 426 | // that this was unreachable, however we have | ||
| 427 | // observed on macOS two processes racing to do | ||
| 428 | // openat() with O_CREAT manifest in ENOENT. | ||
| 429 | error.WouldBlock, error.FileNotFound => continue, | ||
| 412 | else => |e| return e, | 430 | else => |e| return e, |
| 413 | } | 431 | } |
| 414 | }, | 432 | }, |
| ... | @@ -425,7 +443,10 @@ pub const Manifest = struct { | ... | @@ -425,7 +443,10 @@ pub const Manifest = struct { |
| 425 | self.manifest_file = manifest_file; | 443 | self.manifest_file = manifest_file; |
| 426 | self.have_exclusive_lock = true; | 444 | self.have_exclusive_lock = true; |
| 427 | } else |err| switch (err) { | 445 | } else |err| switch (err) { |
| 428 | error.WouldBlock => { | 446 | // There are no dir components, so you would think that this was |
| 447 | // unreachable, however we have observed on macOS two processes racing | ||
| 448 | // to do openat() with O_CREAT manifest in ENOENT. | ||
| 449 | error.WouldBlock, error.FileNotFound => { | ||
| 429 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | 450 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 430 | .lock = .Shared, | 451 | .lock = .Shared, |
| 431 | }); | 452 | }); |
| ... | @@ -469,7 +490,7 @@ pub const Manifest = struct { | ... | @@ -469,7 +490,7 @@ pub const Manifest = struct { |
| 469 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; | 490 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; |
| 470 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; | 491 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; |
| 471 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; | 492 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; |
| 472 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | 493 | _ = fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; |
| 473 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; | 494 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; |
| 474 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; | 495 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; |
| 475 | 496 | ||
| ... | @@ -806,10 +827,10 @@ pub const Manifest = struct { | ... | @@ -806,10 +827,10 @@ pub const Manifest = struct { |
| 806 | self.hash.hasher.final(&bin_digest); | 827 | self.hash.hasher.final(&bin_digest); |
| 807 | 828 | ||
| 808 | var out_digest: [hex_digest_len]u8 = undefined; | 829 | var out_digest: [hex_digest_len]u8 = undefined; |
| 809 | _ = std.fmt.bufPrint( | 830 | _ = fmt.bufPrint( |
| 810 | &out_digest, | 831 | &out_digest, |
| 811 | "{s}", | 832 | "{s}", |
| 812 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | 833 | .{fmt.fmtSliceHexLower(&bin_digest)}, |
| 813 | ) catch unreachable; | 834 | ) catch unreachable; |
| 814 | 835 | ||
| 815 | return out_digest; | 836 | return out_digest; |
| ... | @@ -831,10 +852,10 @@ pub const Manifest = struct { | ... | @@ -831,10 +852,10 @@ pub const Manifest = struct { |
| 831 | var encoded_digest: [hex_digest_len]u8 = undefined; | 852 | var encoded_digest: [hex_digest_len]u8 = undefined; |
| 832 | 853 | ||
| 833 | for (self.files.items) |file| { | 854 | for (self.files.items) |file| { |
| 834 | _ = std.fmt.bufPrint( | 855 | _ = fmt.bufPrint( |
| 835 | &encoded_digest, | 856 | &encoded_digest, |
| 836 | "{s}", | 857 | "{s}", |
| 837 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, | 858 | .{fmt.fmtSliceHexLower(&file.bin_digest)}, |
| 838 | ) catch unreachable; | 859 | ) catch unreachable; |
| 839 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ | 860 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ |
| 840 | file.stat.size, | 861 | file.stat.size, |
| ... | @@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { | ... | @@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { |
| 955 | } | 976 | } |
| 956 | 977 | ||
| 957 | // Create/Write a file, close it, then grab its stat.mtime timestamp. | 978 | // Create/Write a file, close it, then grab its stat.mtime timestamp. |
| 958 | fn testGetCurrentFileTimestamp() !i128 { | 979 | fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 { |
| 959 | const test_out_file = "test-filetimestamp.tmp"; | 980 | const test_out_file = "test-filetimestamp.tmp"; |
| 960 | 981 | ||
| 961 | var file = try fs.cwd().createFile(test_out_file, .{ | 982 | var file = try dir.createFile(test_out_file, .{ |
| 962 | .read = true, | 983 | .read = true, |
| 963 | .truncate = true, | 984 | .truncate = true, |
| 964 | }); | 985 | }); |
| 965 | defer { | 986 | defer { |
| 966 | file.close(); | 987 | file.close(); |
| 967 | fs.cwd().deleteFile(test_out_file) catch {}; | 988 | dir.deleteFile(test_out_file) catch {}; |
| 968 | } | 989 | } |
| 969 | 990 | ||
| 970 | return (try file.stat()).mtime; | 991 | return (try file.stat()).mtime; |
| ... | @@ -976,16 +997,17 @@ test "cache file and then recall it" { | ... | @@ -976,16 +997,17 @@ test "cache file and then recall it" { |
| 976 | return error.SkipZigTest; | 997 | return error.SkipZigTest; |
| 977 | } | 998 | } |
| 978 | 999 | ||
| 979 | const cwd = fs.cwd(); | 1000 | var tmp = testing.tmpDir(.{}); |
| 1001 | defer tmp.cleanup(); | ||
| 980 | 1002 | ||
| 981 | const temp_file = "test.txt"; | 1003 | const temp_file = "test.txt"; |
| 982 | const temp_manifest_dir = "temp_manifest_dir"; | 1004 | const temp_manifest_dir = "temp_manifest_dir"; |
| 983 | 1005 | ||
| 984 | try cwd.writeFile(temp_file, "Hello, world!\n"); | 1006 | try tmp.dir.writeFile(temp_file, "Hello, world!\n"); |
| 985 | 1007 | ||
| 986 | // Wait for file timestamps to tick | 1008 | // Wait for file timestamps to tick |
| 987 | const initial_time = try testGetCurrentFileTimestamp(); | 1009 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 988 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | 1010 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 989 | std.time.sleep(1); | 1011 | std.time.sleep(1); |
| 990 | } | 1012 | } |
| 991 | 1013 | ||
| ... | @@ -995,9 +1017,9 @@ test "cache file and then recall it" { | ... | @@ -995,9 +1017,9 @@ test "cache file and then recall it" { |
| 995 | { | 1017 | { |
| 996 | var cache = Cache{ | 1018 | var cache = Cache{ |
| 997 | .gpa = testing.allocator, | 1019 | .gpa = testing.allocator, |
| 998 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1020 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 999 | }; | 1021 | }; |
| 1000 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1022 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1001 | defer cache.manifest_dir.close(); | 1023 | defer cache.manifest_dir.close(); |
| 1002 | 1024 | ||
| 1003 | { | 1025 | { |
| ... | @@ -1033,9 +1055,6 @@ test "cache file and then recall it" { | ... | @@ -1033,9 +1055,6 @@ test "cache file and then recall it" { |
| 1033 | 1055 | ||
| 1034 | try testing.expectEqual(digest1, digest2); | 1056 | try testing.expectEqual(digest1, digest2); |
| 1035 | } | 1057 | } |
| 1036 | |||
| 1037 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1038 | try cwd.deleteFile(temp_file); | ||
| 1039 | } | 1058 | } |
| 1040 | 1059 | ||
| 1041 | test "check that changing a file makes cache fail" { | 1060 | test "check that changing a file makes cache fail" { |
| ... | @@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" { | ... | @@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" { |
| 1043 | // https://github.com/ziglang/zig/issues/5437 | 1062 | // https://github.com/ziglang/zig/issues/5437 |
| 1044 | return error.SkipZigTest; | 1063 | return error.SkipZigTest; |
| 1045 | } | 1064 | } |
| 1046 | const cwd = fs.cwd(); | 1065 | var tmp = testing.tmpDir(.{}); |
| 1066 | defer tmp.cleanup(); | ||
| 1047 | 1067 | ||
| 1048 | const temp_file = "cache_hash_change_file_test.txt"; | 1068 | const temp_file = "cache_hash_change_file_test.txt"; |
| 1049 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; | 1069 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; |
| 1050 | const original_temp_file_contents = "Hello, world!\n"; | 1070 | const original_temp_file_contents = "Hello, world!\n"; |
| 1051 | const updated_temp_file_contents = "Hello, world; but updated!\n"; | 1071 | const updated_temp_file_contents = "Hello, world; but updated!\n"; |
| 1052 | 1072 | ||
| 1053 | try cwd.deleteTree(temp_manifest_dir); | 1073 | try tmp.dir.writeFile(temp_file, original_temp_file_contents); |
| 1054 | try cwd.deleteTree(temp_file); | ||
| 1055 | |||
| 1056 | try cwd.writeFile(temp_file, original_temp_file_contents); | ||
| 1057 | 1074 | ||
| 1058 | // Wait for file timestamps to tick | 1075 | // Wait for file timestamps to tick |
| 1059 | const initial_time = try testGetCurrentFileTimestamp(); | 1076 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1060 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | 1077 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1061 | std.time.sleep(1); | 1078 | std.time.sleep(1); |
| 1062 | } | 1079 | } |
| 1063 | 1080 | ||
| ... | @@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" { | ... | @@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" { |
| 1067 | { | 1084 | { |
| 1068 | var cache = Cache{ | 1085 | var cache = Cache{ |
| 1069 | .gpa = testing.allocator, | 1086 | .gpa = testing.allocator, |
| 1070 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1087 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1071 | }; | 1088 | }; |
| 1072 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1089 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1073 | defer cache.manifest_dir.close(); | 1090 | defer cache.manifest_dir.close(); |
| 1074 | 1091 | ||
| 1075 | { | 1092 | { |
| ... | @@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" { |
| 1089 | try ch.writeManifest(); | 1106 | try ch.writeManifest(); |
| 1090 | } | 1107 | } |
| 1091 | 1108 | ||
| 1092 | try cwd.writeFile(temp_file, updated_temp_file_contents); | 1109 | try tmp.dir.writeFile(temp_file, updated_temp_file_contents); |
| 1093 | 1110 | ||
| 1094 | { | 1111 | { |
| 1095 | var ch = cache.obtain(); | 1112 | var ch = cache.obtain(); |
| ... | @@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" { | ... | @@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" { |
| 1111 | 1128 | ||
| 1112 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); | 1129 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); |
| 1113 | } | 1130 | } |
| 1114 | |||
| 1115 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1116 | try cwd.deleteTree(temp_file); | ||
| 1117 | } | 1131 | } |
| 1118 | 1132 | ||
| 1119 | test "no file inputs" { | 1133 | test "no file inputs" { |
| ... | @@ -1121,18 +1135,20 @@ test "no file inputs" { | ... | @@ -1121,18 +1135,20 @@ test "no file inputs" { |
| 1121 | // https://github.com/ziglang/zig/issues/5437 | 1135 | // https://github.com/ziglang/zig/issues/5437 |
| 1122 | return error.SkipZigTest; | 1136 | return error.SkipZigTest; |
| 1123 | } | 1137 | } |
| 1124 | const cwd = fs.cwd(); | 1138 | |
| 1139 | var tmp = testing.tmpDir(.{}); | ||
| 1140 | defer tmp.cleanup(); | ||
| 1141 | |||
| 1125 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; | 1142 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; |
| 1126 | defer cwd.deleteTree(temp_manifest_dir) catch {}; | ||
| 1127 | 1143 | ||
| 1128 | var digest1: [hex_digest_len]u8 = undefined; | 1144 | var digest1: [hex_digest_len]u8 = undefined; |
| 1129 | var digest2: [hex_digest_len]u8 = undefined; | 1145 | var digest2: [hex_digest_len]u8 = undefined; |
| 1130 | 1146 | ||
| 1131 | var cache = Cache{ | 1147 | var cache = Cache{ |
| 1132 | .gpa = testing.allocator, | 1148 | .gpa = testing.allocator, |
| 1133 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1149 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1134 | }; | 1150 | }; |
| 1135 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1151 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1136 | defer cache.manifest_dir.close(); | 1152 | defer cache.manifest_dir.close(); |
| 1137 | 1153 | ||
| 1138 | { | 1154 | { |
| ... | @@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" { |
| 1167 | // https://github.com/ziglang/zig/issues/5437 | 1183 | // https://github.com/ziglang/zig/issues/5437 |
| 1168 | return error.SkipZigTest; | 1184 | return error.SkipZigTest; |
| 1169 | } | 1185 | } |
| 1170 | const cwd = fs.cwd(); | 1186 | var tmp = testing.tmpDir(.{}); |
| 1187 | defer tmp.cleanup(); | ||
| 1171 | 1188 | ||
| 1172 | const temp_file1 = "cache_hash_post_file_test1.txt"; | 1189 | const temp_file1 = "cache_hash_post_file_test1.txt"; |
| 1173 | const temp_file2 = "cache_hash_post_file_test2.txt"; | 1190 | const temp_file2 = "cache_hash_post_file_test2.txt"; |
| 1174 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; | 1191 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; |
| 1175 | 1192 | ||
| 1176 | try cwd.writeFile(temp_file1, "Hello, world!\n"); | 1193 | try tmp.dir.writeFile(temp_file1, "Hello, world!\n"); |
| 1177 | try cwd.writeFile(temp_file2, "Hello world the second!\n"); | 1194 | try tmp.dir.writeFile(temp_file2, "Hello world the second!\n"); |
| 1178 | 1195 | ||
| 1179 | // Wait for file timestamps to tick | 1196 | // Wait for file timestamps to tick |
| 1180 | const initial_time = try testGetCurrentFileTimestamp(); | 1197 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1181 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | 1198 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1182 | std.time.sleep(1); | 1199 | std.time.sleep(1); |
| 1183 | } | 1200 | } |
| 1184 | 1201 | ||
| ... | @@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" { |
| 1189 | { | 1206 | { |
| 1190 | var cache = Cache{ | 1207 | var cache = Cache{ |
| 1191 | .gpa = testing.allocator, | 1208 | .gpa = testing.allocator, |
| 1192 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1209 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1193 | }; | 1210 | }; |
| 1194 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1211 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1195 | defer cache.manifest_dir.close(); | 1212 | defer cache.manifest_dir.close(); |
| 1196 | 1213 | ||
| 1197 | { | 1214 | { |
| ... | @@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" { |
| 1224 | try testing.expect(mem.eql(u8, &digest1, &digest2)); | 1241 | try testing.expect(mem.eql(u8, &digest1, &digest2)); |
| 1225 | 1242 | ||
| 1226 | // Modify the file added after initial hash | 1243 | // 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"); |
| 1228 | 1245 | ||
| 1229 | // Wait for file timestamps to tick | 1246 | // Wait for file timestamps to tick |
| 1230 | const initial_time2 = try testGetCurrentFileTimestamp(); | 1247 | const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir); |
| 1231 | while ((try testGetCurrentFileTimestamp()) == initial_time2) { | 1248 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) { |
| 1232 | std.time.sleep(1); | 1249 | std.time.sleep(1); |
| 1233 | } | 1250 | } |
| 1234 | 1251 | ||
| ... | @@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" { |
| 1251 | 1268 | ||
| 1252 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); | 1269 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); |
| 1253 | } | 1270 | } |
| 1254 | |||
| 1255 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1256 | try cwd.deleteFile(temp_file1); | ||
| 1257 | try cwd.deleteFile(temp_file2); | ||
| 1258 | } | 1271 | } |
lib/std/Build/CheckFileStep.zig+62-25| ... | @@ -1,51 +1,88 @@ | ... | @@ -1,51 +1,88 @@ |
| 1 | const std = @import("../std.zig"); | 1 | //! Fail the build step if a file does not match certain checks. |
| 2 | const Step = std.Build.Step; | 2 | //! TODO: make this more flexible, supporting more kinds of checks. |
| 3 | const fs = std.fs; | 3 | //! TODO: generalize the code in std.testing.expectEqualStrings and make this |
| 4 | const mem = std.mem; | 4 | //! CheckFileStep produce those helpful diagnostics when there is not a match. |
| 5 | |||
| 6 | const CheckFileStep = @This(); | ||
| 7 | |||
| 8 | pub const base_id = .check_file; | ||
| 9 | 5 | ||
| 10 | step: Step, | 6 | step: Step, |
| 11 | builder: *std.Build, | ||
| 12 | expected_matches: []const []const u8, | 7 | expected_matches: []const []const u8, |
| 8 | expected_exact: ?[]const u8, | ||
| 13 | source: std.Build.FileSource, | 9 | source: std.Build.FileSource, |
| 14 | max_bytes: usize = 20 * 1024 * 1024, | 10 | max_bytes: usize = 20 * 1024 * 1024, |
| 15 | 11 | ||
| 12 | pub const base_id = .check_file; | ||
| 13 | |||
| 14 | pub const Options = struct { | ||
| 15 | expected_matches: []const []const u8 = &.{}, | ||
| 16 | expected_exact: ?[]const u8 = null, | ||
| 17 | }; | ||
| 18 | |||
| 16 | pub fn create( | 19 | pub fn create( |
| 17 | builder: *std.Build, | 20 | owner: *std.Build, |
| 18 | source: std.Build.FileSource, | 21 | source: std.Build.FileSource, |
| 19 | expected_matches: []const []const u8, | 22 | options: Options, |
| 20 | ) *CheckFileStep { | 23 | ) *CheckFileStep { |
| 21 | const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); | 24 | const self = owner.allocator.create(CheckFileStep) catch @panic("OOM"); |
| 22 | self.* = CheckFileStep{ | 25 | self.* = .{ |
| 23 | .builder = builder, | 26 | .step = Step.init(.{ |
| 24 | .step = Step.init(.check_file, "CheckFile", builder.allocator, make), | 27 | .id = .check_file, |
| 25 | .source = source.dupe(builder), | 28 | .name = "CheckFile", |
| 26 | .expected_matches = builder.dupeStrings(expected_matches), | 29 | .owner = owner, |
| 30 | .makeFn = make, | ||
| 31 | }), | ||
| 32 | .source = source.dupe(owner), | ||
| 33 | .expected_matches = owner.dupeStrings(options.expected_matches), | ||
| 34 | .expected_exact = options.expected_exact, | ||
| 27 | }; | 35 | }; |
| 28 | self.source.addStepDependencies(&self.step); | 36 | self.source.addStepDependencies(&self.step); |
| 29 | return self; | 37 | return self; |
| 30 | } | 38 | } |
| 31 | 39 | ||
| 32 | fn make(step: *Step) !void { | 40 | pub fn setName(self: *CheckFileStep, name: []const u8) void { |
| 41 | self.step.name = name; | ||
| 42 | } | ||
| 43 | |||
| 44 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ||
| 45 | _ = prog_node; | ||
| 46 | const b = step.owner; | ||
| 33 | const self = @fieldParentPtr(CheckFileStep, "step", step); | 47 | const self = @fieldParentPtr(CheckFileStep, "step", step); |
| 34 | 48 | ||
| 35 | const src_path = self.source.getPath(self.builder); | 49 | const src_path = self.source.getPath(b); |
| 36 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | 50 | const contents = fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes) catch |err| { |
| 51 | return step.fail("unable to read '{s}': {s}", .{ | ||
| 52 | src_path, @errorName(err), | ||
| 53 | }); | ||
| 54 | }; | ||
| 37 | 55 | ||
| 38 | for (self.expected_matches) |expected_match| { | 56 | for (self.expected_matches) |expected_match| { |
| 39 | if (mem.indexOf(u8, contents, expected_match) == null) { | 57 | if (mem.indexOf(u8, contents, expected_match) == null) { |
| 40 | std.debug.print( | 58 | return step.fail( |
| 41 | \\ | 59 | \\ |
| 42 | \\========= Expected to find: =================== | 60 | \\========= expected to find: =================== |
| 43 | \\{s} | 61 | \\{s} |
| 44 | \\========= But file does not contain it: ======= | 62 | \\========= but file does not contain it: ======= |
| 45 | \\{s} | 63 | \\{s} |
| 46 | \\ | 64 | \\=============================================== |
| 47 | , .{ expected_match, contents }); | 65 | , .{ expected_match, contents }); |
| 48 | return error.TestFailed; | 66 | } |
| 67 | } | ||
| 68 | |||
| 69 | if (self.expected_exact) |expected_exact| { | ||
| 70 | if (!mem.eql(u8, expected_exact, contents)) { | ||
| 71 | return step.fail( | ||
| 72 | \\ | ||
| 73 | \\========= expected: ===================== | ||
| 74 | \\{s} | ||
| 75 | \\========= but found: ==================== | ||
| 76 | \\{s} | ||
| 77 | \\========= from the following file: ====== | ||
| 78 | \\{s} | ||
| 79 | , .{ expected_exact, contents, src_path }); | ||
| 49 | } | 80 | } |
| 50 | } | 81 | } |
| 51 | } | 82 | } |
| 83 | |||
| 84 | const CheckFileStep = @This(); | ||
| 85 | const std = @import("../std.zig"); | ||
| 86 | const Step = std.Build.Step; | ||
| 87 | const fs = std.fs; | ||
| 88 | const mem = std.mem; |
lib/std/Build/CheckObjectStep.zig+95-91| ... | @@ -10,25 +10,31 @@ const CheckObjectStep = @This(); | ... | @@ -10,25 +10,31 @@ const CheckObjectStep = @This(); |
| 10 | 10 | ||
| 11 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 12 | const Step = std.Build.Step; | 12 | const Step = std.Build.Step; |
| 13 | const EmulatableRunStep = std.Build.EmulatableRunStep; | ||
| 14 | 13 | ||
| 15 | pub const base_id = .check_object; | 14 | pub const base_id = .check_object; |
| 16 | 15 | ||
| 17 | step: Step, | 16 | step: Step, |
| 18 | builder: *std.Build, | ||
| 19 | source: std.Build.FileSource, | 17 | source: std.Build.FileSource, |
| 20 | max_bytes: usize = 20 * 1024 * 1024, | 18 | max_bytes: usize = 20 * 1024 * 1024, |
| 21 | checks: std.ArrayList(Check), | 19 | checks: std.ArrayList(Check), |
| 22 | dump_symtab: bool = false, | 20 | dump_symtab: bool = false, |
| 23 | obj_format: std.Target.ObjectFormat, | 21 | obj_format: std.Target.ObjectFormat, |
| 24 | 22 | ||
| 25 | pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | 23 | pub fn create( |
| 26 | const gpa = builder.allocator; | 24 | owner: *std.Build, |
| 25 | source: std.Build.FileSource, | ||
| 26 | obj_format: std.Target.ObjectFormat, | ||
| 27 | ) *CheckObjectStep { | ||
| 28 | const gpa = owner.allocator; | ||
| 27 | const self = gpa.create(CheckObjectStep) catch @panic("OOM"); | 29 | const self = gpa.create(CheckObjectStep) catch @panic("OOM"); |
| 28 | self.* = .{ | 30 | self.* = .{ |
| 29 | .builder = builder, | 31 | .step = Step.init(.{ |
| 30 | .step = Step.init(.check_file, "CheckObject", gpa, make), | 32 | .id = .check_file, |
| 31 | .source = source.dupe(builder), | 33 | .name = "CheckObject", |
| 34 | .owner = owner, | ||
| 35 | .makeFn = make, | ||
| 36 | }), | ||
| 37 | .source = source.dupe(owner), | ||
| 32 | .checks = std.ArrayList(Check).init(gpa), | 38 | .checks = std.ArrayList(Check).init(gpa), |
| 33 | .obj_format = obj_format, | 39 | .obj_format = obj_format, |
| 34 | }; | 40 | }; |
| ... | @@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std | ... | @@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std |
| 38 | 44 | ||
| 39 | /// Runs and (optionally) compares the output of a binary. | 45 | /// Runs and (optionally) compares the output of a binary. |
| 40 | /// Asserts `self` was generated from an executable step. | 46 | /// Asserts `self` was generated from an executable step. |
| 41 | pub 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. | ||
| 50 | pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep { | ||
| 42 | const dependencies_len = self.step.dependencies.items.len; | 51 | const dependencies_len = self.step.dependencies.items.len; |
| 43 | assert(dependencies_len > 0); | 52 | assert(dependencies_len > 0); |
| 44 | const exe_step = self.step.dependencies.items[dependencies_len - 1]; | 53 | const exe_step = self.step.dependencies.items[dependencies_len - 1]; |
| 45 | const exe = exe_step.cast(std.Build.CompileStep).?; | 54 | const exe = exe_step.cast(std.Build.CompileStep).?; |
| 46 | const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); | 55 | const run = self.step.owner.addRunArtifact(exe); |
| 47 | emulatable_step.step.dependOn(&self.step); | 56 | run.skip_foreign_checks = true; |
| 48 | return emulatable_step; | 57 | run.step.dependOn(&self.step); |
| 58 | return run; | ||
| 49 | } | 59 | } |
| 50 | 60 | ||
| 51 | /// There two types of actions currently suported: | 61 | /// There two types of actions currently suported: |
| ... | @@ -123,7 +133,8 @@ const Action = struct { | ... | @@ -123,7 +133,8 @@ const Action = struct { |
| 123 | /// Will return true if the `phrase` is correctly parsed into an RPN program and | 133 | /// Will return true if the `phrase` is correctly parsed into an RPN program and |
| 124 | /// its reduced, computed value compares using `op` with the expected value, either | 134 | /// its reduced, computed value compares using `op` with the expected value, either |
| 125 | /// a literal or another extracted variable. | 135 | /// a literal or another extracted variable. |
| 126 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { | 136 | fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool { |
| 137 | const gpa = step.owner.allocator; | ||
| 127 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); | 138 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); |
| 128 | var values = std.ArrayList(u64).init(gpa); | 139 | var values = std.ArrayList(u64).init(gpa); |
| 129 | 140 | ||
| ... | @@ -140,11 +151,11 @@ const Action = struct { | ... | @@ -140,11 +151,11 @@ const Action = struct { |
| 140 | } else { | 151 | } else { |
| 141 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { | 152 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { |
| 142 | break :blk global_vars.get(next) orelse { | 153 | break :blk global_vars.get(next) orelse { |
| 143 | std.debug.print( | 154 | try step.addError( |
| 144 | \\ | 155 | \\ |
| 145 | \\========= Variable was not extracted: =========== | 156 | \\========= variable was not extracted: =========== |
| 146 | \\{s} | 157 | \\{s} |
| 147 | \\ | 158 | \\================================================= |
| 148 | , .{next}); | 159 | , .{next}); |
| 149 | return error.UnknownVariable; | 160 | return error.UnknownVariable; |
| 150 | }; | 161 | }; |
| ... | @@ -176,11 +187,11 @@ const Action = struct { | ... | @@ -176,11 +187,11 @@ const Action = struct { |
| 176 | 187 | ||
| 177 | const exp_value = switch (act.expected.?.value) { | 188 | const exp_value = switch (act.expected.?.value) { |
| 178 | .variable => |name| global_vars.get(name) orelse { | 189 | .variable => |name| global_vars.get(name) orelse { |
| 179 | std.debug.print( | 190 | try step.addError( |
| 180 | \\ | 191 | \\ |
| 181 | \\========= Variable was not extracted: =========== | 192 | \\========= variable was not extracted: =========== |
| 182 | \\{s} | 193 | \\{s} |
| 183 | \\ | 194 | \\================================================= |
| 184 | , .{name}); | 195 | , .{name}); |
| 185 | return error.UnknownVariable; | 196 | return error.UnknownVariable; |
| 186 | }, | 197 | }, |
| ... | @@ -249,7 +260,7 @@ const Check = struct { | ... | @@ -249,7 +260,7 @@ const Check = struct { |
| 249 | 260 | ||
| 250 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. | 261 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. |
| 251 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { | 262 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { |
| 252 | var new_check = Check.create(self.builder); | 263 | var new_check = Check.create(self.step.owner); |
| 253 | new_check.match(phrase); | 264 | new_check.match(phrase); |
| 254 | self.checks.append(new_check) catch @panic("OOM"); | 265 | self.checks.append(new_check) catch @panic("OOM"); |
| 255 | } | 266 | } |
| ... | @@ -291,34 +302,34 @@ pub fn checkComputeCompare( | ... | @@ -291,34 +302,34 @@ pub fn checkComputeCompare( |
| 291 | program: []const u8, | 302 | program: []const u8, |
| 292 | expected: ComputeCompareExpected, | 303 | expected: ComputeCompareExpected, |
| 293 | ) void { | 304 | ) void { |
| 294 | var new_check = Check.create(self.builder); | 305 | var new_check = Check.create(self.step.owner); |
| 295 | new_check.computeCmp(program, expected); | 306 | new_check.computeCmp(program, expected); |
| 296 | self.checks.append(new_check) catch @panic("OOM"); | 307 | self.checks.append(new_check) catch @panic("OOM"); |
| 297 | } | 308 | } |
| 298 | 309 | ||
| 299 | fn make(step: *Step) !void { | 310 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 311 | _ = prog_node; | ||
| 312 | const b = step.owner; | ||
| 313 | const gpa = b.allocator; | ||
| 300 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | 314 | const self = @fieldParentPtr(CheckObjectStep, "step", step); |
| 301 | 315 | ||
| 302 | const gpa = self.builder.allocator; | 316 | const src_path = self.source.getPath(b); |
| 303 | const src_path = self.source.getPath(self.builder); | 317 | const contents = fs.cwd().readFileAllocOptions( |
| 304 | const contents = try fs.cwd().readFileAllocOptions( | ||
| 305 | gpa, | 318 | gpa, |
| 306 | src_path, | 319 | src_path, |
| 307 | self.max_bytes, | 320 | self.max_bytes, |
| 308 | null, | 321 | null, |
| 309 | @alignOf(u64), | 322 | @alignOf(u64), |
| 310 | null, | 323 | null, |
| 311 | ); | 324 | ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) }); |
| 312 | 325 | ||
| 313 | const output = switch (self.obj_format) { | 326 | const output = switch (self.obj_format) { |
| 314 | .macho => try MachODumper.parseAndDump(contents, .{ | 327 | .macho => try MachODumper.parseAndDump(step, contents, .{ |
| 315 | .gpa = gpa, | ||
| 316 | .dump_symtab = self.dump_symtab, | 328 | .dump_symtab = self.dump_symtab, |
| 317 | }), | 329 | }), |
| 318 | .elf => @panic("TODO elf parser"), | 330 | .elf => @panic("TODO elf parser"), |
| 319 | .coff => @panic("TODO coff parser"), | 331 | .coff => @panic("TODO coff parser"), |
| 320 | .wasm => try WasmDumper.parseAndDump(contents, .{ | 332 | .wasm => try WasmDumper.parseAndDump(step, contents, .{ |
| 321 | .gpa = gpa, | ||
| 322 | .dump_symtab = self.dump_symtab, | 333 | .dump_symtab = self.dump_symtab, |
| 323 | }), | 334 | }), |
| 324 | else => unreachable, | 335 | else => unreachable, |
| ... | @@ -334,54 +345,50 @@ fn make(step: *Step) !void { | ... | @@ -334,54 +345,50 @@ fn make(step: *Step) !void { |
| 334 | while (it.next()) |line| { | 345 | while (it.next()) |line| { |
| 335 | if (try act.match(line, &vars)) break; | 346 | if (try act.match(line, &vars)) break; |
| 336 | } else { | 347 | } else { |
| 337 | std.debug.print( | 348 | return step.fail( |
| 338 | \\ | 349 | \\ |
| 339 | \\========= Expected to find: ========================== | 350 | \\========= expected to find: ========================== |
| 340 | \\{s} | 351 | \\{s} |
| 341 | \\========= But parsed file does not contain it: ======= | 352 | \\========= but parsed file does not contain it: ======= |
| 342 | \\{s} | 353 | \\{s} |
| 343 | \\ | 354 | \\====================================================== |
| 344 | , .{ act.phrase, output }); | 355 | , .{ act.phrase, output }); |
| 345 | return error.TestFailed; | ||
| 346 | } | 356 | } |
| 347 | }, | 357 | }, |
| 348 | .not_present => { | 358 | .not_present => { |
| 349 | while (it.next()) |line| { | 359 | while (it.next()) |line| { |
| 350 | if (try act.match(line, &vars)) { | 360 | if (try act.match(line, &vars)) { |
| 351 | std.debug.print( | 361 | return step.fail( |
| 352 | \\ | 362 | \\ |
| 353 | \\========= Expected not to find: =================== | 363 | \\========= expected not to find: =================== |
| 354 | \\{s} | 364 | \\{s} |
| 355 | \\========= But parsed file does contain it: ======== | 365 | \\========= but parsed file does contain it: ======== |
| 356 | \\{s} | 366 | \\{s} |
| 357 | \\ | 367 | \\=================================================== |
| 358 | , .{ act.phrase, output }); | 368 | , .{ act.phrase, output }); |
| 359 | return error.TestFailed; | ||
| 360 | } | 369 | } |
| 361 | } | 370 | } |
| 362 | }, | 371 | }, |
| 363 | .compute_cmp => { | 372 | .compute_cmp => { |
| 364 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { | 373 | const res = act.computeCmp(step, vars) catch |err| switch (err) { |
| 365 | error.UnknownVariable => { | 374 | error.UnknownVariable => { |
| 366 | std.debug.print( | 375 | return step.fail( |
| 367 | \\========= From parsed file: ===================== | 376 | \\========= from parsed file: ===================== |
| 368 | \\{s} | 377 | \\{s} |
| 369 | \\ | 378 | \\================================================= |
| 370 | , .{output}); | 379 | , .{output}); |
| 371 | return error.TestFailed; | ||
| 372 | }, | 380 | }, |
| 373 | else => |e| return e, | 381 | else => |e| return e, |
| 374 | }; | 382 | }; |
| 375 | if (!res) { | 383 | if (!res) { |
| 376 | std.debug.print( | 384 | return step.fail( |
| 377 | \\ | 385 | \\ |
| 378 | \\========= Comparison failed for action: =========== | 386 | \\========= comparison failed for action: =========== |
| 379 | \\{s} {} | 387 | \\{s} {} |
| 380 | \\========= From parsed file: ======================= | 388 | \\========= from parsed file: ======================= |
| 381 | \\{s} | 389 | \\{s} |
| 382 | \\ | 390 | \\=================================================== |
| 383 | , .{ act.phrase, act.expected.?, output }); | 391 | , .{ act.phrase, act.expected.?, output }); |
| 384 | return error.TestFailed; | ||
| 385 | } | 392 | } |
| 386 | }, | 393 | }, |
| 387 | } | 394 | } |
| ... | @@ -390,7 +397,6 @@ fn make(step: *Step) !void { | ... | @@ -390,7 +397,6 @@ fn make(step: *Step) !void { |
| 390 | } | 397 | } |
| 391 | 398 | ||
| 392 | const Opts = struct { | 399 | const Opts = struct { |
| 393 | gpa: ?Allocator = null, | ||
| 394 | dump_symtab: bool = false, | 400 | dump_symtab: bool = false, |
| 395 | }; | 401 | }; |
| 396 | 402 | ||
| ... | @@ -398,8 +404,8 @@ const MachODumper = struct { | ... | @@ -398,8 +404,8 @@ const MachODumper = struct { |
| 398 | const LoadCommandIterator = macho.LoadCommandIterator; | 404 | const LoadCommandIterator = macho.LoadCommandIterator; |
| 399 | const symtab_label = "symtab"; | 405 | const symtab_label = "symtab"; |
| 400 | 406 | ||
| 401 | fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { | 407 | fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { |
| 402 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | 408 | const gpa = step.owner.allocator; |
| 403 | var stream = std.io.fixedBufferStream(bytes); | 409 | var stream = std.io.fixedBufferStream(bytes); |
| 404 | const reader = stream.reader(); | 410 | const reader = stream.reader(); |
| 405 | 411 | ||
| ... | @@ -681,8 +687,8 @@ const MachODumper = struct { | ... | @@ -681,8 +687,8 @@ const MachODumper = struct { |
| 681 | const WasmDumper = struct { | 687 | const WasmDumper = struct { |
| 682 | const symtab_label = "symbols"; | 688 | const symtab_label = "symbols"; |
| 683 | 689 | ||
| 684 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | 690 | fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 { |
| 685 | const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator | 691 | const gpa = step.owner.allocator; |
| 686 | if (opts.dump_symtab) { | 692 | if (opts.dump_symtab) { |
| 687 | @panic("TODO: Implement symbol table parsing and dumping"); | 693 | @panic("TODO: Implement symbol table parsing and dumping"); |
| 688 | } | 694 | } |
| ... | @@ -703,20 +709,24 @@ const WasmDumper = struct { | ... | @@ -703,20 +709,24 @@ const WasmDumper = struct { |
| 703 | const writer = output.writer(); | 709 | const writer = output.writer(); |
| 704 | 710 | ||
| 705 | while (reader.readByte()) |current_byte| { | 711 | while (reader.readByte()) |current_byte| { |
| 706 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| { | 712 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { |
| 707 | std.debug.print("Found invalid section id '{d}'\n", .{current_byte}); | 713 | return step.fail("Found invalid section id '{d}'", .{current_byte}); |
| 708 | return err; | ||
| 709 | }; | 714 | }; |
| 710 | 715 | ||
| 711 | const section_length = try std.leb.readULEB128(u32, reader); | 716 | const section_length = try std.leb.readULEB128(u32, reader); |
| 712 | try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer); | 717 | try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); |
| 713 | fbs.pos += section_length; | 718 | fbs.pos += section_length; |
| 714 | } else |_| {} // reached end of stream | 719 | } else |_| {} // reached end of stream |
| 715 | 720 | ||
| 716 | return output.toOwnedSlice(); | 721 | return output.toOwnedSlice(); |
| 717 | } | 722 | } |
| 718 | 723 | ||
| 719 | fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void { | 724 | fn parseAndDumpSection( |
| 725 | step: *Step, | ||
| 726 | section: std.wasm.Section, | ||
| 727 | data: []const u8, | ||
| 728 | writer: anytype, | ||
| 729 | ) !void { | ||
| 720 | var fbs = std.io.fixedBufferStream(data); | 730 | var fbs = std.io.fixedBufferStream(data); |
| 721 | const reader = fbs.reader(); | 731 | const reader = fbs.reader(); |
| 722 | 732 | ||
| ... | @@ -739,7 +749,7 @@ const WasmDumper = struct { | ... | @@ -739,7 +749,7 @@ const WasmDumper = struct { |
| 739 | => { | 749 | => { |
| 740 | const entries = try std.leb.readULEB128(u32, reader); | 750 | const entries = try std.leb.readULEB128(u32, reader); |
| 741 | try writer.print("\nentries {d}\n", .{entries}); | 751 | try writer.print("\nentries {d}\n", .{entries}); |
| 742 | try dumpSection(section, data[fbs.pos..], entries, writer); | 752 | try dumpSection(step, section, data[fbs.pos..], entries, writer); |
| 743 | }, | 753 | }, |
| 744 | .custom => { | 754 | .custom => { |
| 745 | const name_length = try std.leb.readULEB128(u32, reader); | 755 | const name_length = try std.leb.readULEB128(u32, reader); |
| ... | @@ -748,7 +758,7 @@ const WasmDumper = struct { | ... | @@ -748,7 +758,7 @@ const WasmDumper = struct { |
| 748 | try writer.print("\nname {s}\n", .{name}); | 758 | try writer.print("\nname {s}\n", .{name}); |
| 749 | 759 | ||
| 750 | if (mem.eql(u8, name, "name")) { | 760 | if (mem.eql(u8, name, "name")) { |
| 751 | try parseDumpNames(reader, writer, data); | 761 | try parseDumpNames(step, reader, writer, data); |
| 752 | } else if (mem.eql(u8, name, "producers")) { | 762 | } else if (mem.eql(u8, name, "producers")) { |
| 753 | try parseDumpProducers(reader, writer, data); | 763 | try parseDumpProducers(reader, writer, data); |
| 754 | } else if (mem.eql(u8, name, "target_features")) { | 764 | } else if (mem.eql(u8, name, "target_features")) { |
| ... | @@ -764,7 +774,7 @@ const WasmDumper = struct { | ... | @@ -764,7 +774,7 @@ const WasmDumper = struct { |
| 764 | } | 774 | } |
| 765 | } | 775 | } |
| 766 | 776 | ||
| 767 | fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { | 777 | fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { |
| 768 | var fbs = std.io.fixedBufferStream(data); | 778 | var fbs = std.io.fixedBufferStream(data); |
| 769 | const reader = fbs.reader(); | 779 | const reader = fbs.reader(); |
| 770 | 780 | ||
| ... | @@ -774,19 +784,18 @@ const WasmDumper = struct { | ... | @@ -774,19 +784,18 @@ const WasmDumper = struct { |
| 774 | while (i < entries) : (i += 1) { | 784 | while (i < entries) : (i += 1) { |
| 775 | const func_type = try reader.readByte(); | 785 | const func_type = try reader.readByte(); |
| 776 | if (func_type != std.wasm.function_type) { | 786 | if (func_type != std.wasm.function_type) { |
| 777 | std.debug.print("Expected function type, found byte '{d}'\n", .{func_type}); | 787 | return step.fail("expected function type, found byte '{d}'", .{func_type}); |
| 778 | return error.UnexpectedByte; | ||
| 779 | } | 788 | } |
| 780 | const params = try std.leb.readULEB128(u32, reader); | 789 | const params = try std.leb.readULEB128(u32, reader); |
| 781 | try writer.print("params {d}\n", .{params}); | 790 | try writer.print("params {d}\n", .{params}); |
| 782 | var index: u32 = 0; | 791 | var index: u32 = 0; |
| 783 | while (index < params) : (index += 1) { | 792 | while (index < params) : (index += 1) { |
| 784 | try parseDumpType(std.wasm.Valtype, reader, writer); | 793 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 785 | } else index = 0; | 794 | } else index = 0; |
| 786 | const returns = try std.leb.readULEB128(u32, reader); | 795 | const returns = try std.leb.readULEB128(u32, reader); |
| 787 | try writer.print("returns {d}\n", .{returns}); | 796 | try writer.print("returns {d}\n", .{returns}); |
| 788 | while (index < returns) : (index += 1) { | 797 | while (index < returns) : (index += 1) { |
| 789 | try parseDumpType(std.wasm.Valtype, reader, writer); | 798 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 790 | } | 799 | } |
| 791 | } | 800 | } |
| 792 | }, | 801 | }, |
| ... | @@ -800,9 +809,8 @@ const WasmDumper = struct { | ... | @@ -800,9 +809,8 @@ const WasmDumper = struct { |
| 800 | const name = data[fbs.pos..][0..name_len]; | 809 | const name = data[fbs.pos..][0..name_len]; |
| 801 | fbs.pos += name_len; | 810 | fbs.pos += name_len; |
| 802 | 811 | ||
| 803 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| { | 812 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch { |
| 804 | std.debug.print("Invalid import kind\n", .{}); | 813 | return step.fail("invalid import kind", .{}); |
| 805 | return err; | ||
| 806 | }; | 814 | }; |
| 807 | 815 | ||
| 808 | try writer.print( | 816 | try writer.print( |
| ... | @@ -819,11 +827,11 @@ const WasmDumper = struct { | ... | @@ -819,11 +827,11 @@ const WasmDumper = struct { |
| 819 | try parseDumpLimits(reader, writer); | 827 | try parseDumpLimits(reader, writer); |
| 820 | }, | 828 | }, |
| 821 | .global => { | 829 | .global => { |
| 822 | try parseDumpType(std.wasm.Valtype, reader, writer); | 830 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 823 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); | 831 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); |
| 824 | }, | 832 | }, |
| 825 | .table => { | 833 | .table => { |
| 826 | try parseDumpType(std.wasm.RefType, reader, writer); | 834 | try parseDumpType(step, std.wasm.RefType, reader, writer); |
| 827 | try parseDumpLimits(reader, writer); | 835 | try parseDumpLimits(reader, writer); |
| 828 | }, | 836 | }, |
| 829 | } | 837 | } |
| ... | @@ -838,7 +846,7 @@ const WasmDumper = struct { | ... | @@ -838,7 +846,7 @@ const WasmDumper = struct { |
| 838 | .table => { | 846 | .table => { |
| 839 | var i: u32 = 0; | 847 | var i: u32 = 0; |
| 840 | while (i < entries) : (i += 1) { | 848 | while (i < entries) : (i += 1) { |
| 841 | try parseDumpType(std.wasm.RefType, reader, writer); | 849 | try parseDumpType(step, std.wasm.RefType, reader, writer); |
| 842 | try parseDumpLimits(reader, writer); | 850 | try parseDumpLimits(reader, writer); |
| 843 | } | 851 | } |
| 844 | }, | 852 | }, |
| ... | @@ -851,9 +859,9 @@ const WasmDumper = struct { | ... | @@ -851,9 +859,9 @@ const WasmDumper = struct { |
| 851 | .global => { | 859 | .global => { |
| 852 | var i: u32 = 0; | 860 | var i: u32 = 0; |
| 853 | while (i < entries) : (i += 1) { | 861 | while (i < entries) : (i += 1) { |
| 854 | try parseDumpType(std.wasm.Valtype, reader, writer); | 862 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 855 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); | 863 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); |
| 856 | try parseDumpInit(reader, writer); | 864 | try parseDumpInit(step, reader, writer); |
| 857 | } | 865 | } |
| 858 | }, | 866 | }, |
| 859 | .@"export" => { | 867 | .@"export" => { |
| ... | @@ -863,9 +871,8 @@ const WasmDumper = struct { | ... | @@ -863,9 +871,8 @@ const WasmDumper = struct { |
| 863 | const name = data[fbs.pos..][0..name_len]; | 871 | const name = data[fbs.pos..][0..name_len]; |
| 864 | fbs.pos += name_len; | 872 | fbs.pos += name_len; |
| 865 | const kind_byte = try std.leb.readULEB128(u8, reader); | 873 | const kind_byte = try std.leb.readULEB128(u8, reader); |
| 866 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| { | 874 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch { |
| 867 | std.debug.print("invalid export kind value '{d}'\n", .{kind_byte}); | 875 | return step.fail("invalid export kind value '{d}'", .{kind_byte}); |
| 868 | return err; | ||
| 869 | }; | 876 | }; |
| 870 | const index = try std.leb.readULEB128(u32, reader); | 877 | const index = try std.leb.readULEB128(u32, reader); |
| 871 | try writer.print( | 878 | try writer.print( |
| ... | @@ -880,7 +887,7 @@ const WasmDumper = struct { | ... | @@ -880,7 +887,7 @@ const WasmDumper = struct { |
| 880 | var i: u32 = 0; | 887 | var i: u32 = 0; |
| 881 | while (i < entries) : (i += 1) { | 888 | while (i < entries) : (i += 1) { |
| 882 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | 889 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); |
| 883 | try parseDumpInit(reader, writer); | 890 | try parseDumpInit(step, reader, writer); |
| 884 | 891 | ||
| 885 | const function_indexes = try std.leb.readULEB128(u32, reader); | 892 | const function_indexes = try std.leb.readULEB128(u32, reader); |
| 886 | var function_index: u32 = 0; | 893 | var function_index: u32 = 0; |
| ... | @@ -896,7 +903,7 @@ const WasmDumper = struct { | ... | @@ -896,7 +903,7 @@ const WasmDumper = struct { |
| 896 | while (i < entries) : (i += 1) { | 903 | while (i < entries) : (i += 1) { |
| 897 | const index = try std.leb.readULEB128(u32, reader); | 904 | const index = try std.leb.readULEB128(u32, reader); |
| 898 | try writer.print("memory index 0x{x}\n", .{index}); | 905 | try writer.print("memory index 0x{x}\n", .{index}); |
| 899 | try parseDumpInit(reader, writer); | 906 | try parseDumpInit(step, reader, writer); |
| 900 | const size = try std.leb.readULEB128(u32, reader); | 907 | const size = try std.leb.readULEB128(u32, reader); |
| 901 | try writer.print("size {d}\n", .{size}); | 908 | try writer.print("size {d}\n", .{size}); |
| 902 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments | 909 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments |
| ... | @@ -906,11 +913,10 @@ const WasmDumper = struct { | ... | @@ -906,11 +913,10 @@ const WasmDumper = struct { |
| 906 | } | 913 | } |
| 907 | } | 914 | } |
| 908 | 915 | ||
| 909 | fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void { | 916 | fn parseDumpType(step: *Step, comptime WasmType: type, reader: anytype, writer: anytype) !void { |
| 910 | const type_byte = try reader.readByte(); | 917 | const type_byte = try reader.readByte(); |
| 911 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| { | 918 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch { |
| 912 | std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte}); | 919 | return step.fail("Invalid wasm type value '{d}'", .{type_byte}); |
| 913 | return err; | ||
| 914 | }; | 920 | }; |
| 915 | try writer.print("type {s}\n", .{@tagName(valtype)}); | 921 | try writer.print("type {s}\n", .{@tagName(valtype)}); |
| 916 | } | 922 | } |
| ... | @@ -925,11 +931,10 @@ const WasmDumper = struct { | ... | @@ -925,11 +931,10 @@ const WasmDumper = struct { |
| 925 | } | 931 | } |
| 926 | } | 932 | } |
| 927 | 933 | ||
| 928 | fn parseDumpInit(reader: anytype, writer: anytype) !void { | 934 | fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void { |
| 929 | const byte = try std.leb.readULEB128(u8, reader); | 935 | const byte = try std.leb.readULEB128(u8, reader); |
| 930 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| { | 936 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch { |
| 931 | std.debug.print("invalid wasm opcode '{d}'\n", .{byte}); | 937 | return step.fail("invalid wasm opcode '{d}'", .{byte}); |
| 932 | return err; | ||
| 933 | }; | 938 | }; |
| 934 | switch (opcode) { | 939 | switch (opcode) { |
| 935 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), | 940 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), |
| ... | @@ -941,14 +946,13 @@ const WasmDumper = struct { | ... | @@ -941,14 +946,13 @@ const WasmDumper = struct { |
| 941 | } | 946 | } |
| 942 | const end_opcode = try std.leb.readULEB128(u8, reader); | 947 | const end_opcode = try std.leb.readULEB128(u8, reader); |
| 943 | if (end_opcode != std.wasm.opcode(.end)) { | 948 | if (end_opcode != std.wasm.opcode(.end)) { |
| 944 | std.debug.print("expected 'end' opcode in init expression\n", .{}); | 949 | return step.fail("expected 'end' opcode in init expression", .{}); |
| 945 | return error.MissingEndOpcode; | ||
| 946 | } | 950 | } |
| 947 | } | 951 | } |
| 948 | 952 | ||
| 949 | fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void { | 953 | fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void { |
| 950 | while (reader.context.pos < data.len) { | 954 | while (reader.context.pos < data.len) { |
| 951 | try parseDumpType(std.wasm.NameSubsection, reader, writer); | 955 | try parseDumpType(step, std.wasm.NameSubsection, reader, writer); |
| 952 | const size = try std.leb.readULEB128(u32, reader); | 956 | const size = try std.leb.readULEB128(u32, reader); |
| 953 | const entries = try std.leb.readULEB128(u32, reader); | 957 | const entries = try std.leb.readULEB128(u32, reader); |
| 954 | try writer.print( | 958 | try writer.print( |
lib/std/Build/CompileStep.zig+443-378| ... | @@ -1,7 +1,6 @@ | ... | @@ -1,7 +1,6 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("../std.zig"); | 2 | const std = @import("../std.zig"); |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const log = std.log; | ||
| 5 | const fs = std.fs; | 4 | const fs = std.fs; |
| 6 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 7 | const panic = std.debug.panic; | 6 | const panic = std.debug.panic; |
| ... | @@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir; | ... | @@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir; |
| 22 | const InstallArtifactStep = std.Build.InstallArtifactStep; | 21 | const InstallArtifactStep = std.Build.InstallArtifactStep; |
| 23 | const GeneratedFile = std.Build.GeneratedFile; | 22 | const GeneratedFile = std.Build.GeneratedFile; |
| 24 | const ObjCopyStep = std.Build.ObjCopyStep; | 23 | const ObjCopyStep = std.Build.ObjCopyStep; |
| 25 | const EmulatableRunStep = std.Build.EmulatableRunStep; | ||
| 26 | const CheckObjectStep = std.Build.CheckObjectStep; | 24 | const CheckObjectStep = std.Build.CheckObjectStep; |
| 27 | const RunStep = std.Build.RunStep; | 25 | const RunStep = std.Build.RunStep; |
| 28 | const OptionsStep = std.Build.OptionsStep; | 26 | const OptionsStep = std.Build.OptionsStep; |
| ... | @@ -32,7 +30,6 @@ const CompileStep = @This(); | ... | @@ -32,7 +30,6 @@ const CompileStep = @This(); |
| 32 | pub const base_id: Step.Id = .compile; | 30 | pub const base_id: Step.Id = .compile; |
| 33 | 31 | ||
| 34 | step: Step, | 32 | step: Step, |
| 35 | builder: *std.Build, | ||
| 36 | name: []const u8, | 33 | name: []const u8, |
| 37 | target: CrossTarget, | 34 | target: CrossTarget, |
| 38 | target_info: NativeTargetInfo, | 35 | target_info: NativeTargetInfo, |
| ... | @@ -49,9 +46,9 @@ strip: ?bool, | ... | @@ -49,9 +46,9 @@ strip: ?bool, |
| 49 | unwind_tables: ?bool, | 46 | unwind_tables: ?bool, |
| 50 | // keep in sync with src/link.zig:CompressDebugSections | 47 | // keep in sync with src/link.zig:CompressDebugSections |
| 51 | compress_debug_sections: enum { none, zlib } = .none, | 48 | compress_debug_sections: enum { none, zlib } = .none, |
| 52 | lib_paths: ArrayList([]const u8), | 49 | lib_paths: ArrayList(FileSource), |
| 53 | rpaths: ArrayList([]const u8), | 50 | rpaths: ArrayList(FileSource), |
| 54 | framework_dirs: ArrayList([]const u8), | 51 | framework_dirs: ArrayList(FileSource), |
| 55 | frameworks: StringHashMap(FrameworkLinkInfo), | 52 | frameworks: StringHashMap(FrameworkLinkInfo), |
| 56 | verbose_link: bool, | 53 | verbose_link: bool, |
| 57 | verbose_cc: bool, | 54 | verbose_cc: bool, |
| ... | @@ -86,7 +83,6 @@ c_std: std.Build.CStd, | ... | @@ -86,7 +83,6 @@ c_std: std.Build.CStd, |
| 86 | zig_lib_dir: ?[]const u8, | 83 | zig_lib_dir: ?[]const u8, |
| 87 | main_pkg_path: ?[]const u8, | 84 | main_pkg_path: ?[]const u8, |
| 88 | exec_cmd_args: ?[]const ?[]const u8, | 85 | exec_cmd_args: ?[]const ?[]const u8, |
| 89 | name_prefix: []const u8, | ||
| 90 | filter: ?[]const u8, | 86 | filter: ?[]const u8, |
| 91 | test_evented_io: bool = false, | 87 | test_evented_io: bool = false, |
| 92 | test_runner: ?[]const u8, | 88 | test_runner: ?[]const u8, |
| ... | @@ -210,10 +206,17 @@ want_lto: ?bool = null, | ... | @@ -210,10 +206,17 @@ want_lto: ?bool = null, |
| 210 | use_llvm: ?bool = null, | 206 | use_llvm: ?bool = null, |
| 211 | use_lld: ?bool = null, | 207 | use_lld: ?bool = null, |
| 212 | 208 | ||
| 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. | ||
| 213 | expect_errors: []const []const u8 = &.{}, | ||
| 214 | |||
| 213 | output_path_source: GeneratedFile, | 215 | output_path_source: GeneratedFile, |
| 214 | output_lib_path_source: GeneratedFile, | 216 | output_lib_path_source: GeneratedFile, |
| 215 | output_h_path_source: GeneratedFile, | 217 | output_h_path_source: GeneratedFile, |
| 216 | output_pdb_path_source: GeneratedFile, | 218 | output_pdb_path_source: GeneratedFile, |
| 219 | output_dirname_source: GeneratedFile, | ||
| 217 | 220 | ||
| 218 | pub const CSourceFiles = struct { | 221 | pub const CSourceFiles = struct { |
| 219 | files: []const []const u8, | 222 | files: []const []const u8, |
| ... | @@ -277,6 +280,7 @@ pub const Options = struct { | ... | @@ -277,6 +280,7 @@ pub const Options = struct { |
| 277 | kind: Kind, | 280 | kind: Kind, |
| 278 | linkage: ?Linkage = null, | 281 | linkage: ?Linkage = null, |
| 279 | version: ?std.builtin.Version = null, | 282 | version: ?std.builtin.Version = null, |
| 283 | max_rss: usize = 0, | ||
| 280 | }; | 284 | }; |
| 281 | 285 | ||
| 282 | pub const Kind = enum { | 286 | pub const Kind = enum { |
| ... | @@ -284,7 +288,6 @@ pub const Kind = enum { | ... | @@ -284,7 +288,6 @@ pub const Kind = enum { |
| 284 | lib, | 288 | lib, |
| 285 | obj, | 289 | obj, |
| 286 | @"test", | 290 | @"test", |
| 287 | test_exe, | ||
| 288 | }; | 291 | }; |
| 289 | 292 | ||
| 290 | pub const Linkage = enum { dynamic, static }; | 293 | pub const Linkage = enum { dynamic, static }; |
| ... | @@ -305,18 +308,35 @@ pub const EmitOption = union(enum) { | ... | @@ -305,18 +308,35 @@ pub const EmitOption = union(enum) { |
| 305 | } | 308 | } |
| 306 | }; | 309 | }; |
| 307 | 310 | ||
| 308 | pub fn create(builder: *std.Build, options: Options) *CompileStep { | 311 | pub fn create(owner: *std.Build, options: Options) *CompileStep { |
| 309 | const name = builder.dupe(options.name); | 312 | const name = owner.dupe(options.name); |
| 310 | const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null; | 313 | const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null; |
| 311 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { | 314 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { |
| 312 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); | 315 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); |
| 313 | } | 316 | } |
| 314 | 317 | ||
| 315 | const self = builder.allocator.create(CompileStep) catch @panic("OOM"); | 318 | // Avoid the common case of the step name looking like "zig test test". |
| 319 | const name_adjusted = if (options.kind == .@"test" and mem.eql(u8, name, "test")) | ||
| 320 | "" | ||
| 321 | else | ||
| 322 | owner.fmt("{s} ", .{name}); | ||
| 323 | |||
| 324 | const step_name = owner.fmt("{s} {s}{s} {s}", .{ | ||
| 325 | switch (options.kind) { | ||
| 326 | .exe => "zig build-exe", | ||
| 327 | .lib => "zig build-lib", | ||
| 328 | .obj => "zig build-obj", | ||
| 329 | .@"test" => "zig test", | ||
| 330 | }, | ||
| 331 | name_adjusted, | ||
| 332 | @tagName(options.optimize), | ||
| 333 | options.target.zigTriple(owner.allocator) catch @panic("OOM"), | ||
| 334 | }); | ||
| 335 | |||
| 336 | const self = owner.allocator.create(CompileStep) catch @panic("OOM"); | ||
| 316 | self.* = CompileStep{ | 337 | self.* = CompileStep{ |
| 317 | .strip = null, | 338 | .strip = null, |
| 318 | .unwind_tables = null, | 339 | .unwind_tables = null, |
| 319 | .builder = builder, | ||
| 320 | .verbose_link = false, | 340 | .verbose_link = false, |
| 321 | .verbose_cc = false, | 341 | .verbose_cc = false, |
| 322 | .optimize = options.optimize, | 342 | .optimize = options.optimize, |
| ... | @@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 325 | .kind = options.kind, | 345 | .kind = options.kind, |
| 326 | .root_src = root_src, | 346 | .root_src = root_src, |
| 327 | .name = name, | 347 | .name = name, |
| 328 | .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), | 348 | .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator), |
| 329 | .step = Step.init(base_id, name, builder.allocator, make), | 349 | .step = Step.init(.{ |
| 350 | .id = base_id, | ||
| 351 | .name = step_name, | ||
| 352 | .owner = owner, | ||
| 353 | .makeFn = make, | ||
| 354 | .max_rss = options.max_rss, | ||
| 355 | }), | ||
| 330 | .version = options.version, | 356 | .version = options.version, |
| 331 | .out_filename = undefined, | 357 | .out_filename = undefined, |
| 332 | .out_h_filename = builder.fmt("{s}.h", .{name}), | 358 | .out_h_filename = owner.fmt("{s}.h", .{name}), |
| 333 | .out_lib_filename = undefined, | 359 | .out_lib_filename = undefined, |
| 334 | .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), | 360 | .out_pdb_filename = owner.fmt("{s}.pdb", .{name}), |
| 335 | .major_only_filename = null, | 361 | .major_only_filename = null, |
| 336 | .name_only_filename = null, | 362 | .name_only_filename = null, |
| 337 | .modules = std.StringArrayHashMap(*Module).init(builder.allocator), | 363 | .modules = std.StringArrayHashMap(*Module).init(owner.allocator), |
| 338 | .include_dirs = ArrayList(IncludeDir).init(builder.allocator), | 364 | .include_dirs = ArrayList(IncludeDir).init(owner.allocator), |
| 339 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | 365 | .link_objects = ArrayList(LinkObject).init(owner.allocator), |
| 340 | .c_macros = ArrayList([]const u8).init(builder.allocator), | 366 | .c_macros = ArrayList([]const u8).init(owner.allocator), |
| 341 | .lib_paths = ArrayList([]const u8).init(builder.allocator), | 367 | .lib_paths = ArrayList(FileSource).init(owner.allocator), |
| 342 | .rpaths = ArrayList([]const u8).init(builder.allocator), | 368 | .rpaths = ArrayList(FileSource).init(owner.allocator), |
| 343 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), | 369 | .framework_dirs = ArrayList(FileSource).init(owner.allocator), |
| 344 | .installed_headers = ArrayList(*Step).init(builder.allocator), | 370 | .installed_headers = ArrayList(*Step).init(owner.allocator), |
| 345 | .object_src = undefined, | 371 | .object_src = undefined, |
| 346 | .c_std = std.Build.CStd.C99, | 372 | .c_std = std.Build.CStd.C99, |
| 347 | .zig_lib_dir = null, | 373 | .zig_lib_dir = null, |
| 348 | .main_pkg_path = null, | 374 | .main_pkg_path = null, |
| 349 | .exec_cmd_args = null, | 375 | .exec_cmd_args = null, |
| 350 | .name_prefix = "", | ||
| 351 | .filter = null, | 376 | .filter = null, |
| 352 | .test_runner = null, | 377 | .test_runner = null, |
| 353 | .disable_stack_probing = false, | 378 | .disable_stack_probing = false, |
| ... | @@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 363 | .output_lib_path_source = GeneratedFile{ .step = &self.step }, | 388 | .output_lib_path_source = GeneratedFile{ .step = &self.step }, |
| 364 | .output_h_path_source = GeneratedFile{ .step = &self.step }, | 389 | .output_h_path_source = GeneratedFile{ .step = &self.step }, |
| 365 | .output_pdb_path_source = GeneratedFile{ .step = &self.step }, | 390 | .output_pdb_path_source = GeneratedFile{ .step = &self.step }, |
| 391 | .output_dirname_source = GeneratedFile{ .step = &self.step }, | ||
| 366 | 392 | ||
| 367 | .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), | 393 | .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), |
| 368 | }; | 394 | }; |
| ... | @@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 372 | } | 398 | } |
| 373 | 399 | ||
| 374 | fn computeOutFileNames(self: *CompileStep) void { | 400 | fn computeOutFileNames(self: *CompileStep) void { |
| 401 | const b = self.step.owner; | ||
| 375 | const target = self.target_info.target; | 402 | const target = self.target_info.target; |
| 376 | 403 | ||
| 377 | self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ | 404 | self.out_filename = std.zig.binNameAlloc(b.allocator, .{ |
| 378 | .root_name = self.name, | 405 | .root_name = self.name, |
| 379 | .target = target, | 406 | .target = target, |
| 380 | .output_mode = switch (self.kind) { | 407 | .output_mode = switch (self.kind) { |
| 381 | .lib => .Lib, | 408 | .lib => .Lib, |
| 382 | .obj => .Obj, | 409 | .obj => .Obj, |
| 383 | .exe, .@"test", .test_exe => .Exe, | 410 | .exe, .@"test" => .Exe, |
| 384 | }, | 411 | }, |
| 385 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { | 412 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { |
| 386 | .dynamic => .Dynamic, | 413 | .dynamic => .Dynamic, |
| ... | @@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void { | ... | @@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void { |
| 394 | self.out_lib_filename = self.out_filename; | 421 | self.out_lib_filename = self.out_filename; |
| 395 | } else if (self.version) |version| { | 422 | } else if (self.version) |version| { |
| 396 | if (target.isDarwin()) { | 423 | if (target.isDarwin()) { |
| 397 | self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ | 424 | self.major_only_filename = b.fmt("lib{s}.{d}.dylib", .{ |
| 398 | self.name, | 425 | self.name, |
| 399 | version.major, | 426 | version.major, |
| 400 | }); | 427 | }); |
| 401 | self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); | 428 | self.name_only_filename = b.fmt("lib{s}.dylib", .{self.name}); |
| 402 | self.out_lib_filename = self.out_filename; | 429 | self.out_lib_filename = self.out_filename; |
| 403 | } else if (target.os.tag == .windows) { | 430 | } else if (target.os.tag == .windows) { |
| 404 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | 431 | self.out_lib_filename = b.fmt("{s}.lib", .{self.name}); |
| 405 | } else { | 432 | } else { |
| 406 | self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); | 433 | self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major }); |
| 407 | self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); | 434 | self.name_only_filename = b.fmt("lib{s}.so", .{self.name}); |
| 408 | self.out_lib_filename = self.out_filename; | 435 | self.out_lib_filename = self.out_filename; |
| 409 | } | 436 | } |
| 410 | } else { | 437 | } else { |
| 411 | if (target.isDarwin()) { | 438 | if (target.isDarwin()) { |
| 412 | self.out_lib_filename = self.out_filename; | 439 | self.out_lib_filename = self.out_filename; |
| 413 | } else if (target.os.tag == .windows) { | 440 | } else if (target.os.tag == .windows) { |
| 414 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | 441 | self.out_lib_filename = b.fmt("{s}.lib", .{self.name}); |
| 415 | } else { | 442 | } else { |
| 416 | self.out_lib_filename = self.out_filename; | 443 | self.out_lib_filename = self.out_filename; |
| 417 | } | 444 | } |
| 418 | } | 445 | } |
| 419 | if (self.output_dir != null) { | 446 | if (self.output_dir != null) { |
| 420 | self.output_lib_path_source.path = self.builder.pathJoin( | 447 | self.output_lib_path_source.path = b.pathJoin( |
| 421 | &.{ self.output_dir.?, self.out_lib_filename }, | 448 | &.{ self.output_dir.?, self.out_lib_filename }, |
| 422 | ); | 449 | ); |
| 423 | } | 450 | } |
| ... | @@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void { | ... | @@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void { |
| 425 | } | 452 | } |
| 426 | 453 | ||
| 427 | pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { | 454 | pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { |
| 428 | self.output_dir = self.builder.dupePath(dir); | 455 | const b = self.step.owner; |
| 456 | self.output_dir = b.dupePath(dir); | ||
| 429 | } | 457 | } |
| 430 | 458 | ||
| 431 | pub fn install(self: *CompileStep) void { | 459 | pub fn install(self: *CompileStep) void { |
| 432 | self.builder.installArtifact(self); | 460 | const b = self.step.owner; |
| 461 | b.installArtifact(self); | ||
| 433 | } | 462 | } |
| 434 | 463 | ||
| 435 | pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { | 464 | pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { |
| 436 | const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); | 465 | const b = cs.step.owner; |
| 437 | a.builder.getInstallStep().dependOn(&install_file.step); | 466 | const install_file = b.addInstallHeaderFile(src_path, dest_rel_path); |
| 438 | a.installed_headers.append(&install_file.step) catch @panic("OOM"); | 467 | b.getInstallStep().dependOn(&install_file.step); |
| 468 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | ||
| 439 | } | 469 | } |
| 440 | 470 | ||
| 441 | pub const InstallConfigHeaderOptions = struct { | 471 | pub const InstallConfigHeaderOptions = struct { |
| ... | @@ -449,13 +479,14 @@ pub fn installConfigHeader( | ... | @@ -449,13 +479,14 @@ pub fn installConfigHeader( |
| 449 | options: InstallConfigHeaderOptions, | 479 | options: InstallConfigHeaderOptions, |
| 450 | ) void { | 480 | ) void { |
| 451 | const dest_rel_path = options.dest_rel_path orelse config_header.include_path; | 481 | const dest_rel_path = options.dest_rel_path orelse config_header.include_path; |
| 452 | const install_file = cs.builder.addInstallFileWithDir( | 482 | const b = cs.step.owner; |
| 483 | const install_file = b.addInstallFileWithDir( | ||
| 453 | .{ .generated = &config_header.output_file }, | 484 | .{ .generated = &config_header.output_file }, |
| 454 | options.install_dir, | 485 | options.install_dir, |
| 455 | dest_rel_path, | 486 | dest_rel_path, |
| 456 | ); | 487 | ); |
| 457 | install_file.step.dependOn(&config_header.step); | 488 | install_file.step.dependOn(&config_header.step); |
| 458 | cs.builder.getInstallStep().dependOn(&install_file.step); | 489 | b.getInstallStep().dependOn(&install_file.step); |
| 459 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | 490 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); |
| 460 | } | 491 | } |
| 461 | 492 | ||
| ... | @@ -472,91 +503,83 @@ pub fn installHeadersDirectory( | ... | @@ -472,91 +503,83 @@ pub fn installHeadersDirectory( |
| 472 | } | 503 | } |
| 473 | 504 | ||
| 474 | pub fn installHeadersDirectoryOptions( | 505 | pub fn installHeadersDirectoryOptions( |
| 475 | a: *CompileStep, | 506 | cs: *CompileStep, |
| 476 | options: std.Build.InstallDirStep.Options, | 507 | options: std.Build.InstallDirStep.Options, |
| 477 | ) void { | 508 | ) void { |
| 478 | const install_dir = a.builder.addInstallDirectory(options); | 509 | const b = cs.step.owner; |
| 479 | a.builder.getInstallStep().dependOn(&install_dir.step); | 510 | const install_dir = b.addInstallDirectory(options); |
| 480 | a.installed_headers.append(&install_dir.step) catch @panic("OOM"); | 511 | b.getInstallStep().dependOn(&install_dir.step); |
| 512 | cs.installed_headers.append(&install_dir.step) catch @panic("OOM"); | ||
| 481 | } | 513 | } |
| 482 | 514 | ||
| 483 | pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { | 515 | pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void { |
| 484 | assert(l.kind == .lib); | 516 | assert(l.kind == .lib); |
| 485 | const install_step = a.builder.getInstallStep(); | 517 | const b = cs.step.owner; |
| 518 | const install_step = b.getInstallStep(); | ||
| 486 | // Copy each element from installed_headers, modifying the builder | 519 | // Copy each element from installed_headers, modifying the builder |
| 487 | // to be the new parent's builder. | 520 | // to be the new parent's builder. |
| 488 | for (l.installed_headers.items) |step| { | 521 | for (l.installed_headers.items) |step| { |
| 489 | const step_copy = switch (step.id) { | 522 | const step_copy = switch (step.id) { |
| 490 | inline .install_file, .install_dir => |id| blk: { | 523 | inline .install_file, .install_dir => |id| blk: { |
| 491 | const T = id.Type(); | 524 | const T = id.Type(); |
| 492 | const ptr = a.builder.allocator.create(T) catch @panic("OOM"); | 525 | const ptr = b.allocator.create(T) catch @panic("OOM"); |
| 493 | ptr.* = step.cast(T).?.*; | 526 | ptr.* = step.cast(T).?.*; |
| 494 | ptr.override_source_builder = ptr.builder; | 527 | ptr.dest_builder = b; |
| 495 | ptr.builder = a.builder; | ||
| 496 | break :blk &ptr.step; | 528 | break :blk &ptr.step; |
| 497 | }, | 529 | }, |
| 498 | else => unreachable, | 530 | else => unreachable, |
| 499 | }; | 531 | }; |
| 500 | a.installed_headers.append(step_copy) catch @panic("OOM"); | 532 | cs.installed_headers.append(step_copy) catch @panic("OOM"); |
| 501 | install_step.dependOn(step_copy); | 533 | install_step.dependOn(step_copy); |
| 502 | } | 534 | } |
| 503 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); | 535 | cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); |
| 504 | } | 536 | } |
| 505 | 537 | ||
| 506 | pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { | 538 | pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { |
| 539 | const b = cs.step.owner; | ||
| 507 | var copy = options; | 540 | var copy = options; |
| 508 | if (copy.basename == null) { | 541 | if (copy.basename == null) { |
| 509 | if (options.format) |f| { | 542 | if (options.format) |f| { |
| 510 | copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); | 543 | copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); |
| 511 | } else { | 544 | } else { |
| 512 | copy.basename = cs.name; | 545 | copy.basename = cs.name; |
| 513 | } | 546 | } |
| 514 | } | 547 | } |
| 515 | return cs.builder.addObjCopy(cs.getOutputSource(), copy); | 548 | return b.addObjCopy(cs.getOutputSource(), copy); |
| 516 | } | 549 | } |
| 517 | 550 | ||
| 518 | /// Deprecated: use `std.Build.addRunArtifact` | 551 | /// Deprecated: use `std.Build.addRunArtifact` |
| 519 | /// This function will run in the context of the package that created the executable, | 552 | /// This function will run in the context of the package that created the executable, |
| 520 | /// which is undesirable when running an executable provided by a dependency package. | 553 | /// which is undesirable when running an executable provided by a dependency package. |
| 521 | pub fn run(exe: *CompileStep) *RunStep { | 554 | pub fn run(cs: *CompileStep) *RunStep { |
| 522 | return exe.builder.addRunArtifact(exe); | 555 | return cs.step.owner.addRunArtifact(cs); |
| 523 | } | ||
| 524 | |||
| 525 | /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. | ||
| 526 | /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. | ||
| 527 | /// When a binary cannot be ran through emulation or the option is disabled, a warning | ||
| 528 | /// will be printed and the binary will *NOT* be ran. | ||
| 529 | pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep { | ||
| 530 | assert(exe.kind == .exe or exe.kind == .test_exe); | ||
| 531 | |||
| 532 | const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); | ||
| 533 | if (exe.vcpkg_bin_path) |path| { | ||
| 534 | RunStep.addPathDirInternal(&run_step.step, exe.builder, path); | ||
| 535 | } | ||
| 536 | return run_step; | ||
| 537 | } | 556 | } |
| 538 | 557 | ||
| 539 | pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | 558 | pub fn checkObject(self: *CompileStep) *CheckObjectStep { |
| 540 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | 559 | return CheckObjectStep.create(self.step.owner, self.getOutputSource(), self.target_info.target.ofmt); |
| 541 | } | 560 | } |
| 542 | 561 | ||
| 543 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { | 562 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { |
| 544 | self.linker_script = source.dupe(self.builder); | 563 | const b = self.step.owner; |
| 564 | self.linker_script = source.dupe(b); | ||
| 545 | source.addStepDependencies(&self.step); | 565 | source.addStepDependencies(&self.step); |
| 546 | } | 566 | } |
| 547 | 567 | ||
| 548 | pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { | 568 | pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { |
| 549 | self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM"); | 569 | const b = self.step.owner; |
| 570 | self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM"); | ||
| 550 | } | 571 | } |
| 551 | 572 | ||
| 552 | pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { | 573 | pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { |
| 553 | self.frameworks.put(self.builder.dupe(framework_name), .{ | 574 | const b = self.step.owner; |
| 575 | self.frameworks.put(b.dupe(framework_name), .{ | ||
| 554 | .needed = true, | 576 | .needed = true, |
| 555 | }) catch @panic("OOM"); | 577 | }) catch @panic("OOM"); |
| 556 | } | 578 | } |
| 557 | 579 | ||
| 558 | pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { | 580 | pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { |
| 559 | self.frameworks.put(self.builder.dupe(framework_name), .{ | 581 | const b = self.step.owner; |
| 582 | self.frameworks.put(b.dupe(framework_name), .{ | ||
| 560 | .weak = true, | 583 | .weak = true, |
| 561 | }) catch @panic("OOM"); | 584 | }) catch @panic("OOM"); |
| 562 | } | 585 | } |
| ... | @@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool { | ... | @@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool { |
| 595 | if (!self.target.isWindows() and !self.target.isUefi()) return false; | 618 | if (!self.target.isWindows() and !self.target.isUefi()) return false; |
| 596 | if (self.target.getObjectFormat() == .c) return false; | 619 | if (self.target.getObjectFormat() == .c) return false; |
| 597 | if (self.strip == true) return false; | 620 | if (self.strip == true) return false; |
| 598 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; | 621 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test"; |
| 599 | } | 622 | } |
| 600 | 623 | ||
| 601 | pub fn linkLibC(self: *CompileStep) void { | 624 | pub fn linkLibC(self: *CompileStep) void { |
| ... | @@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void { | ... | @@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void { |
| 609 | /// If the value is omitted, it is set to 1. | 632 | /// If the value is omitted, it is set to 1. |
| 610 | /// `name` and `value` need not live longer than the function call. | 633 | /// `name` and `value` need not live longer than the function call. |
| 611 | pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { | 634 | pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { |
| 612 | const macro = std.Build.constructCMacro(self.builder.allocator, name, value); | 635 | const b = self.step.owner; |
| 636 | const macro = std.Build.constructCMacro(b.allocator, name, value); | ||
| 613 | self.c_macros.append(macro) catch @panic("OOM"); | 637 | self.c_macros.append(macro) catch @panic("OOM"); |
| 614 | } | 638 | } |
| 615 | 639 | ||
| 616 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | 640 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. |
| 617 | pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { | 641 | pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { |
| 618 | self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); | 642 | const b = self.step.owner; |
| 643 | self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM"); | ||
| 619 | } | 644 | } |
| 620 | 645 | ||
| 621 | /// This one has no integration with anything, it just puts -lname on the command line. | 646 | /// This one has no integration with anything, it just puts -lname on the command line. |
| 622 | /// Prefer to use `linkSystemLibrary` instead. | 647 | /// Prefer to use `linkSystemLibrary` instead. |
| 623 | pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { | 648 | pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { |
| 649 | const b = self.step.owner; | ||
| 624 | self.link_objects.append(.{ | 650 | self.link_objects.append(.{ |
| 625 | .system_lib = .{ | 651 | .system_lib = .{ |
| 626 | .name = self.builder.dupe(name), | 652 | .name = b.dupe(name), |
| 627 | .needed = false, | 653 | .needed = false, |
| 628 | .weak = false, | 654 | .weak = false, |
| 629 | .use_pkg_config = .no, | 655 | .use_pkg_config = .no, |
| ... | @@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { | ... | @@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { |
| 634 | /// This one has no integration with anything, it just puts -needed-lname on the command line. | 660 | /// This one has no integration with anything, it just puts -needed-lname on the command line. |
| 635 | /// Prefer to use `linkSystemLibraryNeeded` instead. | 661 | /// Prefer to use `linkSystemLibraryNeeded` instead. |
| 636 | pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { | 662 | pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { |
| 663 | const b = self.step.owner; | ||
| 637 | self.link_objects.append(.{ | 664 | self.link_objects.append(.{ |
| 638 | .system_lib = .{ | 665 | .system_lib = .{ |
| 639 | .name = self.builder.dupe(name), | 666 | .name = b.dupe(name), |
| 640 | .needed = true, | 667 | .needed = true, |
| 641 | .weak = false, | 668 | .weak = false, |
| 642 | .use_pkg_config = .no, | 669 | .use_pkg_config = .no, |
| ... | @@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { | ... | @@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { |
| 647 | /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the | 674 | /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the |
| 648 | /// command line. Prefer to use `linkSystemLibraryWeak` instead. | 675 | /// command line. Prefer to use `linkSystemLibraryWeak` instead. |
| 649 | pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { | 676 | pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { |
| 677 | const b = self.step.owner; | ||
| 650 | self.link_objects.append(.{ | 678 | self.link_objects.append(.{ |
| 651 | .system_lib = .{ | 679 | .system_lib = .{ |
| 652 | .name = self.builder.dupe(name), | 680 | .name = b.dupe(name), |
| 653 | .needed = false, | 681 | .needed = false, |
| 654 | .weak = true, | 682 | .weak = true, |
| 655 | .use_pkg_config = .no, | 683 | .use_pkg_config = .no, |
| ... | @@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { | ... | @@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { |
| 660 | /// This links against a system library, exclusively using pkg-config to find the library. | 688 | /// This links against a system library, exclusively using pkg-config to find the library. |
| 661 | /// Prefer to use `linkSystemLibrary` instead. | 689 | /// Prefer to use `linkSystemLibrary` instead. |
| 662 | pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { | 690 | pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { |
| 691 | const b = self.step.owner; | ||
| 663 | self.link_objects.append(.{ | 692 | self.link_objects.append(.{ |
| 664 | .system_lib = .{ | 693 | .system_lib = .{ |
| 665 | .name = self.builder.dupe(lib_name), | 694 | .name = b.dupe(lib_name), |
| 666 | .needed = false, | 695 | .needed = false, |
| 667 | .weak = false, | 696 | .weak = false, |
| 668 | .use_pkg_config = .force, | 697 | .use_pkg_config = .force, |
| ... | @@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) | ... | @@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) |
| 673 | /// This links against a system library, exclusively using pkg-config to find the library. | 702 | /// This links against a system library, exclusively using pkg-config to find the library. |
| 674 | /// Prefer to use `linkSystemLibraryNeeded` instead. | 703 | /// Prefer to use `linkSystemLibraryNeeded` instead. |
| 675 | pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { | 704 | pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { |
| 705 | const b = self.step.owner; | ||
| 676 | self.link_objects.append(.{ | 706 | self.link_objects.append(.{ |
| 677 | .system_lib = .{ | 707 | .system_lib = .{ |
| 678 | .name = self.builder.dupe(lib_name), | 708 | .name = b.dupe(lib_name), |
| 679 | .needed = true, | 709 | .needed = true, |
| 680 | .weak = false, | 710 | .weak = false, |
| 681 | .use_pkg_config = .force, | 711 | .use_pkg_config = .force, |
| ... | @@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons | ... | @@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons |
| 685 | 715 | ||
| 686 | /// Run pkg-config for the given library name and parse the output, returning the arguments | 716 | /// Run pkg-config for the given library name and parse the output, returning the arguments |
| 687 | /// that should be passed to zig to link the given library. | 717 | /// that should be passed to zig to link the given library. |
| 688 | pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { | 718 | fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { |
| 719 | const b = self.step.owner; | ||
| 689 | const pkg_name = match: { | 720 | const pkg_name = match: { |
| 690 | // First we have to map the library name to pkg config name. Unfortunately, | 721 | // First we have to map the library name to pkg config name. Unfortunately, |
| 691 | // there are several examples where this is not straightforward: | 722 | // there are several examples where this is not straightforward: |
| 692 | // -lSDL2 -> pkg-config sdl2 | 723 | // -lSDL2 -> pkg-config sdl2 |
| 693 | // -lgdk-3 -> pkg-config gdk-3.0 | 724 | // -lgdk-3 -> pkg-config gdk-3.0 |
| 694 | // -latk-1.0 -> pkg-config atk | 725 | // -latk-1.0 -> pkg-config atk |
| 695 | const pkgs = try getPkgConfigList(self.builder); | 726 | const pkgs = try getPkgConfigList(b); |
| 696 | 727 | ||
| 697 | // Exact match means instant winner. | 728 | // Exact match means instant winner. |
| 698 | for (pkgs) |pkg| { | 729 | for (pkgs) |pkg| { |
| ... | @@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u | ... | @@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 732 | }; | 763 | }; |
| 733 | 764 | ||
| 734 | var code: u8 = undefined; | 765 | var code: u8 = undefined; |
| 735 | const stdout = if (self.builder.execAllowFail(&[_][]const u8{ | 766 | const stdout = if (b.execAllowFail(&[_][]const u8{ |
| 736 | "pkg-config", | 767 | "pkg-config", |
| 737 | pkg_name, | 768 | pkg_name, |
| 738 | "--cflags", | 769 | "--cflags", |
| ... | @@ -742,11 +773,10 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u | ... | @@ -742,11 +773,10 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 742 | error.ExecNotSupported => return error.PkgConfigFailed, | 773 | error.ExecNotSupported => return error.PkgConfigFailed, |
| 743 | error.ExitCodeFailure => return error.PkgConfigFailed, | 774 | error.ExitCodeFailure => return error.PkgConfigFailed, |
| 744 | error.FileNotFound => return error.PkgConfigNotInstalled, | 775 | error.FileNotFound => return error.PkgConfigNotInstalled, |
| 745 | error.ChildExecFailed => return error.PkgConfigFailed, | ||
| 746 | else => return err, | 776 | else => return err, |
| 747 | }; | 777 | }; |
| 748 | 778 | ||
| 749 | var zig_args = ArrayList([]const u8).init(self.builder.allocator); | 779 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 750 | defer zig_args.deinit(); | 780 | defer zig_args.deinit(); |
| 751 | 781 | ||
| 752 | var it = mem.tokenize(u8, stdout, " \r\n\t"); | 782 | var it = mem.tokenize(u8, stdout, " \r\n\t"); |
| ... | @@ -771,8 +801,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u | ... | @@ -771,8 +801,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 771 | try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); | 801 | try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); |
| 772 | } else if (mem.startsWith(u8, tok, "-D")) { | 802 | } else if (mem.startsWith(u8, tok, "-D")) { |
| 773 | try zig_args.append(tok); | 803 | try zig_args.append(tok); |
| 774 | } else if (self.builder.verbose) { | 804 | } else if (b.debug_pkg_config) { |
| 775 | log.warn("Ignoring pkg-config flag '{s}'", .{tok}); | 805 | return self.step.fail("unknown pkg-config flag '{s}'", .{tok}); |
| 776 | } | 806 | } |
| 777 | } | 807 | } |
| 778 | 808 | ||
| ... | @@ -795,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | ... | @@ -795,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 795 | needed: bool = false, | 825 | needed: bool = false, |
| 796 | weak: bool = false, | 826 | weak: bool = false, |
| 797 | }) void { | 827 | }) void { |
| 828 | const b = self.step.owner; | ||
| 798 | if (isLibCLibrary(name)) { | 829 | if (isLibCLibrary(name)) { |
| 799 | self.linkLibC(); | 830 | self.linkLibC(); |
| 800 | return; | 831 | return; |
| ... | @@ -806,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | ... | @@ -806,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 806 | 837 | ||
| 807 | self.link_objects.append(.{ | 838 | self.link_objects.append(.{ |
| 808 | .system_lib = .{ | 839 | .system_lib = .{ |
| 809 | .name = self.builder.dupe(name), | 840 | .name = b.dupe(name), |
| 810 | .needed = opts.needed, | 841 | .needed = opts.needed, |
| 811 | .weak = opts.weak, | 842 | .weak = opts.weak, |
| 812 | .use_pkg_config = .yes, | 843 | .use_pkg_config = .yes, |
| ... | @@ -814,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | ... | @@ -814,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 814 | }) catch @panic("OOM"); | 845 | }) catch @panic("OOM"); |
| 815 | } | 846 | } |
| 816 | 847 | ||
| 817 | pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { | 848 | pub fn setName(self: *CompileStep, text: []const u8) void { |
| 818 | assert(self.kind == .@"test" or self.kind == .test_exe); | 849 | const b = self.step.owner; |
| 819 | self.name_prefix = self.builder.dupe(text); | 850 | assert(self.kind == .@"test"); |
| 851 | self.name = b.dupe(text); | ||
| 820 | } | 852 | } |
| 821 | 853 | ||
| 822 | pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { | 854 | pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { |
| 823 | assert(self.kind == .@"test" or self.kind == .test_exe); | 855 | const b = self.step.owner; |
| 824 | self.filter = if (text) |t| self.builder.dupe(t) else null; | 856 | assert(self.kind == .@"test"); |
| 857 | self.filter = if (text) |t| b.dupe(t) else null; | ||
| 825 | } | 858 | } |
| 826 | 859 | ||
| 827 | pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { | 860 | pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { |
| 828 | assert(self.kind == .@"test" or self.kind == .test_exe); | 861 | const b = self.step.owner; |
| 829 | self.test_runner = if (path) |p| self.builder.dupePath(p) else null; | 862 | assert(self.kind == .@"test"); |
| 863 | self.test_runner = if (path) |p| b.dupePath(p) else null; | ||
| 830 | } | 864 | } |
| 831 | 865 | ||
| 832 | /// Handy when you have many C/C++ source files and want them all to have the same flags. | 866 | /// Handy when you have many C/C++ source files and want them all to have the same flags. |
| 833 | pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { | 867 | pub 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"); | ||
| 835 | 870 | ||
| 836 | const files_copy = self.builder.dupeStrings(files); | 871 | const files_copy = b.dupeStrings(files); |
| 837 | const flags_copy = self.builder.dupeStrings(flags); | 872 | const flags_copy = b.dupeStrings(flags); |
| 838 | 873 | ||
| 839 | c_source_files.* = .{ | 874 | c_source_files.* = .{ |
| 840 | .files = files_copy, | 875 | .files = files_copy, |
| ... | @@ -851,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con | ... | @@ -851,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con |
| 851 | } | 886 | } |
| 852 | 887 | ||
| 853 | pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { | 888 | pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { |
| 854 | const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM"); | 889 | const b = self.step.owner; |
| 855 | c_source_file.* = source.dupe(self.builder); | 890 | const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM"); |
| 891 | c_source_file.* = source.dupe(b); | ||
| 856 | self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); | 892 | self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); |
| 857 | source.source.addStepDependencies(&self.step); | 893 | source.source.addStepDependencies(&self.step); |
| 858 | } | 894 | } |
| ... | @@ -866,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { | ... | @@ -866,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { |
| 866 | } | 902 | } |
| 867 | 903 | ||
| 868 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { | 904 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { |
| 869 | self.zig_lib_dir = self.builder.dupePath(dir_path); | 905 | const b = self.step.owner; |
| 906 | self.zig_lib_dir = b.dupePath(dir_path); | ||
| 870 | } | 907 | } |
| 871 | 908 | ||
| 872 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { | 909 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { |
| 873 | self.main_pkg_path = self.builder.dupePath(dir_path); | 910 | const b = self.step.owner; |
| 911 | self.main_pkg_path = b.dupePath(dir_path); | ||
| 874 | } | 912 | } |
| 875 | 913 | ||
| 876 | pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { | 914 | pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { |
| 877 | self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; | 915 | const b = self.step.owner; |
| 916 | self.libc_file = if (libc_file) |f| f.dupe(b) else null; | ||
| 878 | } | 917 | } |
| 879 | 918 | ||
| 880 | /// Returns the generated executable, library or object file. | 919 | /// Returns the generated executable, library or object file. |
| 881 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | 920 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. |
| 882 | pub fn getOutputSource(self: *CompileStep) FileSource { | 921 | pub fn getOutputSource(self: *CompileStep) FileSource { |
| 883 | return FileSource{ .generated = &self.output_path_source }; | 922 | return .{ .generated = &self.output_path_source }; |
| 923 | } | ||
| 924 | |||
| 925 | pub fn getOutputDirectorySource(self: *CompileStep) FileSource { | ||
| 926 | return .{ .generated = &self.output_dirname_source }; | ||
| 884 | } | 927 | } |
| 885 | 928 | ||
| 886 | /// Returns the generated import library. This function can only be called for libraries. | 929 | /// Returns the generated import library. This function can only be called for libraries. |
| 887 | pub fn getOutputLibSource(self: *CompileStep) FileSource { | 930 | pub fn getOutputLibSource(self: *CompileStep) FileSource { |
| 888 | assert(self.kind == .lib); | 931 | assert(self.kind == .lib); |
| 889 | return FileSource{ .generated = &self.output_lib_path_source }; | 932 | return .{ .generated = &self.output_lib_path_source }; |
| 890 | } | 933 | } |
| 891 | 934 | ||
| 892 | /// Returns the generated header file. | 935 | /// Returns the generated header file. |
| 893 | /// This function can only be called for libraries or object files which have `emit_h` set. | 936 | /// This function can only be called for libraries or object files which have `emit_h` set. |
| 894 | pub fn getOutputHSource(self: *CompileStep) FileSource { | 937 | pub fn getOutputHSource(self: *CompileStep) FileSource { |
| 895 | assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); | 938 | assert(self.kind != .exe and self.kind != .@"test"); |
| 896 | assert(self.emit_h); | 939 | assert(self.emit_h); |
| 897 | return FileSource{ .generated = &self.output_h_path_source }; | 940 | return .{ .generated = &self.output_h_path_source }; |
| 898 | } | 941 | } |
| 899 | 942 | ||
| 900 | /// Returns the generated PDB file. This function can only be called for Windows and UEFI. | 943 | /// Returns the generated PDB file. This function can only be called for Windows and UEFI. |
| 901 | pub fn getOutputPdbSource(self: *CompileStep) FileSource { | 944 | pub fn getOutputPdbSource(self: *CompileStep) FileSource { |
| 902 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? | 945 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? |
| 903 | assert(self.target.isWindows() or self.target.isUefi()); | 946 | assert(self.target.isWindows() or self.target.isUefi()); |
| 904 | return FileSource{ .generated = &self.output_pdb_path_source }; | 947 | return .{ .generated = &self.output_pdb_path_source }; |
| 905 | } | 948 | } |
| 906 | 949 | ||
| 907 | pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { | 950 | pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { |
| 951 | const b = self.step.owner; | ||
| 908 | self.link_objects.append(.{ | 952 | self.link_objects.append(.{ |
| 909 | .assembly_file = .{ .path = self.builder.dupe(path) }, | 953 | .assembly_file = .{ .path = b.dupe(path) }, |
| 910 | }) catch @panic("OOM"); | 954 | }) catch @panic("OOM"); |
| 911 | } | 955 | } |
| 912 | 956 | ||
| 913 | pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { | 957 | pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { |
| 914 | const source_duped = source.dupe(self.builder); | 958 | const b = self.step.owner; |
| 959 | const source_duped = source.dupe(b); | ||
| 915 | self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); | 960 | self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); |
| 916 | source_duped.addStepDependencies(&self.step); | 961 | source_duped.addStepDependencies(&self.step); |
| 917 | } | 962 | } |
| ... | @@ -921,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { | ... | @@ -921,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { |
| 921 | } | 966 | } |
| 922 | 967 | ||
| 923 | pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { | 968 | pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { |
| 924 | self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM"); | 969 | const b = self.step.owner; |
| 970 | self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM"); | ||
| 925 | source.addStepDependencies(&self.step); | 971 | source.addStepDependencies(&self.step); |
| 926 | } | 972 | } |
| 927 | 973 | ||
| ... | @@ -936,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath"); | ... | @@ -936,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath"); |
| 936 | pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); | 982 | pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); |
| 937 | 983 | ||
| 938 | pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { | 984 | pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { |
| 939 | self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM"); | 985 | const b = self.step.owner; |
| 986 | self.include_dirs.append(IncludeDir{ .raw_path_system = b.dupe(path) }) catch @panic("OOM"); | ||
| 940 | } | 987 | } |
| 941 | 988 | ||
| 942 | pub fn addIncludePath(self: *CompileStep, path: []const u8) void { | 989 | pub fn addIncludePath(self: *CompileStep, path: []const u8) void { |
| 943 | self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM"); | 990 | const b = self.step.owner; |
| 991 | self.include_dirs.append(IncludeDir{ .raw_path = b.dupe(path) }) catch @panic("OOM"); | ||
| 944 | } | 992 | } |
| 945 | 993 | ||
| 946 | pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { | 994 | pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { |
| ... | @@ -949,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi | ... | @@ -949,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi |
| 949 | } | 997 | } |
| 950 | 998 | ||
| 951 | pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { | 999 | pub 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 | |||
| 1004 | pub fn addLibraryPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { | ||
| 1005 | self.lib_paths.append(directory_source) catch @panic("OOM"); | ||
| 1006 | directory_source.addStepDependencies(&self.step); | ||
| 953 | } | 1007 | } |
| 954 | 1008 | ||
| 955 | pub fn addRPath(self: *CompileStep, path: []const u8) void { | 1009 | pub 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 | |||
| 1014 | pub fn addRPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { | ||
| 1015 | self.rpaths.append(directory_source) catch @panic("OOM"); | ||
| 1016 | directory_source.addStepDependencies(&self.step); | ||
| 957 | } | 1017 | } |
| 958 | 1018 | ||
| 959 | pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { | 1019 | pub 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 | |||
| 1024 | pub fn addFrameworkPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { | ||
| 1025 | self.framework_dirs.append(directory_source) catch @panic("OOM"); | ||
| 1026 | directory_source.addStepDependencies(&self.step); | ||
| 961 | } | 1027 | } |
| 962 | 1028 | ||
| 963 | /// Adds a module to be used with `@import` and exposing it in the current | 1029 | /// Adds a module to be used with `@import` and exposing it in the current |
| 964 | /// package's module table using `name`. | 1030 | /// package's module table using `name`. |
| 965 | pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { | 1031 | pub 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"); | ||
| 967 | 1034 | ||
| 968 | var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator); | 1035 | var done = std.AutoHashMap(*Module, void).init(b.allocator); |
| 969 | defer done.deinit(); | 1036 | defer done.deinit(); |
| 970 | cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); | 1037 | cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); |
| 971 | } | 1038 | } |
| ... | @@ -973,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { | ... | @@ -973,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { |
| 973 | /// Adds a module to be used with `@import` without exposing it in the current | 1040 | /// Adds a module to be used with `@import` without exposing it in the current |
| 974 | /// package's module table. | 1041 | /// package's module table. |
| 975 | pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void { | 1042 | pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void { |
| 976 | const module = cs.builder.createModule(options); | 1043 | const b = cs.step.owner; |
| 1044 | const module = b.createModule(options); | ||
| 977 | return addModule(cs, name, module); | 1045 | return addModule(cs, name, module); |
| 978 | } | 1046 | } |
| 979 | 1047 | ||
| ... | @@ -993,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM | ... | @@ -993,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM |
| 993 | /// If Vcpkg was found on the system, it will be added to include and lib | 1061 | /// If Vcpkg was found on the system, it will be added to include and lib |
| 994 | /// paths for the specified target. | 1062 | /// paths for the specified target. |
| 995 | pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { | 1063 | pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { |
| 1064 | const b = self.step.owner; | ||
| 996 | // Ideally in the Unattempted case we would call the function recursively | 1065 | // Ideally in the Unattempted case we would call the function recursively |
| 997 | // after findVcpkgRoot and have only one switch statement, but the compiler | 1066 | // after findVcpkgRoot and have only one switch statement, but the compiler |
| 998 | // cannot resolve the error set. | 1067 | // cannot resolve the error set. |
| 999 | switch (self.builder.vcpkg_root) { | 1068 | switch (b.vcpkg_root) { |
| 1000 | .unattempted => { | 1069 | .unattempted => { |
| 1001 | self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| | 1070 | b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root| |
| 1002 | VcpkgRoot{ .found = root } | 1071 | VcpkgRoot{ .found = root } |
| 1003 | else | 1072 | else |
| 1004 | .not_found; | 1073 | .not_found; |
| ... | @@ -1007,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { | ... | @@ -1007,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { |
| 1007 | .found => {}, | 1076 | .found => {}, |
| 1008 | } | 1077 | } |
| 1009 | 1078 | ||
| 1010 | switch (self.builder.vcpkg_root) { | 1079 | switch (b.vcpkg_root) { |
| 1011 | .unattempted => unreachable, | 1080 | .unattempted => unreachable, |
| 1012 | .not_found => return error.VcpkgNotFound, | 1081 | .not_found => return error.VcpkgNotFound, |
| 1013 | .found => |root| { | 1082 | .found => |root| { |
| 1014 | const allocator = self.builder.allocator; | 1083 | const allocator = b.allocator; |
| 1015 | const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); | 1084 | const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); |
| 1016 | defer self.builder.allocator.free(triplet); | 1085 | defer b.allocator.free(triplet); |
| 1017 | 1086 | ||
| 1018 | const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); | 1087 | const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" }); |
| 1019 | errdefer allocator.free(include_path); | 1088 | errdefer allocator.free(include_path); |
| 1020 | try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); | 1089 | try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); |
| 1021 | 1090 | ||
| 1022 | const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); | 1091 | const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" }); |
| 1023 | try self.lib_paths.append(lib_path); | 1092 | try self.lib_paths.append(.{ .path = lib_path }); |
| 1024 | 1093 | ||
| 1025 | self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); | 1094 | self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" }); |
| 1026 | }, | 1095 | }, |
| 1027 | } | 1096 | } |
| 1028 | } | 1097 | } |
| 1029 | 1098 | ||
| 1030 | pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { | 1099 | pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { |
| 1100 | const b = self.step.owner; | ||
| 1031 | assert(self.kind == .@"test"); | 1101 | assert(self.kind == .@"test"); |
| 1032 | const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); | 1102 | const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); |
| 1033 | for (args, 0..) |arg, i| { | 1103 | for (args, 0..) |arg, i| { |
| 1034 | duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; | 1104 | duped_args[i] = if (arg) |a| b.dupe(a) else null; |
| 1035 | } | 1105 | } |
| 1036 | self.exec_cmd_args = duped_args; | 1106 | self.exec_cmd_args = duped_args; |
| 1037 | } | 1107 | } |
| ... | @@ -1040,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { | ... | @@ -1040,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { |
| 1040 | self.step.dependOn(&other.step); | 1110 | self.step.dependOn(&other.step); |
| 1041 | self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); | 1111 | self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); |
| 1042 | self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); | 1112 | self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); |
| 1113 | |||
| 1114 | for (other.installed_headers.items) |install_step| { | ||
| 1115 | self.step.dependOn(install_step); | ||
| 1116 | } | ||
| 1043 | } | 1117 | } |
| 1044 | 1118 | ||
| 1045 | fn appendModuleArgs( | 1119 | fn appendModuleArgs( |
| 1046 | cs: *CompileStep, | 1120 | cs: *CompileStep, |
| 1047 | zig_args: *ArrayList([]const u8), | 1121 | zig_args: *ArrayList([]const u8), |
| 1048 | ) error{OutOfMemory}!void { | 1122 | ) error{OutOfMemory}!void { |
| 1123 | const b = cs.step.owner; | ||
| 1049 | // First, traverse the whole dependency graph and give every module a unique name, ideally one | 1124 | // First, traverse the whole dependency graph and give every module a unique name, ideally one |
| 1050 | // named after what it's called somewhere in the graph. It will help here to have both a mapping | 1125 | // named after what it's called somewhere in the graph. It will help here to have both a mapping |
| 1051 | // from module to name and a set of all the currently-used names. | 1126 | // from module to name and a set of all the currently-used names. |
| 1052 | var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator); | 1127 | var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator); |
| 1053 | var names = std.StringHashMap(void).init(cs.builder.allocator); | 1128 | var names = std.StringHashMap(void).init(b.allocator); |
| 1054 | 1129 | ||
| 1055 | var to_name = std.ArrayList(struct { | 1130 | var to_name = std.ArrayList(struct { |
| 1056 | name: []const u8, | 1131 | name: []const u8, |
| 1057 | mod: *Module, | 1132 | mod: *Module, |
| 1058 | }).init(cs.builder.allocator); | 1133 | }).init(b.allocator); |
| 1059 | { | 1134 | { |
| 1060 | var it = cs.modules.iterator(); | 1135 | var it = cs.modules.iterator(); |
| 1061 | while (it.next()) |kv| { | 1136 | while (it.next()) |kv| { |
| ... | @@ -1076,7 +1151,7 @@ fn appendModuleArgs( | ... | @@ -1076,7 +1151,7 @@ fn appendModuleArgs( |
| 1076 | if (mod_names.contains(dep.mod)) continue; | 1151 | if (mod_names.contains(dep.mod)) continue; |
| 1077 | 1152 | ||
| 1078 | // We'll use this buffer to store the name we decide on | 1153 | // We'll use this buffer to store the name we decide on |
| 1079 | var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32); | 1154 | var buf = try b.allocator.alloc(u8, dep.name.len + 32); |
| 1080 | // First, try just the exposed dependency name | 1155 | // First, try just the exposed dependency name |
| 1081 | std.mem.copy(u8, buf, dep.name); | 1156 | std.mem.copy(u8, buf, dep.name); |
| 1082 | var name = buf[0..dep.name.len]; | 1157 | var name = buf[0..dep.name.len]; |
| ... | @@ -1113,15 +1188,15 @@ fn appendModuleArgs( | ... | @@ -1113,15 +1188,15 @@ fn appendModuleArgs( |
| 1113 | const mod = kv.key_ptr.*; | 1188 | const mod = kv.key_ptr.*; |
| 1114 | const name = kv.value_ptr.*; | 1189 | const name = kv.value_ptr.*; |
| 1115 | 1190 | ||
| 1116 | const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies); | 1191 | const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies); |
| 1117 | const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); | 1192 | const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); |
| 1118 | try zig_args.append("--mod"); | 1193 | try zig_args.append("--mod"); |
| 1119 | try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); | 1194 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); |
| 1120 | } | 1195 | } |
| 1121 | } | 1196 | } |
| 1122 | 1197 | ||
| 1123 | // Lastly, output the root dependencies | 1198 | // Lastly, output the root dependencies |
| 1124 | const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules); | 1199 | const deps_str = try constructDepString(b.allocator, mod_names, cs.modules); |
| 1125 | if (deps_str.len > 0) { | 1200 | if (deps_str.len > 0) { |
| 1126 | try zig_args.append("--deps"); | 1201 | try zig_args.append("--deps"); |
| 1127 | try zig_args.append(deps_str); | 1202 | try zig_args.append(deps_str); |
| ... | @@ -1151,43 +1226,36 @@ fn constructDepString( | ... | @@ -1151,43 +1226,36 @@ fn constructDepString( |
| 1151 | } | 1226 | } |
| 1152 | } | 1227 | } |
| 1153 | 1228 | ||
| 1154 | fn make(step: *Step) !void { | 1229 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 1230 | const b = step.owner; | ||
| 1155 | const self = @fieldParentPtr(CompileStep, "step", step); | 1231 | const self = @fieldParentPtr(CompileStep, "step", step); |
| 1156 | const builder = self.builder; | ||
| 1157 | 1232 | ||
| 1158 | if (self.root_src == null and self.link_objects.items.len == 0) { | 1233 | if (self.root_src == null and self.link_objects.items.len == 0) { |
| 1159 | log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); | 1234 | return step.fail("the linker needs one or more objects to link", .{}); |
| 1160 | return error.NeedAnObject; | ||
| 1161 | } | 1235 | } |
| 1162 | 1236 | ||
| 1163 | var zig_args = ArrayList([]const u8).init(builder.allocator); | 1237 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 1164 | defer zig_args.deinit(); | 1238 | defer zig_args.deinit(); |
| 1165 | 1239 | ||
| 1166 | try zig_args.append(builder.zig_exe); | 1240 | try zig_args.append(b.zig_exe); |
| 1167 | 1241 | ||
| 1168 | const cmd = switch (self.kind) { | 1242 | const cmd = switch (self.kind) { |
| 1169 | .lib => "build-lib", | 1243 | .lib => "build-lib", |
| 1170 | .exe => "build-exe", | 1244 | .exe => "build-exe", |
| 1171 | .obj => "build-obj", | 1245 | .obj => "build-obj", |
| 1172 | .@"test" => "test", | 1246 | .@"test" => "test", |
| 1173 | .test_exe => "test", | ||
| 1174 | }; | 1247 | }; |
| 1175 | try zig_args.append(cmd); | 1248 | try zig_args.append(cmd); |
| 1176 | 1249 | ||
| 1177 | if (builder.color != .auto) { | 1250 | if (b.reference_trace) |some| { |
| 1178 | try zig_args.append("--color"); | 1251 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some})); |
| 1179 | try zig_args.append(@tagName(builder.color)); | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | if (builder.reference_trace) |some| { | ||
| 1183 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); | ||
| 1184 | } | 1252 | } |
| 1185 | 1253 | ||
| 1186 | try addFlag(&zig_args, "LLVM", self.use_llvm); | 1254 | try addFlag(&zig_args, "LLVM", self.use_llvm); |
| 1187 | try addFlag(&zig_args, "LLD", self.use_lld); | 1255 | try addFlag(&zig_args, "LLD", self.use_lld); |
| 1188 | 1256 | ||
| 1189 | if (self.target.ofmt) |ofmt| { | 1257 | if (self.target.ofmt) |ofmt| { |
| 1190 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); | 1258 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); |
| 1191 | } | 1259 | } |
| 1192 | 1260 | ||
| 1193 | if (self.entry_symbol_name) |entry| { | 1261 | if (self.entry_symbol_name) |entry| { |
| ... | @@ -1197,18 +1265,18 @@ fn make(step: *Step) !void { | ... | @@ -1197,18 +1265,18 @@ fn make(step: *Step) !void { |
| 1197 | 1265 | ||
| 1198 | if (self.stack_size) |stack_size| { | 1266 | if (self.stack_size) |stack_size| { |
| 1199 | try zig_args.append("--stack"); | 1267 | try zig_args.append("--stack"); |
| 1200 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); | 1268 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size})); |
| 1201 | } | 1269 | } |
| 1202 | 1270 | ||
| 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)); |
| 1204 | 1272 | ||
| 1205 | // We will add link objects from transitive dependencies, but we want to keep | 1273 | // We will add link objects from transitive dependencies, but we want to keep |
| 1206 | // all link objects in the same order provided. | 1274 | // all link objects in the same order provided. |
| 1207 | // This array is used to keep self.link_objects immutable. | 1275 | // This array is used to keep self.link_objects immutable. |
| 1208 | var transitive_deps: TransitiveDeps = .{ | 1276 | var transitive_deps: TransitiveDeps = .{ |
| 1209 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | 1277 | .link_objects = ArrayList(LinkObject).init(b.allocator), |
| 1210 | .seen_system_libs = StringHashMap(void).init(builder.allocator), | 1278 | .seen_system_libs = StringHashMap(void).init(b.allocator), |
| 1211 | .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), | 1279 | .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator), |
| 1212 | .is_linking_libcpp = self.is_linking_libcpp, | 1280 | .is_linking_libcpp = self.is_linking_libcpp, |
| 1213 | .is_linking_libc = self.is_linking_libc, | 1281 | .is_linking_libc = self.is_linking_libc, |
| 1214 | .frameworks = &self.frameworks, | 1282 | .frameworks = &self.frameworks, |
| ... | @@ -1221,14 +1289,13 @@ fn make(step: *Step) !void { | ... | @@ -1221,14 +1289,13 @@ fn make(step: *Step) !void { |
| 1221 | 1289 | ||
| 1222 | for (transitive_deps.link_objects.items) |link_object| { | 1290 | for (transitive_deps.link_objects.items) |link_object| { |
| 1223 | switch (link_object) { | 1291 | switch (link_object) { |
| 1224 | .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), | 1292 | .static_path => |static_path| try zig_args.append(static_path.getPath(b)), |
| 1225 | 1293 | ||
| 1226 | .other_step => |other| switch (other.kind) { | 1294 | .other_step => |other| switch (other.kind) { |
| 1227 | .exe => @panic("Cannot link with an executable build artifact"), | 1295 | .exe => @panic("Cannot link with an executable build artifact"), |
| 1228 | .test_exe => @panic("Cannot link with an executable build artifact"), | ||
| 1229 | .@"test" => @panic("Cannot link with a test"), | 1296 | .@"test" => @panic("Cannot link with a test"), |
| 1230 | .obj => { | 1297 | .obj => { |
| 1231 | try zig_args.append(other.getOutputSource().getPath(builder)); | 1298 | try zig_args.append(other.getOutputSource().getPath(b)); |
| 1232 | }, | 1299 | }, |
| 1233 | .lib => l: { | 1300 | .lib => l: { |
| 1234 | if (self.isStaticLibrary() and other.isStaticLibrary()) { | 1301 | if (self.isStaticLibrary() and other.isStaticLibrary()) { |
| ... | @@ -1236,7 +1303,7 @@ fn make(step: *Step) !void { | ... | @@ -1236,7 +1303,7 @@ fn make(step: *Step) !void { |
| 1236 | break :l; | 1303 | break :l; |
| 1237 | } | 1304 | } |
| 1238 | 1305 | ||
| 1239 | const full_path_lib = other.getOutputLibSource().getPath(builder); | 1306 | const full_path_lib = other.getOutputLibSource().getPath(b); |
| 1240 | try zig_args.append(full_path_lib); | 1307 | try zig_args.append(full_path_lib); |
| 1241 | 1308 | ||
| 1242 | if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { | 1309 | if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { |
| ... | @@ -1251,14 +1318,11 @@ fn make(step: *Step) !void { | ... | @@ -1251,14 +1318,11 @@ fn make(step: *Step) !void { |
| 1251 | .system_lib => |system_lib| { | 1318 | .system_lib => |system_lib| { |
| 1252 | const prefix: []const u8 = prefix: { | 1319 | const prefix: []const u8 = prefix: { |
| 1253 | if (system_lib.needed) break :prefix "-needed-l"; | 1320 | if (system_lib.needed) break :prefix "-needed-l"; |
| 1254 | if (system_lib.weak) { | 1321 | if (system_lib.weak) break :prefix "-weak-l"; |
| 1255 | if (self.target.isDarwin()) break :prefix "-weak-l"; | ||
| 1256 | log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); | ||
| 1257 | } | ||
| 1258 | break :prefix "-l"; | 1322 | break :prefix "-l"; |
| 1259 | }; | 1323 | }; |
| 1260 | switch (system_lib.use_pkg_config) { | 1324 | switch (system_lib.use_pkg_config) { |
| 1261 | .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), | 1325 | .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), |
| 1262 | .yes, .force => { | 1326 | .yes, .force => { |
| 1263 | if (self.runPkgConfig(system_lib.name)) |args| { | 1327 | if (self.runPkgConfig(system_lib.name)) |args| { |
| 1264 | try zig_args.appendSlice(args); | 1328 | try zig_args.appendSlice(args); |
| ... | @@ -1272,7 +1336,7 @@ fn make(step: *Step) !void { | ... | @@ -1272,7 +1336,7 @@ fn make(step: *Step) !void { |
| 1272 | .yes => { | 1336 | .yes => { |
| 1273 | // pkg-config failed, so fall back to linking the library | 1337 | // pkg-config failed, so fall back to linking the library |
| 1274 | // by name directly. | 1338 | // by name directly. |
| 1275 | try zig_args.append(builder.fmt("{s}{s}", .{ | 1339 | try zig_args.append(b.fmt("{s}{s}", .{ |
| 1276 | prefix, | 1340 | prefix, |
| 1277 | system_lib.name, | 1341 | system_lib.name, |
| 1278 | })); | 1342 | })); |
| ... | @@ -1295,7 +1359,7 @@ fn make(step: *Step) !void { | ... | @@ -1295,7 +1359,7 @@ fn make(step: *Step) !void { |
| 1295 | try zig_args.append("--"); | 1359 | try zig_args.append("--"); |
| 1296 | prev_has_extra_flags = false; | 1360 | prev_has_extra_flags = false; |
| 1297 | } | 1361 | } |
| 1298 | try zig_args.append(asm_file.getPath(builder)); | 1362 | try zig_args.append(asm_file.getPath(b)); |
| 1299 | }, | 1363 | }, |
| 1300 | 1364 | ||
| 1301 | .c_source_file => |c_source_file| { | 1365 | .c_source_file => |c_source_file| { |
| ... | @@ -1312,7 +1376,7 @@ fn make(step: *Step) !void { | ... | @@ -1312,7 +1376,7 @@ fn make(step: *Step) !void { |
| 1312 | } | 1376 | } |
| 1313 | try zig_args.append("--"); | 1377 | try zig_args.append("--"); |
| 1314 | } | 1378 | } |
| 1315 | try zig_args.append(c_source_file.source.getPath(builder)); | 1379 | try zig_args.append(c_source_file.source.getPath(b)); |
| 1316 | }, | 1380 | }, |
| 1317 | 1381 | ||
| 1318 | .c_source_files => |c_source_files| { | 1382 | .c_source_files => |c_source_files| { |
| ... | @@ -1330,7 +1394,7 @@ fn make(step: *Step) !void { | ... | @@ -1330,7 +1394,7 @@ fn make(step: *Step) !void { |
| 1330 | try zig_args.append("--"); | 1394 | try zig_args.append("--"); |
| 1331 | } | 1395 | } |
| 1332 | for (c_source_files.files) |file| { | 1396 | for (c_source_files.files) |file| { |
| 1333 | try zig_args.append(builder.pathFromRoot(file)); | 1397 | try zig_args.append(b.pathFromRoot(file)); |
| 1334 | } | 1398 | } |
| 1335 | }, | 1399 | }, |
| 1336 | } | 1400 | } |
| ... | @@ -1346,7 +1410,7 @@ fn make(step: *Step) !void { | ... | @@ -1346,7 +1410,7 @@ fn make(step: *Step) !void { |
| 1346 | 1410 | ||
| 1347 | if (self.image_base) |image_base| { | 1411 | if (self.image_base) |image_base| { |
| 1348 | try zig_args.append("--image-base"); | 1412 | try zig_args.append("--image-base"); |
| 1349 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); | 1413 | try zig_args.append(b.fmt("0x{x}", .{image_base})); |
| 1350 | } | 1414 | } |
| 1351 | 1415 | ||
| 1352 | if (self.filter) |filter| { | 1416 | if (self.filter) |filter| { |
| ... | @@ -1358,39 +1422,34 @@ fn make(step: *Step) !void { | ... | @@ -1358,39 +1422,34 @@ fn make(step: *Step) !void { |
| 1358 | try zig_args.append("--test-evented-io"); | 1422 | try zig_args.append("--test-evented-io"); |
| 1359 | } | 1423 | } |
| 1360 | 1424 | ||
| 1361 | if (self.name_prefix.len != 0) { | ||
| 1362 | try zig_args.append("--test-name-prefix"); | ||
| 1363 | try zig_args.append(self.name_prefix); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | if (self.test_runner) |test_runner| { | 1425 | if (self.test_runner) |test_runner| { |
| 1367 | try zig_args.append("--test-runner"); | 1426 | try zig_args.append("--test-runner"); |
| 1368 | try zig_args.append(builder.pathFromRoot(test_runner)); | 1427 | try zig_args.append(b.pathFromRoot(test_runner)); |
| 1369 | } | 1428 | } |
| 1370 | 1429 | ||
| 1371 | for (builder.debug_log_scopes) |log_scope| { | 1430 | for (b.debug_log_scopes) |log_scope| { |
| 1372 | try zig_args.append("--debug-log"); | 1431 | try zig_args.append("--debug-log"); |
| 1373 | try zig_args.append(log_scope); | 1432 | try zig_args.append(log_scope); |
| 1374 | } | 1433 | } |
| 1375 | 1434 | ||
| 1376 | if (builder.debug_compile_errors) { | 1435 | if (b.debug_compile_errors) { |
| 1377 | try zig_args.append("--debug-compile-errors"); | 1436 | try zig_args.append("--debug-compile-errors"); |
| 1378 | } | 1437 | } |
| 1379 | 1438 | ||
| 1380 | if (builder.verbose_cimport) try zig_args.append("--verbose-cimport"); | 1439 | if (b.verbose_cimport) try zig_args.append("--verbose-cimport"); |
| 1381 | if (builder.verbose_air) try zig_args.append("--verbose-air"); | 1440 | if (b.verbose_air) try zig_args.append("--verbose-air"); |
| 1382 | if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); | 1441 | if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); |
| 1383 | if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); | 1442 | if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); |
| 1384 | if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); | 1443 | if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); |
| 1385 | if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); | 1444 | if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); |
| 1386 | 1445 | ||
| 1387 | if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); | 1446 | if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg); |
| 1388 | if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); | 1447 | if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg); |
| 1389 | if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); | 1448 | if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg); |
| 1390 | if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); | 1449 | if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg); |
| 1391 | if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); | 1450 | if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg); |
| 1392 | if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); | 1451 | if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg); |
| 1393 | if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); | 1452 | if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg); |
| 1394 | 1453 | ||
| 1395 | if (self.emit_h) try zig_args.append("-femit-h"); | 1454 | if (self.emit_h) try zig_args.append("-femit-h"); |
| 1396 | 1455 | ||
| ... | @@ -1431,31 +1490,31 @@ fn make(step: *Step) !void { | ... | @@ -1431,31 +1490,31 @@ fn make(step: *Step) !void { |
| 1431 | } | 1490 | } |
| 1432 | if (self.link_z_common_page_size) |size| { | 1491 | if (self.link_z_common_page_size) |size| { |
| 1433 | try zig_args.append("-z"); | 1492 | try zig_args.append("-z"); |
| 1434 | try zig_args.append(builder.fmt("common-page-size={d}", .{size})); | 1493 | try zig_args.append(b.fmt("common-page-size={d}", .{size})); |
| 1435 | } | 1494 | } |
| 1436 | if (self.link_z_max_page_size) |size| { | 1495 | if (self.link_z_max_page_size) |size| { |
| 1437 | try zig_args.append("-z"); | 1496 | try zig_args.append("-z"); |
| 1438 | try zig_args.append(builder.fmt("max-page-size={d}", .{size})); | 1497 | try zig_args.append(b.fmt("max-page-size={d}", .{size})); |
| 1439 | } | 1498 | } |
| 1440 | 1499 | ||
| 1441 | if (self.libc_file) |libc_file| { | 1500 | if (self.libc_file) |libc_file| { |
| 1442 | try zig_args.append("--libc"); | 1501 | try zig_args.append("--libc"); |
| 1443 | try zig_args.append(libc_file.getPath(builder)); | 1502 | try zig_args.append(libc_file.getPath(b)); |
| 1444 | } else if (builder.libc_file) |libc_file| { | 1503 | } else if (b.libc_file) |libc_file| { |
| 1445 | try zig_args.append("--libc"); | 1504 | try zig_args.append("--libc"); |
| 1446 | try zig_args.append(libc_file); | 1505 | try zig_args.append(libc_file); |
| 1447 | } | 1506 | } |
| 1448 | 1507 | ||
| 1449 | switch (self.optimize) { | 1508 | switch (self.optimize) { |
| 1450 | .Debug => {}, // Skip since it's the default. | 1509 | .Debug => {}, // Skip since it's the default. |
| 1451 | else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})), | 1510 | else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), |
| 1452 | } | 1511 | } |
| 1453 | 1512 | ||
| 1454 | try zig_args.append("--cache-dir"); | 1513 | try zig_args.append("--cache-dir"); |
| 1455 | try zig_args.append(builder.cache_root.path orelse "."); | 1514 | try zig_args.append(b.cache_root.path orelse "."); |
| 1456 | 1515 | ||
| 1457 | try zig_args.append("--global-cache-dir"); | 1516 | try zig_args.append("--global-cache-dir"); |
| 1458 | try zig_args.append(builder.global_cache_root.path orelse "."); | 1517 | try zig_args.append(b.global_cache_root.path orelse "."); |
| 1459 | 1518 | ||
| 1460 | try zig_args.append("--name"); | 1519 | try zig_args.append("--name"); |
| 1461 | try zig_args.append(self.name); | 1520 | try zig_args.append(self.name); |
| ... | @@ -1467,11 +1526,11 @@ fn make(step: *Step) !void { | ... | @@ -1467,11 +1526,11 @@ fn make(step: *Step) !void { |
| 1467 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { | 1526 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { |
| 1468 | if (self.version) |version| { | 1527 | if (self.version) |version| { |
| 1469 | try zig_args.append("--version"); | 1528 | try zig_args.append("--version"); |
| 1470 | try zig_args.append(builder.fmt("{}", .{version})); | 1529 | try zig_args.append(b.fmt("{}", .{version})); |
| 1471 | } | 1530 | } |
| 1472 | 1531 | ||
| 1473 | if (self.target.isDarwin()) { | 1532 | if (self.target.isDarwin()) { |
| 1474 | const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ | 1533 | const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{ |
| 1475 | self.target.libPrefix(), | 1534 | self.target.libPrefix(), |
| 1476 | self.name, | 1535 | self.name, |
| 1477 | self.target.dynamicLibSuffix(), | 1536 | self.target.dynamicLibSuffix(), |
| ... | @@ -1485,7 +1544,7 @@ fn make(step: *Step) !void { | ... | @@ -1485,7 +1544,7 @@ fn make(step: *Step) !void { |
| 1485 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); | 1544 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); |
| 1486 | } | 1545 | } |
| 1487 | if (self.pagezero_size) |pagezero_size| { | 1546 | if (self.pagezero_size) |pagezero_size| { |
| 1488 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); | 1547 | const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size}); |
| 1489 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); | 1548 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); |
| 1490 | } | 1549 | } |
| 1491 | if (self.search_strategy) |strat| switch (strat) { | 1550 | if (self.search_strategy) |strat| switch (strat) { |
| ... | @@ -1493,7 +1552,7 @@ fn make(step: *Step) !void { | ... | @@ -1493,7 +1552,7 @@ fn make(step: *Step) !void { |
| 1493 | .dylibs_first => try zig_args.append("-search_dylibs_first"), | 1552 | .dylibs_first => try zig_args.append("-search_dylibs_first"), |
| 1494 | }; | 1553 | }; |
| 1495 | if (self.headerpad_size) |headerpad_size| { | 1554 | if (self.headerpad_size) |headerpad_size| { |
| 1496 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); | 1555 | const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size}); |
| 1497 | try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); | 1556 | try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); |
| 1498 | } | 1557 | } |
| 1499 | if (self.headerpad_max_install_names) { | 1558 | if (self.headerpad_max_install_names) { |
| ... | @@ -1541,16 +1600,16 @@ fn make(step: *Step) !void { | ... | @@ -1541,16 +1600,16 @@ fn make(step: *Step) !void { |
| 1541 | try zig_args.append("--export-table"); | 1600 | try zig_args.append("--export-table"); |
| 1542 | } | 1601 | } |
| 1543 | if (self.initial_memory) |initial_memory| { | 1602 | if (self.initial_memory) |initial_memory| { |
| 1544 | try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); | 1603 | try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory})); |
| 1545 | } | 1604 | } |
| 1546 | if (self.max_memory) |max_memory| { | 1605 | if (self.max_memory) |max_memory| { |
| 1547 | try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); | 1606 | try zig_args.append(b.fmt("--max-memory={d}", .{max_memory})); |
| 1548 | } | 1607 | } |
| 1549 | if (self.shared_memory) { | 1608 | if (self.shared_memory) { |
| 1550 | try zig_args.append("--shared-memory"); | 1609 | try zig_args.append("--shared-memory"); |
| 1551 | } | 1610 | } |
| 1552 | if (self.global_base) |global_base| { | 1611 | if (self.global_base) |global_base| { |
| 1553 | try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); | 1612 | try zig_args.append(b.fmt("--global-base={d}", .{global_base})); |
| 1554 | } | 1613 | } |
| 1555 | 1614 | ||
| 1556 | if (self.code_model != .default) { | 1615 | if (self.code_model != .default) { |
| ... | @@ -1558,16 +1617,16 @@ fn make(step: *Step) !void { | ... | @@ -1558,16 +1617,16 @@ fn make(step: *Step) !void { |
| 1558 | try zig_args.append(@tagName(self.code_model)); | 1617 | try zig_args.append(@tagName(self.code_model)); |
| 1559 | } | 1618 | } |
| 1560 | if (self.wasi_exec_model) |model| { | 1619 | if (self.wasi_exec_model) |model| { |
| 1561 | try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); | 1620 | try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)})); |
| 1562 | } | 1621 | } |
| 1563 | for (self.export_symbol_names) |symbol_name| { | 1622 | for (self.export_symbol_names) |symbol_name| { |
| 1564 | try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); | 1623 | try zig_args.append(b.fmt("--export={s}", .{symbol_name})); |
| 1565 | } | 1624 | } |
| 1566 | 1625 | ||
| 1567 | if (!self.target.isNative()) { | 1626 | if (!self.target.isNative()) { |
| 1568 | try zig_args.appendSlice(&.{ | 1627 | try zig_args.appendSlice(&.{ |
| 1569 | "-target", try self.target.zigTriple(builder.allocator), | 1628 | "-target", try self.target.zigTriple(b.allocator), |
| 1570 | "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), | 1629 | "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()), |
| 1571 | }); | 1630 | }); |
| 1572 | 1631 | ||
| 1573 | if (self.target.dynamic_linker.get()) |dynamic_linker| { | 1632 | if (self.target.dynamic_linker.get()) |dynamic_linker| { |
| ... | @@ -1578,12 +1637,12 @@ fn make(step: *Step) !void { | ... | @@ -1578,12 +1637,12 @@ fn make(step: *Step) !void { |
| 1578 | 1637 | ||
| 1579 | if (self.linker_script) |linker_script| { | 1638 | if (self.linker_script) |linker_script| { |
| 1580 | try zig_args.append("--script"); | 1639 | try zig_args.append("--script"); |
| 1581 | try zig_args.append(linker_script.getPath(builder)); | 1640 | try zig_args.append(linker_script.getPath(b)); |
| 1582 | } | 1641 | } |
| 1583 | 1642 | ||
| 1584 | if (self.version_script) |version_script| { | 1643 | if (self.version_script) |version_script| { |
| 1585 | try zig_args.append("--version-script"); | 1644 | try zig_args.append("--version-script"); |
| 1586 | try zig_args.append(builder.pathFromRoot(version_script)); | 1645 | try zig_args.append(b.pathFromRoot(version_script)); |
| 1587 | } | 1646 | } |
| 1588 | 1647 | ||
| 1589 | if (self.kind == .@"test") { | 1648 | if (self.kind == .@"test") { |
| ... | @@ -1596,83 +1655,7 @@ fn make(step: *Step) !void { | ... | @@ -1596,83 +1655,7 @@ fn make(step: *Step) !void { |
| 1596 | try zig_args.append("--test-cmd-bin"); | 1655 | try zig_args.append("--test-cmd-bin"); |
| 1597 | } | 1656 | } |
| 1598 | } | 1657 | } |
| 1599 | } else { | ||
| 1600 | const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; | ||
| 1601 | |||
| 1602 | switch (builder.host.getExternalExecutor(self.target_info, .{ | ||
| 1603 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | ||
| 1604 | .link_libc = transitive_deps.is_linking_libc, | ||
| 1605 | })) { | ||
| 1606 | .native => {}, | ||
| 1607 | .bad_dl, .bad_os_or_cpu => { | ||
| 1608 | try zig_args.append("--test-no-exec"); | ||
| 1609 | }, | ||
| 1610 | .rosetta => if (builder.enable_rosetta) { | ||
| 1611 | try zig_args.append("--test-cmd-bin"); | ||
| 1612 | } else { | ||
| 1613 | try zig_args.append("--test-no-exec"); | ||
| 1614 | }, | ||
| 1615 | .qemu => |bin_name| ok: { | ||
| 1616 | if (builder.enable_qemu) qemu: { | ||
| 1617 | const glibc_dir_arg = if (need_cross_glibc) | ||
| 1618 | builder.glibc_runtimes_dir orelse break :qemu | ||
| 1619 | else | ||
| 1620 | null; | ||
| 1621 | try zig_args.append("--test-cmd"); | ||
| 1622 | try zig_args.append(bin_name); | ||
| 1623 | if (glibc_dir_arg) |dir| { | ||
| 1624 | // TODO look into making this a call to `linuxTriple`. This | ||
| 1625 | // needs the directory to be called "i686" rather than | ||
| 1626 | // "x86" which is why we do it manually here. | ||
| 1627 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | ||
| 1628 | const cpu_arch = self.target.getCpuArch(); | ||
| 1629 | const os_tag = self.target.getOsTag(); | ||
| 1630 | const abi = self.target.getAbi(); | ||
| 1631 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | ||
| 1632 | "i686" | ||
| 1633 | else | ||
| 1634 | @tagName(cpu_arch); | ||
| 1635 | const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ | ||
| 1636 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | ||
| 1637 | }); | ||
| 1638 | |||
| 1639 | try zig_args.append("--test-cmd"); | ||
| 1640 | try zig_args.append("-L"); | ||
| 1641 | try zig_args.append("--test-cmd"); | ||
| 1642 | try zig_args.append(full_dir); | ||
| 1643 | } | ||
| 1644 | try zig_args.append("--test-cmd-bin"); | ||
| 1645 | break :ok; | ||
| 1646 | } | ||
| 1647 | try zig_args.append("--test-no-exec"); | ||
| 1648 | }, | ||
| 1649 | .wine => |bin_name| if (builder.enable_wine) { | ||
| 1650 | try zig_args.append("--test-cmd"); | ||
| 1651 | try zig_args.append(bin_name); | ||
| 1652 | try zig_args.append("--test-cmd-bin"); | ||
| 1653 | } else { | ||
| 1654 | try zig_args.append("--test-no-exec"); | ||
| 1655 | }, | ||
| 1656 | .wasmtime => |bin_name| if (builder.enable_wasmtime) { | ||
| 1657 | try zig_args.append("--test-cmd"); | ||
| 1658 | try zig_args.append(bin_name); | ||
| 1659 | try zig_args.append("--test-cmd"); | ||
| 1660 | try zig_args.append("--dir=."); | ||
| 1661 | try zig_args.append("--test-cmd-bin"); | ||
| 1662 | } else { | ||
| 1663 | try zig_args.append("--test-no-exec"); | ||
| 1664 | }, | ||
| 1665 | .darling => |bin_name| if (builder.enable_darling) { | ||
| 1666 | try zig_args.append("--test-cmd"); | ||
| 1667 | try zig_args.append(bin_name); | ||
| 1668 | try zig_args.append("--test-cmd-bin"); | ||
| 1669 | } else { | ||
| 1670 | try zig_args.append("--test-no-exec"); | ||
| 1671 | }, | ||
| 1672 | } | ||
| 1673 | } | 1658 | } |
| 1674 | } else if (self.kind == .test_exe) { | ||
| 1675 | try zig_args.append("--test-no-exec"); | ||
| 1676 | } | 1659 | } |
| 1677 | 1660 | ||
| 1678 | try self.appendModuleArgs(&zig_args); | 1661 | try self.appendModuleArgs(&zig_args); |
| ... | @@ -1681,18 +1664,18 @@ fn make(step: *Step) !void { | ... | @@ -1681,18 +1664,18 @@ fn make(step: *Step) !void { |
| 1681 | switch (include_dir) { | 1664 | switch (include_dir) { |
| 1682 | .raw_path => |include_path| { | 1665 | .raw_path => |include_path| { |
| 1683 | try zig_args.append("-I"); | 1666 | try zig_args.append("-I"); |
| 1684 | try zig_args.append(builder.pathFromRoot(include_path)); | 1667 | try zig_args.append(b.pathFromRoot(include_path)); |
| 1685 | }, | 1668 | }, |
| 1686 | .raw_path_system => |include_path| { | 1669 | .raw_path_system => |include_path| { |
| 1687 | if (builder.sysroot != null) { | 1670 | if (b.sysroot != null) { |
| 1688 | try zig_args.append("-iwithsysroot"); | 1671 | try zig_args.append("-iwithsysroot"); |
| 1689 | } else { | 1672 | } else { |
| 1690 | try zig_args.append("-isystem"); | 1673 | try zig_args.append("-isystem"); |
| 1691 | } | 1674 | } |
| 1692 | 1675 | ||
| 1693 | const resolved_include_path = builder.pathFromRoot(include_path); | 1676 | const resolved_include_path = b.pathFromRoot(include_path); |
| 1694 | 1677 | ||
| 1695 | const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { | 1678 | const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { |
| 1696 | // We need to check for disk designator and strip it out from dir path so | 1679 | // We need to check for disk designator and strip it out from dir path so |
| 1697 | // that zig/clang can concat resolved_include_path with sysroot. | 1680 | // that zig/clang can concat resolved_include_path with sysroot. |
| 1698 | const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); | 1681 | const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); |
| ... | @@ -1708,17 +1691,14 @@ fn make(step: *Step) !void { | ... | @@ -1708,17 +1691,14 @@ fn make(step: *Step) !void { |
| 1708 | }, | 1691 | }, |
| 1709 | .other_step => |other| { | 1692 | .other_step => |other| { |
| 1710 | if (other.emit_h) { | 1693 | if (other.emit_h) { |
| 1711 | const h_path = other.getOutputHSource().getPath(builder); | 1694 | const h_path = other.getOutputHSource().getPath(b); |
| 1712 | try zig_args.append("-isystem"); | 1695 | try zig_args.append("-isystem"); |
| 1713 | try zig_args.append(fs.path.dirname(h_path).?); | 1696 | try zig_args.append(fs.path.dirname(h_path).?); |
| 1714 | } | 1697 | } |
| 1715 | if (other.installed_headers.items.len > 0) { | 1698 | if (other.installed_headers.items.len > 0) { |
| 1716 | for (other.installed_headers.items) |install_step| { | ||
| 1717 | try install_step.make(); | ||
| 1718 | } | ||
| 1719 | try zig_args.append("-I"); | 1699 | try zig_args.append("-I"); |
| 1720 | try zig_args.append(builder.pathJoin(&.{ | 1700 | try zig_args.append(b.pathJoin(&.{ |
| 1721 | other.builder.install_prefix, "include", | 1701 | other.step.owner.install_prefix, "include", |
| 1722 | })); | 1702 | })); |
| 1723 | } | 1703 | } |
| 1724 | }, | 1704 | }, |
| ... | @@ -1730,33 +1710,35 @@ fn make(step: *Step) !void { | ... | @@ -1730,33 +1710,35 @@ fn make(step: *Step) !void { |
| 1730 | } | 1710 | } |
| 1731 | } | 1711 | } |
| 1732 | 1712 | ||
| 1733 | for (self.lib_paths.items) |lib_path| { | 1713 | for (self.c_macros.items) |c_macro| { |
| 1734 | try zig_args.append("-L"); | 1714 | try zig_args.append("-D"); |
| 1735 | try zig_args.append(lib_path); | 1715 | try zig_args.append(c_macro); |
| 1736 | } | 1716 | } |
| 1737 | 1717 | ||
| 1738 | for (self.rpaths.items) |rpath| { | 1718 | try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len); |
| 1739 | try zig_args.append("-rpath"); | 1719 | for (self.lib_paths.items) |lib_path| { |
| 1740 | try zig_args.append(rpath); | 1720 | zig_args.appendAssumeCapacity("-L"); |
| 1721 | zig_args.appendAssumeCapacity(lib_path.getPath2(b, step)); | ||
| 1741 | } | 1722 | } |
| 1742 | 1723 | ||
| 1743 | for (self.c_macros.items) |c_macro| { | 1724 | try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len); |
| 1744 | try zig_args.append("-D"); | 1725 | for (self.rpaths.items) |rpath| { |
| 1745 | try zig_args.append(c_macro); | 1726 | zig_args.appendAssumeCapacity("-rpath"); |
| 1727 | zig_args.appendAssumeCapacity(rpath.getPath2(b, step)); | ||
| 1746 | } | 1728 | } |
| 1747 | 1729 | ||
| 1748 | if (self.target.isDarwin()) { | 1730 | for (self.framework_dirs.items) |directory_source| { |
| 1749 | for (self.framework_dirs.items) |dir| { | 1731 | if (b.sysroot != null) { |
| 1750 | if (builder.sysroot != null) { | 1732 | try zig_args.append("-iframeworkwithsysroot"); |
| 1751 | try zig_args.append("-iframeworkwithsysroot"); | 1733 | } else { |
| 1752 | } else { | 1734 | try zig_args.append("-iframework"); |
| 1753 | try zig_args.append("-iframework"); | ||
| 1754 | } | ||
| 1755 | try zig_args.append(dir); | ||
| 1756 | try zig_args.append("-F"); | ||
| 1757 | try zig_args.append(dir); | ||
| 1758 | } | 1735 | } |
| 1736 | try zig_args.append(directory_source.getPath2(b, step)); | ||
| 1737 | try zig_args.append("-F"); | ||
| 1738 | try zig_args.append(directory_source.getPath2(b, step)); | ||
| 1739 | } | ||
| 1759 | 1740 | ||
| 1741 | { | ||
| 1760 | var it = self.frameworks.iterator(); | 1742 | var it = self.frameworks.iterator(); |
| 1761 | while (it.next()) |entry| { | 1743 | while (it.next()) |entry| { |
| 1762 | const name = entry.key_ptr.*; | 1744 | const name = entry.key_ptr.*; |
| ... | @@ -1770,29 +1752,45 @@ fn make(step: *Step) !void { | ... | @@ -1770,29 +1752,45 @@ fn make(step: *Step) !void { |
| 1770 | } | 1752 | } |
| 1771 | try zig_args.append(name); | 1753 | try zig_args.append(name); |
| 1772 | } | 1754 | } |
| 1773 | } else { | ||
| 1774 | if (self.framework_dirs.items.len > 0) { | ||
| 1775 | log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | if (self.frameworks.count() > 0) { | ||
| 1779 | log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); | ||
| 1780 | } | ||
| 1781 | } | 1755 | } |
| 1782 | 1756 | ||
| 1783 | if (builder.sysroot) |sysroot| { | 1757 | if (b.sysroot) |sysroot| { |
| 1784 | try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); | 1758 | try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); |
| 1785 | } | 1759 | } |
| 1786 | 1760 | ||
| 1787 | for (builder.search_prefixes.items) |search_prefix| { | 1761 | for (b.search_prefixes.items) |search_prefix| { |
| 1788 | try zig_args.append("-L"); | 1762 | var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| { |
| 1789 | try zig_args.append(builder.pathJoin(&.{ | 1763 | return step.fail("unable to open prefix directory '{s}': {s}", .{ |
| 1790 | search_prefix, "lib", | 1764 | search_prefix, @errorName(err), |
| 1791 | })); | 1765 | }); |
| 1792 | try zig_args.append("-I"); | 1766 | }; |
| 1793 | try zig_args.append(builder.pathJoin(&.{ | 1767 | defer prefix_dir.close(); |
| 1794 | search_prefix, "include", | 1768 | |
| 1795 | })); | 1769 | // Avoid passing -L and -I flags for nonexistent directories. |
| 1770 | // This prevents a warning, that should probably be upgraded to an error in Zig's | ||
| 1771 | // CLI parsing code, when the linker sees an -L directory that does not exist. | ||
| 1772 | |||
| 1773 | if (prefix_dir.accessZ("lib", .{})) |_| { | ||
| 1774 | try zig_args.appendSlice(&.{ | ||
| 1775 | "-L", try fs.path.join(b.allocator, &.{ search_prefix, "lib" }), | ||
| 1776 | }); | ||
| 1777 | } else |err| switch (err) { | ||
| 1778 | error.FileNotFound => {}, | ||
| 1779 | else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{ | ||
| 1780 | search_prefix, @errorName(e), | ||
| 1781 | }), | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | if (prefix_dir.accessZ("include", .{})) |_| { | ||
| 1785 | try zig_args.appendSlice(&.{ | ||
| 1786 | "-I", try fs.path.join(b.allocator, &.{ search_prefix, "include" }), | ||
| 1787 | }); | ||
| 1788 | } else |err| switch (err) { | ||
| 1789 | error.FileNotFound => {}, | ||
| 1790 | else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{ | ||
| 1791 | search_prefix, @errorName(e), | ||
| 1792 | }), | ||
| 1793 | } | ||
| 1796 | } | 1794 | } |
| 1797 | 1795 | ||
| 1798 | try addFlag(&zig_args, "valgrind", self.valgrind_support); | 1796 | try addFlag(&zig_args, "valgrind", self.valgrind_support); |
| ... | @@ -1801,15 +1799,15 @@ fn make(step: *Step) !void { | ... | @@ -1801,15 +1799,15 @@ fn make(step: *Step) !void { |
| 1801 | 1799 | ||
| 1802 | if (self.zig_lib_dir) |dir| { | 1800 | if (self.zig_lib_dir) |dir| { |
| 1803 | try zig_args.append("--zig-lib-dir"); | 1801 | try zig_args.append("--zig-lib-dir"); |
| 1804 | try zig_args.append(builder.pathFromRoot(dir)); | 1802 | try zig_args.append(b.pathFromRoot(dir)); |
| 1805 | } else if (builder.zig_lib_dir) |dir| { | 1803 | } else if (b.zig_lib_dir) |dir| { |
| 1806 | try zig_args.append("--zig-lib-dir"); | 1804 | try zig_args.append("--zig-lib-dir"); |
| 1807 | try zig_args.append(dir); | 1805 | try zig_args.append(dir); |
| 1808 | } | 1806 | } |
| 1809 | 1807 | ||
| 1810 | if (self.main_pkg_path) |dir| { | 1808 | if (self.main_pkg_path) |dir| { |
| 1811 | try zig_args.append("--main-pkg-path"); | 1809 | try zig_args.append("--main-pkg-path"); |
| 1812 | try zig_args.append(builder.pathFromRoot(dir)); | 1810 | try zig_args.append(b.pathFromRoot(dir)); |
| 1813 | } | 1811 | } |
| 1814 | 1812 | ||
| 1815 | try addFlag(&zig_args, "PIC", self.force_pic); | 1813 | try addFlag(&zig_args, "PIC", self.force_pic); |
| ... | @@ -1831,6 +1829,7 @@ fn make(step: *Step) !void { | ... | @@ -1831,6 +1829,7 @@ fn make(step: *Step) !void { |
| 1831 | } | 1829 | } |
| 1832 | 1830 | ||
| 1833 | try zig_args.append("--enable-cache"); | 1831 | try zig_args.append("--enable-cache"); |
| 1832 | try zig_args.append("--listen=-"); | ||
| 1834 | 1833 | ||
| 1835 | // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux | 1834 | // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux |
| 1836 | // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and | 1835 | // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and |
| ... | @@ -1841,15 +1840,15 @@ fn make(step: *Step) !void { | ... | @@ -1841,15 +1840,15 @@ fn make(step: *Step) !void { |
| 1841 | args_length += arg.len + 1; // +1 to account for null terminator | 1840 | args_length += arg.len + 1; // +1 to account for null terminator |
| 1842 | } | 1841 | } |
| 1843 | if (args_length >= 30 * 1024) { | 1842 | if (args_length >= 30 * 1024) { |
| 1844 | try builder.cache_root.handle.makePath("args"); | 1843 | try b.cache_root.handle.makePath("args"); |
| 1845 | 1844 | ||
| 1846 | const args_to_escape = zig_args.items[2..]; | 1845 | const args_to_escape = zig_args.items[2..]; |
| 1847 | var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len); | 1846 | var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len); |
| 1848 | arg_blk: for (args_to_escape) |arg| { | 1847 | arg_blk: for (args_to_escape) |arg| { |
| 1849 | for (arg, 0..) |c, arg_idx| { | 1848 | for (arg, 0..) |c, arg_idx| { |
| 1850 | if (c == '\\' or c == '"') { | 1849 | if (c == '\\' or c == '"') { |
| 1851 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy | 1850 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy |
| 1852 | var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1); | 1851 | var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1); |
| 1853 | const writer = escaped.writer(); | 1852 | const writer = escaped.writer(); |
| 1854 | try writer.writeAll(arg[0..arg_idx]); | 1853 | try writer.writeAll(arg[0..arg_idx]); |
| 1855 | for (arg[arg_idx..]) |to_escape| { | 1854 | for (arg[arg_idx..]) |to_escape| { |
| ... | @@ -1865,8 +1864,8 @@ fn make(step: *Step) !void { | ... | @@ -1865,8 +1864,8 @@ fn make(step: *Step) !void { |
| 1865 | 1864 | ||
| 1866 | // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with | 1865 | // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with |
| 1867 | // other zig build commands running in parallel. | 1866 | // other zig build commands running in parallel. |
| 1868 | const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); | 1867 | const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items); |
| 1869 | const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); | 1868 | const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); |
| 1870 | 1869 | ||
| 1871 | var args_hash: [Sha256.digest_length]u8 = undefined; | 1870 | var args_hash: [Sha256.digest_length]u8 = undefined; |
| 1872 | Sha256.hash(args, &args_hash, .{}); | 1871 | Sha256.hash(args, &args_hash, .{}); |
| ... | @@ -1878,28 +1877,35 @@ fn make(step: *Step) !void { | ... | @@ -1878,28 +1877,35 @@ fn make(step: *Step) !void { |
| 1878 | ); | 1877 | ); |
| 1879 | 1878 | ||
| 1880 | const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; | 1879 | const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; |
| 1881 | try builder.cache_root.handle.writeFile(args_file, args); | 1880 | try b.cache_root.handle.writeFile(args_file, args); |
| 1882 | 1881 | ||
| 1883 | const resolved_args_file = try mem.concat(builder.allocator, u8, &.{ | 1882 | const resolved_args_file = try mem.concat(b.allocator, u8, &.{ |
| 1884 | "@", | 1883 | "@", |
| 1885 | try builder.cache_root.join(builder.allocator, &.{args_file}), | 1884 | try b.cache_root.join(b.allocator, &.{args_file}), |
| 1886 | }); | 1885 | }); |
| 1887 | 1886 | ||
| 1888 | zig_args.shrinkRetainingCapacity(2); | 1887 | zig_args.shrinkRetainingCapacity(2); |
| 1889 | try zig_args.append(resolved_args_file); | 1888 | try zig_args.append(resolved_args_file); |
| 1890 | } | 1889 | } |
| 1891 | 1890 | ||
| 1892 | const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); | 1891 | const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { |
| 1893 | const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); | 1892 | error.NeedCompileErrorCheck => { |
| 1893 | assert(self.expect_errors.len != 0); | ||
| 1894 | try checkCompileErrors(self); | ||
| 1895 | return; | ||
| 1896 | }, | ||
| 1897 | else => |e| return e, | ||
| 1898 | }; | ||
| 1899 | const build_output_dir = fs.path.dirname(output_bin_path).?; | ||
| 1894 | 1900 | ||
| 1895 | if (self.output_dir) |output_dir| { | 1901 | if (self.output_dir) |output_dir| { |
| 1896 | var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); | 1902 | var src_dir = try fs.cwd().openIterableDir(build_output_dir, .{}); |
| 1897 | defer src_dir.close(); | 1903 | defer src_dir.close(); |
| 1898 | 1904 | ||
| 1899 | // Create the output directory if it doesn't exist. | 1905 | // Create the output directory if it doesn't exist. |
| 1900 | try std.fs.cwd().makePath(output_dir); | 1906 | try fs.cwd().makePath(output_dir); |
| 1901 | 1907 | ||
| 1902 | var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); | 1908 | var dest_dir = try fs.cwd().openDir(output_dir, .{}); |
| 1903 | defer dest_dir.close(); | 1909 | defer dest_dir.close(); |
| 1904 | 1910 | ||
| 1905 | var it = src_dir.iterate(); | 1911 | var it = src_dir.iterate(); |
| ... | @@ -1923,25 +1929,34 @@ fn make(step: *Step) !void { | ... | @@ -1923,25 +1929,34 @@ fn make(step: *Step) !void { |
| 1923 | 1929 | ||
| 1924 | // Update generated files | 1930 | // Update generated files |
| 1925 | if (self.output_dir != null) { | 1931 | if (self.output_dir != null) { |
| 1926 | self.output_path_source.path = builder.pathJoin( | 1932 | self.output_dirname_source.path = self.output_dir.?; |
| 1933 | |||
| 1934 | self.output_path_source.path = b.pathJoin( | ||
| 1927 | &.{ self.output_dir.?, self.out_filename }, | 1935 | &.{ self.output_dir.?, self.out_filename }, |
| 1928 | ); | 1936 | ); |
| 1929 | 1937 | ||
| 1930 | if (self.emit_h) { | 1938 | if (self.emit_h) { |
| 1931 | self.output_h_path_source.path = builder.pathJoin( | 1939 | self.output_h_path_source.path = b.pathJoin( |
| 1932 | &.{ self.output_dir.?, self.out_h_filename }, | 1940 | &.{ self.output_dir.?, self.out_h_filename }, |
| 1933 | ); | 1941 | ); |
| 1934 | } | 1942 | } |
| 1935 | 1943 | ||
| 1936 | if (self.target.isWindows() or self.target.isUefi()) { | 1944 | if (self.target.isWindows() or self.target.isUefi()) { |
| 1937 | self.output_pdb_path_source.path = builder.pathJoin( | 1945 | self.output_pdb_path_source.path = b.pathJoin( |
| 1938 | &.{ self.output_dir.?, self.out_pdb_filename }, | 1946 | &.{ self.output_dir.?, self.out_pdb_filename }, |
| 1939 | ); | 1947 | ); |
| 1940 | } | 1948 | } |
| 1941 | } | 1949 | } |
| 1942 | 1950 | ||
| 1943 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { | 1951 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and |
| 1944 | try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); | 1952 | self.version != null and self.target.wantSharedLibSymLinks()) |
| 1953 | { | ||
| 1954 | try doAtomicSymLinks( | ||
| 1955 | step, | ||
| 1956 | self.getOutputSource().getPath(b), | ||
| 1957 | self.major_only_filename.?, | ||
| 1958 | self.name_only_filename.?, | ||
| 1959 | ); | ||
| 1945 | } | 1960 | } |
| 1946 | } | 1961 | } |
| 1947 | 1962 | ||
| ... | @@ -1983,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { | ... | @@ -1983,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { |
| 1983 | } | 1998 | } |
| 1984 | 1999 | ||
| 1985 | pub fn doAtomicSymLinks( | 2000 | pub fn doAtomicSymLinks( |
| 1986 | allocator: Allocator, | 2001 | step: *Step, |
| 1987 | output_path: []const u8, | 2002 | output_path: []const u8, |
| 1988 | filename_major_only: []const u8, | 2003 | filename_major_only: []const u8, |
| 1989 | filename_name_only: []const u8, | 2004 | filename_name_only: []const u8, |
| 1990 | ) !void { | 2005 | ) !void { |
| 2006 | const arena = step.owner.allocator; | ||
| 1991 | const out_dir = fs.path.dirname(output_path) orelse "."; | 2007 | const out_dir = fs.path.dirname(output_path) orelse "."; |
| 1992 | const out_basename = fs.path.basename(output_path); | 2008 | const out_basename = fs.path.basename(output_path); |
| 1993 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 | 2009 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 |
| 1994 | const major_only_path = try fs.path.join( | 2010 | const major_only_path = try fs.path.join(arena, &.{ out_dir, filename_major_only }); |
| 1995 | allocator, | 2011 | fs.atomicSymLink(arena, out_basename, major_only_path) catch |err| { |
| 1996 | &[_][]const u8{ out_dir, filename_major_only }, | 2012 | return step.fail("unable to symlink {s} -> {s}: {s}", .{ |
| 1997 | ); | 2013 | major_only_path, out_basename, @errorName(err), |
| 1998 | fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { | 2014 | }); |
| 1999 | log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); | ||
| 2000 | return err; | ||
| 2001 | }; | 2015 | }; |
| 2002 | // sym link for libfoo.so to libfoo.so.1 | 2016 | // sym link for libfoo.so to libfoo.so.1 |
| 2003 | const name_only_path = try fs.path.join( | 2017 | const name_only_path = try fs.path.join(arena, &.{ out_dir, filename_name_only }); |
| 2004 | allocator, | 2018 | fs.atomicSymLink(arena, filename_major_only, name_only_path) catch |err| { |
| 2005 | &[_][]const u8{ out_dir, filename_name_only }, | 2019 | return step.fail("Unable to symlink {s} -> {s}: {s}", .{ |
| 2006 | ); | 2020 | name_only_path, filename_major_only, @errorName(err), |
| 2007 | fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { | 2021 | }); |
| 2008 | log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); | ||
| 2009 | return err; | ||
| 2010 | }; | 2022 | }; |
| 2011 | } | 2023 | } |
| 2012 | 2024 | ||
| ... | @@ -2042,7 +2054,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { | ... | @@ -2042,7 +2054,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { |
| 2042 | error.FileNotFound => error.PkgConfigNotInstalled, | 2054 | error.FileNotFound => error.PkgConfigNotInstalled, |
| 2043 | error.InvalidName => error.PkgConfigNotInstalled, | 2055 | error.InvalidName => error.PkgConfigNotInstalled, |
| 2044 | error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, | 2056 | error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, |
| 2045 | error.ChildExecFailed => error.PkgConfigFailed, | ||
| 2046 | else => return err, | 2057 | else => return err, |
| 2047 | }; | 2058 | }; |
| 2048 | self.pkg_config_pkg_list = result; | 2059 | self.pkg_config_pkg_list = result; |
| ... | @@ -2119,3 +2130,57 @@ const TransitiveDeps = struct { | ... | @@ -2119,3 +2130,57 @@ const TransitiveDeps = struct { |
| 2119 | } | 2130 | } |
| 2120 | } | 2131 | } |
| 2121 | }; | 2132 | }; |
| 2133 | |||
| 2134 | fn checkCompileErrors(self: *CompileStep) !void { | ||
| 2135 | // Clear this field so that it does not get printed by the build runner. | ||
| 2136 | const actual_eb = self.step.result_error_bundle; | ||
| 2137 | self.step.result_error_bundle = std.zig.ErrorBundle.empty; | ||
| 2138 | |||
| 2139 | const arena = self.step.owner.allocator; | ||
| 2140 | |||
| 2141 | var actual_stderr_list = std.ArrayList(u8).init(arena); | ||
| 2142 | try actual_eb.renderToWriter(.{ | ||
| 2143 | .ttyconf = .no_color, | ||
| 2144 | .include_reference_trace = false, | ||
| 2145 | .include_source_line = false, | ||
| 2146 | }, actual_stderr_list.writer()); | ||
| 2147 | const actual_stderr = try actual_stderr_list.toOwnedSlice(); | ||
| 2148 | |||
| 2149 | // Render the expected lines into a string that we can compare verbatim. | ||
| 2150 | var expected_generated = std.ArrayList(u8).init(arena); | ||
| 2151 | |||
| 2152 | var actual_line_it = mem.split(u8, actual_stderr, "\n"); | ||
| 2153 | for (self.expect_errors) |expect_line| { | ||
| 2154 | const actual_line = actual_line_it.next() orelse { | ||
| 2155 | try expected_generated.appendSlice(expect_line); | ||
| 2156 | try expected_generated.append('\n'); | ||
| 2157 | continue; | ||
| 2158 | }; | ||
| 2159 | if (mem.endsWith(u8, actual_line, expect_line)) { | ||
| 2160 | try expected_generated.appendSlice(actual_line); | ||
| 2161 | try expected_generated.append('\n'); | ||
| 2162 | continue; | ||
| 2163 | } | ||
| 2164 | if (mem.startsWith(u8, expect_line, ":?:?: ")) { | ||
| 2165 | if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) { | ||
| 2166 | try expected_generated.appendSlice(actual_line); | ||
| 2167 | try expected_generated.append('\n'); | ||
| 2168 | continue; | ||
| 2169 | } | ||
| 2170 | } | ||
| 2171 | try expected_generated.appendSlice(expect_line); | ||
| 2172 | try expected_generated.append('\n'); | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; | ||
| 2176 | |||
| 2177 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile | ||
| 2178 | return self.step.fail( | ||
| 2179 | \\ | ||
| 2180 | \\========= expected: ===================== | ||
| 2181 | \\{s} | ||
| 2182 | \\========= but found: ==================== | ||
| 2183 | \\{s} | ||
| 2184 | \\========================================= | ||
| 2185 | , .{ expected_generated.items, actual_stderr }); | ||
| 2186 | } |
lib/std/Build/ConfigHeaderStep.zig+82-73| ... | @@ -1,9 +1,3 @@ | ... | @@ -1,9 +1,3 @@ |
| 1 | const std = @import("../std.zig"); | ||
| 2 | const ConfigHeaderStep = @This(); | ||
| 3 | const Step = std.Build.Step; | ||
| 4 | |||
| 5 | pub const base_id: Step.Id = .config_header; | ||
| 6 | |||
| 7 | pub const Style = union(enum) { | 1 | pub const Style = union(enum) { |
| 8 | /// The configure format supported by autotools. It uses `#undef foo` to | 2 | /// The configure format supported by autotools. It uses `#undef foo` to |
| 9 | /// mark lines that can be substituted with different values. | 3 | /// mark lines that can be substituted with different values. |
| ... | @@ -34,7 +28,6 @@ pub const Value = union(enum) { | ... | @@ -34,7 +28,6 @@ pub const Value = union(enum) { |
| 34 | }; | 28 | }; |
| 35 | 29 | ||
| 36 | step: Step, | 30 | step: Step, |
| 37 | builder: *std.Build, | ||
| 38 | values: std.StringArrayHashMap(Value), | 31 | values: std.StringArrayHashMap(Value), |
| 39 | output_file: std.Build.GeneratedFile, | 32 | output_file: std.Build.GeneratedFile, |
| 40 | 33 | ||
| ... | @@ -42,43 +35,57 @@ style: Style, | ... | @@ -42,43 +35,57 @@ style: Style, |
| 42 | max_bytes: usize, | 35 | max_bytes: usize, |
| 43 | include_path: []const u8, | 36 | include_path: []const u8, |
| 44 | 37 | ||
| 38 | pub const base_id: Step.Id = .config_header; | ||
| 39 | |||
| 45 | pub const Options = struct { | 40 | pub const Options = struct { |
| 46 | style: Style = .blank, | 41 | style: Style = .blank, |
| 47 | max_bytes: usize = 2 * 1024 * 1024, | 42 | max_bytes: usize = 2 * 1024 * 1024, |
| 48 | include_path: ?[]const u8 = null, | 43 | include_path: ?[]const u8 = null, |
| 44 | first_ret_addr: ?usize = null, | ||
| 49 | }; | 45 | }; |
| 50 | 46 | ||
| 51 | pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { | 47 | pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep { |
| 52 | const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); | 48 | const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM"); |
| 53 | const name = if (options.style.getFileSource()) |s| | ||
| 54 | builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() }) | ||
| 55 | else | ||
| 56 | builder.fmt("configure {s} header", .{@tagName(options.style)}); | ||
| 57 | self.* = .{ | ||
| 58 | .builder = builder, | ||
| 59 | .step = Step.init(base_id, name, builder.allocator, make), | ||
| 60 | .style = options.style, | ||
| 61 | .values = std.StringArrayHashMap(Value).init(builder.allocator), | ||
| 62 | 49 | ||
| 63 | .max_bytes = options.max_bytes, | 50 | var include_path: []const u8 = "config.h"; |
| 64 | .include_path = "config.h", | ||
| 65 | .output_file = .{ .step = &self.step }, | ||
| 66 | }; | ||
| 67 | 51 | ||
| 68 | if (options.style.getFileSource()) |s| switch (s) { | 52 | if (options.style.getFileSource()) |s| switch (s) { |
| 69 | .path => |p| { | 53 | .path => |p| { |
| 70 | const basename = std.fs.path.basename(p); | 54 | const basename = std.fs.path.basename(p); |
| 71 | if (std.mem.endsWith(u8, basename, ".h.in")) { | 55 | if (std.mem.endsWith(u8, basename, ".h.in")) { |
| 72 | self.include_path = basename[0 .. basename.len - 3]; | 56 | include_path = basename[0 .. basename.len - 3]; |
| 73 | } | 57 | } |
| 74 | }, | 58 | }, |
| 75 | else => {}, | 59 | else => {}, |
| 76 | }; | 60 | }; |
| 77 | 61 | ||
| 78 | if (options.include_path) |include_path| { | 62 | if (options.include_path) |p| { |
| 79 | self.include_path = include_path; | 63 | include_path = p; |
| 80 | } | 64 | } |
| 81 | 65 | ||
| 66 | const name = if (options.style.getFileSource()) |s| | ||
| 67 | owner.fmt("configure {s} header {s} to {s}", .{ | ||
| 68 | @tagName(options.style), s.getDisplayName(), include_path, | ||
| 69 | }) | ||
| 70 | else | ||
| 71 | owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path }); | ||
| 72 | |||
| 73 | self.* = .{ | ||
| 74 | .step = Step.init(.{ | ||
| 75 | .id = base_id, | ||
| 76 | .name = name, | ||
| 77 | .owner = owner, | ||
| 78 | .makeFn = make, | ||
| 79 | .first_ret_addr = options.first_ret_addr orelse @returnAddress(), | ||
| 80 | }), | ||
| 81 | .style = options.style, | ||
| 82 | .values = std.StringArrayHashMap(Value).init(owner.allocator), | ||
| 83 | |||
| 84 | .max_bytes = options.max_bytes, | ||
| 85 | .include_path = include_path, | ||
| 86 | .output_file = .{ .step = &self.step }, | ||
| 87 | }; | ||
| 88 | |||
| 82 | return self; | 89 | return self; |
| 83 | } | 90 | } |
| 84 | 91 | ||
| ... | @@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v | ... | @@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v |
| 146 | } | 153 | } |
| 147 | } | 154 | } |
| 148 | 155 | ||
| 149 | fn make(step: *Step) !void { | 156 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 157 | _ = prog_node; | ||
| 158 | const b = step.owner; | ||
| 150 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); | 159 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); |
| 151 | const gpa = self.builder.allocator; | 160 | const gpa = b.allocator; |
| 152 | 161 | const arena = b.allocator; | |
| 153 | // The cache is used here not really as a way to speed things up - because writing | ||
| 154 | // the data to a file would probably be very fast - but as a way to find a canonical | ||
| 155 | // location to put build artifacts. | ||
| 156 | 162 | ||
| 157 | // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep | 163 | var man = b.cache.obtain(); |
| 158 | // files, then two ConfigHeaderStep executing in parallel might clobber each other. | 164 | defer man.deinit(); |
| 159 | 165 | ||
| 160 | // TODO port the cache system from the compiler to zig std lib. Until then | ||
| 161 | // we construct the path directly, and no "cache hit" detection happens; | ||
| 162 | // the files are always written. | ||
| 163 | // Note there is very similar code over in WriteFileStep | ||
| 164 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | ||
| 165 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new | 166 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new |
| 166 | // random bytes when ConfigHeaderStep implementation is modified in a | 167 | // random bytes when ConfigHeaderStep implementation is modified in a |
| 167 | // non-backwards-compatible way. | 168 | // non-backwards-compatible way. |
| 168 | var hash = Hasher.init("PGuDTpidxyMqnkGM"); | 169 | man.hash.add(@as(u32, 0xdef08d23)); |
| 169 | 170 | ||
| 170 | var output = std.ArrayList(u8).init(gpa); | 171 | var output = std.ArrayList(u8).init(gpa); |
| 171 | defer output.deinit(); | 172 | defer output.deinit(); |
| ... | @@ -177,15 +178,15 @@ fn make(step: *Step) !void { | ... | @@ -177,15 +178,15 @@ fn make(step: *Step) !void { |
| 177 | switch (self.style) { | 178 | switch (self.style) { |
| 178 | .autoconf => |file_source| { | 179 | .autoconf => |file_source| { |
| 179 | try output.appendSlice(c_generated_line); | 180 | try output.appendSlice(c_generated_line); |
| 180 | const src_path = file_source.getPath(self.builder); | 181 | const src_path = file_source.getPath(b); |
| 181 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | 182 | const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); |
| 182 | try render_autoconf(contents, &output, self.values, src_path); | 183 | try render_autoconf(step, contents, &output, self.values, src_path); |
| 183 | }, | 184 | }, |
| 184 | .cmake => |file_source| { | 185 | .cmake => |file_source| { |
| 185 | try output.appendSlice(c_generated_line); | 186 | try output.appendSlice(c_generated_line); |
| 186 | const src_path = file_source.getPath(self.builder); | 187 | const src_path = file_source.getPath(b); |
| 187 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | 188 | const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); |
| 188 | try render_cmake(contents, &output, self.values, src_path); | 189 | try render_cmake(step, contents, &output, self.values, src_path); |
| 189 | }, | 190 | }, |
| 190 | .blank => { | 191 | .blank => { |
| 191 | try output.appendSlice(c_generated_line); | 192 | try output.appendSlice(c_generated_line); |
| ... | @@ -197,43 +198,44 @@ fn make(step: *Step) !void { | ... | @@ -197,43 +198,44 @@ fn make(step: *Step) !void { |
| 197 | }, | 198 | }, |
| 198 | } | 199 | } |
| 199 | 200 | ||
| 200 | hash.update(output.items); | 201 | man.hash.addBytes(output.items); |
| 201 | 202 | ||
| 202 | var digest: [16]u8 = undefined; | 203 | if (try step.cacheHit(&man)) { |
| 203 | hash.final(&digest); | 204 | const digest = man.final(); |
| 204 | var hash_basename: [digest.len * 2]u8 = undefined; | 205 | self.output_file.path = try b.cache_root.join(arena, &.{ |
| 205 | _ = std.fmt.bufPrint( | 206 | "o", &digest, self.include_path, |
| 206 | &hash_basename, | 207 | }); |
| 207 | "{s}", | 208 | return; |
| 208 | .{std.fmt.fmtSliceHexLower(&digest)}, | 209 | } |
| 209 | ) catch unreachable; | ||
| 210 | 210 | ||
| 211 | const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename }); | 211 | const digest = man.final(); |
| 212 | 212 | ||
| 213 | // If output_path has directory parts, deal with them. Example: | 213 | // If output_path has directory parts, deal with them. Example: |
| 214 | // output_dir is zig-cache/o/HASH | 214 | // output_dir is zig-cache/o/HASH |
| 215 | // output_path is libavutil/avconfig.h | 215 | // output_path is libavutil/avconfig.h |
| 216 | // We want to open directory zig-cache/o/HASH/libavutil/ | 216 | // We want to open directory zig-cache/o/HASH/libavutil/ |
| 217 | // but keep output_dir as zig-cache/o/HASH for -I include | 217 | // but keep output_dir as zig-cache/o/HASH for -I include |
| 218 | const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d| | 218 | const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, self.include_path }); |
| 219 | try std.fs.path.join(gpa, &.{ output_dir, d }) | 219 | const sub_path_dirname = std.fs.path.dirname(sub_path).?; |
| 220 | else | ||
| 221 | output_dir; | ||
| 222 | 220 | ||
| 223 | var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { | 221 | b.cache_root.handle.makePath(sub_path_dirname) catch |err| { |
| 224 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 222 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 225 | return err; | 223 | b.cache_root, sub_path_dirname, @errorName(err), |
| 224 | }); | ||
| 226 | }; | 225 | }; |
| 227 | defer dir.close(); | ||
| 228 | 226 | ||
| 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 | }; | ||
| 230 | 232 | ||
| 231 | self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{ | 233 | self.output_file.path = try b.cache_root.join(arena, &.{sub_path}); |
| 232 | output_dir, self.include_path, | 234 | try man.writeManifest(); |
| 233 | }); | ||
| 234 | } | 235 | } |
| 235 | 236 | ||
| 236 | fn render_autoconf( | 237 | fn render_autoconf( |
| 238 | step: *Step, | ||
| 237 | contents: []const u8, | 239 | contents: []const u8, |
| 238 | output: *std.ArrayList(u8), | 240 | output: *std.ArrayList(u8), |
| 239 | values: std.StringArrayHashMap(Value), | 241 | values: std.StringArrayHashMap(Value), |
| ... | @@ -260,7 +262,7 @@ fn render_autoconf( | ... | @@ -260,7 +262,7 @@ fn render_autoconf( |
| 260 | } | 262 | } |
| 261 | const name = it.rest(); | 263 | const name = it.rest(); |
| 262 | const kv = values_copy.fetchSwapRemove(name) orelse { | 264 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 263 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 265 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 264 | src_path, line_index + 1, name, | 266 | src_path, line_index + 1, name, |
| 265 | }); | 267 | }); |
| 266 | any_errors = true; | 268 | any_errors = true; |
| ... | @@ -270,15 +272,17 @@ fn render_autoconf( | ... | @@ -270,15 +272,17 @@ fn render_autoconf( |
| 270 | } | 272 | } |
| 271 | 273 | ||
| 272 | for (values_copy.keys()) |name| { | 274 | for (values_copy.keys()) |name| { |
| 273 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | 275 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 276 | any_errors = true; | ||
| 274 | } | 277 | } |
| 275 | 278 | ||
| 276 | if (any_errors) { | 279 | if (any_errors) { |
| 277 | return error.HeaderConfigFailed; | 280 | return error.MakeFailed; |
| 278 | } | 281 | } |
| 279 | } | 282 | } |
| 280 | 283 | ||
| 281 | fn render_cmake( | 284 | fn render_cmake( |
| 285 | step: *Step, | ||
| 282 | contents: []const u8, | 286 | contents: []const u8, |
| 283 | output: *std.ArrayList(u8), | 287 | output: *std.ArrayList(u8), |
| 284 | values: std.StringArrayHashMap(Value), | 288 | values: std.StringArrayHashMap(Value), |
| ... | @@ -304,14 +308,14 @@ fn render_cmake( | ... | @@ -304,14 +308,14 @@ fn render_cmake( |
| 304 | continue; | 308 | continue; |
| 305 | } | 309 | } |
| 306 | const name = it.next() orelse { | 310 | const name = it.next() orelse { |
| 307 | std.debug.print("{s}:{d}: error: missing define name\n", .{ | 311 | try step.addError("{s}:{d}: error: missing define name", .{ |
| 308 | src_path, line_index + 1, | 312 | src_path, line_index + 1, |
| 309 | }); | 313 | }); |
| 310 | any_errors = true; | 314 | any_errors = true; |
| 311 | continue; | 315 | continue; |
| 312 | }; | 316 | }; |
| 313 | const kv = values_copy.fetchSwapRemove(name) orelse { | 317 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 314 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 318 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 315 | src_path, line_index + 1, name, | 319 | src_path, line_index + 1, name, |
| 316 | }); | 320 | }); |
| 317 | any_errors = true; | 321 | any_errors = true; |
| ... | @@ -321,7 +325,8 @@ fn render_cmake( | ... | @@ -321,7 +325,8 @@ fn render_cmake( |
| 321 | } | 325 | } |
| 322 | 326 | ||
| 323 | for (values_copy.keys()) |name| { | 327 | for (values_copy.keys()) |name| { |
| 324 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | 328 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 329 | any_errors = true; | ||
| 325 | } | 330 | } |
| 326 | 331 | ||
| 327 | if (any_errors) { | 332 | if (any_errors) { |
| ... | @@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! | ... | @@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! |
| 426 | }, | 431 | }, |
| 427 | } | 432 | } |
| 428 | } | 433 | } |
| 434 | |||
| 435 | const std = @import("../std.zig"); | ||
| 436 | const ConfigHeaderStep = @This(); | ||
| 437 | const 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 | |||
| 7 | const std = @import("../std.zig"); | ||
| 8 | const Step = std.Build.Step; | ||
| 9 | const CompileStep = std.Build.CompileStep; | ||
| 10 | const RunStep = std.Build.RunStep; | ||
| 11 | |||
| 12 | const fs = std.fs; | ||
| 13 | const process = std.process; | ||
| 14 | const EnvMap = process.EnvMap; | ||
| 15 | |||
| 16 | const EmulatableRunStep = @This(); | ||
| 17 | |||
| 18 | pub const base_id = .emulatable_run; | ||
| 19 | |||
| 20 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | ||
| 21 | |||
| 22 | step: Step, | ||
| 23 | builder: *std.Build, | ||
| 24 | |||
| 25 | /// The artifact (executable) to be run by this step | ||
| 26 | exe: *CompileStep, | ||
| 27 | |||
| 28 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | ||
| 29 | expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, | ||
| 30 | |||
| 31 | /// Override this field to modify the environment | ||
| 32 | env_map: ?*EnvMap, | ||
| 33 | |||
| 34 | /// Set this to modify the current working directory | ||
| 35 | cwd: ?[]const u8, | ||
| 36 | |||
| 37 | stdout_action: RunStep.StdIoAction = .inherit, | ||
| 38 | stderr_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. | ||
| 42 | hide_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. | ||
| 48 | pub 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 | |||
| 70 | fn 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 | |||
| 144 | pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void { | ||
| 145 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | ||
| 146 | } | ||
| 147 | |||
| 148 | pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void { | ||
| 149 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | ||
| 150 | } | ||
| 151 | |||
| 152 | fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { | ||
| 153 | if (step.hide_foreign_binaries_warning) return; | ||
| 154 | const builder = step.builder; | ||
| 155 | const artifact = step.exe; | ||
| 156 | |||
| 157 | const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error"); | ||
| 158 | const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error"); | ||
| 159 | const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error"); | ||
| 160 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; | ||
| 161 | switch (builder.host.getExternalExecutor(target_info, .{ | ||
| 162 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | ||
| 163 | .link_libc = artifact.is_linking_libc, | ||
| 164 | })) { | ||
| 165 | .native => unreachable, | ||
| 166 | .bad_dl => |foreign_dl| { | ||
| 167 | const host_dl = builder.host.dynamic_linker.get() orelse "(none)"; | ||
| 168 | std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{ | ||
| 169 | host_dl, foreign_dl, host_dl, | ||
| 170 | }); | ||
| 171 | }, | ||
| 172 | .bad_os_or_cpu => { | ||
| 173 | std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{ | ||
| 174 | host_name, foreign_name, | ||
| 175 | }); | ||
| 176 | }, | ||
| 177 | .darling => if (!builder.enable_darling) { | ||
| 178 | std.debug.print( | ||
| 179 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 180 | "from the target ({s}). Consider enabling darling.\n", | ||
| 181 | .{ host_name, foreign_name }, | ||
| 182 | ); | ||
| 183 | }, | ||
| 184 | .rosetta => if (!builder.enable_rosetta) { | ||
| 185 | std.debug.print( | ||
| 186 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 187 | "from the target ({s}). Consider enabling rosetta.\n", | ||
| 188 | .{ host_name, foreign_name }, | ||
| 189 | ); | ||
| 190 | }, | ||
| 191 | .wine => if (!builder.enable_wine) { | ||
| 192 | std.debug.print( | ||
| 193 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 194 | "from the target ({s}). Consider enabling wine.\n", | ||
| 195 | .{ host_name, foreign_name }, | ||
| 196 | ); | ||
| 197 | }, | ||
| 198 | .qemu => if (!builder.enable_qemu) { | ||
| 199 | std.debug.print( | ||
| 200 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 201 | "from the target ({s}). Consider enabling qemu.\n", | ||
| 202 | .{ host_name, foreign_name }, | ||
| 203 | ); | ||
| 204 | }, | ||
| 205 | .wasmtime => { | ||
| 206 | std.debug.print( | ||
| 207 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 208 | "from the target ({s}). Consider enabling wasmtime.\n", | ||
| 209 | .{ host_name, foreign_name }, | ||
| 210 | ); | ||
| 211 | }, | ||
| 212 | } | ||
| 213 | } | ||
lib/std/Build/FmtStep.zig+63-22| ... | @@ -1,32 +1,73 @@ | ... | @@ -1,32 +1,73 @@ |
| 1 | const std = @import("../std.zig"); | 1 | //! This step has two modes: |
| 2 | const Step = std.Build.Step; | 2 | //! * Modify mode: directly modify source files, formatting them in place. |
| 3 | const FmtStep = @This(); | 3 | //! * Check mode: fail the step if a non-conforming file is found. |
| 4 | |||
| 5 | step: Step, | ||
| 6 | paths: []const []const u8, | ||
| 7 | exclude_paths: []const []const u8, | ||
| 8 | check: bool, | ||
| 4 | 9 | ||
| 5 | pub const base_id = .fmt; | 10 | pub const base_id = .fmt; |
| 6 | 11 | ||
| 7 | step: Step, | 12 | pub const Options = struct { |
| 8 | builder: *std.Build, | 13 | paths: []const []const u8 = &.{}, |
| 9 | argv: [][]const u8, | 14 | exclude_paths: []const []const u8 = &.{}, |
| 10 | 15 | /// If true, fails the build step when any non-conforming files are encountered. | |
| 11 | pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { | 16 | check: bool = false, |
| 12 | const self = builder.allocator.create(FmtStep) catch @panic("OOM"); | 17 | }; |
| 13 | const name = "zig fmt"; | ||
| 14 | self.* = FmtStep{ | ||
| 15 | .step = Step.init(.fmt, name, builder.allocator, make), | ||
| 16 | .builder = builder, | ||
| 17 | .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), | ||
| 18 | }; | ||
| 19 | 18 | ||
| 20 | self.argv[0] = builder.zig_exe; | 19 | pub fn create(owner: *std.Build, options: Options) *FmtStep { |
| 21 | self.argv[1] = "fmt"; | 20 | const self = owner.allocator.create(FmtStep) catch @panic("OOM"); |
| 22 | for (paths, 0..) |path, i| { | 21 | const name = if (options.check) "zig fmt --check" else "zig fmt"; |
| 23 | self.argv[2 + i] = builder.pathFromRoot(path); | 22 | self.* = .{ |
| 24 | } | 23 | .step = Step.init(.{ |
| 24 | .id = base_id, | ||
| 25 | .name = name, | ||
| 26 | .owner = owner, | ||
| 27 | .makeFn = make, | ||
| 28 | }), | ||
| 29 | .paths = options.paths, | ||
| 30 | .exclude_paths = options.exclude_paths, | ||
| 31 | .check = options.check, | ||
| 32 | }; | ||
| 25 | return self; | 33 | return self; |
| 26 | } | 34 | } |
| 27 | 35 | ||
| 28 | fn make(step: *Step) !void { | 36 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 37 | // zig fmt is fast enough that no progress is needed. | ||
| 38 | _ = prog_node; | ||
| 39 | |||
| 40 | // TODO: if check=false, this means we are modifying source files in place, which | ||
| 41 | // is an operation that could race against other operations also modifying source files | ||
| 42 | // in place. In this case, this step should obtain a write lock while making those | ||
| 43 | // modifications. | ||
| 44 | |||
| 45 | const b = step.owner; | ||
| 46 | const arena = b.allocator; | ||
| 29 | const self = @fieldParentPtr(FmtStep, "step", step); | 47 | const self = @fieldParentPtr(FmtStep, "step", step); |
| 30 | 48 | ||
| 31 | return self.builder.spawnChild(self.argv); | 49 | var argv: std.ArrayListUnmanaged([]const u8) = .{}; |
| 50 | try argv.ensureUnusedCapacity(arena, 2 + 1 + self.paths.len + 2 * self.exclude_paths.len); | ||
| 51 | |||
| 52 | argv.appendAssumeCapacity(b.zig_exe); | ||
| 53 | argv.appendAssumeCapacity("fmt"); | ||
| 54 | |||
| 55 | if (self.check) { | ||
| 56 | argv.appendAssumeCapacity("--check"); | ||
| 57 | } | ||
| 58 | |||
| 59 | for (self.paths) |p| { | ||
| 60 | argv.appendAssumeCapacity(b.pathFromRoot(p)); | ||
| 61 | } | ||
| 62 | |||
| 63 | for (self.exclude_paths) |p| { | ||
| 64 | argv.appendAssumeCapacity("--exclude"); | ||
| 65 | argv.appendAssumeCapacity(b.pathFromRoot(p)); | ||
| 66 | } | ||
| 67 | |||
| 68 | return step.evalChildProcess(argv.items); | ||
| 32 | } | 69 | } |
| 70 | |||
| 71 | const std = @import("../std.zig"); | ||
| 72 | const Step = std.Build.Step; | ||
| 73 | const FmtStep = @This(); |
lib/std/Build/InstallArtifactStep.zig+77-27| ... | @@ -3,83 +3,133 @@ const Step = std.Build.Step; | ... | @@ -3,83 +3,133 @@ const Step = std.Build.Step; |
| 3 | const CompileStep = std.Build.CompileStep; | 3 | const CompileStep = std.Build.CompileStep; |
| 4 | const InstallDir = std.Build.InstallDir; | 4 | const InstallDir = std.Build.InstallDir; |
| 5 | const InstallArtifactStep = @This(); | 5 | const InstallArtifactStep = @This(); |
| 6 | const fs = std.fs; | ||
| 6 | 7 | ||
| 7 | pub const base_id = .install_artifact; | 8 | pub const base_id = .install_artifact; |
| 8 | 9 | ||
| 9 | step: Step, | 10 | step: Step, |
| 10 | builder: *std.Build, | 11 | dest_builder: *std.Build, |
| 11 | artifact: *CompileStep, | 12 | artifact: *CompileStep, |
| 12 | dest_dir: InstallDir, | 13 | dest_dir: InstallDir, |
| 13 | pdb_dir: ?InstallDir, | 14 | pdb_dir: ?InstallDir, |
| 14 | h_dir: ?InstallDir, | 15 | h_dir: ?InstallDir, |
| 16 | /// If non-null, adds additional path components relative to dest_dir, and | ||
| 17 | /// overrides the basename of the CompileStep. | ||
| 18 | dest_sub_path: ?[]const u8, | ||
| 15 | 19 | ||
| 16 | pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { | 20 | pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { |
| 17 | if (artifact.install_step) |s| return s; | 21 | if (artifact.install_step) |s| return s; |
| 18 | 22 | ||
| 19 | const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); | 23 | const self = owner.allocator.create(InstallArtifactStep) catch @panic("OOM"); |
| 20 | self.* = InstallArtifactStep{ | 24 | self.* = InstallArtifactStep{ |
| 21 | .builder = builder, | 25 | .step = Step.init(.{ |
| 22 | .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), | 26 | .id = base_id, |
| 27 | .name = owner.fmt("install {s}", .{artifact.name}), | ||
| 28 | .owner = owner, | ||
| 29 | .makeFn = make, | ||
| 30 | }), | ||
| 31 | .dest_builder = owner, | ||
| 23 | .artifact = artifact, | 32 | .artifact = artifact, |
| 24 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { | 33 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { |
| 25 | .obj => @panic("Cannot install a .obj build artifact."), | 34 | .obj => @panic("Cannot install a .obj build artifact."), |
| 26 | .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."), | 35 | .exe, .@"test" => InstallDir{ .bin = {} }, |
| 27 | .exe, .test_exe => InstallDir{ .bin = {} }, | ||
| 28 | .lib => InstallDir{ .lib = {} }, | 36 | .lib => InstallDir{ .lib = {} }, |
| 29 | }, | 37 | }, |
| 30 | .pdb_dir = if (artifact.producesPdbFile()) blk: { | 38 | .pdb_dir = if (artifact.producesPdbFile()) blk: { |
| 31 | if (artifact.kind == .exe or artifact.kind == .test_exe) { | 39 | if (artifact.kind == .exe or artifact.kind == .@"test") { |
| 32 | break :blk InstallDir{ .bin = {} }; | 40 | break :blk InstallDir{ .bin = {} }; |
| 33 | } else { | 41 | } else { |
| 34 | break :blk InstallDir{ .lib = {} }; | 42 | break :blk InstallDir{ .lib = {} }; |
| 35 | } | 43 | } |
| 36 | } else null, | 44 | } else null, |
| 37 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, | 45 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, |
| 46 | .dest_sub_path = null, | ||
| 38 | }; | 47 | }; |
| 39 | self.step.dependOn(&artifact.step); | 48 | self.step.dependOn(&artifact.step); |
| 40 | artifact.install_step = self; | 49 | artifact.install_step = self; |
| 41 | 50 | ||
| 42 | builder.pushInstalledFile(self.dest_dir, artifact.out_filename); | 51 | owner.pushInstalledFile(self.dest_dir, artifact.out_filename); |
| 43 | if (self.artifact.isDynamicLibrary()) { | 52 | if (self.artifact.isDynamicLibrary()) { |
| 44 | if (artifact.major_only_filename) |name| { | 53 | if (artifact.major_only_filename) |name| { |
| 45 | builder.pushInstalledFile(.lib, name); | 54 | owner.pushInstalledFile(.lib, name); |
| 46 | } | 55 | } |
| 47 | if (artifact.name_only_filename) |name| { | 56 | if (artifact.name_only_filename) |name| { |
| 48 | builder.pushInstalledFile(.lib, name); | 57 | owner.pushInstalledFile(.lib, name); |
| 49 | } | 58 | } |
| 50 | if (self.artifact.target.isWindows()) { | 59 | if (self.artifact.target.isWindows()) { |
| 51 | builder.pushInstalledFile(.lib, artifact.out_lib_filename); | 60 | owner.pushInstalledFile(.lib, artifact.out_lib_filename); |
| 52 | } | 61 | } |
| 53 | } | 62 | } |
| 54 | if (self.pdb_dir) |pdb_dir| { | 63 | if (self.pdb_dir) |pdb_dir| { |
| 55 | builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); | 64 | owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); |
| 56 | } | 65 | } |
| 57 | if (self.h_dir) |h_dir| { | 66 | if (self.h_dir) |h_dir| { |
| 58 | builder.pushInstalledFile(h_dir, artifact.out_h_filename); | 67 | owner.pushInstalledFile(h_dir, artifact.out_h_filename); |
| 59 | } | 68 | } |
| 60 | return self; | 69 | return self; |
| 61 | } | 70 | } |
| 62 | 71 | ||
| 63 | fn make(step: *Step) !void { | 72 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 73 | _ = prog_node; | ||
| 74 | const src_builder = step.owner; | ||
| 64 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); | 75 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); |
| 65 | const builder = self.builder; | 76 | const dest_builder = self.dest_builder; |
| 66 | 77 | ||
| 67 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); | 78 | const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename; |
| 68 | try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); | 79 | const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path); |
| 69 | if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { | 80 | const cwd = fs.cwd(); |
| 70 | try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); | 81 | |
| 82 | var all_cached = true; | ||
| 83 | |||
| 84 | { | ||
| 85 | const full_src_path = self.artifact.getOutputSource().getPath(src_builder); | ||
| 86 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { | ||
| 87 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 88 | full_src_path, full_dest_path, @errorName(err), | ||
| 89 | }); | ||
| 90 | }; | ||
| 91 | all_cached = all_cached and p == .fresh; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (self.artifact.isDynamicLibrary() and | ||
| 95 | self.artifact.version != null and | ||
| 96 | self.artifact.target.wantSharedLibSymLinks()) | ||
| 97 | { | ||
| 98 | try CompileStep.doAtomicSymLinks(step, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); | ||
| 71 | } | 99 | } |
| 72 | if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { | 100 | if (self.artifact.isDynamicLibrary() and |
| 73 | const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); | 101 | self.artifact.target.isWindows() and |
| 74 | try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path); | 102 | self.artifact.emit_implib != .no_emit) |
| 103 | { | ||
| 104 | const full_src_path = self.artifact.getOutputLibSource().getPath(src_builder); | ||
| 105 | const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); | ||
| 106 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { | ||
| 107 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 108 | full_src_path, full_implib_path, @errorName(err), | ||
| 109 | }); | ||
| 110 | }; | ||
| 111 | all_cached = all_cached and p == .fresh; | ||
| 75 | } | 112 | } |
| 76 | if (self.pdb_dir) |pdb_dir| { | 113 | if (self.pdb_dir) |pdb_dir| { |
| 77 | const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); | 114 | const full_src_path = self.artifact.getOutputPdbSource().getPath(src_builder); |
| 78 | try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path); | 115 | const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); |
| 116 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { | ||
| 117 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 118 | full_src_path, full_pdb_path, @errorName(err), | ||
| 119 | }); | ||
| 120 | }; | ||
| 121 | all_cached = all_cached and p == .fresh; | ||
| 79 | } | 122 | } |
| 80 | if (self.h_dir) |h_dir| { | 123 | if (self.h_dir) |h_dir| { |
| 81 | const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); | 124 | const full_src_path = self.artifact.getOutputHSource().getPath(src_builder); |
| 82 | try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path); | 125 | const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename); |
| 126 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { | ||
| 127 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 128 | full_src_path, full_h_path, @errorName(err), | ||
| 129 | }); | ||
| 130 | }; | ||
| 131 | all_cached = all_cached and p == .fresh; | ||
| 83 | } | 132 | } |
| 84 | self.artifact.installed_path = full_dest_path; | 133 | self.artifact.installed_path = full_dest_path; |
| 134 | step.result_cached = all_cached; | ||
| 85 | } | 135 | } |
lib/std/Build/InstallDirStep.zig+43-26| ... | @@ -4,14 +4,12 @@ const fs = std.fs; | ... | @@ -4,14 +4,12 @@ const fs = std.fs; |
| 4 | const Step = std.Build.Step; | 4 | const Step = std.Build.Step; |
| 5 | const InstallDir = std.Build.InstallDir; | 5 | const InstallDir = std.Build.InstallDir; |
| 6 | const InstallDirStep = @This(); | 6 | const InstallDirStep = @This(); |
| 7 | const log = std.log; | ||
| 8 | 7 | ||
| 9 | step: Step, | 8 | step: Step, |
| 10 | builder: *std.Build, | ||
| 11 | options: Options, | 9 | options: Options, |
| 12 | /// This is used by the build system when a file being installed comes from one | 10 | /// This is used by the build system when a file being installed comes from one |
| 13 | /// package but is being installed by another. | 11 | /// package but is being installed by another. |
| 14 | override_source_builder: ?*std.Build = null, | 12 | dest_builder: *std.Build, |
| 15 | 13 | ||
| 16 | pub const base_id = .install_dir; | 14 | pub const base_id = .install_dir; |
| 17 | 15 | ||
| ... | @@ -40,31 +38,35 @@ pub const Options = struct { | ... | @@ -40,31 +38,35 @@ pub const Options = struct { |
| 40 | } | 38 | } |
| 41 | }; | 39 | }; |
| 42 | 40 | ||
| 43 | pub fn init( | 41 | pub fn init(owner: *std.Build, options: Options) InstallDirStep { |
| 44 | builder: *std.Build, | 42 | owner.pushInstalledFile(options.install_dir, options.install_subdir); |
| 45 | options: Options, | 43 | return .{ |
| 46 | ) InstallDirStep { | 44 | .step = Step.init(.{ |
| 47 | builder.pushInstalledFile(options.install_dir, options.install_subdir); | 45 | .id = .install_dir, |
| 48 | return InstallDirStep{ | 46 | .name = owner.fmt("install {s}/", .{options.source_dir}), |
| 49 | .builder = builder, | 47 | .owner = owner, |
| 50 | .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), | 48 | .makeFn = make, |
| 51 | .options = options.dupe(builder), | 49 | }), |
| 50 | .options = options.dupe(owner), | ||
| 51 | .dest_builder = owner, | ||
| 52 | }; | 52 | }; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | fn make(step: *Step) !void { | 55 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 56 | _ = prog_node; | ||
| 56 | const self = @fieldParentPtr(InstallDirStep, "step", step); | 57 | const self = @fieldParentPtr(InstallDirStep, "step", step); |
| 57 | const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); | 58 | const dest_builder = self.dest_builder; |
| 58 | const src_builder = self.override_source_builder orelse self.builder; | 59 | const arena = dest_builder.allocator; |
| 59 | const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); | 60 | const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir); |
| 60 | var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { | 61 | const src_builder = self.step.owner; |
| 61 | log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ | 62 | var src_dir = src_builder.build_root.handle.openIterableDir(self.options.source_dir, .{}) catch |err| { |
| 62 | full_src_dir, @errorName(err), | 63 | return step.fail("unable to open source directory '{}{s}': {s}", .{ |
| 64 | src_builder.build_root, self.options.source_dir, @errorName(err), | ||
| 63 | }); | 65 | }); |
| 64 | return error.StepFailed; | ||
| 65 | }; | 66 | }; |
| 66 | defer src_dir.close(); | 67 | defer src_dir.close(); |
| 67 | var it = try src_dir.walk(self.builder.allocator); | 68 | var it = try src_dir.walk(arena); |
| 69 | var all_cached = true; | ||
| 68 | next_entry: while (try it.next()) |entry| { | 70 | next_entry: while (try it.next()) |entry| { |
| 69 | for (self.options.exclude_extensions) |ext| { | 71 | for (self.options.exclude_extensions) |ext| { |
| 70 | if (mem.endsWith(u8, entry.path, ext)) { | 72 | if (mem.endsWith(u8, entry.path, ext)) { |
| ... | @@ -72,22 +74,37 @@ fn make(step: *Step) !void { | ... | @@ -72,22 +74,37 @@ fn make(step: *Step) !void { |
| 72 | } | 74 | } |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); | 77 | // relative to src build root |
| 76 | const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); | 78 | const src_sub_path = try fs.path.join(arena, &.{ self.options.source_dir, entry.path }); |
| 79 | const dest_path = try fs.path.join(arena, &.{ dest_prefix, entry.path }); | ||
| 80 | const cwd = fs.cwd(); | ||
| 77 | 81 | ||
| 78 | switch (entry.kind) { | 82 | switch (entry.kind) { |
| 79 | .Directory => try fs.cwd().makePath(dest_path), | 83 | .Directory => try cwd.makePath(dest_path), |
| 80 | .File => { | 84 | .File => { |
| 81 | for (self.options.blank_extensions) |ext| { | 85 | for (self.options.blank_extensions) |ext| { |
| 82 | if (mem.endsWith(u8, entry.path, ext)) { | 86 | if (mem.endsWith(u8, entry.path, ext)) { |
| 83 | try self.builder.truncateFile(dest_path); | 87 | try dest_builder.truncateFile(dest_path); |
| 84 | continue :next_entry; | 88 | continue :next_entry; |
| 85 | } | 89 | } |
| 86 | } | 90 | } |
| 87 | 91 | ||
| 88 | try self.builder.updateFile(full_path, dest_path); | 92 | const prev_status = fs.Dir.updateFile( |
| 93 | src_builder.build_root.handle, | ||
| 94 | src_sub_path, | ||
| 95 | cwd, | ||
| 96 | dest_path, | ||
| 97 | .{}, | ||
| 98 | ) catch |err| { | ||
| 99 | return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{ | ||
| 100 | src_builder.build_root, src_sub_path, dest_path, @errorName(err), | ||
| 101 | }); | ||
| 102 | }; | ||
| 103 | all_cached = all_cached and prev_status == .fresh; | ||
| 89 | }, | 104 | }, |
| 90 | else => continue, | 105 | else => continue, |
| 91 | } | 106 | } |
| 92 | } | 107 | } |
| 108 | |||
| 109 | step.result_cached = all_cached; | ||
| 93 | } | 110 | } |
lib/std/Build/InstallFileStep.zig+34-17| ... | @@ -3,38 +3,55 @@ const Step = std.Build.Step; | ... | @@ -3,38 +3,55 @@ const Step = std.Build.Step; |
| 3 | const FileSource = std.Build.FileSource; | 3 | const FileSource = std.Build.FileSource; |
| 4 | const InstallDir = std.Build.InstallDir; | 4 | const InstallDir = std.Build.InstallDir; |
| 5 | const InstallFileStep = @This(); | 5 | const InstallFileStep = @This(); |
| 6 | const assert = std.debug.assert; | ||
| 6 | 7 | ||
| 7 | pub const base_id = .install_file; | 8 | pub const base_id = .install_file; |
| 8 | 9 | ||
| 9 | step: Step, | 10 | step: Step, |
| 10 | builder: *std.Build, | ||
| 11 | source: FileSource, | 11 | source: FileSource, |
| 12 | dir: InstallDir, | 12 | dir: InstallDir, |
| 13 | dest_rel_path: []const u8, | 13 | dest_rel_path: []const u8, |
| 14 | /// This is used by the build system when a file being installed comes from one | 14 | /// This is used by the build system when a file being installed comes from one |
| 15 | /// package but is being installed by another. | 15 | /// package but is being installed by another. |
| 16 | override_source_builder: ?*std.Build = null, | 16 | dest_builder: *std.Build, |
| 17 | 17 | ||
| 18 | pub fn init( | 18 | pub fn create( |
| 19 | builder: *std.Build, | 19 | owner: *std.Build, |
| 20 | source: FileSource, | 20 | source: FileSource, |
| 21 | dir: InstallDir, | 21 | dir: InstallDir, |
| 22 | dest_rel_path: []const u8, | 22 | dest_rel_path: []const u8, |
| 23 | ) InstallFileStep { | 23 | ) *InstallFileStep { |
| 24 | builder.pushInstalledFile(dir, dest_rel_path); | 24 | assert(dest_rel_path.len != 0); |
| 25 | return InstallFileStep{ | 25 | owner.pushInstalledFile(dir, dest_rel_path); |
| 26 | .builder = builder, | 26 | const self = owner.allocator.create(InstallFileStep) catch @panic("OOM"); |
| 27 | .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), | 27 | self.* = .{ |
| 28 | .source = source.dupe(builder), | 28 | .step = Step.init(.{ |
| 29 | .dir = dir.dupe(builder), | 29 | .id = base_id, |
| 30 | .dest_rel_path = builder.dupePath(dest_rel_path), | 30 | .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), |
| 31 | .owner = owner, | ||
| 32 | .makeFn = make, | ||
| 33 | }), | ||
| 34 | .source = source.dupe(owner), | ||
| 35 | .dir = dir.dupe(owner), | ||
| 36 | .dest_rel_path = owner.dupePath(dest_rel_path), | ||
| 37 | .dest_builder = owner, | ||
| 31 | }; | 38 | }; |
| 39 | source.addStepDependencies(&self.step); | ||
| 40 | return self; | ||
| 32 | } | 41 | } |
| 33 | 42 | ||
| 34 | fn make(step: *Step) !void { | 43 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 44 | _ = prog_node; | ||
| 45 | const src_builder = step.owner; | ||
| 35 | const self = @fieldParentPtr(InstallFileStep, "step", step); | 46 | const self = @fieldParentPtr(InstallFileStep, "step", step); |
| 36 | const src_builder = self.override_source_builder orelse self.builder; | 47 | const dest_builder = self.dest_builder; |
| 37 | const full_src_path = self.source.getPath(src_builder); | 48 | const full_src_path = self.source.getPath2(src_builder, step); |
| 38 | const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); | 49 | const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path); |
| 39 | try self.builder.updateFile(full_src_path, full_dest_path); | 50 | const cwd = std.fs.cwd(); |
| 51 | const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { | ||
| 52 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 53 | full_src_path, full_dest_path, @errorName(err), | ||
| 54 | }); | ||
| 55 | }; | ||
| 56 | step.result_cached = prev == .fresh; | ||
| 40 | } | 57 | } |
lib/std/Build/LogStep.zig deleted-23| ... | @@ -1,23 +0,0 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const log = std.log; | ||
| 3 | const Step = std.Build.Step; | ||
| 4 | const LogStep = @This(); | ||
| 5 | |||
| 6 | pub const base_id = .log; | ||
| 7 | |||
| 8 | step: Step, | ||
| 9 | builder: *std.Build, | ||
| 10 | data: []const u8, | ||
| 11 | |||
| 12 | pub 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 | |||
| 20 | fn make(step: *Step) anyerror!void { | ||
| 21 | const self = @fieldParentPtr(LogStep, "step", step); | ||
| 22 | log.info("{s}", .{self.data}); | ||
| 23 | } | ||
lib/std/Build/ObjCopyStep.zig+15-31| ... | @@ -21,7 +21,6 @@ pub const RawFormat = enum { | ... | @@ -21,7 +21,6 @@ pub const RawFormat = enum { |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | step: Step, | 23 | step: Step, |
| 24 | builder: *std.Build, | ||
| 25 | file_source: std.Build.FileSource, | 24 | file_source: std.Build.FileSource, |
| 26 | basename: []const u8, | 25 | basename: []const u8, |
| 27 | output_file: std.Build.GeneratedFile, | 26 | output_file: std.Build.GeneratedFile, |
| ... | @@ -38,19 +37,18 @@ pub const Options = struct { | ... | @@ -38,19 +37,18 @@ pub const Options = struct { |
| 38 | }; | 37 | }; |
| 39 | 38 | ||
| 40 | pub fn create( | 39 | pub fn create( |
| 41 | builder: *std.Build, | 40 | owner: *std.Build, |
| 42 | file_source: std.Build.FileSource, | 41 | file_source: std.Build.FileSource, |
| 43 | options: Options, | 42 | options: Options, |
| 44 | ) *ObjCopyStep { | 43 | ) *ObjCopyStep { |
| 45 | const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM"); | 44 | const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM"); |
| 46 | self.* = ObjCopyStep{ | 45 | self.* = ObjCopyStep{ |
| 47 | .step = Step.init( | 46 | .step = Step.init(.{ |
| 48 | base_id, | 47 | .id = base_id, |
| 49 | builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), | 48 | .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}), |
| 50 | builder.allocator, | 49 | .owner = owner, |
| 51 | make, | 50 | .makeFn = make, |
| 52 | ), | 51 | }), |
| 53 | .builder = builder, | ||
| 54 | .file_source = file_source, | 52 | .file_source = file_source, |
| 55 | .basename = options.basename orelse file_source.getDisplayName(), | 53 | .basename = options.basename orelse file_source.getDisplayName(), |
| 56 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, | 54 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, |
| ... | @@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { | ... | @@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { |
| 67 | return .{ .generated = &self.output_file }; | 65 | return .{ .generated = &self.output_file }; |
| 68 | } | 66 | } |
| 69 | 67 | ||
| 70 | fn make(step: *Step) !void { | 68 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 69 | const b = step.owner; | ||
| 71 | const self = @fieldParentPtr(ObjCopyStep, "step", step); | 70 | const self = @fieldParentPtr(ObjCopyStep, "step", step); |
| 72 | const b = self.builder; | ||
| 73 | 71 | ||
| 74 | var man = b.cache.obtain(); | 72 | var man = b.cache.obtain(); |
| 75 | defer man.deinit(); | 73 | defer man.deinit(); |
| ... | @@ -84,7 +82,7 @@ fn make(step: *Step) !void { | ... | @@ -84,7 +82,7 @@ fn make(step: *Step) !void { |
| 84 | man.hash.addOptional(self.pad_to); | 82 | man.hash.addOptional(self.pad_to); |
| 85 | man.hash.addOptional(self.format); | 83 | man.hash.addOptional(self.format); |
| 86 | 84 | ||
| 87 | if (man.hit() catch |err| failWithCacheError(man, err)) { | 85 | if (try step.cacheHit(&man)) { |
| 88 | // Cache hit, skip subprocess execution. | 86 | // Cache hit, skip subprocess execution. |
| 89 | const digest = man.final(); | 87 | const digest = man.final(); |
| 90 | self.output_file.path = try b.cache_root.join(b.allocator, &.{ | 88 | self.output_file.path = try b.cache_root.join(b.allocator, &.{ |
| ... | @@ -97,8 +95,7 @@ fn make(step: *Step) !void { | ... | @@ -97,8 +95,7 @@ fn make(step: *Step) !void { |
| 97 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); | 95 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); |
| 98 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | 96 | const cache_path = "o" ++ fs.path.sep_str ++ digest; |
| 99 | b.cache_root.handle.makePath(cache_path) catch |err| { | 97 | b.cache_root.handle.makePath(cache_path) catch |err| { |
| 100 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | 98 | return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) }); |
| 101 | return err; | ||
| 102 | }; | 99 | }; |
| 103 | 100 | ||
| 104 | var argv = std.ArrayList([]const u8).init(b.allocator); | 101 | var argv = std.ArrayList([]const u8).init(b.allocator); |
| ... | @@ -116,23 +113,10 @@ fn make(step: *Step) !void { | ... | @@ -116,23 +113,10 @@ fn make(step: *Step) !void { |
| 116 | }; | 113 | }; |
| 117 | 114 | ||
| 118 | try argv.appendSlice(&.{ full_src_path, full_dest_path }); | 115 | try argv.appendSlice(&.{ full_src_path, full_dest_path }); |
| 119 | _ = try self.builder.execFromStep(argv.items, &self.step); | 116 | |
| 117 | try argv.append("--listen=-"); | ||
| 118 | _ = try step.evalZigProcess(argv.items, prog_node); | ||
| 120 | 119 | ||
| 121 | self.output_file.path = full_dest_path; | 120 | self.output_file.path = full_dest_path; |
| 122 | try man.writeManifest(); | 121 | try man.writeManifest(); |
| 123 | } | 122 | } |
| 124 | |||
| 125 | /// TODO consolidate this with the same function in RunStep? | ||
| 126 | /// Also properly deal with concurrency (see open PR) | ||
| 127 | fn 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 | |||
| 135 | fn failWithSimpleError(err: anyerror) noreturn { | ||
| 136 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 137 | std.process.exit(1); | ||
| 138 | } |
lib/std/Build/OptionsStep.zig+23-18| ... | @@ -12,21 +12,24 @@ pub const base_id = .options; | ... | @@ -12,21 +12,24 @@ pub const base_id = .options; |
| 12 | 12 | ||
| 13 | step: Step, | 13 | step: Step, |
| 14 | generated_file: GeneratedFile, | 14 | generated_file: GeneratedFile, |
| 15 | builder: *std.Build, | ||
| 16 | 15 | ||
| 17 | contents: std.ArrayList(u8), | 16 | contents: std.ArrayList(u8), |
| 18 | artifact_args: std.ArrayList(OptionArtifactArg), | 17 | artifact_args: std.ArrayList(OptionArtifactArg), |
| 19 | file_source_args: std.ArrayList(OptionFileSourceArg), | 18 | file_source_args: std.ArrayList(OptionFileSourceArg), |
| 20 | 19 | ||
| 21 | pub fn create(builder: *std.Build) *OptionsStep { | 20 | pub fn create(owner: *std.Build) *OptionsStep { |
| 22 | const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); | 21 | const self = owner.allocator.create(OptionsStep) catch @panic("OOM"); |
| 23 | self.* = .{ | 22 | self.* = .{ |
| 24 | .builder = builder, | 23 | .step = Step.init(.{ |
| 25 | .step = Step.init(.options, "options", builder.allocator, make), | 24 | .id = base_id, |
| 25 | .name = "options", | ||
| 26 | .owner = owner, | ||
| 27 | .makeFn = make, | ||
| 28 | }), | ||
| 26 | .generated_file = undefined, | 29 | .generated_file = undefined, |
| 27 | .contents = std.ArrayList(u8).init(builder.allocator), | 30 | .contents = std.ArrayList(u8).init(owner.allocator), |
| 28 | .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator), | 31 | .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator), |
| 29 | .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator), | 32 | .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator), |
| 30 | }; | 33 | }; |
| 31 | self.generated_file = .{ .step = &self.step }; | 34 | self.generated_file = .{ .step = &self.step }; |
| 32 | 35 | ||
| ... | @@ -192,7 +195,7 @@ pub fn addOptionFileSource( | ... | @@ -192,7 +195,7 @@ pub fn addOptionFileSource( |
| 192 | ) void { | 195 | ) void { |
| 193 | self.file_source_args.append(.{ | 196 | self.file_source_args.append(.{ |
| 194 | .name = name, | 197 | .name = name, |
| 195 | .source = source.dupe(self.builder), | 198 | .source = source.dupe(self.step.owner), |
| 196 | }) catch @panic("OOM"); | 199 | }) catch @panic("OOM"); |
| 197 | source.addStepDependencies(&self.step); | 200 | source.addStepDependencies(&self.step); |
| 198 | } | 201 | } |
| ... | @@ -200,12 +203,12 @@ pub fn addOptionFileSource( | ... | @@ -200,12 +203,12 @@ pub fn addOptionFileSource( |
| 200 | /// The value is the path in the cache dir. | 203 | /// The value is the path in the cache dir. |
| 201 | /// Adds a dependency automatically. | 204 | /// Adds a dependency automatically. |
| 202 | pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { | 205 | pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { |
| 203 | self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM"); | 206 | self.artifact_args.append(.{ .name = self.step.owner.dupe(name), .artifact = artifact }) catch @panic("OOM"); |
| 204 | self.step.dependOn(&artifact.step); | 207 | self.step.dependOn(&artifact.step); |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 207 | pub fn createModule(self: *OptionsStep) *std.Build.Module { | 210 | pub fn createModule(self: *OptionsStep) *std.Build.Module { |
| 208 | return self.builder.createModule(.{ | 211 | return self.step.owner.createModule(.{ |
| 209 | .source_file = self.getSource(), | 212 | .source_file = self.getSource(), |
| 210 | .dependencies = &.{}, | 213 | .dependencies = &.{}, |
| 211 | }); | 214 | }); |
| ... | @@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource { | ... | @@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource { |
| 215 | return .{ .generated = &self.generated_file }; | 218 | return .{ .generated = &self.generated_file }; |
| 216 | } | 219 | } |
| 217 | 220 | ||
| 218 | fn make(step: *Step) !void { | 221 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 222 | // This step completes so quickly that no progress is necessary. | ||
| 223 | _ = prog_node; | ||
| 224 | |||
| 225 | const b = step.owner; | ||
| 219 | const self = @fieldParentPtr(OptionsStep, "step", step); | 226 | const self = @fieldParentPtr(OptionsStep, "step", step); |
| 220 | 227 | ||
| 221 | for (self.artifact_args.items) |item| { | 228 | for (self.artifact_args.items) |item| { |
| 222 | self.addOption( | 229 | self.addOption( |
| 223 | []const u8, | 230 | []const u8, |
| 224 | item.name, | 231 | item.name, |
| 225 | self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)), | 232 | b.pathFromRoot(item.artifact.getOutputSource().getPath(b)), |
| 226 | ); | 233 | ); |
| 227 | } | 234 | } |
| 228 | 235 | ||
| ... | @@ -230,20 +237,18 @@ fn make(step: *Step) !void { | ... | @@ -230,20 +237,18 @@ fn make(step: *Step) !void { |
| 230 | self.addOption( | 237 | self.addOption( |
| 231 | []const u8, | 238 | []const u8, |
| 232 | item.name, | 239 | item.name, |
| 233 | item.source.getPath(self.builder), | 240 | item.source.getPath(b), |
| 234 | ); | 241 | ); |
| 235 | } | 242 | } |
| 236 | 243 | ||
| 237 | var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{}); | 244 | var options_dir = try b.cache_root.handle.makeOpenPath("options", .{}); |
| 238 | defer options_dir.close(); | 245 | defer options_dir.close(); |
| 239 | 246 | ||
| 240 | const basename = self.hashContentsToFileName(); | 247 | const basename = self.hashContentsToFileName(); |
| 241 | 248 | ||
| 242 | try options_dir.writeFile(&basename, self.contents.items); | 249 | try options_dir.writeFile(&basename, self.contents.items); |
| 243 | 250 | ||
| 244 | self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{ | 251 | self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename }); |
| 245 | "options", &basename, | ||
| 246 | }); | ||
| 247 | } | 252 | } |
| 248 | 253 | ||
| 249 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { | 254 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { |
lib/std/Build/RemoveDirStep.zig+24-11| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const log = std.log; | ||
| 3 | const fs = std.fs; | 2 | const fs = std.fs; |
| 4 | const Step = std.Build.Step; | 3 | const Step = std.Build.Step; |
| 5 | const RemoveDirStep = @This(); | 4 | const RemoveDirStep = @This(); |
| ... | @@ -7,23 +6,37 @@ const RemoveDirStep = @This(); | ... | @@ -7,23 +6,37 @@ const RemoveDirStep = @This(); |
| 7 | pub const base_id = .remove_dir; | 6 | pub const base_id = .remove_dir; |
| 8 | 7 | ||
| 9 | step: Step, | 8 | step: Step, |
| 10 | builder: *std.Build, | ||
| 11 | dir_path: []const u8, | 9 | dir_path: []const u8, |
| 12 | 10 | ||
| 13 | pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { | 11 | pub fn init(owner: *std.Build, dir_path: []const u8) RemoveDirStep { |
| 14 | return RemoveDirStep{ | 12 | return RemoveDirStep{ |
| 15 | .builder = builder, | 13 | .step = Step.init(.{ |
| 16 | .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), | 14 | .id = .remove_dir, |
| 17 | .dir_path = builder.dupePath(dir_path), | 15 | .name = owner.fmt("RemoveDir {s}", .{dir_path}), |
| 16 | .owner = owner, | ||
| 17 | .makeFn = make, | ||
| 18 | }), | ||
| 19 | .dir_path = owner.dupePath(dir_path), | ||
| 18 | }; | 20 | }; |
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | fn make(step: *Step) !void { | 23 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 24 | // TODO update progress node while walking file system. | ||
| 25 | // Should the standard library support this use case?? | ||
| 26 | _ = prog_node; | ||
| 27 | |||
| 28 | const b = step.owner; | ||
| 22 | const self = @fieldParentPtr(RemoveDirStep, "step", step); | 29 | const self = @fieldParentPtr(RemoveDirStep, "step", step); |
| 23 | 30 | ||
| 24 | const full_path = self.builder.pathFromRoot(self.dir_path); | 31 | b.build_root.handle.deleteTree(self.dir_path) catch |err| { |
| 25 | fs.cwd().deleteTree(full_path) catch |err| { | 32 | if (b.build_root.path) |base| { |
| 26 | log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) }); | 33 | return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{ |
| 27 | return err; | 34 | base, self.dir_path, @errorName(err), |
| 35 | }); | ||
| 36 | } else { | ||
| 37 | return step.fail("unable to recursively delete path '{s}': {s}", .{ | ||
| 38 | self.dir_path, @errorName(err), | ||
| 39 | }); | ||
| 40 | } | ||
| 28 | }; | 41 | }; |
| 29 | } | 42 | } |
lib/std/Build/RunStep.zig+975-271| ... | @@ -10,76 +10,136 @@ const ArrayList = std.ArrayList; | ... | @@ -10,76 +10,136 @@ const ArrayList = std.ArrayList; |
| 10 | const EnvMap = process.EnvMap; | 10 | const EnvMap = process.EnvMap; |
| 11 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 12 | const ExecError = std.Build.ExecError; | 12 | const ExecError = std.Build.ExecError; |
| 13 | 13 | const assert = std.debug.assert; | |
| 14 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | ||
| 15 | 14 | ||
| 16 | const RunStep = @This(); | 15 | const RunStep = @This(); |
| 17 | 16 | ||
| 18 | pub const base_id: Step.Id = .run; | 17 | pub const base_id: Step.Id = .run; |
| 19 | 18 | ||
| 20 | step: Step, | 19 | step: Step, |
| 21 | builder: *std.Build, | ||
| 22 | 20 | ||
| 23 | /// See also addArg and addArgs to modifying this directly | 21 | /// See also addArg and addArgs to modifying this directly |
| 24 | argv: ArrayList(Arg), | 22 | argv: ArrayList(Arg), |
| 25 | 23 | ||
| 26 | /// Set this to modify the current working directory | 24 | /// 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. | ||
| 27 | cwd: ?[]const u8, | 27 | cwd: ?[]const u8, |
| 28 | 28 | ||
| 29 | /// Override this field to modify the environment, or use setEnvironmentVariable | 29 | /// Override this field to modify the environment, or use setEnvironmentVariable |
| 30 | env_map: ?*EnvMap, | 30 | env_map: ?*EnvMap, |
| 31 | 31 | ||
| 32 | stdout_action: StdIoAction = .inherit, | 32 | /// Configures whether the RunStep is considered to have side-effects, and also |
| 33 | stderr_action: StdIoAction = .inherit, | 33 | /// whether the RunStep will inherit stdio streams, forwarding them to the |
| 34 | 34 | /// parent process, in which case will require a global lock to prevent other | |
| 35 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, | 35 | /// steps from interfering with stdio while the subprocess associated with this |
| 36 | 36 | /// RunStep is running. | |
| 37 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | 37 | /// If the RunStep is determined to not have side-effects, then execution will |
| 38 | expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, | 38 | /// be skipped if all output files are up-to-date and input files are |
| 39 | 39 | /// unchanged. | |
| 40 | /// Print the command before running it | 40 | stdio: StdIo = .infer_from_args, |
| 41 | print: bool, | 41 | /// This field must be `null` if stdio is `inherit`. |
| 42 | /// Controls whether execution is skipped if the output file is up-to-date. | 42 | stdin: ?[]const u8 = null, |
| 43 | /// The default is to always run if there is no output file, and to skip | ||
| 44 | /// running if all output files are up-to-date. | ||
| 45 | condition: enum { output_outdated, always } = .output_outdated, | ||
| 46 | 43 | ||
| 47 | /// Additional file paths relative to build.zig that, when modified, indicate | 44 | /// Additional file paths relative to build.zig that, when modified, indicate |
| 48 | /// that the RunStep should be re-executed. | 45 | /// that the RunStep should be re-executed. |
| 46 | /// If the RunStep is determined to have side-effects, this field is ignored | ||
| 47 | /// and the RunStep is always executed when it appears in the build graph. | ||
| 49 | extra_file_dependencies: []const []const u8 = &.{}, | 48 | extra_file_dependencies: []const []const u8 = &.{}, |
| 50 | 49 | ||
| 51 | pub 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. | ||
| 53 | rename_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. | ||
| 64 | skip_foreign_checks: bool = false, | ||
| 65 | |||
| 66 | /// If stderr or stdout exceeds this amount, the child process is killed and | ||
| 67 | /// the step fails. | ||
| 68 | max_stdio_size: usize = 10 * 1024 * 1024, | ||
| 69 | |||
| 70 | captured_stdout: ?*Output = null, | ||
| 71 | captured_stderr: ?*Output = null, | ||
| 72 | |||
| 73 | has_side_effects: bool = false, | ||
| 74 | |||
| 75 | pub const StdIo = union(enum) { | ||
| 76 | /// Whether the RunStep has side-effects will be determined by whether or not one | ||
| 77 | /// of the args is an output file (added with `addOutputFileArg`). | ||
| 78 | /// If the RunStep is determined to have side-effects, this is the same as `inherit`. | ||
| 79 | /// The step will fail if the subprocess crashes or returns a non-zero exit code. | ||
| 80 | infer_from_args, | ||
| 81 | /// Causes the RunStep to be considered to have side-effects, and therefore | ||
| 82 | /// always execute when it appears in the build graph. | ||
| 83 | /// It also means that this step will obtain a global lock to prevent other | ||
| 84 | /// steps from running in the meantime. | ||
| 85 | /// The step will fail if the subprocess crashes or returns a non-zero exit code. | ||
| 52 | inherit, | 86 | inherit, |
| 53 | ignore, | 87 | /// Causes the RunStep to be considered to *not* have side-effects. The |
| 54 | expect_exact: []const u8, | 88 | /// process will be re-executed if any of the input dependencies are |
| 55 | expect_matches: []const []const u8, | 89 | /// modified. The exit code and standard I/O streams will be checked for |
| 90 | /// certain conditions, and the step will succeed or fail based on these | ||
| 91 | /// conditions. | ||
| 92 | /// Note that an explicit check for exit code 0 needs to be added to this | ||
| 93 | /// list if such a check is desireable. | ||
| 94 | check: std.ArrayList(Check), | ||
| 95 | /// This RunStep is running a zig unit test binary and will communicate | ||
| 96 | /// extra metadata over the IPC protocol. | ||
| 97 | zig_test, | ||
| 98 | |||
| 99 | pub const Check = union(enum) { | ||
| 100 | expect_stderr_exact: []const u8, | ||
| 101 | expect_stderr_match: []const u8, | ||
| 102 | expect_stdout_exact: []const u8, | ||
| 103 | expect_stdout_match: []const u8, | ||
| 104 | expect_term: std.process.Child.Term, | ||
| 105 | }; | ||
| 56 | }; | 106 | }; |
| 57 | 107 | ||
| 58 | pub const Arg = union(enum) { | 108 | pub const Arg = union(enum) { |
| 59 | artifact: *CompileStep, | 109 | artifact: *CompileStep, |
| 60 | file_source: std.Build.FileSource, | 110 | file_source: std.Build.FileSource, |
| 111 | directory_source: std.Build.FileSource, | ||
| 61 | bytes: []u8, | 112 | bytes: []u8, |
| 62 | output: Output, | 113 | output: *Output, |
| 114 | }; | ||
| 63 | 115 | ||
| 64 | pub const Output = struct { | 116 | pub const Output = struct { |
| 65 | generated_file: *std.Build.GeneratedFile, | 117 | generated_file: std.Build.GeneratedFile, |
| 66 | basename: []const u8, | 118 | prefix: []const u8, |
| 67 | }; | 119 | basename: []const u8, |
| 68 | }; | 120 | }; |
| 69 | 121 | ||
| 70 | pub fn create(builder: *std.Build, name: []const u8) *RunStep { | 122 | pub fn create(owner: *std.Build, name: []const u8) *RunStep { |
| 71 | const self = builder.allocator.create(RunStep) catch @panic("OOM"); | 123 | const self = owner.allocator.create(RunStep) catch @panic("OOM"); |
| 72 | self.* = RunStep{ | 124 | self.* = .{ |
| 73 | .builder = builder, | 125 | .step = Step.init(.{ |
| 74 | .step = Step.init(base_id, name, builder.allocator, make), | 126 | .id = base_id, |
| 75 | .argv = ArrayList(Arg).init(builder.allocator), | 127 | .name = name, |
| 128 | .owner = owner, | ||
| 129 | .makeFn = make, | ||
| 130 | }), | ||
| 131 | .argv = ArrayList(Arg).init(owner.allocator), | ||
| 76 | .cwd = null, | 132 | .cwd = null, |
| 77 | .env_map = null, | 133 | .env_map = null, |
| 78 | .print = builder.verbose, | ||
| 79 | }; | 134 | }; |
| 80 | return self; | 135 | return self; |
| 81 | } | 136 | } |
| 82 | 137 | ||
| 138 | pub fn setName(self: *RunStep, name: []const u8) void { | ||
| 139 | self.step.name = name; | ||
| 140 | self.rename_step_with_output_arg = false; | ||
| 141 | } | ||
| 142 | |||
| 83 | pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { | 143 | pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { |
| 84 | self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); | 144 | self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); |
| 85 | self.step.dependOn(&artifact.step); | 145 | self.step.dependOn(&artifact.step); |
| ... | @@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { | ... | @@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { |
| 89 | /// run, and returns a FileSource which can be used as inputs to other APIs | 149 | /// run, and returns a FileSource which can be used as inputs to other APIs |
| 90 | /// throughout the build system. | 150 | /// throughout the build system. |
| 91 | pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { | 151 | pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { |
| 92 | const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM"); | 152 | return addPrefixedOutputFileArg(rs, "", basename); |
| 93 | generated_file.* = .{ .step = &rs.step }; | 153 | } |
| 94 | rs.argv.append(.{ .output = .{ | ||
| 95 | .generated_file = generated_file, | ||
| 96 | .basename = rs.builder.dupe(basename), | ||
| 97 | } }) catch @panic("OOM"); | ||
| 98 | 154 | ||
| 99 | return .{ .generated = generated_file }; | 155 | pub fn addPrefixedOutputFileArg( |
| 156 | rs: *RunStep, | ||
| 157 | prefix: []const u8, | ||
| 158 | basename: []const u8, | ||
| 159 | ) std.Build.FileSource { | ||
| 160 | const b = rs.step.owner; | ||
| 161 | |||
| 162 | const output = b.allocator.create(Output) catch @panic("OOM"); | ||
| 163 | output.* = .{ | ||
| 164 | .prefix = prefix, | ||
| 165 | .basename = basename, | ||
| 166 | .generated_file = .{ .step = &rs.step }, | ||
| 167 | }; | ||
| 168 | rs.argv.append(.{ .output = output }) catch @panic("OOM"); | ||
| 169 | |||
| 170 | if (rs.rename_step_with_output_arg) { | ||
| 171 | rs.setName(b.fmt("{s} ({s})", .{ rs.step.name, basename })); | ||
| 172 | } | ||
| 173 | |||
| 174 | return .{ .generated = &output.generated_file }; | ||
| 100 | } | 175 | } |
| 101 | 176 | ||
| 102 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { | 177 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { |
| 103 | self.argv.append(Arg{ | 178 | self.argv.append(.{ |
| 104 | .file_source = file_source.dupe(self.builder), | 179 | .file_source = file_source.dupe(self.step.owner), |
| 105 | }) catch @panic("OOM"); | 180 | }) catch @panic("OOM"); |
| 106 | file_source.addStepDependencies(&self.step); | 181 | file_source.addStepDependencies(&self.step); |
| 107 | } | 182 | } |
| 108 | 183 | ||
| 184 | pub 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 | |||
| 109 | pub fn addArg(self: *RunStep, arg: []const u8) void { | 191 | pub fn addArg(self: *RunStep, arg: []const u8) void { |
| 110 | self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM"); | 192 | self.argv.append(.{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM"); |
| 111 | } | 193 | } |
| 112 | 194 | ||
| 113 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | 195 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { |
| ... | @@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { | ... | @@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { |
| 117 | } | 199 | } |
| 118 | 200 | ||
| 119 | pub fn clearEnvironment(self: *RunStep) void { | 201 | pub fn clearEnvironment(self: *RunStep) void { |
| 120 | const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM"); | 202 | const b = self.step.owner; |
| 121 | new_env_map.* = EnvMap.init(self.builder.allocator); | 203 | const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM"); |
| 204 | new_env_map.* = EnvMap.init(b.allocator); | ||
| 122 | self.env_map = new_env_map; | 205 | self.env_map = new_env_map; |
| 123 | } | 206 | } |
| 124 | 207 | ||
| 125 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | 208 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { |
| 126 | addPathDirInternal(&self.step, self.builder, search_path); | 209 | const b = self.step.owner; |
| 127 | } | 210 | const env_map = getEnvMapInternal(self); |
| 128 | |||
| 129 | /// For internal use only, users of `RunStep` should use `addPathDir` directly. | ||
| 130 | pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void { | ||
| 131 | const env_map = getEnvMapInternal(step, builder.allocator); | ||
| 132 | 211 | ||
| 133 | const key = "PATH"; | 212 | const key = "PATH"; |
| 134 | var prev_path = env_map.get(key); | 213 | var prev_path = env_map.get(key); |
| 135 | 214 | ||
| 136 | if (prev_path) |pp| { | 215 | if (prev_path) |pp| { |
| 137 | const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); | 216 | const new_path = b.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); |
| 138 | env_map.put(key, new_path) catch @panic("OOM"); | 217 | env_map.put(key, new_path) catch @panic("OOM"); |
| 139 | } else { | 218 | } else { |
| 140 | env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM"); | 219 | env_map.put(key, b.dupePath(search_path)) catch @panic("OOM"); |
| 141 | } | 220 | } |
| 142 | } | 221 | } |
| 143 | 222 | ||
| 144 | pub fn getEnvMap(self: *RunStep) *EnvMap { | 223 | pub fn getEnvMap(self: *RunStep) *EnvMap { |
| 145 | return getEnvMapInternal(&self.step, self.builder.allocator); | 224 | return getEnvMapInternal(self); |
| 146 | } | 225 | } |
| 147 | 226 | ||
| 148 | fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { | 227 | fn getEnvMapInternal(self: *RunStep) *EnvMap { |
| 149 | const maybe_env_map = switch (step.id) { | 228 | const arena = self.step.owner.allocator; |
| 150 | .run => step.cast(RunStep).?.env_map, | 229 | return self.env_map orelse { |
| 151 | .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map, | 230 | const env_map = arena.create(EnvMap) catch @panic("OOM"); |
| 152 | else => unreachable, | 231 | env_map.* = process.getEnvMap(arena) catch @panic("unhandled error"); |
| 153 | }; | 232 | self.env_map = env_map; |
| 154 | return maybe_env_map orelse { | ||
| 155 | const env_map = allocator.create(EnvMap) catch @panic("OOM"); | ||
| 156 | env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); | ||
| 157 | switch (step.id) { | ||
| 158 | .run => step.cast(RunStep).?.env_map = env_map, | ||
| 159 | .emulatable_run => step.cast(RunStep).?.env_map = env_map, | ||
| 160 | else => unreachable, | ||
| 161 | } | ||
| 162 | return env_map; | 233 | return env_map; |
| 163 | }; | 234 | }; |
| 164 | } | 235 | } |
| 165 | 236 | ||
| 166 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | 237 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { |
| 238 | const b = self.step.owner; | ||
| 167 | const env_map = self.getEnvMap(); | 239 | const env_map = self.getEnvMap(); |
| 168 | env_map.put( | 240 | env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); |
| 169 | self.builder.dupe(key), | 241 | } |
| 170 | self.builder.dupe(value), | 242 | |
| 171 | ) catch @panic("unhandled error"); | 243 | pub fn removeEnvironmentVariable(self: *RunStep, key: []const u8) void { |
| 244 | self.getEnvMap().remove(key); | ||
| 172 | } | 245 | } |
| 173 | 246 | ||
| 247 | /// Adds a check for exact stderr match. Does not add any other checks. | ||
| 174 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | 248 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { |
| 175 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | 249 | const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; |
| 250 | self.addCheck(new_check); | ||
| 176 | } | 251 | } |
| 177 | 252 | ||
| 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. | ||
| 178 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | 255 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { |
| 179 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | 256 | const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) }; |
| 257 | self.addCheck(new_check); | ||
| 258 | if (!self.hasTermCheck()) { | ||
| 259 | self.expectExitCode(0); | ||
| 260 | } | ||
| 180 | } | 261 | } |
| 181 | 262 | ||
| 182 | fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | 263 | pub fn expectExitCode(self: *RunStep, code: u8) void { |
| 183 | return switch (action) { | 264 | const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } }; |
| 184 | .ignore => .Ignore, | 265 | self.addCheck(new_check); |
| 185 | .inherit => .Inherit, | 266 | } |
| 186 | .expect_exact, .expect_matches => .Pipe, | 267 | |
| 268 | pub fn hasTermCheck(self: RunStep) bool { | ||
| 269 | for (self.stdio.check.items) |check| switch (check) { | ||
| 270 | .expect_term => return true, | ||
| 271 | else => continue, | ||
| 187 | }; | 272 | }; |
| 273 | return false; | ||
| 188 | } | 274 | } |
| 189 | 275 | ||
| 190 | fn needOutputCheck(self: RunStep) bool { | 276 | pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { |
| 191 | switch (self.condition) { | 277 | switch (self.stdio) { |
| 192 | .always => return false, | 278 | .infer_from_args => { |
| 193 | .output_outdated => {}, | 279 | self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) }; |
| 280 | self.stdio.check.append(new_check) catch @panic("OOM"); | ||
| 281 | }, | ||
| 282 | .check => |*checks| checks.append(new_check) catch @panic("OOM"), | ||
| 283 | else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"), | ||
| 194 | } | 284 | } |
| 195 | if (self.extra_file_dependencies.len > 0) return true; | 285 | } |
| 286 | |||
| 287 | pub 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 | |||
| 302 | pub fn captureStdOut(self: *RunStep) *std.Build.GeneratedFile { | ||
| 303 | assert(self.stdio != .inherit); | ||
| 196 | 304 | ||
| 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. | ||
| 318 | fn 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 | |||
| 328 | fn hasAnyOutputArgs(self: RunStep) bool { | ||
| 329 | if (self.captured_stdout != null) return true; | ||
| 330 | if (self.captured_stderr != null) return true; | ||
| 197 | for (self.argv.items) |arg| switch (arg) { | 331 | for (self.argv.items) |arg| switch (arg) { |
| 198 | .output => return true, | 332 | .output => return true, |
| 199 | else => continue, | 333 | else => continue, |
| 200 | }; | 334 | }; |
| 335 | return false; | ||
| 336 | } | ||
| 337 | |||
| 338 | fn 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 | } | ||
| 201 | 351 | ||
| 352 | fn checksContainStderr(checks: []const StdIo.Check) bool { | ||
| 353 | for (checks) |check| switch (check) { | ||
| 354 | .expect_stdout_exact, | ||
| 355 | .expect_stdout_match, | ||
| 356 | .expect_term, | ||
| 357 | => continue, | ||
| 358 | |||
| 359 | .expect_stderr_exact, | ||
| 360 | .expect_stderr_match, | ||
| 361 | => return true, | ||
| 362 | }; | ||
| 202 | return false; | 363 | return false; |
| 203 | } | 364 | } |
| 204 | 365 | ||
| 205 | fn make(step: *Step) !void { | 366 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 367 | const b = step.owner; | ||
| 368 | const arena = b.allocator; | ||
| 206 | const self = @fieldParentPtr(RunStep, "step", step); | 369 | const self = @fieldParentPtr(RunStep, "step", step); |
| 207 | const need_output_check = self.needOutputCheck(); | 370 | const has_side_effects = self.hasSideEffects(); |
| 208 | 371 | ||
| 209 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | 372 | var argv_list = ArrayList([]const u8).init(arena); |
| 210 | var output_placeholders = ArrayList(struct { | 373 | var output_placeholders = ArrayList(struct { |
| 211 | index: usize, | 374 | index: usize, |
| 212 | output: Arg.Output, | 375 | output: *Output, |
| 213 | }).init(self.builder.allocator); | 376 | }).init(arena); |
| 214 | 377 | ||
| 215 | var man = self.builder.cache.obtain(); | 378 | var man = b.cache.obtain(); |
| 216 | defer man.deinit(); | 379 | defer man.deinit(); |
| 217 | 380 | ||
| 218 | for (self.argv.items) |arg| { | 381 | for (self.argv.items) |arg| { |
| ... | @@ -222,23 +385,29 @@ fn make(step: *Step) !void { | ... | @@ -222,23 +385,29 @@ fn make(step: *Step) !void { |
| 222 | man.hash.addBytes(bytes); | 385 | man.hash.addBytes(bytes); |
| 223 | }, | 386 | }, |
| 224 | .file_source => |file| { | 387 | .file_source => |file| { |
| 225 | const file_path = file.getPath(self.builder); | 388 | const file_path = file.getPath(b); |
| 226 | try argv_list.append(file_path); | 389 | try argv_list.append(file_path); |
| 227 | _ = try man.addFile(file_path, null); | 390 | _ = try man.addFile(file_path, null); |
| 228 | }, | 391 | }, |
| 392 | .directory_source => |file| { | ||
| 393 | const file_path = file.getPath(b); | ||
| 394 | try argv_list.append(file_path); | ||
| 395 | man.hash.addBytes(file_path); | ||
| 396 | }, | ||
| 229 | .artifact => |artifact| { | 397 | .artifact => |artifact| { |
| 230 | if (artifact.target.isWindows()) { | 398 | if (artifact.target.isWindows()) { |
| 231 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | 399 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 232 | self.addPathForDynLibs(artifact); | 400 | self.addPathForDynLibs(artifact); |
| 233 | } | 401 | } |
| 234 | const file_path = artifact.installed_path orelse | 402 | const file_path = artifact.installed_path orelse |
| 235 | artifact.getOutputSource().getPath(self.builder); | 403 | artifact.getOutputSource().getPath(b); |
| 236 | 404 | ||
| 237 | try argv_list.append(file_path); | 405 | try argv_list.append(file_path); |
| 238 | 406 | ||
| 239 | _ = try man.addFile(file_path, null); | 407 | _ = try man.addFile(file_path, null); |
| 240 | }, | 408 | }, |
| 241 | .output => |output| { | 409 | .output => |output| { |
| 410 | man.hash.addBytes(output.prefix); | ||
| 242 | man.hash.addBytes(output.basename); | 411 | man.hash.addBytes(output.basename); |
| 243 | // Add a placeholder into the argument list because we need the | 412 | // Add a placeholder into the argument list because we need the |
| 244 | // manifest hash to be updated with all arguments before the | 413 | // manifest hash to be updated with all arguments before the |
| ... | @@ -252,60 +421,77 @@ fn make(step: *Step) !void { | ... | @@ -252,60 +421,77 @@ fn make(step: *Step) !void { |
| 252 | } | 421 | } |
| 253 | } | 422 | } |
| 254 | 423 | ||
| 255 | if (need_output_check) { | 424 | if (self.captured_stdout) |output| { |
| 256 | for (self.extra_file_dependencies) |file_path| { | 425 | man.hash.addBytes(output.basename); |
| 257 | _ = try man.addFile(self.builder.pathFromRoot(file_path), null); | 426 | } |
| 258 | } | ||
| 259 | 427 | ||
| 260 | if (man.hit() catch |err| failWithCacheError(man, err)) { | 428 | if (self.captured_stderr) |output| { |
| 261 | // cache hit, skip running command | 429 | man.hash.addBytes(output.basename); |
| 262 | const digest = man.final(); | 430 | } |
| 263 | for (output_placeholders.items) |placeholder| { | ||
| 264 | placeholder.output.generated_file.path = try self.builder.cache_root.join( | ||
| 265 | self.builder.allocator, | ||
| 266 | &.{ "o", &digest, placeholder.output.basename }, | ||
| 267 | ); | ||
| 268 | } | ||
| 269 | return; | ||
| 270 | } | ||
| 271 | 431 | ||
| 272 | const digest = man.final(); | 432 | hashStdIo(&man.hash, self.stdio); |
| 273 | 433 | ||
| 434 | if (has_side_effects) { | ||
| 435 | try runCommand(self, argv_list.items, has_side_effects, null, prog_node); | ||
| 436 | return; | ||
| 437 | } | ||
| 438 | |||
| 439 | for (self.extra_file_dependencies) |file_path| { | ||
| 440 | _ = try man.addFile(b.pathFromRoot(file_path), null); | ||
| 441 | } | ||
| 442 | |||
| 443 | if (try step.cacheHit(&man)) { | ||
| 444 | // cache hit, skip running command | ||
| 445 | const digest = man.final(); | ||
| 274 | for (output_placeholders.items) |placeholder| { | 446 | for (output_placeholders.items) |placeholder| { |
| 275 | const output_path = try self.builder.cache_root.join( | 447 | placeholder.output.generated_file.path = try b.cache_root.join(arena, &.{ |
| 276 | self.builder.allocator, | 448 | "o", &digest, placeholder.output.basename, |
| 277 | &.{ "o", &digest, placeholder.output.basename }, | 449 | }); |
| 278 | ); | 450 | } |
| 279 | const output_dir = fs.path.dirname(output_path).?; | 451 | |
| 280 | fs.cwd().makePath(output_dir) catch |err| { | 452 | if (self.captured_stdout) |output| { |
| 281 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 453 | output.generated_file.path = try b.cache_root.join(arena, &.{ |
| 282 | return err; | 454 | "o", &digest, output.basename, |
| 283 | }; | 455 | }); |
| 456 | } | ||
| 284 | 457 | ||
| 285 | placeholder.output.generated_file.path = output_path; | 458 | if (self.captured_stderr) |output| { |
| 286 | argv_list.items[placeholder.index] = output_path; | 459 | output.generated_file.path = try b.cache_root.join(arena, &.{ |
| 460 | "o", &digest, output.basename, | ||
| 461 | }); | ||
| 287 | } | 462 | } |
| 463 | |||
| 464 | step.result_cached = true; | ||
| 465 | return; | ||
| 288 | } | 466 | } |
| 289 | 467 | ||
| 290 | try runCommand( | 468 | const digest = man.final(); |
| 291 | argv_list.items, | 469 | |
| 292 | self.builder, | 470 | for (output_placeholders.items) |placeholder| { |
| 293 | self.expected_term, | 471 | const output_components = .{ "o", &digest, placeholder.output.basename }; |
| 294 | self.stdout_action, | 472 | const output_sub_path = try fs.path.join(arena, &output_components); |
| 295 | self.stderr_action, | 473 | const output_sub_dir_path = fs.path.dirname(output_sub_path).?; |
| 296 | self.stdin_behavior, | 474 | b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { |
| 297 | self.env_map, | 475 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 298 | self.cwd, | 476 | b.cache_root, output_sub_dir_path, @errorName(err), |
| 299 | self.print, | 477 | }); |
| 300 | ); | 478 | }; |
| 301 | 479 | const output_path = try b.cache_root.join(arena, &output_components); | |
| 302 | if (need_output_check) { | 480 | placeholder.output.generated_file.path = output_path; |
| 303 | try man.writeManifest(); | 481 | const cli_arg = if (placeholder.output.prefix.len == 0) |
| 482 | output_path | ||
| 483 | else | ||
| 484 | b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); | ||
| 485 | argv_list.items[placeholder.index] = cli_arg; | ||
| 304 | } | 486 | } |
| 487 | |||
| 488 | try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node); | ||
| 489 | |||
| 490 | try step.writeManifest(&man); | ||
| 305 | } | 491 | } |
| 306 | 492 | ||
| 307 | fn formatTerm( | 493 | fn formatTerm( |
| 308 | term: ?std.ChildProcess.Term, | 494 | term: ?std.process.Child.Term, |
| 309 | comptime fmt: []const u8, | 495 | comptime fmt: []const u8, |
| 310 | options: std.fmt.FormatOptions, | 496 | options: std.fmt.FormatOptions, |
| 311 | writer: anytype, | 497 | writer: anytype, |
| ... | @@ -321,11 +507,11 @@ fn formatTerm( | ... | @@ -321,11 +507,11 @@ fn formatTerm( |
| 321 | try writer.writeAll("exited with any code"); | 507 | try writer.writeAll("exited with any code"); |
| 322 | } | 508 | } |
| 323 | } | 509 | } |
| 324 | fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) { | 510 | fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { |
| 325 | return .{ .data = term }; | 511 | return .{ .data = term }; |
| 326 | } | 512 | } |
| 327 | 513 | ||
| 328 | fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool { | 514 | fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool { |
| 329 | return if (expected) |e| switch (e) { | 515 | return if (expected) |e| switch (e) { |
| 330 | .Exited => |expected_code| switch (actual) { | 516 | .Exited => |expected_code| switch (actual) { |
| 331 | .Exited => |actual_code| expected_code == actual_code, | 517 | .Exited => |actual_code| expected_code == actual_code, |
| ... | @@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) | ... | @@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) |
| 349 | }; | 535 | }; |
| 350 | } | 536 | } |
| 351 | 537 | ||
| 352 | pub fn runCommand( | 538 | fn runCommand( |
| 539 | self: *RunStep, | ||
| 353 | argv: []const []const u8, | 540 | argv: []const []const u8, |
| 354 | builder: *std.Build, | 541 | has_side_effects: bool, |
| 355 | expected_term: ?std.ChildProcess.Term, | 542 | digest: ?*const [std.Build.Cache.hex_digest_len]u8, |
| 356 | stdout_action: StdIoAction, | 543 | prog_node: *std.Progress.Node, |
| 357 | stderr_action: StdIoAction, | ||
| 358 | stdin_behavior: std.ChildProcess.StdIo, | ||
| 359 | env_map: ?*EnvMap, | ||
| 360 | maybe_cwd: ?[]const u8, | ||
| 361 | print: bool, | ||
| 362 | ) !void { | 544 | ) !void { |
| 363 | const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path; | 545 | const step = &self.step; |
| 364 | 546 | const b = step.owner; | |
| 365 | if (!std.process.can_spawn) { | 547 | const arena = b.allocator; |
| 366 | const cmd = try std.mem.join(builder.allocator, " ", argv); | 548 | |
| 367 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ | 549 | try step.handleChildProcUnsupported(self.cwd, argv); |
| 368 | @tagName(builtin.os.tag), cmd, | 550 | try Step.handleVerbose2(step.owner, self.cwd, self.env_map, argv); |
| 369 | }); | 551 | |
| 370 | builder.allocator.free(cmd); | 552 | const allow_skip = switch (self.stdio) { |
| 371 | return ExecError.ExecNotSupported; | 553 | .check, .zig_test => self.skip_foreign_checks, |
| 372 | } | 554 | else => false, |
| 555 | }; | ||
| 556 | |||
| 557 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); | ||
| 558 | defer interp_argv.deinit(); | ||
| 559 | |||
| 560 | const result = spawnChildAndCollect(self, argv, has_side_effects, prog_node) catch |err| term: { | ||
| 561 | // InvalidExe: cpu arch mismatch | ||
| 562 | // FileNotFound: can happen with a wrong dynamic linker path | ||
| 563 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { | ||
| 564 | // TODO: learn the target from the binary directly rather than from | ||
| 565 | // relying on it being a CompileStep. This will make this logic | ||
| 566 | // work even for the edge case that the binary was produced by a | ||
| 567 | // third party. | ||
| 568 | const exe = switch (self.argv.items[0]) { | ||
| 569 | .artifact => |exe| exe, | ||
| 570 | else => break :interpret, | ||
| 571 | }; | ||
| 572 | switch (exe.kind) { | ||
| 573 | .exe, .@"test" => {}, | ||
| 574 | else => break :interpret, | ||
| 575 | } | ||
| 576 | |||
| 577 | const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; | ||
| 578 | switch (b.host.getExternalExecutor(exe.target_info, .{ | ||
| 579 | .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, | ||
| 580 | .link_libc = exe.is_linking_libc, | ||
| 581 | })) { | ||
| 582 | .native, .rosetta => { | ||
| 583 | if (allow_skip) return error.MakeSkipped; | ||
| 584 | break :interpret; | ||
| 585 | }, | ||
| 586 | .wine => |bin_name| { | ||
| 587 | if (b.enable_wine) { | ||
| 588 | try interp_argv.append(bin_name); | ||
| 589 | try interp_argv.appendSlice(argv); | ||
| 590 | } else { | ||
| 591 | return failForeign(self, "-fwine", argv[0], exe); | ||
| 592 | } | ||
| 593 | }, | ||
| 594 | .qemu => |bin_name| { | ||
| 595 | if (b.enable_qemu) { | ||
| 596 | const glibc_dir_arg = if (need_cross_glibc) | ||
| 597 | b.glibc_runtimes_dir orelse return | ||
| 598 | else | ||
| 599 | null; | ||
| 600 | |||
| 601 | try interp_argv.append(bin_name); | ||
| 602 | |||
| 603 | if (glibc_dir_arg) |dir| { | ||
| 604 | // TODO look into making this a call to `linuxTriple`. This | ||
| 605 | // needs the directory to be called "i686" rather than | ||
| 606 | // "x86" which is why we do it manually here. | ||
| 607 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | ||
| 608 | const cpu_arch = exe.target.getCpuArch(); | ||
| 609 | const os_tag = exe.target.getOsTag(); | ||
| 610 | const abi = exe.target.getAbi(); | ||
| 611 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | ||
| 612 | "i686" | ||
| 613 | else | ||
| 614 | @tagName(cpu_arch); | ||
| 615 | const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ | ||
| 616 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | ||
| 617 | }); | ||
| 618 | |||
| 619 | try interp_argv.append("-L"); | ||
| 620 | try interp_argv.append(full_dir); | ||
| 621 | } | ||
| 622 | |||
| 623 | try interp_argv.appendSlice(argv); | ||
| 624 | } else { | ||
| 625 | return failForeign(self, "-fqemu", argv[0], exe); | ||
| 626 | } | ||
| 627 | }, | ||
| 628 | .darling => |bin_name| { | ||
| 629 | if (b.enable_darling) { | ||
| 630 | try interp_argv.append(bin_name); | ||
| 631 | try interp_argv.appendSlice(argv); | ||
| 632 | } else { | ||
| 633 | return failForeign(self, "-fdarling", argv[0], exe); | ||
| 634 | } | ||
| 635 | }, | ||
| 636 | .wasmtime => |bin_name| { | ||
| 637 | if (b.enable_wasmtime) { | ||
| 638 | try interp_argv.append(bin_name); | ||
| 639 | try interp_argv.append("--dir=."); | ||
| 640 | try interp_argv.append(argv[0]); | ||
| 641 | try interp_argv.append("--"); | ||
| 642 | try interp_argv.appendSlice(argv[1..]); | ||
| 643 | } else { | ||
| 644 | return failForeign(self, "-fwasmtime", argv[0], exe); | ||
| 645 | } | ||
| 646 | }, | ||
| 647 | .bad_dl => |foreign_dl| { | ||
| 648 | if (allow_skip) return error.MakeSkipped; | ||
| 649 | |||
| 650 | const host_dl = b.host.dynamic_linker.get() orelse "(none)"; | ||
| 651 | |||
| 652 | return step.fail( | ||
| 653 | \\the host system is unable to execute binaries from the target | ||
| 654 | \\ because the host dynamic linker is '{s}', | ||
| 655 | \\ while the target dynamic linker is '{s}'. | ||
| 656 | \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step | ||
| 657 | , .{ host_dl, foreign_dl }); | ||
| 658 | }, | ||
| 659 | .bad_os_or_cpu => { | ||
| 660 | if (allow_skip) return error.MakeSkipped; | ||
| 661 | |||
| 662 | const host_name = try b.host.target.zigTriple(b.allocator); | ||
| 663 | const foreign_name = try exe.target.zigTriple(b.allocator); | ||
| 664 | |||
| 665 | return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ | ||
| 666 | host_name, foreign_name, | ||
| 667 | }); | ||
| 668 | }, | ||
| 669 | } | ||
| 373 | 670 | ||
| 374 | var child = std.ChildProcess.init(argv, builder.allocator); | 671 | if (exe.target.isWindows()) { |
| 375 | child.cwd = cwd; | 672 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 376 | child.env_map = env_map orelse builder.env_map; | 673 | self.addPathForDynLibs(exe); |
| 674 | } | ||
| 377 | 675 | ||
| 378 | child.stdin_behavior = stdin_behavior; | 676 | try Step.handleVerbose2(step.owner, self.cwd, self.env_map, interp_argv.items); |
| 379 | child.stdout_behavior = stdIoActionToBehavior(stdout_action); | ||
| 380 | child.stderr_behavior = stdIoActionToBehavior(stderr_action); | ||
| 381 | 677 | ||
| 382 | if (print) | 678 | break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| { |
| 383 | printCmd(cwd, argv); | 679 | return step.fail("unable to spawn interpreter {s}: {s}", .{ |
| 680 | interp_argv.items[0], @errorName(e), | ||
| 681 | }); | ||
| 682 | }; | ||
| 683 | } | ||
| 384 | 684 | ||
| 385 | child.spawn() catch |err| { | 685 | return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); |
| 386 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | ||
| 387 | return err; | ||
| 388 | }; | 686 | }; |
| 389 | 687 | ||
| 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; | ||
| 391 | 691 | ||
| 392 | var stdout: ?[]const u8 = null; | 692 | // Capture stdout and stderr to GeneratedFile objects. |
| 393 | defer if (stdout) |s| builder.allocator.free(s); | 693 | const Stream = struct { |
| 694 | captured: ?*Output, | ||
| 695 | is_null: bool, | ||
| 696 | bytes: []const u8, | ||
| 697 | }; | ||
| 698 | for ([_]Stream{ | ||
| 699 | .{ | ||
| 700 | .captured = self.captured_stdout, | ||
| 701 | .is_null = result.stdio.stdout_null, | ||
| 702 | .bytes = result.stdio.stdout, | ||
| 703 | }, | ||
| 704 | .{ | ||
| 705 | .captured = self.captured_stderr, | ||
| 706 | .is_null = result.stdio.stderr_null, | ||
| 707 | .bytes = result.stdio.stderr, | ||
| 708 | }, | ||
| 709 | }) |stream| { | ||
| 710 | if (stream.captured) |output| { | ||
| 711 | assert(!stream.is_null); | ||
| 712 | |||
| 713 | const output_components = .{ "o", digest.?, output.basename }; | ||
| 714 | const output_path = try b.cache_root.join(arena, &output_components); | ||
| 715 | output.generated_file.path = output_path; | ||
| 716 | |||
| 717 | const sub_path = try fs.path.join(arena, &output_components); | ||
| 718 | const sub_path_dirname = fs.path.dirname(sub_path).?; | ||
| 719 | b.cache_root.handle.makePath(sub_path_dirname) catch |err| { | ||
| 720 | return step.fail("unable to make path '{}{s}': {s}", .{ | ||
| 721 | b.cache_root, sub_path_dirname, @errorName(err), | ||
| 722 | }); | ||
| 723 | }; | ||
| 724 | b.cache_root.handle.writeFile(sub_path, stream.bytes) catch |err| { | ||
| 725 | return step.fail("unable to write file '{}{s}': {s}", .{ | ||
| 726 | b.cache_root, sub_path, @errorName(err), | ||
| 727 | }); | ||
| 728 | }; | ||
| 729 | } | ||
| 730 | } | ||
| 394 | 731 | ||
| 395 | switch (stdout_action) { | 732 | const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; |
| 396 | .expect_exact, .expect_matches => { | 733 | |
| 397 | stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size); | 734 | switch (self.stdio) { |
| 735 | .check => |checks| for (checks.items) |check| switch (check) { | ||
| 736 | .expect_stderr_exact => |expected_bytes| { | ||
| 737 | assert(!result.stdio.stderr_null); | ||
| 738 | if (!mem.eql(u8, expected_bytes, result.stdio.stderr)) { | ||
| 739 | return step.fail( | ||
| 740 | \\ | ||
| 741 | \\========= expected this stderr: ========= | ||
| 742 | \\{s} | ||
| 743 | \\========= but found: ==================== | ||
| 744 | \\{s} | ||
| 745 | \\========= from the following command: === | ||
| 746 | \\{s} | ||
| 747 | , .{ | ||
| 748 | expected_bytes, | ||
| 749 | result.stdio.stderr, | ||
| 750 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 751 | }); | ||
| 752 | } | ||
| 753 | }, | ||
| 754 | .expect_stderr_match => |match| { | ||
| 755 | assert(!result.stdio.stderr_null); | ||
| 756 | if (mem.indexOf(u8, result.stdio.stderr, match) == null) { | ||
| 757 | return step.fail( | ||
| 758 | \\ | ||
| 759 | \\========= expected to find in stderr: ========= | ||
| 760 | \\{s} | ||
| 761 | \\========= but stderr does not contain it: ===== | ||
| 762 | \\{s} | ||
| 763 | \\========= from the following command: ========= | ||
| 764 | \\{s} | ||
| 765 | , .{ | ||
| 766 | match, | ||
| 767 | result.stdio.stderr, | ||
| 768 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 769 | }); | ||
| 770 | } | ||
| 771 | }, | ||
| 772 | .expect_stdout_exact => |expected_bytes| { | ||
| 773 | assert(!result.stdio.stdout_null); | ||
| 774 | if (!mem.eql(u8, expected_bytes, result.stdio.stdout)) { | ||
| 775 | return step.fail( | ||
| 776 | \\ | ||
| 777 | \\========= expected this stdout: ========= | ||
| 778 | \\{s} | ||
| 779 | \\========= but found: ==================== | ||
| 780 | \\{s} | ||
| 781 | \\========= from the following command: === | ||
| 782 | \\{s} | ||
| 783 | , .{ | ||
| 784 | expected_bytes, | ||
| 785 | result.stdio.stdout, | ||
| 786 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 787 | }); | ||
| 788 | } | ||
| 789 | }, | ||
| 790 | .expect_stdout_match => |match| { | ||
| 791 | assert(!result.stdio.stdout_null); | ||
| 792 | if (mem.indexOf(u8, result.stdio.stdout, match) == null) { | ||
| 793 | return step.fail( | ||
| 794 | \\ | ||
| 795 | \\========= expected to find in stdout: ========= | ||
| 796 | \\{s} | ||
| 797 | \\========= but stdout does not contain it: ===== | ||
| 798 | \\{s} | ||
| 799 | \\========= from the following command: ========= | ||
| 800 | \\{s} | ||
| 801 | , .{ | ||
| 802 | match, | ||
| 803 | result.stdio.stdout, | ||
| 804 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 805 | }); | ||
| 806 | } | ||
| 807 | }, | ||
| 808 | .expect_term => |expected_term| { | ||
| 809 | if (!termMatches(expected_term, result.term)) { | ||
| 810 | return step.fail("the following command {} (expected {}):\n{s}", .{ | ||
| 811 | fmtTerm(result.term), | ||
| 812 | fmtTerm(expected_term), | ||
| 813 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 814 | }); | ||
| 815 | } | ||
| 816 | }, | ||
| 817 | }, | ||
| 818 | .zig_test => { | ||
| 819 | const expected_term: std.process.Child.Term = .{ .Exited = 0 }; | ||
| 820 | if (!termMatches(expected_term, result.term)) { | ||
| 821 | return step.fail("the following command {} (expected {}):\n{s}", .{ | ||
| 822 | fmtTerm(result.term), | ||
| 823 | fmtTerm(expected_term), | ||
| 824 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 825 | }); | ||
| 826 | } | ||
| 827 | if (!result.stdio.test_results.isSuccess()) { | ||
| 828 | return step.fail( | ||
| 829 | "the following test command failed:\n{s}", | ||
| 830 | .{try Step.allocPrintCmd(arena, self.cwd, final_argv)}, | ||
| 831 | ); | ||
| 832 | } | ||
| 833 | }, | ||
| 834 | else => { | ||
| 835 | try step.handleChildProcessTerm(result.term, self.cwd, final_argv); | ||
| 398 | }, | 836 | }, |
| 399 | .inherit, .ignore => {}, | ||
| 400 | } | 837 | } |
| 838 | } | ||
| 401 | 839 | ||
| 402 | var stderr: ?[]const u8 = null; | 840 | const ChildProcResult = struct { |
| 403 | defer if (stderr) |s| builder.allocator.free(s); | 841 | term: std.process.Child.Term, |
| 842 | elapsed_ns: u64, | ||
| 843 | peak_rss: usize, | ||
| 404 | 844 | ||
| 405 | switch (stderr_action) { | 845 | stdio: StdIoResult, |
| 406 | .expect_exact, .expect_matches => { | 846 | }; |
| 407 | stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size); | 847 | |
| 408 | }, | 848 | fn spawnChildAndCollect( |
| 409 | .inherit, .ignore => {}, | 849 | self: *RunStep, |
| 850 | argv: []const []const u8, | ||
| 851 | has_side_effects: bool, | ||
| 852 | prog_node: *std.Progress.Node, | ||
| 853 | ) !ChildProcResult { | ||
| 854 | const b = self.step.owner; | ||
| 855 | const arena = b.allocator; | ||
| 856 | |||
| 857 | var child = std.process.Child.init(argv, arena); | ||
| 858 | if (self.cwd) |cwd| { | ||
| 859 | child.cwd = b.pathFromRoot(cwd); | ||
| 860 | } else { | ||
| 861 | child.cwd = b.build_root.path; | ||
| 862 | child.cwd_dir = b.build_root.handle; | ||
| 410 | } | 863 | } |
| 864 | child.env_map = self.env_map orelse b.env_map; | ||
| 865 | child.request_resource_usage_statistics = true; | ||
| 411 | 866 | ||
| 412 | const term = child.wait() catch |err| { | 867 | child.stdin_behavior = switch (self.stdio) { |
| 413 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | 868 | .infer_from_args => if (has_side_effects) .Inherit else .Close, |
| 414 | return err; | 869 | .inherit => .Inherit, |
| 870 | .check => .Close, | ||
| 871 | .zig_test => .Pipe, | ||
| 415 | }; | 872 | }; |
| 873 | child.stdout_behavior = switch (self.stdio) { | ||
| 874 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 875 | .inherit => .Inherit, | ||
| 876 | .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, | ||
| 877 | .zig_test => .Pipe, | ||
| 878 | }; | ||
| 879 | child.stderr_behavior = switch (self.stdio) { | ||
| 880 | .infer_from_args => if (has_side_effects) .Inherit else .Pipe, | ||
| 881 | .inherit => .Inherit, | ||
| 882 | .check => .Pipe, | ||
| 883 | .zig_test => .Pipe, | ||
| 884 | }; | ||
| 885 | if (self.captured_stdout != null) child.stdout_behavior = .Pipe; | ||
| 886 | if (self.captured_stderr != null) child.stderr_behavior = .Pipe; | ||
| 887 | if (self.stdin != null) { | ||
| 888 | assert(child.stdin_behavior != .Inherit); | ||
| 889 | child.stdin_behavior = .Pipe; | ||
| 890 | } | ||
| 416 | 891 | ||
| 417 | if (!termMatches(expected_term, term)) { | 892 | try child.spawn(); |
| 418 | if (builder.prominent_compile_errors) { | 893 | var timer = try std.time.Timer.start(); |
| 419 | std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) }); | 894 | |
| 420 | } else { | 895 | const result = if (self.stdio == .zig_test) |
| 421 | std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); | 896 | evalZigTest(self, &child, prog_node) |
| 422 | printCmd(cwd, argv); | 897 | else |
| 898 | evalGeneric(self, &child); | ||
| 899 | |||
| 900 | const term = try child.wait(); | ||
| 901 | const elapsed_ns = timer.read(); | ||
| 902 | |||
| 903 | return .{ | ||
| 904 | .stdio = try result, | ||
| 905 | .term = term, | ||
| 906 | .elapsed_ns = elapsed_ns, | ||
| 907 | .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, | ||
| 908 | }; | ||
| 909 | } | ||
| 910 | |||
| 911 | const 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 | |||
| 921 | fn evalZigTest( | ||
| 922 | self: *RunStep, | ||
| 923 | child: *std.process.Child, | ||
| 924 | prog_node: *std.Progress.Node, | ||
| 925 | ) !StdIoResult { | ||
| 926 | const gpa = self.step.owner.allocator; | ||
| 927 | const arena = self.step.owner.allocator; | ||
| 928 | |||
| 929 | var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ | ||
| 930 | .stdout = child.stdout.?, | ||
| 931 | .stderr = child.stderr.?, | ||
| 932 | }); | ||
| 933 | defer poller.deinit(); | ||
| 934 | |||
| 935 | try sendMessage(child.stdin.?, .query_test_metadata); | ||
| 936 | |||
| 937 | const Header = std.zig.Server.Message.Header; | ||
| 938 | |||
| 939 | const stdout = poller.fifo(.stdout); | ||
| 940 | const stderr = poller.fifo(.stderr); | ||
| 941 | |||
| 942 | var fail_count: u32 = 0; | ||
| 943 | var skip_count: u32 = 0; | ||
| 944 | var leak_count: u32 = 0; | ||
| 945 | var test_count: u32 = 0; | ||
| 946 | |||
| 947 | var metadata: ?TestMetadata = null; | ||
| 948 | |||
| 949 | var sub_prog_node: ?std.Progress.Node = null; | ||
| 950 | defer if (sub_prog_node) |*n| n.end(); | ||
| 951 | |||
| 952 | poll: while (true) { | ||
| 953 | while (stdout.readableLength() < @sizeOf(Header)) { | ||
| 954 | if (!(try poller.poll())) break :poll; | ||
| 955 | } | ||
| 956 | const header = stdout.reader().readStruct(Header) catch unreachable; | ||
| 957 | while (stdout.readableLength() < header.bytes_len) { | ||
| 958 | if (!(try poller.poll())) break :poll; | ||
| 423 | } | 959 | } |
| 424 | return error.UnexpectedExit; | 960 | const body = stdout.readableSliceOfLen(header.bytes_len); |
| 961 | |||
| 962 | switch (header.tag) { | ||
| 963 | .zig_version => { | ||
| 964 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | ||
| 965 | return self.step.fail( | ||
| 966 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", | ||
| 967 | .{ builtin.zig_version_string, body }, | ||
| 968 | ); | ||
| 969 | } | ||
| 970 | }, | ||
| 971 | .test_metadata => { | ||
| 972 | const TmHdr = std.zig.Server.Message.TestMetadata; | ||
| 973 | const tm_hdr = @ptrCast(*align(1) const TmHdr, body); | ||
| 974 | test_count = tm_hdr.tests_len; | ||
| 975 | |||
| 976 | const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)]; | ||
| 977 | const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)]; | ||
| 978 | const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)]; | ||
| 979 | const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len]; | ||
| 980 | |||
| 981 | const names = std.mem.bytesAsSlice(u32, names_bytes); | ||
| 982 | const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes); | ||
| 983 | const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes); | ||
| 984 | const names_aligned = try arena.alloc(u32, names.len); | ||
| 985 | for (names_aligned, names) |*dest, src| dest.* = src; | ||
| 986 | |||
| 987 | const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len); | ||
| 988 | for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src; | ||
| 989 | |||
| 990 | const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len); | ||
| 991 | for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src; | ||
| 992 | |||
| 993 | prog_node.setEstimatedTotalItems(names.len); | ||
| 994 | metadata = .{ | ||
| 995 | .string_bytes = try arena.dupe(u8, string_bytes), | ||
| 996 | .names = names_aligned, | ||
| 997 | .async_frame_lens = async_frame_lens_aligned, | ||
| 998 | .expected_panic_msgs = expected_panic_msgs_aligned, | ||
| 999 | .next_index = 0, | ||
| 1000 | .prog_node = prog_node, | ||
| 1001 | }; | ||
| 1002 | |||
| 1003 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); | ||
| 1004 | }, | ||
| 1005 | .test_results => { | ||
| 1006 | const md = metadata.?; | ||
| 1007 | |||
| 1008 | const TrHdr = std.zig.Server.Message.TestResults; | ||
| 1009 | const tr_hdr = @ptrCast(*align(1) const TrHdr, body); | ||
| 1010 | fail_count += @boolToInt(tr_hdr.flags.fail); | ||
| 1011 | skip_count += @boolToInt(tr_hdr.flags.skip); | ||
| 1012 | leak_count += @boolToInt(tr_hdr.flags.leak); | ||
| 1013 | |||
| 1014 | if (tr_hdr.flags.fail or tr_hdr.flags.leak) { | ||
| 1015 | const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); | ||
| 1016 | const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n"); | ||
| 1017 | const label = if (tr_hdr.flags.fail) "failed" else "leaked"; | ||
| 1018 | if (msg.len > 0) { | ||
| 1019 | try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg }); | ||
| 1020 | } else { | ||
| 1021 | try self.step.addError("'{s}' {s}", .{ name, label }); | ||
| 1022 | } | ||
| 1023 | stderr.discard(msg.len); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); | ||
| 1027 | }, | ||
| 1028 | else => {}, // ignore other messages | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | stdout.discard(body.len); | ||
| 425 | } | 1032 | } |
| 426 | 1033 | ||
| 427 | switch (stderr_action) { | 1034 | if (stderr.readableLength() > 0) { |
| 428 | .inherit, .ignore => {}, | 1035 | const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n"); |
| 429 | .expect_exact => |expected_bytes| { | 1036 | if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg); |
| 430 | if (!mem.eql(u8, expected_bytes, stderr.?)) { | ||
| 431 | std.debug.print( | ||
| 432 | \\ | ||
| 433 | \\========= Expected this stderr: ========= | ||
| 434 | \\{s} | ||
| 435 | \\========= But found: ==================== | ||
| 436 | \\{s} | ||
| 437 | \\ | ||
| 438 | , .{ expected_bytes, stderr.? }); | ||
| 439 | printCmd(cwd, argv); | ||
| 440 | return error.TestFailed; | ||
| 441 | } | ||
| 442 | }, | ||
| 443 | .expect_matches => |matches| for (matches) |match| { | ||
| 444 | if (mem.indexOf(u8, stderr.?, match) == null) { | ||
| 445 | std.debug.print( | ||
| 446 | \\ | ||
| 447 | \\========= Expected to find in stderr: ========= | ||
| 448 | \\{s} | ||
| 449 | \\========= But stderr does not contain it: ===== | ||
| 450 | \\{s} | ||
| 451 | \\ | ||
| 452 | , .{ match, stderr.? }); | ||
| 453 | printCmd(cwd, argv); | ||
| 454 | return error.TestFailed; | ||
| 455 | } | ||
| 456 | }, | ||
| 457 | } | 1037 | } |
| 458 | 1038 | ||
| 459 | switch (stdout_action) { | 1039 | // Send EOF to stdin. |
| 460 | .inherit, .ignore => {}, | 1040 | child.stdin.?.close(); |
| 461 | .expect_exact => |expected_bytes| { | 1041 | child.stdin = null; |
| 462 | if (!mem.eql(u8, expected_bytes, stdout.?)) { | 1042 | |
| 463 | std.debug.print( | 1043 | return .{ |
| 464 | \\ | 1044 | .stdout = &.{}, |
| 465 | \\========= Expected this stdout: ========= | 1045 | .stderr = &.{}, |
| 466 | \\{s} | 1046 | .stdout_null = true, |
| 467 | \\========= But found: ==================== | 1047 | .stderr_null = true, |
| 468 | \\{s} | 1048 | .test_results = .{ |
| 469 | \\ | 1049 | .test_count = test_count, |
| 470 | , .{ expected_bytes, stdout.? }); | 1050 | .fail_count = fail_count, |
| 471 | printCmd(cwd, argv); | 1051 | .skip_count = skip_count, |
| 472 | return error.TestFailed; | 1052 | .leak_count = leak_count, |
| 473 | } | ||
| 474 | }, | ||
| 475 | .expect_matches => |matches| for (matches) |match| { | ||
| 476 | if (mem.indexOf(u8, stdout.?, match) == null) { | ||
| 477 | std.debug.print( | ||
| 478 | \\ | ||
| 479 | \\========= Expected to find in stdout: ========= | ||
| 480 | \\{s} | ||
| 481 | \\========= But stdout does not contain it: ===== | ||
| 482 | \\{s} | ||
| 483 | \\ | ||
| 484 | , .{ match, stdout.? }); | ||
| 485 | printCmd(cwd, argv); | ||
| 486 | return error.TestFailed; | ||
| 487 | } | ||
| 488 | }, | 1053 | }, |
| 1054 | }; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | const 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 | |||
| 1070 | fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | ||
| 1071 | while (metadata.next_index < metadata.names.len) { | ||
| 1072 | const i = metadata.next_index; | ||
| 1073 | metadata.next_index += 1; | ||
| 1074 | |||
| 1075 | if (metadata.async_frame_lens[i] != 0) continue; | ||
| 1076 | if (metadata.expected_panic_msgs[i] != 0) continue; | ||
| 1077 | |||
| 1078 | const name = metadata.testName(i); | ||
| 1079 | if (sub_prog_node.*) |*n| n.end(); | ||
| 1080 | sub_prog_node.* = metadata.prog_node.start(name, 0); | ||
| 1081 | |||
| 1082 | try sendRunTestMessage(in, i); | ||
| 1083 | return; | ||
| 1084 | } else { | ||
| 1085 | try sendMessage(in, .exit); | ||
| 489 | } | 1086 | } |
| 490 | } | 1087 | } |
| 491 | 1088 | ||
| 492 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | 1089 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { |
| 493 | const i = man.failed_file_index orelse failWithSimpleError(err); | 1090 | const header: std.zig.Client.Message.Header = .{ |
| 494 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | 1091 | .tag = tag, |
| 495 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | 1092 | .bytes_len = 0, |
| 496 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | 1093 | }; |
| 497 | std.process.exit(1); | 1094 | try file.writeAll(std.mem.asBytes(&header)); |
| 498 | } | 1095 | } |
| 499 | 1096 | ||
| 500 | fn failWithSimpleError(err: anyerror) noreturn { | 1097 | fn sendRunTestMessage(file: std.fs.File, index: u32) !void { |
| 501 | std.debug.print("{s}\n", .{@errorName(err)}); | 1098 | const header: std.zig.Client.Message.Header = .{ |
| 502 | std.process.exit(1); | 1099 | .tag = .run_test, |
| 1100 | .bytes_len = 4, | ||
| 1101 | }; | ||
| 1102 | const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index); | ||
| 1103 | try file.writeAll(full_msg); | ||
| 503 | } | 1104 | } |
| 504 | 1105 | ||
| 505 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | 1106 | fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult { |
| 506 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | 1107 | const arena = self.step.owner.allocator; |
| 507 | for (argv) |arg| { | 1108 | |
| 508 | std.debug.print("{s} ", .{arg}); | 1109 | if (self.stdin) |stdin| { |
| 1110 | child.stdin.?.writeAll(stdin) catch |err| { | ||
| 1111 | return self.step.fail("unable to write stdin: {s}", .{@errorName(err)}); | ||
| 1112 | }; | ||
| 1113 | child.stdin.?.close(); | ||
| 1114 | child.stdin = null; | ||
| 509 | } | 1115 | } |
| 510 | std.debug.print("\n", .{}); | ||
| 511 | } | ||
| 512 | 1116 | ||
| 513 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { | 1117 | // These are not optionals, as a workaround for |
| 514 | addPathForDynLibsInternal(&self.step, self.builder, artifact); | 1118 | // https://github.com/ziglang/zig/issues/14783 |
| 1119 | var stdout_bytes: []const u8 = undefined; | ||
| 1120 | var stderr_bytes: []const u8 = undefined; | ||
| 1121 | var stdout_null = true; | ||
| 1122 | var stderr_null = true; | ||
| 1123 | |||
| 1124 | if (child.stdout) |stdout| { | ||
| 1125 | if (child.stderr) |stderr| { | ||
| 1126 | var poller = std.io.poll(arena, enum { stdout, stderr }, .{ | ||
| 1127 | .stdout = stdout, | ||
| 1128 | .stderr = stderr, | ||
| 1129 | }); | ||
| 1130 | defer poller.deinit(); | ||
| 1131 | |||
| 1132 | while (try poller.poll()) { | ||
| 1133 | if (poller.fifo(.stdout).count > self.max_stdio_size) | ||
| 1134 | return error.StdoutStreamTooLong; | ||
| 1135 | if (poller.fifo(.stderr).count > self.max_stdio_size) | ||
| 1136 | return error.StderrStreamTooLong; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); | ||
| 1140 | stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); | ||
| 1141 | stdout_null = false; | ||
| 1142 | stderr_null = false; | ||
| 1143 | } else { | ||
| 1144 | stdout_bytes = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); | ||
| 1145 | stdout_null = false; | ||
| 1146 | } | ||
| 1147 | } else if (child.stderr) |stderr| { | ||
| 1148 | stderr_bytes = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); | ||
| 1149 | stderr_null = false; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | if (!stderr_null and stderr_bytes.len > 0) { | ||
| 1153 | // Treat stderr as an error message. | ||
| 1154 | const stderr_is_diagnostic = self.captured_stderr == null and switch (self.stdio) { | ||
| 1155 | .check => |checks| !checksContainStderr(checks.items), | ||
| 1156 | else => true, | ||
| 1157 | }; | ||
| 1158 | if (stderr_is_diagnostic) { | ||
| 1159 | try self.step.result_error_msgs.append(arena, stderr_bytes); | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | return .{ | ||
| 1164 | .stdout = stdout_bytes, | ||
| 1165 | .stderr = stderr_bytes, | ||
| 1166 | .stdout_null = stdout_null, | ||
| 1167 | .stderr_null = stderr_null, | ||
| 1168 | .test_results = .{}, | ||
| 1169 | }; | ||
| 515 | } | 1170 | } |
| 516 | 1171 | ||
| 517 | /// This should only be used for internal usage, this is called automatically | 1172 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { |
| 518 | /// for the user. | 1173 | const b = self.step.owner; |
| 519 | pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void { | ||
| 520 | for (artifact.link_objects.items) |link_object| { | 1174 | for (artifact.link_objects.items) |link_object| { |
| 521 | switch (link_object) { | 1175 | switch (link_object) { |
| 522 | .other_step => |other| { | 1176 | .other_step => |other| { |
| 523 | if (other.target.isWindows() and other.isDynamicLibrary()) { | 1177 | if (other.target.isWindows() and other.isDynamicLibrary()) { |
| 524 | addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?); | 1178 | addPathDir(self, fs.path.dirname(other.getOutputSource().getPath(b)).?); |
| 525 | addPathForDynLibsInternal(step, builder, other); | 1179 | addPathForDynLibs(self, other); |
| 526 | } | 1180 | } |
| 527 | }, | 1181 | }, |
| 528 | else => {}, | 1182 | else => {}, |
| 529 | } | 1183 | } |
| 530 | } | 1184 | } |
| 531 | } | 1185 | } |
| 1186 | |||
| 1187 | fn 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 | |||
| 1213 | fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { | ||
| 1214 | switch (stdio) { | ||
| 1215 | .infer_from_args, .inherit, .zig_test => {}, | ||
| 1216 | .check => |checks| for (checks.items) |check| { | ||
| 1217 | hh.add(@as(std.meta.Tag(StdIo.Check), check)); | ||
| 1218 | switch (check) { | ||
| 1219 | .expect_stderr_exact, | ||
| 1220 | .expect_stderr_match, | ||
| 1221 | .expect_stdout_exact, | ||
| 1222 | .expect_stdout_match, | ||
| 1223 | => |s| hh.addBytes(s), | ||
| 1224 | |||
| 1225 | .expect_term => |term| { | ||
| 1226 | hh.add(@as(std.meta.Tag(std.process.Child.Term), term)); | ||
| 1227 | switch (term) { | ||
| 1228 | .Exited => |x| hh.add(x), | ||
| 1229 | .Signal, .Stopped, .Unknown => |x| hh.add(x), | ||
| 1230 | } | ||
| 1231 | }, | ||
| 1232 | } | ||
| 1233 | }, | ||
| 1234 | } | ||
| 1235 | } |
lib/std/Build/Step.zig+467-25| ... | @@ -1,9 +1,77 @@ | ... | @@ -1,9 +1,77 @@ |
| 1 | id: Id, | 1 | id: Id, |
| 2 | name: []const u8, | 2 | name: []const u8, |
| 3 | makeFn: *const fn (self: *Step) anyerror!void, | 3 | owner: *Build, |
| 4 | makeFn: MakeFn, | ||
| 5 | |||
| 4 | dependencies: std.ArrayList(*Step), | 6 | dependencies: std.ArrayList(*Step), |
| 5 | loop_flag: bool, | 7 | /// This field is empty during execution of the user's build script, and |
| 6 | done_flag: bool, | 8 | /// then populated during dependency loop checking in the build runner. |
| 9 | dependants: std.ArrayListUnmanaged(*Step), | ||
| 10 | state: 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. | ||
| 30 | max_rss: usize, | ||
| 31 | |||
| 32 | result_error_msgs: std.ArrayListUnmanaged([]const u8), | ||
| 33 | result_error_bundle: std.zig.ErrorBundle, | ||
| 34 | result_cached: bool, | ||
| 35 | result_duration_ns: ?u64, | ||
| 36 | /// 0 means unavailable or not reported. | ||
| 37 | result_peak_rss: usize, | ||
| 38 | test_results: TestResults, | ||
| 39 | |||
| 40 | /// The return addresss associated with creation of this step that can be useful | ||
| 41 | /// to print along with debugging messages. | ||
| 42 | debug_stack_trace: [n_debug_stack_frames]usize, | ||
| 43 | |||
| 44 | pub 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 | |||
| 59 | pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; | ||
| 60 | |||
| 61 | const n_debug_stack_frames = 4; | ||
| 62 | |||
| 63 | pub 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 | }; | ||
| 7 | 75 | ||
| 8 | pub const Id = enum { | 76 | pub const Id = enum { |
| 9 | top_level, | 77 | top_level, |
| ... | @@ -17,7 +85,6 @@ pub const Id = enum { | ... | @@ -17,7 +85,6 @@ pub const Id = enum { |
| 17 | translate_c, | 85 | translate_c, |
| 18 | write_file, | 86 | write_file, |
| 19 | run, | 87 | run, |
| 20 | emulatable_run, | ||
| 21 | check_file, | 88 | check_file, |
| 22 | check_object, | 89 | check_object, |
| 23 | config_header, | 90 | config_header, |
| ... | @@ -38,7 +105,6 @@ pub const Id = enum { | ... | @@ -38,7 +105,6 @@ pub const Id = enum { |
| 38 | .translate_c => Build.TranslateCStep, | 105 | .translate_c => Build.TranslateCStep, |
| 39 | .write_file => Build.WriteFileStep, | 106 | .write_file => Build.WriteFileStep, |
| 40 | .run => Build.RunStep, | 107 | .run => Build.RunStep, |
| 41 | .emulatable_run => Build.EmulatableRunStep, | ||
| 42 | .check_file => Build.CheckFileStep, | 108 | .check_file => Build.CheckFileStep, |
| 43 | .check_object => Build.CheckObjectStep, | 109 | .check_object => Build.CheckObjectStep, |
| 44 | .config_header => Build.ConfigHeaderStep, | 110 | .config_header => Build.ConfigHeaderStep, |
| ... | @@ -49,39 +115,99 @@ pub const Id = enum { | ... | @@ -49,39 +115,99 @@ pub const Id = enum { |
| 49 | } | 115 | } |
| 50 | }; | 116 | }; |
| 51 | 117 | ||
| 52 | pub fn init( | 118 | pub const Options = struct { |
| 53 | id: Id, | 119 | id: Id, |
| 54 | name: []const u8, | 120 | name: []const u8, |
| 55 | allocator: Allocator, | 121 | owner: *Build, |
| 56 | makeFn: *const fn (self: *Step) anyerror!void, | 122 | makeFn: MakeFn = makeNoOp, |
| 57 | ) Step { | 123 | first_ret_addr: ?usize = null, |
| 58 | return Step{ | 124 | max_rss: usize = 0, |
| 59 | .id = id, | 125 | }; |
| 60 | .name = allocator.dupe(u8, name) catch @panic("OOM"), | 126 | |
| 61 | .makeFn = makeFn, | 127 | pub fn init(options: Options) Step { |
| 62 | .dependencies = std.ArrayList(*Step).init(allocator), | 128 | const arena = options.owner.allocator; |
| 63 | .loop_flag = false, | 129 | |
| 64 | .done_flag = false, | 130 | var addresses = [1]usize{0} ** n_debug_stack_frames; |
| 131 | const first_ret_addr = options.first_ret_addr orelse @returnAddress(); | ||
| 132 | var stack_trace = std.builtin.StackTrace{ | ||
| 133 | .instruction_addresses = &addresses, | ||
| 134 | .index = 0, | ||
| 65 | }; | 135 | }; |
| 66 | } | 136 | std.debug.captureStackTrace(first_ret_addr, &stack_trace); |
| 67 | 137 | ||
| 68 | pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { | 138 | return .{ |
| 69 | return init(id, name, allocator, makeNoOp); | 139 | .id = options.id, |
| 140 | .name = arena.dupe(u8, options.name) catch @panic("OOM"), | ||
| 141 | .owner = options.owner, | ||
| 142 | .makeFn = options.makeFn, | ||
| 143 | .dependencies = std.ArrayList(*Step).init(arena), | ||
| 144 | .dependants = .{}, | ||
| 145 | .state = .precheck_unstarted, | ||
| 146 | .max_rss = options.max_rss, | ||
| 147 | .debug_stack_trace = addresses, | ||
| 148 | .result_error_msgs = .{}, | ||
| 149 | .result_error_bundle = std.zig.ErrorBundle.empty, | ||
| 150 | .result_cached = false, | ||
| 151 | .result_duration_ns = null, | ||
| 152 | .result_peak_rss = 0, | ||
| 153 | .test_results = .{}, | ||
| 154 | }; | ||
| 70 | } | 155 | } |
| 71 | 156 | ||
| 72 | pub fn make(self: *Step) !void { | 157 | /// If the Step's `make` function reports `error.MakeFailed`, it indicates they |
| 73 | if (self.done_flag) return; | 158 | /// have already reported the error. Otherwise, we add a simple error report |
| 159 | /// here. | ||
| 160 | pub 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 | } | ||
| 74 | 175 | ||
| 75 | try self.makeFn(self); | 176 | if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) { |
| 76 | self.done_flag = true; | 177 | const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{ |
| 178 | s.result_peak_rss, s.max_rss, | ||
| 179 | }) catch @panic("OOM"); | ||
| 180 | s.result_error_msgs.append(arena, msg) catch @panic("OOM"); | ||
| 181 | return error.MakeFailed; | ||
| 182 | } | ||
| 77 | } | 183 | } |
| 78 | 184 | ||
| 79 | pub fn dependOn(self: *Step, other: *Step) void { | 185 | pub fn dependOn(self: *Step, other: *Step) void { |
| 80 | self.dependencies.append(other) catch @panic("OOM"); | 186 | self.dependencies.append(other) catch @panic("OOM"); |
| 81 | } | 187 | } |
| 82 | 188 | ||
| 83 | fn makeNoOp(self: *Step) anyerror!void { | 189 | pub fn getStackTrace(s: *Step) std.builtin.StackTrace { |
| 84 | _ = self; | 190 | const stack_addresses = &s.debug_stack_trace; |
| 191 | var len: usize = 0; | ||
| 192 | while (len < n_debug_stack_frames and stack_addresses[len] != 0) { | ||
| 193 | len += 1; | ||
| 194 | } | ||
| 195 | return .{ | ||
| 196 | .instruction_addresses = stack_addresses, | ||
| 197 | .index = len, | ||
| 198 | }; | ||
| 199 | } | ||
| 200 | |||
| 201 | fn makeNoOp(step: *Step, prog_node: *std.Progress.Node) anyerror!void { | ||
| 202 | _ = prog_node; | ||
| 203 | |||
| 204 | var all_cached = true; | ||
| 205 | |||
| 206 | for (step.dependencies.items) |dep| { | ||
| 207 | all_cached = all_cached and dep.result_cached; | ||
| 208 | } | ||
| 209 | |||
| 210 | step.result_cached = all_cached; | ||
| 85 | } | 211 | } |
| 86 | 212 | ||
| 87 | pub fn cast(step: *Step, comptime T: type) ?*T { | 213 | pub fn cast(step: *Step, comptime T: type) ?*T { |
| ... | @@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T { | ... | @@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 91 | return null; | 217 | return null; |
| 92 | } | 218 | } |
| 93 | 219 | ||
| 220 | /// For debugging purposes, prints identifying information about this Step. | ||
| 221 | pub 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 | |||
| 94 | const Step = @This(); | 242 | const Step = @This(); |
| 95 | const std = @import("../std.zig"); | 243 | const std = @import("../std.zig"); |
| 96 | const Build = std.Build; | 244 | const Build = std.Build; |
| 97 | const Allocator = std.mem.Allocator; | 245 | const Allocator = std.mem.Allocator; |
| 246 | const assert = std.debug.assert; | ||
| 247 | const builtin = @import("builtin"); | ||
| 248 | |||
| 249 | pub 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 | |||
| 267 | pub 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 | |||
| 272 | pub 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. | ||
| 280 | pub 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 | |||
| 419 | fn 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 | |||
| 427 | pub 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 | |||
| 435 | pub 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 | |||
| 449 | pub 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 | |||
| 462 | pub 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 | |||
| 487 | pub 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 | |||
| 495 | pub 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 | |||
| 521 | pub 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 | |||
| 526 | fn 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 | |||
| 533 | pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void { | ||
| 534 | if (s.test_results.isSuccess()) { | ||
| 535 | man.writeManifest() catch |err| { | ||
| 536 | try s.addError("unable to write cache manifest: {s}", .{@errorName(err)}); | ||
| 537 | }; | ||
| 538 | } | ||
| 539 | } |
lib/std/Build/TranslateCStep.zig+30-22| ... | @@ -11,7 +11,6 @@ const TranslateCStep = @This(); | ... | @@ -11,7 +11,6 @@ const TranslateCStep = @This(); |
| 11 | pub const base_id = .translate_c; | 11 | pub const base_id = .translate_c; |
| 12 | 12 | ||
| 13 | step: Step, | 13 | step: Step, |
| 14 | builder: *std.Build, | ||
| 15 | source: std.Build.FileSource, | 14 | source: std.Build.FileSource, |
| 16 | include_dirs: std.ArrayList([]const u8), | 15 | include_dirs: std.ArrayList([]const u8), |
| 17 | c_macros: std.ArrayList([]const u8), | 16 | c_macros: std.ArrayList([]const u8), |
| ... | @@ -26,15 +25,19 @@ pub const Options = struct { | ... | @@ -26,15 +25,19 @@ pub const Options = struct { |
| 26 | optimize: std.builtin.OptimizeMode, | 25 | optimize: std.builtin.OptimizeMode, |
| 27 | }; | 26 | }; |
| 28 | 27 | ||
| 29 | pub fn create(builder: *std.Build, options: Options) *TranslateCStep { | 28 | pub fn create(owner: *std.Build, options: Options) *TranslateCStep { |
| 30 | const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); | 29 | const self = owner.allocator.create(TranslateCStep) catch @panic("OOM"); |
| 31 | const source = options.source_file.dupe(builder); | 30 | const source = options.source_file.dupe(owner); |
| 32 | self.* = TranslateCStep{ | 31 | self.* = TranslateCStep{ |
| 33 | .step = Step.init(.translate_c, "translate-c", builder.allocator, make), | 32 | .step = Step.init(.{ |
| 34 | .builder = builder, | 33 | .id = .translate_c, |
| 34 | .name = "translate-c", | ||
| 35 | .owner = owner, | ||
| 36 | .makeFn = make, | ||
| 37 | }), | ||
| 35 | .source = source, | 38 | .source = source, |
| 36 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | 39 | .include_dirs = std.ArrayList([]const u8).init(owner.allocator), |
| 37 | .c_macros = std.ArrayList([]const u8).init(builder.allocator), | 40 | .c_macros = std.ArrayList([]const u8).init(owner.allocator), |
| 38 | .out_basename = undefined, | 41 | .out_basename = undefined, |
| 39 | .target = options.target, | 42 | .target = options.target, |
| 40 | .optimize = options.optimize, | 43 | .optimize = options.optimize, |
| ... | @@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct { | ... | @@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct { |
| 54 | 57 | ||
| 55 | /// Creates a step to build an executable from the translated source. | 58 | /// Creates a step to build an executable from the translated source. |
| 56 | pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { | 59 | pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { |
| 57 | return self.builder.addExecutable(.{ | 60 | return self.step.owner.addExecutable(.{ |
| 58 | .root_source_file = .{ .generated = &self.output_file }, | 61 | .root_source_file = .{ .generated = &self.output_file }, |
| 59 | .name = options.name orelse "translated_c", | 62 | .name = options.name orelse "translated_c", |
| 60 | .version = options.version, | 63 | .version = options.version, |
| ... | @@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp | ... | @@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp |
| 65 | } | 68 | } |
| 66 | 69 | ||
| 67 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { | 70 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { |
| 68 | self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM"); | 71 | self.include_dirs.append(self.step.owner.dupePath(include_dir)) catch @panic("OOM"); |
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | 74 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { |
| 72 | return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); | 75 | return CheckFileStep.create( |
| 76 | self.step.owner, | ||
| 77 | .{ .generated = &self.output_file }, | ||
| 78 | .{ .expected_matches = expected_matches }, | ||
| 79 | ); | ||
| 73 | } | 80 | } |
| 74 | 81 | ||
| 75 | /// If the value is omitted, it is set to 1. | 82 | /// If the value is omitted, it is set to 1. |
| 76 | /// `name` and `value` need not live longer than the function call. | 83 | /// `name` and `value` need not live longer than the function call. |
| 77 | pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { | 84 | pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { |
| 78 | const macro = std.Build.constructCMacro(self.builder.allocator, name, value); | 85 | const macro = std.Build.constructCMacro(self.step.owner.allocator, name, value); |
| 79 | self.c_macros.append(macro) catch @panic("OOM"); | 86 | self.c_macros.append(macro) catch @panic("OOM"); |
| 80 | } | 87 | } |
| 81 | 88 | ||
| 82 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | 89 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. |
| 83 | pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { | 90 | pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { |
| 84 | self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); | 91 | self.c_macros.append(self.step.owner.dupe(name_and_value)) catch @panic("OOM"); |
| 85 | } | 92 | } |
| 86 | 93 | ||
| 87 | fn make(step: *Step) !void { | 94 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 95 | const b = step.owner; | ||
| 88 | const self = @fieldParentPtr(TranslateCStep, "step", step); | 96 | const self = @fieldParentPtr(TranslateCStep, "step", step); |
| 89 | 97 | ||
| 90 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | 98 | var argv_list = std.ArrayList([]const u8).init(b.allocator); |
| 91 | try argv_list.append(self.builder.zig_exe); | 99 | try argv_list.append(b.zig_exe); |
| 92 | try argv_list.append("translate-c"); | 100 | try argv_list.append("translate-c"); |
| 93 | try argv_list.append("-lc"); | 101 | try argv_list.append("-lc"); |
| 94 | 102 | ||
| 95 | try argv_list.append("--enable-cache"); | 103 | try argv_list.append("--enable-cache"); |
| 104 | try argv_list.append("--listen=-"); | ||
| 96 | 105 | ||
| 97 | if (!self.target.isNative()) { | 106 | if (!self.target.isNative()) { |
| 98 | try argv_list.append("-target"); | 107 | try argv_list.append("-target"); |
| 99 | try argv_list.append(try self.target.zigTriple(self.builder.allocator)); | 108 | try argv_list.append(try self.target.zigTriple(b.allocator)); |
| 100 | } | 109 | } |
| 101 | 110 | ||
| 102 | switch (self.optimize) { | 111 | switch (self.optimize) { |
| 103 | .Debug => {}, // Skip since it's the default. | 112 | .Debug => {}, // Skip since it's the default. |
| 104 | else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})), | 113 | else => try argv_list.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), |
| 105 | } | 114 | } |
| 106 | 115 | ||
| 107 | for (self.include_dirs.items) |include_dir| { | 116 | for (self.include_dirs.items) |include_dir| { |
| ... | @@ -114,16 +123,15 @@ fn make(step: *Step) !void { | ... | @@ -114,16 +123,15 @@ fn make(step: *Step) !void { |
| 114 | try argv_list.append(c_macro); | 123 | try argv_list.append(c_macro); |
| 115 | } | 124 | } |
| 116 | 125 | ||
| 117 | try argv_list.append(self.source.getPath(self.builder)); | 126 | try argv_list.append(self.source.getPath(b)); |
| 118 | 127 | ||
| 119 | const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); | 128 | const output_path = try step.evalZigProcess(argv_list.items, prog_node); |
| 120 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | ||
| 121 | 129 | ||
| 122 | self.out_basename = fs.path.basename(output_path); | 130 | self.out_basename = fs.path.basename(output_path); |
| 123 | const output_dir = fs.path.dirname(output_path).?; | 131 | const output_dir = fs.path.dirname(output_path).?; |
| 124 | 132 | ||
| 125 | self.output_file.path = try fs.path.join( | 133 | self.output_file.path = try fs.path.join( |
| 126 | self.builder.allocator, | 134 | b.allocator, |
| 127 | &[_][]const u8{ output_dir, self.out_basename }, | 135 | &[_][]const u8{ output_dir, self.out_basename }, |
| 128 | ); | 136 | ); |
| 129 | } | 137 | } |
lib/std/Build/WriteFileStep.zig+145-67| ... | @@ -10,11 +10,11 @@ | ... | @@ -10,11 +10,11 @@ |
| 10 | //! control. | 10 | //! control. |
| 11 | 11 | ||
| 12 | step: Step, | 12 | step: Step, |
| 13 | builder: *std.Build, | ||
| 14 | /// The elements here are pointers because we need stable pointers for the | 13 | /// The elements here are pointers because we need stable pointers for the |
| 15 | /// GeneratedFile field. | 14 | /// GeneratedFile field. |
| 16 | files: std.ArrayListUnmanaged(*File), | 15 | files: std.ArrayListUnmanaged(*File), |
| 17 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), | 16 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), |
| 17 | generated_directory: std.Build.GeneratedFile, | ||
| 18 | 18 | ||
| 19 | pub const base_id = .write_file; | 19 | pub const base_id = .write_file; |
| 20 | 20 | ||
| ... | @@ -34,24 +34,34 @@ pub const Contents = union(enum) { | ... | @@ -34,24 +34,34 @@ pub const Contents = union(enum) { |
| 34 | copy: std.Build.FileSource, | 34 | copy: std.Build.FileSource, |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | pub fn init(builder: *std.Build) WriteFileStep { | 37 | pub fn create(owner: *std.Build) *WriteFileStep { |
| 38 | return .{ | 38 | const wf = owner.allocator.create(WriteFileStep) catch @panic("OOM"); |
| 39 | .builder = builder, | 39 | wf.* = .{ |
| 40 | .step = Step.init(.write_file, "writefile", builder.allocator, make), | 40 | .step = Step.init(.{ |
| 41 | .id = .write_file, | ||
| 42 | .name = "WriteFile", | ||
| 43 | .owner = owner, | ||
| 44 | .makeFn = make, | ||
| 45 | }), | ||
| 41 | .files = .{}, | 46 | .files = .{}, |
| 42 | .output_source_files = .{}, | 47 | .output_source_files = .{}, |
| 48 | .generated_directory = .{ .step = &wf.step }, | ||
| 43 | }; | 49 | }; |
| 50 | return wf; | ||
| 44 | } | 51 | } |
| 45 | 52 | ||
| 46 | pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { | 53 | pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { |
| 47 | const gpa = wf.builder.allocator; | 54 | const b = wf.step.owner; |
| 55 | const gpa = b.allocator; | ||
| 48 | const file = gpa.create(File) catch @panic("OOM"); | 56 | const file = gpa.create(File) catch @panic("OOM"); |
| 49 | file.* = .{ | 57 | file.* = .{ |
| 50 | .generated_file = .{ .step = &wf.step }, | 58 | .generated_file = .{ .step = &wf.step }, |
| 51 | .sub_path = wf.builder.dupePath(sub_path), | 59 | .sub_path = b.dupePath(sub_path), |
| 52 | .contents = .{ .bytes = wf.builder.dupe(bytes) }, | 60 | .contents = .{ .bytes = b.dupe(bytes) }, |
| 53 | }; | 61 | }; |
| 54 | wf.files.append(gpa, file) catch @panic("OOM"); | 62 | wf.files.append(gpa, file) catch @panic("OOM"); |
| 63 | |||
| 64 | wf.maybeUpdateName(); | ||
| 55 | } | 65 | } |
| 56 | 66 | ||
| 57 | /// Place the file into the generated directory within the local cache, | 67 | /// Place the file into the generated directory within the local cache, |
| ... | @@ -62,14 +72,18 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { | ... | @@ -62,14 +72,18 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { |
| 62 | /// required sub-path exists. | 72 | /// required sub-path exists. |
| 63 | /// This is the option expected to be used most commonly with `addCopyFile`. | 73 | /// This is the option expected to be used most commonly with `addCopyFile`. |
| 64 | pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { | 74 | pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { |
| 65 | const gpa = wf.builder.allocator; | 75 | const b = wf.step.owner; |
| 76 | const gpa = b.allocator; | ||
| 66 | const file = gpa.create(File) catch @panic("OOM"); | 77 | const file = gpa.create(File) catch @panic("OOM"); |
| 67 | file.* = .{ | 78 | file.* = .{ |
| 68 | .generated_file = .{ .step = &wf.step }, | 79 | .generated_file = .{ .step = &wf.step }, |
| 69 | .sub_path = wf.builder.dupePath(sub_path), | 80 | .sub_path = b.dupePath(sub_path), |
| 70 | .contents = .{ .copy = source }, | 81 | .contents = .{ .copy = source }, |
| 71 | }; | 82 | }; |
| 72 | wf.files.append(gpa, file) catch @panic("OOM"); | 83 | wf.files.append(gpa, file) catch @panic("OOM"); |
| 84 | |||
| 85 | wf.maybeUpdateName(); | ||
| 86 | source.addStepDependencies(&wf.step); | ||
| 73 | } | 87 | } |
| 74 | 88 | ||
| 75 | /// A path relative to the package root. | 89 | /// A path relative to the package root. |
| ... | @@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ | ... | @@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ |
| 79 | /// those changes to version control. | 93 | /// those changes to version control. |
| 80 | /// A file added this way is not available with `getFileSource`. | 94 | /// A file added this way is not available with `getFileSource`. |
| 81 | pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { | 95 | pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { |
| 82 | wf.output_source_files.append(wf.builder.allocator, .{ | 96 | const b = wf.step.owner; |
| 97 | wf.output_source_files.append(b.allocator, .{ | ||
| 83 | .contents = .{ .copy = source }, | 98 | .contents = .{ .copy = source }, |
| 84 | .sub_path = sub_path, | 99 | .sub_path = sub_path, |
| 85 | }) catch @panic("OOM"); | 100 | }) catch @panic("OOM"); |
| 101 | source.addStepDependencies(&wf.step); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// A path relative to the package root. | ||
| 105 | /// Be careful with this because it updates source files. This should not be | ||
| 106 | /// used as part of the normal build process, but as a utility occasionally | ||
| 107 | /// run by a developer with intent to modify source files and then commit | ||
| 108 | /// those changes to version control. | ||
| 109 | /// A file added this way is not available with `getFileSource`. | ||
| 110 | pub fn addBytesToSource(wf: *WriteFileStep, bytes: []const u8, sub_path: []const u8) void { | ||
| 111 | const b = wf.step.owner; | ||
| 112 | wf.output_source_files.append(b.allocator, .{ | ||
| 113 | .contents = .{ .bytes = bytes }, | ||
| 114 | .sub_path = sub_path, | ||
| 115 | }) catch @panic("OOM"); | ||
| 86 | } | 116 | } |
| 87 | 117 | ||
| 88 | /// Gets a file source for the given sub_path. If the file does not exist, returns `null`. | 118 | /// Gets a file source for the given sub_path. If the file does not exist, returns `null`. |
| ... | @@ -95,21 +125,63 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo | ... | @@ -95,21 +125,63 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo |
| 95 | return null; | 125 | return null; |
| 96 | } | 126 | } |
| 97 | 127 | ||
| 98 | fn make(step: *Step) !void { | 128 | /// Returns a `FileSource` representing the base directory that contains all the |
| 129 | /// files from this `WriteFileStep`. | ||
| 130 | pub fn getDirectorySource(wf: *WriteFileStep) std.Build.FileSource { | ||
| 131 | return .{ .generated = &wf.generated_directory }; | ||
| 132 | } | ||
| 133 | |||
| 134 | fn 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 | |||
| 143 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ||
| 144 | _ = prog_node; | ||
| 145 | const b = step.owner; | ||
| 99 | const wf = @fieldParentPtr(WriteFileStep, "step", step); | 146 | const wf = @fieldParentPtr(WriteFileStep, "step", step); |
| 100 | 147 | ||
| 101 | // Writing to source files is kind of an extra capability of this | 148 | // Writing to source files is kind of an extra capability of this |
| 102 | // WriteFileStep - arguably it should be a different step. But anyway here | 149 | // WriteFileStep - arguably it should be a different step. But anyway here |
| 103 | // it is, it happens unconditionally and does not interact with the other | 150 | // it is, it happens unconditionally and does not interact with the other |
| 104 | // files here. | 151 | // files here. |
| 152 | var any_miss = false; | ||
| 105 | for (wf.output_source_files.items) |output_source_file| { | 153 | for (wf.output_source_files.items) |output_source_file| { |
| 106 | const basename = fs.path.basename(output_source_file.sub_path); | ||
| 107 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { | 154 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { |
| 108 | var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{}); | 155 | b.build_root.handle.makePath(dirname) catch |err| { |
| 109 | defer dir.close(); | 156 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 110 | try writeFile(wf, dir, output_source_file.contents, basename); | 157 | b.build_root, dirname, @errorName(err), |
| 111 | } else { | 158 | }); |
| 112 | try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename); | 159 | }; |
| 160 | } | ||
| 161 | switch (output_source_file.contents) { | ||
| 162 | .bytes => |bytes| { | ||
| 163 | b.build_root.handle.writeFile(output_source_file.sub_path, bytes) catch |err| { | ||
| 164 | return step.fail("unable to write file '{}{s}': {s}", .{ | ||
| 165 | b.build_root, output_source_file.sub_path, @errorName(err), | ||
| 166 | }); | ||
| 167 | }; | ||
| 168 | any_miss = true; | ||
| 169 | }, | ||
| 170 | .copy => |file_source| { | ||
| 171 | const source_path = file_source.getPath(b); | ||
| 172 | const prev_status = fs.Dir.updateFile( | ||
| 173 | fs.cwd(), | ||
| 174 | source_path, | ||
| 175 | b.build_root.handle, | ||
| 176 | output_source_file.sub_path, | ||
| 177 | .{}, | ||
| 178 | ) catch |err| { | ||
| 179 | return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{ | ||
| 180 | source_path, b.build_root, output_source_file.sub_path, @errorName(err), | ||
| 181 | }); | ||
| 182 | }; | ||
| 183 | any_miss = any_miss or prev_status == .stale; | ||
| 184 | }, | ||
| 113 | } | 185 | } |
| 114 | } | 186 | } |
| 115 | 187 | ||
| ... | @@ -120,7 +192,7 @@ fn make(step: *Step) !void { | ... | @@ -120,7 +192,7 @@ fn make(step: *Step) !void { |
| 120 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | 192 | // If, for example, a hard-coded path was used as the location to put WriteFileStep |
| 121 | // files, then two WriteFileSteps executing in parallel might clobber each other. | 193 | // files, then two WriteFileSteps executing in parallel might clobber each other. |
| 122 | 194 | ||
| 123 | var man = wf.builder.cache.obtain(); | 195 | var man = b.cache.obtain(); |
| 124 | defer man.deinit(); | 196 | defer man.deinit(); |
| 125 | 197 | ||
| 126 | // Random bytes to make WriteFileStep unique. Refresh this with | 198 | // Random bytes to make WriteFileStep unique. Refresh this with |
| ... | @@ -135,76 +207,82 @@ fn make(step: *Step) !void { | ... | @@ -135,76 +207,82 @@ fn make(step: *Step) !void { |
| 135 | man.hash.addBytes(bytes); | 207 | man.hash.addBytes(bytes); |
| 136 | }, | 208 | }, |
| 137 | .copy => |file_source| { | 209 | .copy => |file_source| { |
| 138 | _ = try man.addFile(file_source.getPath(wf.builder), null); | 210 | _ = try man.addFile(file_source.getPath(b), null); |
| 139 | }, | 211 | }, |
| 140 | } | 212 | } |
| 141 | } | 213 | } |
| 142 | 214 | ||
| 143 | if (man.hit() catch |err| failWithCacheError(man, err)) { | 215 | if (try step.cacheHit(&man)) { |
| 144 | // Cache hit, skip writing file data. | ||
| 145 | const digest = man.final(); | 216 | const digest = man.final(); |
| 146 | for (wf.files.items) |file| { | 217 | for (wf.files.items) |file| { |
| 147 | file.generated_file.path = try wf.builder.cache_root.join( | 218 | file.generated_file.path = try b.cache_root.join(b.allocator, &.{ |
| 148 | wf.builder.allocator, | 219 | "o", &digest, file.sub_path, |
| 149 | &.{ "o", &digest, file.sub_path }, | 220 | }); |
| 150 | ); | ||
| 151 | } | 221 | } |
| 222 | wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); | ||
| 152 | return; | 223 | return; |
| 153 | } | 224 | } |
| 154 | 225 | ||
| 155 | const digest = man.final(); | 226 | const digest = man.final(); |
| 156 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | 227 | const cache_path = "o" ++ fs.path.sep_str ++ digest; |
| 157 | 228 | ||
| 158 | var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { | 229 | wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); |
| 159 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | 230 | |
| 160 | return err; | 231 | var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { |
| 232 | return step.fail("unable to make path '{}{s}': {s}", .{ | ||
| 233 | b.cache_root, cache_path, @errorName(err), | ||
| 234 | }); | ||
| 161 | }; | 235 | }; |
| 162 | defer cache_dir.close(); | 236 | defer cache_dir.close(); |
| 163 | 237 | ||
| 164 | for (wf.files.items) |file| { | 238 | for (wf.files.items) |file| { |
| 165 | const basename = fs.path.basename(file.sub_path); | ||
| 166 | if (fs.path.dirname(file.sub_path)) |dirname| { | 239 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 167 | var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{}); | 240 | cache_dir.makePath(dirname) catch |err| { |
| 168 | defer dir.close(); | 241 | return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ |
| 169 | try writeFile(wf, dir, file.contents, basename); | 242 | b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err), |
| 170 | } else { | 243 | }); |
| 171 | try writeFile(wf, cache_dir, file.contents, basename); | 244 | }; |
| 245 | } | ||
| 246 | switch (file.contents) { | ||
| 247 | .bytes => |bytes| { | ||
| 248 | cache_dir.writeFile(file.sub_path, bytes) catch |err| { | ||
| 249 | return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{ | ||
| 250 | b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err), | ||
| 251 | }); | ||
| 252 | }; | ||
| 253 | }, | ||
| 254 | .copy => |file_source| { | ||
| 255 | const source_path = file_source.getPath(b); | ||
| 256 | const prev_status = fs.Dir.updateFile( | ||
| 257 | fs.cwd(), | ||
| 258 | source_path, | ||
| 259 | cache_dir, | ||
| 260 | file.sub_path, | ||
| 261 | .{}, | ||
| 262 | ) catch |err| { | ||
| 263 | return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ | ||
| 264 | source_path, | ||
| 265 | b.cache_root, | ||
| 266 | cache_path, | ||
| 267 | fs.path.sep, | ||
| 268 | file.sub_path, | ||
| 269 | @errorName(err), | ||
| 270 | }); | ||
| 271 | }; | ||
| 272 | // At this point we already will mark the step as a cache miss. | ||
| 273 | // But this is kind of a partial cache hit since individual | ||
| 274 | // file copies may be avoided. Oh well, this information is | ||
| 275 | // discarded. | ||
| 276 | _ = prev_status; | ||
| 277 | }, | ||
| 172 | } | 278 | } |
| 173 | 279 | ||
| 174 | file.generated_file.path = try wf.builder.cache_root.join( | 280 | file.generated_file.path = try b.cache_root.join(b.allocator, &.{ |
| 175 | wf.builder.allocator, | 281 | cache_path, file.sub_path, |
| 176 | &.{ cache_path, file.sub_path }, | 282 | }); |
| 177 | ); | ||
| 178 | } | ||
| 179 | |||
| 180 | try man.writeManifest(); | ||
| 181 | } | ||
| 182 | |||
| 183 | fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { | ||
| 184 | // TODO after landing concurrency PR, improve error reporting here | ||
| 185 | switch (contents) { | ||
| 186 | .bytes => |bytes| return dir.writeFile(basename, bytes), | ||
| 187 | .copy => |file_source| { | ||
| 188 | const source_path = file_source.getPath(wf.builder); | ||
| 189 | const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); | ||
| 190 | _ = prev_status; // TODO logging (affected by open PR regarding concurrency) | ||
| 191 | }, | ||
| 192 | } | 283 | } |
| 193 | } | ||
| 194 | |||
| 195 | /// TODO consolidate this with the same function in RunStep? | ||
| 196 | /// Also properly deal with concurrency (see open PR) | ||
| 197 | fn 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 | } | ||
| 204 | 284 | ||
| 205 | fn failWithSimpleError(err: anyerror) noreturn { | 285 | try step.writeManifest(&man); |
| 206 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 207 | std.process.exit(1); | ||
| 208 | } | 286 | } |
| 209 | 287 | ||
| 210 | const std = @import("../std.zig"); | 288 | const std = @import("../std.zig"); |
lib/std/Progress.zig+72-26| ... | @@ -126,6 +126,21 @@ pub const Node = struct { | ... | @@ -126,6 +126,21 @@ pub const Node = struct { |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | /// Thread-safe. | ||
| 130 | pub fn setName(self: *Node, name: []const u8) void { | ||
| 131 | const progress = self.context; | ||
| 132 | progress.update_mutex.lock(); | ||
| 133 | defer progress.update_mutex.unlock(); | ||
| 134 | self.name = name; | ||
| 135 | if (self.parent) |parent| { | ||
| 136 | @atomicStore(?*Node, &parent.recently_updated_child, self, .Release); | ||
| 137 | if (parent.parent) |grand_parent| { | ||
| 138 | @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .Release); | ||
| 139 | } | ||
| 140 | if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 129 | /// Thread-safe. 0 means unknown. | 144 | /// Thread-safe. 0 means unknown. |
| 130 | pub fn setEstimatedTotalItems(self: *Node, count: usize) void { | 145 | pub fn setEstimatedTotalItems(self: *Node, count: usize) void { |
| 131 | @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic); | 146 | @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic); |
| ... | @@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void { | ... | @@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void { |
| 174 | if (self.timer) |*timer| { | 189 | if (self.timer) |*timer| { |
| 175 | if (!self.update_mutex.tryLock()) return; | 190 | if (!self.update_mutex.tryLock()) return; |
| 176 | defer self.update_mutex.unlock(); | 191 | defer self.update_mutex.unlock(); |
| 177 | const now = timer.read(); | 192 | maybeRefreshWithHeldLock(self, timer); |
| 178 | if (now < self.initial_delay_ns) return; | ||
| 179 | // TODO I have observed this to happen sometimes. I think we need to follow Rust's | ||
| 180 | // lead and guarantee monotonically increasing times in the std lib itself. | ||
| 181 | if (now < self.prev_refresh_timestamp) return; | ||
| 182 | if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; | ||
| 183 | return self.refreshWithHeldLock(); | ||
| 184 | } | 193 | } |
| 185 | } | 194 | } |
| 186 | 195 | ||
| 196 | fn maybeRefreshWithHeldLock(self: *Progress, timer: *std.time.Timer) void { | ||
| 197 | const now = timer.read(); | ||
| 198 | if (now < self.initial_delay_ns) return; | ||
| 199 | // TODO I have observed this to happen sometimes. I think we need to follow Rust's | ||
| 200 | // lead and guarantee monotonically increasing times in the std lib itself. | ||
| 201 | if (now < self.prev_refresh_timestamp) return; | ||
| 202 | if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; | ||
| 203 | return self.refreshWithHeldLock(); | ||
| 204 | } | ||
| 205 | |||
| 187 | /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. | 206 | /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. |
| 188 | pub fn refresh(self: *Progress) void { | 207 | pub fn refresh(self: *Progress) void { |
| 189 | if (!self.update_mutex.tryLock()) return; | 208 | if (!self.update_mutex.tryLock()) return; |
| ... | @@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void { | ... | @@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void { |
| 192 | return self.refreshWithHeldLock(); | 211 | return self.refreshWithHeldLock(); |
| 193 | } | 212 | } |
| 194 | 213 | ||
| 195 | fn refreshWithHeldLock(self: *Progress) void { | 214 | fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void { |
| 196 | const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; | 215 | const file = p.terminal orelse return; |
| 197 | if (is_dumb and self.dont_print_on_dumb) return; | 216 | var end = end_ptr.*; |
| 198 | 217 | if (p.columns_written > 0) { | |
| 199 | const file = self.terminal orelse return; | ||
| 200 | |||
| 201 | var end: usize = 0; | ||
| 202 | if (self.columns_written > 0) { | ||
| 203 | // restore the cursor position by moving the cursor | 218 | // restore the cursor position by moving the cursor |
| 204 | // `columns_written` cells to the left, then clear the rest of the | 219 | // `columns_written` cells to the left, then clear the rest of the |
| 205 | // line | 220 | // line |
| 206 | if (self.supports_ansi_escape_codes) { | 221 | if (p.supports_ansi_escape_codes) { |
| 207 | end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len; | 222 | end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len; |
| 208 | end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; | 223 | end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; |
| 209 | } else if (builtin.os.tag == .windows) winapi: { | 224 | } else if (builtin.os.tag == .windows) winapi: { |
| 210 | std.debug.assert(self.is_windows_terminal); | 225 | std.debug.assert(p.is_windows_terminal); |
| 211 | 226 | ||
| 212 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | 227 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| 213 | if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) { | 228 | if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) { |
| 214 | // stop trying to write to this file | 229 | // stop trying to write to this file |
| 215 | self.terminal = null; | 230 | p.terminal = null; |
| 216 | break :winapi; | 231 | break :winapi; |
| 217 | } | 232 | } |
| 218 | 233 | ||
| 219 | var cursor_pos = windows.COORD{ | 234 | var cursor_pos = windows.COORD{ |
| 220 | .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written), | 235 | .X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written), |
| 221 | .Y = info.dwCursorPosition.Y, | 236 | .Y = info.dwCursorPosition.Y, |
| 222 | }; | 237 | }; |
| 223 | 238 | ||
| ... | @@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void { | ... | @@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void { |
| 235 | &written, | 250 | &written, |
| 236 | ) != windows.TRUE) { | 251 | ) != windows.TRUE) { |
| 237 | // stop trying to write to this file | 252 | // stop trying to write to this file |
| 238 | self.terminal = null; | 253 | p.terminal = null; |
| 239 | break :winapi; | 254 | break :winapi; |
| 240 | } | 255 | } |
| 241 | if (windows.kernel32.FillConsoleOutputCharacterW( | 256 | if (windows.kernel32.FillConsoleOutputCharacterW( |
| ... | @@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void { | ... | @@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void { |
| 246 | &written, | 261 | &written, |
| 247 | ) != windows.TRUE) { | 262 | ) != windows.TRUE) { |
| 248 | // stop trying to write to this file | 263 | // stop trying to write to this file |
| 249 | self.terminal = null; | 264 | p.terminal = null; |
| 250 | break :winapi; | 265 | break :winapi; |
| 251 | } | 266 | } |
| 252 | if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) { | 267 | if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) { |
| 253 | // stop trying to write to this file | 268 | // stop trying to write to this file |
| 254 | self.terminal = null; | 269 | p.terminal = null; |
| 255 | break :winapi; | 270 | break :winapi; |
| 256 | } | 271 | } |
| 257 | } else { | 272 | } else { |
| 258 | // we are in a "dumb" terminal like in acme or writing to a file | 273 | // we are in a "dumb" terminal like in acme or writing to a file |
| 259 | self.output_buffer[end] = '\n'; | 274 | p.output_buffer[end] = '\n'; |
| 260 | end += 1; | 275 | end += 1; |
| 261 | } | 276 | } |
| 262 | 277 | ||
| 263 | self.columns_written = 0; | 278 | p.columns_written = 0; |
| 264 | } | 279 | } |
| 280 | end_ptr.* = end; | ||
| 281 | } | ||
| 282 | |||
| 283 | fn 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); | ||
| 265 | 291 | ||
| 266 | if (!self.done) { | 292 | if (!self.done) { |
| 267 | var need_ellipse = false; | 293 | var need_ellipse = false; |
| ... | @@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { | ... | @@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { |
| 318 | self.columns_written = 0; | 344 | self.columns_written = 0; |
| 319 | } | 345 | } |
| 320 | 346 | ||
| 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. | ||
| 349 | pub 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 | |||
| 362 | pub fn unlock_stderr(p: *Progress) void { | ||
| 363 | std.debug.getStderrMutex().unlock(); | ||
| 364 | p.update_mutex.unlock(); | ||
| 365 | } | ||
| 366 | |||
| 321 | fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { | 367 | fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { |
| 322 | if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { | 368 | if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { |
| 323 | const amt = written.len; | 369 | const amt = written.len; |
lib/std/Thread.zig+5| ... | @@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig"); | ... | @@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig"); |
| 16 | pub const Semaphore = @import("Thread/Semaphore.zig"); | 16 | pub const Semaphore = @import("Thread/Semaphore.zig"); |
| 17 | pub const Condition = @import("Thread/Condition.zig"); | 17 | pub const Condition = @import("Thread/Condition.zig"); |
| 18 | pub const RwLock = @import("Thread/RwLock.zig"); | 18 | pub const RwLock = @import("Thread/RwLock.zig"); |
| 19 | pub const Pool = @import("Thread/Pool.zig"); | ||
| 20 | pub const WaitGroup = @import("Thread/WaitGroup.zig"); | ||
| 19 | 21 | ||
| 20 | pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; | 22 | pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; |
| 21 | const is_gnu = target.abi.isGnu(); | 23 | const is_gnu = target.abi.isGnu(); |
| ... | @@ -945,6 +947,7 @@ const LinuxThreadImpl = struct { | ... | @@ -945,6 +947,7 @@ const LinuxThreadImpl = struct { |
| 945 | 947 | ||
| 946 | // map all memory needed without read/write permissions | 948 | // map all memory needed without read/write permissions |
| 947 | // to avoid committing the whole region right away | 949 | // to avoid committing the whole region right away |
| 950 | // anonymous mapping ensures file descriptor limits are not exceeded | ||
| 948 | const mapped = os.mmap( | 951 | const mapped = os.mmap( |
| 949 | null, | 952 | null, |
| 950 | map_bytes, | 953 | map_bytes, |
| ... | @@ -956,6 +959,8 @@ const LinuxThreadImpl = struct { | ... | @@ -956,6 +959,8 @@ const LinuxThreadImpl = struct { |
| 956 | error.MemoryMappingNotSupported => unreachable, | 959 | error.MemoryMappingNotSupported => unreachable, |
| 957 | error.AccessDenied => unreachable, | 960 | error.AccessDenied => unreachable, |
| 958 | error.PermissionDenied => unreachable, | 961 | error.PermissionDenied => unreachable, |
| 962 | error.ProcessFdQuotaExceeded => unreachable, | ||
| 963 | error.SystemFdQuotaExceeded => unreachable, | ||
| 959 | else => |e| return e, | 964 | else => |e| return e, |
| 960 | }; | 965 | }; |
| 961 | assert(mapped.len >= map_bytes); | 966 | assert(mapped.len >= map_bytes); |
lib/std/Thread/Pool.zig created+159| ... | @@ -0,0 +1,159 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Pool = @This(); | ||
| 4 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 5 | |||
| 6 | mutex: std.Thread.Mutex = .{}, | ||
| 7 | cond: std.Thread.Condition = .{}, | ||
| 8 | run_queue: RunQueue = .{}, | ||
| 9 | is_running: bool = true, | ||
| 10 | allocator: std.mem.Allocator, | ||
| 11 | threads: []std.Thread, | ||
| 12 | |||
| 13 | const RunQueue = std.SinglyLinkedList(Runnable); | ||
| 14 | const Runnable = struct { | ||
| 15 | runFn: RunProto, | ||
| 16 | }; | ||
| 17 | |||
| 18 | const RunProto = *const fn (*Runnable) void; | ||
| 19 | |||
| 20 | pub const Options = struct { | ||
| 21 | allocator: std.mem.Allocator, | ||
| 22 | n_jobs: ?u32 = null, | ||
| 23 | }; | ||
| 24 | |||
| 25 | pub 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 | |||
| 51 | pub fn deinit(pool: *Pool) void { | ||
| 52 | pool.join(pool.threads.len); // kill and join all threads. | ||
| 53 | pool.* = undefined; | ||
| 54 | } | ||
| 55 | |||
| 56 | fn 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 | |||
| 79 | pub 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 | |||
| 122 | fn 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 | |||
| 145 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Atomic = std.atomic.Atomic; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const WaitGroup = @This(); | ||
| 5 | |||
| 6 | const is_waiting: usize = 1 << 0; | ||
| 7 | const one_pending: usize = 1 << 1; | ||
| 8 | |||
| 9 | state: Atomic(usize) = Atomic(usize).init(0), | ||
| 10 | event: std.Thread.ResetEvent = .{}, | ||
| 11 | |||
| 12 | pub 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 | |||
| 17 | pub 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 | |||
| 27 | pub 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 | |||
| 36 | pub fn reset(self: *WaitGroup) void { | ||
| 37 | self.state.store(0, .Monotonic); | ||
| 38 | self.event.reset(); | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn isDone(wg: *WaitGroup) bool { | ||
| 42 | const state = wg.state.load(.Acquire); | ||
| 43 | assert(state & is_waiting == 0); | ||
| 44 | |||
| 45 | return (state / one_pending) == 0; | ||
| 46 | } | ||
lib/std/Uri.zig+96-13| ... | @@ -16,15 +16,27 @@ fragment: ?[]const u8, | ... | @@ -16,15 +16,27 @@ fragment: ?[]const u8, |
| 16 | 16 | ||
| 17 | /// Applies URI encoding and replaces all reserved characters with their respective %XX code. | 17 | /// Applies URI encoding and replaces all reserved characters with their respective %XX code. |
| 18 | pub fn escapeString(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { | 18 | pub fn escapeString(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { |
| 19 | return escapeStringWithFn(allocator, input, isUnreserved); | ||
| 20 | } | ||
| 21 | |||
| 22 | pub fn escapePath(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { | ||
| 23 | return escapeStringWithFn(allocator, input, isPathChar); | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn escapeQuery(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { | ||
| 27 | return escapeStringWithFn(allocator, input, isQueryChar); | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn escapeStringWithFn(allocator: std.mem.Allocator, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) std.mem.Allocator.Error![]const u8 { | ||
| 19 | var outsize: usize = 0; | 31 | var outsize: usize = 0; |
| 20 | for (input) |c| { | 32 | for (input) |c| { |
| 21 | outsize += if (isUnreserved(c)) @as(usize, 1) else 3; | 33 | outsize += if (keepUnescaped(c)) @as(usize, 1) else 3; |
| 22 | } | 34 | } |
| 23 | var output = try allocator.alloc(u8, outsize); | 35 | var output = try allocator.alloc(u8, outsize); |
| 24 | var outptr: usize = 0; | 36 | var outptr: usize = 0; |
| 25 | 37 | ||
| 26 | for (input) |c| { | 38 | for (input) |c| { |
| 27 | if (isUnreserved(c)) { | 39 | if (keepUnescaped(c)) { |
| 28 | output[outptr] = c; | 40 | output[outptr] = c; |
| 29 | outptr += 1; | 41 | outptr += 1; |
| 30 | } else { | 42 | } else { |
| ... | @@ -94,13 +106,14 @@ pub fn unescapeString(allocator: std.mem.Allocator, input: []const u8) error{Out | ... | @@ -94,13 +106,14 @@ pub fn unescapeString(allocator: std.mem.Allocator, input: []const u8) error{Out |
| 94 | 106 | ||
| 95 | pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort }; | 107 | pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort }; |
| 96 | 108 | ||
| 97 | /// Parses the URI or returns an error. | 109 | /// Parses the URI or returns an error. This function is not compliant, but is required to parse |
| 110 | /// some forms of URIs in the wild. Such as HTTP Location headers. | ||
| 98 | /// The return value will contain unescaped strings pointing into the | 111 | /// The return value will contain unescaped strings pointing into the |
| 99 | /// original `text`. Each component that is provided, will be non-`null`. | 112 | /// original `text`. Each component that is provided, will be non-`null`. |
| 100 | pub fn parse(text: []const u8) ParseError!Uri { | 113 | pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { |
| 101 | var reader = SliceReader{ .slice = text }; | 114 | var reader = SliceReader{ .slice = text }; |
| 102 | var uri = Uri{ | 115 | var uri = Uri{ |
| 103 | .scheme = reader.readWhile(isSchemeChar), | 116 | .scheme = "", |
| 104 | .user = null, | 117 | .user = null, |
| 105 | .password = null, | 118 | .password = null, |
| 106 | .host = null, | 119 | .host = null, |
| ... | @@ -110,14 +123,6 @@ pub fn parse(text: []const u8) ParseError!Uri { | ... | @@ -110,14 +123,6 @@ pub fn parse(text: []const u8) ParseError!Uri { |
| 110 | .fragment = null, | 123 | .fragment = null, |
| 111 | }; | 124 | }; |
| 112 | 125 | ||
| 113 | // after the scheme, a ':' must appear | ||
| 114 | if (reader.get()) |c| { | ||
| 115 | if (c != ':') | ||
| 116 | return error.UnexpectedCharacter; | ||
| 117 | } else { | ||
| 118 | return error.InvalidFormat; | ||
| 119 | } | ||
| 120 | |||
| 121 | if (reader.peekPrefix("//")) { // authority part | 126 | if (reader.peekPrefix("//")) { // authority part |
| 122 | std.debug.assert(reader.get().? == '/'); | 127 | std.debug.assert(reader.get().? == '/'); |
| 123 | std.debug.assert(reader.get().? == '/'); | 128 | std.debug.assert(reader.get().? == '/'); |
| ... | @@ -179,6 +184,76 @@ pub fn parse(text: []const u8) ParseError!Uri { | ... | @@ -179,6 +184,76 @@ pub fn parse(text: []const u8) ParseError!Uri { |
| 179 | return uri; | 184 | return uri; |
| 180 | } | 185 | } |
| 181 | 186 | ||
| 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`. | ||
| 190 | pub 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. | ||
| 210 | pub 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 | |||
| 182 | const SliceReader = struct { | 257 | const SliceReader = struct { |
| 183 | const Self = @This(); | 258 | const Self = @This(); |
| 184 | 259 | ||
| ... | @@ -284,6 +359,14 @@ fn isPathSeparator(c: u8) bool { | ... | @@ -284,6 +359,14 @@ fn isPathSeparator(c: u8) bool { |
| 284 | }; | 359 | }; |
| 285 | } | 360 | } |
| 286 | 361 | ||
| 362 | fn isPathChar(c: u8) bool { | ||
| 363 | return isUnreserved(c) or isSubLimit(c) or c == '/' or c == ':' or c == '@'; | ||
| 364 | } | ||
| 365 | |||
| 366 | fn isQueryChar(c: u8) bool { | ||
| 367 | return isPathChar(c) or c == '?'; | ||
| 368 | } | ||
| 369 | |||
| 287 | fn isQuerySeparator(c: u8) bool { | 370 | fn isQuerySeparator(c: u8) bool { |
| 288 | return switch (c) { | 371 | return switch (c) { |
| 289 | '#' => true, | 372 | '#' => true, |
lib/std/array_list.zig+26-7| ... | @@ -141,11 +141,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -141,11 +141,21 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 141 | return cloned; | 141 | return cloned; |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | /// Insert `item` at index `n` by moving `list[n .. list.len]` to make room. | 144 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. |
| 145 | /// If `n` is equal to the length of the list this operation is equivalent to append. | ||
| 145 | /// This operation is O(N). | 146 | /// This operation is O(N). |
| 146 | /// Invalidates pointers if additional memory is needed. | 147 | /// Invalidates pointers if additional memory is needed. |
| 147 | pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void { | 148 | pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void { |
| 148 | try self.ensureUnusedCapacity(1); | 149 | try self.ensureUnusedCapacity(1); |
| 150 | self.insertAssumeCapacity(n, item); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. | ||
| 154 | /// If `n` is equal to the length of the list this operation is equivalent to append. | ||
| 155 | /// This operation is O(N). | ||
| 156 | /// Asserts that there is enough capacity for the new item. | ||
| 157 | pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void { | ||
| 158 | assert(self.items.len < self.capacity); | ||
| 149 | self.items.len += 1; | 159 | self.items.len += 1; |
| 150 | 160 | ||
| 151 | mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); | 161 | mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); |
| ... | @@ -609,12 +619,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -609,12 +619,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 609 | return cloned; | 619 | return cloned; |
| 610 | } | 620 | } |
| 611 | 621 | ||
| 612 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` | 622 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. |
| 613 | /// to higher indices to make room. | 623 | /// If `n` is equal to the length of the list this operation is equivalent to append. |
| 614 | /// This operation is O(N). | 624 | /// This operation is O(N). |
| 615 | /// Invalidates pointers if additional memory is needed. | 625 | /// Invalidates pointers if additional memory is needed. |
| 616 | pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void { | 626 | pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void { |
| 617 | try self.ensureUnusedCapacity(allocator, 1); | 627 | try self.ensureUnusedCapacity(allocator, 1); |
| 628 | self.insertAssumeCapacity(n, item); | ||
| 629 | } | ||
| 630 | |||
| 631 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room. | ||
| 632 | /// If `n` is equal to the length of the list this operation is equivalent to append. | ||
| 633 | /// This operation is O(N). | ||
| 634 | /// Asserts that there is enough capacity for the new item. | ||
| 635 | pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void { | ||
| 636 | assert(self.items.len < self.capacity); | ||
| 618 | self.items.len += 1; | 637 | self.items.len += 1; |
| 619 | 638 | ||
| 620 | mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); | 639 | mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); |
| ... | @@ -1309,9 +1328,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" { | ... | @@ -1309,9 +1328,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" { |
| 1309 | var list = ArrayList(i32).init(a); | 1328 | var list = ArrayList(i32).init(a); |
| 1310 | defer list.deinit(); | 1329 | defer list.deinit(); |
| 1311 | 1330 | ||
| 1312 | try list.append(1); | 1331 | try list.insert(0, 1); |
| 1313 | try list.append(2); | 1332 | try list.append(2); |
| 1314 | try list.append(3); | 1333 | try list.insert(2, 3); |
| 1315 | try list.insert(0, 5); | 1334 | try list.insert(0, 5); |
| 1316 | try testing.expect(list.items[0] == 5); | 1335 | try testing.expect(list.items[0] == 5); |
| 1317 | try testing.expect(list.items[1] == 1); | 1336 | try testing.expect(list.items[1] == 1); |
| ... | @@ -1322,9 +1341,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" { | ... | @@ -1322,9 +1341,9 @@ test "std.ArrayList/ArrayListUnmanaged.insert" { |
| 1322 | var list = ArrayListUnmanaged(i32){}; | 1341 | var list = ArrayListUnmanaged(i32){}; |
| 1323 | defer list.deinit(a); | 1342 | defer list.deinit(a); |
| 1324 | 1343 | ||
| 1325 | try list.append(a, 1); | 1344 | try list.insert(a, 0, 1); |
| 1326 | try list.append(a, 2); | 1345 | try list.append(a, 2); |
| 1327 | try list.append(a, 3); | 1346 | try list.insert(a, 2, 3); |
| 1328 | try list.insert(a, 0, 5); | 1347 | try list.insert(a, 0, 5); |
| 1329 | try testing.expect(list.items[0] == 5); | 1348 | try testing.expect(list.items[0] == 5); |
| 1330 | try testing.expect(list.items[1] == 1); | 1349 | try testing.expect(list.items[1] == 1); |
lib/std/c.zig+2-1| ... | @@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n | ... | @@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n |
| 153 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; | 153 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; |
| 154 | pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int; | 154 | pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int; |
| 155 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | 155 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; |
| 156 | pub extern "c" fn waitpid(pid: c.pid_t, stat_loc: ?*c_int, options: c_int) c.pid_t; | 156 | pub extern "c" fn waitpid(pid: c.pid_t, status: ?*c_int, options: c_int) c.pid_t; |
| 157 | pub extern "c" fn wait4(pid: c.pid_t, status: ?*c_int, options: c_int, ru: ?*c.rusage) c.pid_t; | ||
| 157 | pub extern "c" fn fork() c_int; | 158 | pub extern "c" fn fork() c_int; |
| 158 | pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; | 159 | pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; |
| 159 | pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int; | 160 | pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int; |
lib/std/c/darwin.zig+477| ... | @@ -8,6 +8,7 @@ const iovec_const = std.os.iovec_const; | ... | @@ -8,6 +8,7 @@ const iovec_const = std.os.iovec_const; |
| 8 | 8 | ||
| 9 | pub const aarch64 = @import("darwin/aarch64.zig"); | 9 | pub const aarch64 = @import("darwin/aarch64.zig"); |
| 10 | pub const x86_64 = @import("darwin/x86_64.zig"); | 10 | pub const x86_64 = @import("darwin/x86_64.zig"); |
| 11 | pub const cssm = @import("darwin/cssm.zig"); | ||
| 11 | 12 | ||
| 12 | const arch_bits = switch (native_arch) { | 13 | const arch_bits = switch (native_arch) { |
| 13 | .aarch64 => @import("darwin/aarch64.zig"), | 14 | .aarch64 => @import("darwin/aarch64.zig"), |
| ... | @@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE { | ... | @@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE { |
| 2179 | return @intToEnum(KernE, @truncate(u32, @intCast(usize, err))); | 2180 | return @intToEnum(KernE, @truncate(u32, @intCast(usize, err))); |
| 2180 | } | 2181 | } |
| 2181 | 2182 | ||
| 2183 | pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError { | ||
| 2184 | if (std.os.unexpected_error_tracing) { | ||
| 2185 | std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)}); | ||
| 2186 | std.debug.dumpCurrentStackTrace(null); | ||
| 2187 | } | ||
| 2188 | return error.Unexpected; | ||
| 2189 | } | ||
| 2190 | |||
| 2182 | /// Kernel return values | 2191 | /// Kernel return values |
| 2183 | pub const KernE = enum(u32) { | 2192 | pub const KernE = enum(u32) { |
| 2184 | SUCCESS = 0, | 2193 | SUCCESS = 0, |
| ... | @@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31; | ... | @@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31; |
| 3085 | pub const caddr_t = ?[*]u8; | 3094 | pub const caddr_t = ?[*]u8; |
| 3086 | 3095 | ||
| 3087 | pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; | 3096 | pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; |
| 3097 | |||
| 3098 | pub const MachError = error{ | ||
| 3099 | /// Not enough permissions held to perform the requested kernel | ||
| 3100 | /// call. | ||
| 3101 | PermissionDenied, | ||
| 3102 | } || std.os.UnexpectedError; | ||
| 3103 | |||
| 3104 | pub 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 | |||
| 3516 | pub 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 | |||
| 3552 | pub 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 | |||
| 3562 | pub 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 | ||
| 5 | pub const DB_RECORDTYPE_SCHEMA_START = 0x00000000; | ||
| 6 | pub const DB_RECORDTYPE_SCHEMA_END = DB_RECORDTYPE_SCHEMA_START + 4; | ||
| 7 | |||
| 8 | // Open Group Application Name Space Range Definition | ||
| 9 | pub const DB_RECORDTYPE_OPEN_GROUP_START = 0x0000000A; | ||
| 10 | pub const DB_RECORDTYPE_OPEN_GROUP_END = DB_RECORDTYPE_OPEN_GROUP_START + 8; | ||
| 11 | |||
| 12 | // Industry At Large Application Name Space Range Definition | ||
| 13 | pub const DB_RECORDTYPE_APP_DEFINED_START = 0x80000000; | ||
| 14 | pub const DB_RECORDTYPE_APP_DEFINED_END = 0xffffffff; | ||
| 15 | |||
| 16 | pub const DB_RECORDTYPE = enum(u32) { | ||
| 17 | // Record Types defined in the Schema Management Name Space | ||
| 18 | SCHEMA_INFO = DB_RECORDTYPE_SCHEMA_START + 0, | ||
| 19 | SCHEMA_INDEXES = DB_RECORDTYPE_SCHEMA_START + 1, | ||
| 20 | SCHEMA_ATTRIBUTES = DB_RECORDTYPE_SCHEMA_START + 2, | ||
| 21 | SCHEMA_PARSING_MODULE = DB_RECORDTYPE_SCHEMA_START + 3, | ||
| 22 | |||
| 23 | // Record Types defined in the Open Group Application Name Space | ||
| 24 | ANY = DB_RECORDTYPE_OPEN_GROUP_START + 0, | ||
| 25 | CERT = DB_RECORDTYPE_OPEN_GROUP_START + 1, | ||
| 26 | CRL = DB_RECORDTYPE_OPEN_GROUP_START + 2, | ||
| 27 | POLICY = DB_RECORDTYPE_OPEN_GROUP_START + 3, | ||
| 28 | GENERIC = DB_RECORDTYPE_OPEN_GROUP_START + 4, | ||
| 29 | PUBLIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 5, | ||
| 30 | PRIVATE_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 6, | ||
| 31 | SYMMETRIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 7, | ||
| 32 | ALL_KEYS = DB_RECORDTYPE_OPEN_GROUP_START + 8, | ||
| 33 | |||
| 34 | // AppleFileDL record types | ||
| 35 | GENERIC_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 0, | ||
| 36 | INTERNET_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 1, | ||
| 37 | APPLESHARE_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 2, | ||
| 38 | |||
| 39 | X509_CERTIFICATE = DB_RECORDTYPE_APP_DEFINED_START + 0x1000, | ||
| 40 | USER_TRUST, | ||
| 41 | X509_CRL, | ||
| 42 | UNLOCK_REFERRAL, | ||
| 43 | EXTENDED_ATTRIBUTE, | ||
| 44 | METADATA = DB_RECORDTYPE_APP_DEFINED_START + 0x8000, | ||
| 45 | |||
| 46 | _, | ||
| 47 | }; | ||
lib/std/child_process.zig+50-125| ... | @@ -17,10 +17,12 @@ const Os = std.builtin.Os; | ... | @@ -17,10 +17,12 @@ const Os = std.builtin.Os; |
| 17 | const TailQueue = std.TailQueue; | 17 | const TailQueue = std.TailQueue; |
| 18 | const maxInt = std.math.maxInt; | 18 | const maxInt = std.math.maxInt; |
| 19 | const assert = std.debug.assert; | 19 | const assert = std.debug.assert; |
| 20 | const is_darwin = builtin.target.isDarwin(); | ||
| 20 | 21 | ||
| 21 | pub const ChildProcess = struct { | 22 | pub const ChildProcess = struct { |
| 22 | pub const Id = switch (builtin.os.tag) { | 23 | pub const Id = switch (builtin.os.tag) { |
| 23 | .windows => windows.HANDLE, | 24 | .windows => windows.HANDLE, |
| 25 | .wasi => void, | ||
| 24 | else => os.pid_t, | 26 | else => os.pid_t, |
| 25 | }; | 27 | }; |
| 26 | 28 | ||
| ... | @@ -70,6 +72,43 @@ pub const ChildProcess = struct { | ... | @@ -70,6 +72,43 @@ pub const ChildProcess = struct { |
| 70 | /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent. | 72 | /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent. |
| 71 | start_suspended: bool = false, | 73 | start_suspended: bool = false, |
| 72 | 74 | ||
| 75 | /// Set to true to obtain rusage information for the child process. | ||
| 76 | /// Depending on the target platform and implementation status, the | ||
| 77 | /// requested statistics may or may not be available. If they are | ||
| 78 | /// available, then the `resource_usage_statistics` field will be populated | ||
| 79 | /// after calling `wait`. | ||
| 80 | /// On Linux, this obtains rusage statistics from wait4(). | ||
| 81 | request_resource_usage_statistics: bool = false, | ||
| 82 | |||
| 83 | /// This is available after calling wait if | ||
| 84 | /// `request_resource_usage_statistics` was set to `true` before calling | ||
| 85 | /// `spawn`. | ||
| 86 | resource_usage_statistics: ResourceUsageStatistics = .{}, | ||
| 87 | |||
| 88 | pub const ResourceUsageStatistics = struct { | ||
| 89 | rusage: @TypeOf(rusage_init) = rusage_init, | ||
| 90 | |||
| 91 | /// Returns the peak resident set size of the child process, in bytes, | ||
| 92 | /// if available. | ||
| 93 | pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize { | ||
| 94 | switch (builtin.os.tag) { | ||
| 95 | .linux => { | ||
| 96 | if (rus.rusage) |ru| { | ||
| 97 | return @intCast(usize, ru.maxrss) * 1024; | ||
| 98 | } else { | ||
| 99 | return null; | ||
| 100 | } | ||
| 101 | }, | ||
| 102 | else => return null, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | const rusage_init = switch (builtin.os.tag) { | ||
| 107 | .linux => @as(?std.os.rusage, null), | ||
| 108 | else => {}, | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | |||
| 73 | pub const Arg0Expand = os.Arg0Expand; | 112 | pub const Arg0Expand = os.Arg0Expand; |
| 74 | 113 | ||
| 75 | pub const SpawnError = error{ | 114 | pub const SpawnError = error{ |
| ... | @@ -90,8 +129,7 @@ pub const ChildProcess = struct { | ... | @@ -90,8 +129,7 @@ pub const ChildProcess = struct { |
| 90 | os.SetIdError || | 129 | os.SetIdError || |
| 91 | os.ChangeCurDirError || | 130 | os.ChangeCurDirError || |
| 92 | windows.CreateProcessError || | 131 | windows.CreateProcessError || |
| 93 | windows.WaitForSingleObjectError || | 132 | windows.WaitForSingleObjectError; |
| 94 | os.posix_spawn.Error; | ||
| 95 | 133 | ||
| 96 | pub const Term = union(enum) { | 134 | pub const Term = union(enum) { |
| 97 | Exited: u8, | 135 | Exited: u8, |
| ... | @@ -143,10 +181,6 @@ pub const ChildProcess = struct { | ... | @@ -143,10 +181,6 @@ pub const ChildProcess = struct { |
| 143 | @compileError("the target operating system cannot spawn processes"); | 181 | @compileError("the target operating system cannot spawn processes"); |
| 144 | } | 182 | } |
| 145 | 183 | ||
| 146 | if (comptime builtin.target.isDarwin()) { | ||
| 147 | return self.spawnMacos(); | ||
| 148 | } | ||
| 149 | |||
| 150 | if (builtin.os.tag == .windows) { | 184 | if (builtin.os.tag == .windows) { |
| 151 | return self.spawnWindows(); | 185 | return self.spawnWindows(); |
| 152 | } else { | 186 | } else { |
| ... | @@ -337,10 +371,16 @@ pub const ChildProcess = struct { | ... | @@ -337,10 +371,16 @@ pub const ChildProcess = struct { |
| 337 | } | 371 | } |
| 338 | 372 | ||
| 339 | fn waitUnwrapped(self: *ChildProcess) !void { | 373 | fn waitUnwrapped(self: *ChildProcess) !void { |
| 340 | const res: os.WaitPidResult = if (comptime builtin.target.isDarwin()) | 374 | const res: os.WaitPidResult = res: { |
| 341 | try os.posix_spawn.waitpid(self.id, 0) | 375 | if (builtin.os.tag == .linux and self.request_resource_usage_statistics) { |
| 342 | else | 376 | var ru: std.os.rusage = undefined; |
| 343 | os.waitpid(self.id, 0); | 377 | const res = os.wait4(self.id, 0, &ru); |
| 378 | self.resource_usage_statistics.rusage = ru; | ||
| 379 | break :res res; | ||
| 380 | } | ||
| 381 | |||
| 382 | break :res os.waitpid(self.id, 0); | ||
| 383 | }; | ||
| 344 | const status = res.status; | 384 | const status = res.status; |
| 345 | self.cleanupStreams(); | 385 | self.cleanupStreams(); |
| 346 | self.handleWaitResult(status); | 386 | self.handleWaitResult(status); |
| ... | @@ -416,121 +456,6 @@ pub const ChildProcess = struct { | ... | @@ -416,121 +456,6 @@ pub const ChildProcess = struct { |
| 416 | Term{ .Unknown = status }; | 456 | Term{ .Unknown = status }; |
| 417 | } | 457 | } |
| 418 | 458 | ||
| 419 | fn spawnMacos(self: *ChildProcess) SpawnError!void { | ||
| 420 | const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; | ||
| 421 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | ||
| 422 | errdefer if (self.stdin_behavior == StdIo.Pipe) destroyPipe(stdin_pipe); | ||
| 423 | |||
| 424 | const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | ||
| 425 | errdefer if (self.stdout_behavior == StdIo.Pipe) destroyPipe(stdout_pipe); | ||
| 426 | |||
| 427 | const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | ||
| 428 | errdefer if (self.stderr_behavior == StdIo.Pipe) destroyPipe(stderr_pipe); | ||
| 429 | |||
| 430 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); | ||
| 431 | const dev_null_fd = if (any_ignore) | ||
| 432 | os.openZ("/dev/null", os.O.RDWR, 0) catch |err| switch (err) { | ||
| 433 | error.PathAlreadyExists => unreachable, | ||
| 434 | error.NoSpaceLeft => unreachable, | ||
| 435 | error.FileTooBig => unreachable, | ||
| 436 | error.DeviceBusy => unreachable, | ||
| 437 | error.FileLocksNotSupported => unreachable, | ||
| 438 | error.BadPathName => unreachable, // Windows-only | ||
| 439 | error.InvalidHandle => unreachable, // WASI-only | ||
| 440 | error.WouldBlock => unreachable, | ||
| 441 | else => |e| return e, | ||
| 442 | } | ||
| 443 | else | ||
| 444 | undefined; | ||
| 445 | defer if (any_ignore) os.close(dev_null_fd); | ||
| 446 | |||
| 447 | var attr = try os.posix_spawn.Attr.init(); | ||
| 448 | defer attr.deinit(); | ||
| 449 | var flags: u16 = os.darwin.POSIX_SPAWN_SETSIGDEF | os.darwin.POSIX_SPAWN_SETSIGMASK; | ||
| 450 | if (self.disable_aslr) { | ||
| 451 | flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR; | ||
| 452 | } | ||
| 453 | if (self.start_suspended) { | ||
| 454 | flags |= os.darwin.POSIX_SPAWN_START_SUSPENDED; | ||
| 455 | } | ||
| 456 | try attr.set(flags); | ||
| 457 | |||
| 458 | var actions = try os.posix_spawn.Actions.init(); | ||
| 459 | defer actions.deinit(); | ||
| 460 | |||
| 461 | try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe, os.STDIN_FILENO, dev_null_fd); | ||
| 462 | try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe, os.STDOUT_FILENO, dev_null_fd); | ||
| 463 | try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe, os.STDERR_FILENO, dev_null_fd); | ||
| 464 | |||
| 465 | if (self.cwd_dir) |cwd| { | ||
| 466 | try actions.fchdir(cwd.fd); | ||
| 467 | } else if (self.cwd) |cwd| { | ||
| 468 | try actions.chdir(cwd); | ||
| 469 | } | ||
| 470 | |||
| 471 | var arena_allocator = std.heap.ArenaAllocator.init(self.allocator); | ||
| 472 | defer arena_allocator.deinit(); | ||
| 473 | const arena = arena_allocator.allocator(); | ||
| 474 | |||
| 475 | const argv_buf = try arena.allocSentinel(?[*:0]u8, self.argv.len, null); | ||
| 476 | for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | ||
| 477 | |||
| 478 | const envp = if (self.env_map) |env_map| m: { | ||
| 479 | const envp_buf = try createNullDelimitedEnvMap(arena, env_map); | ||
| 480 | break :m envp_buf.ptr; | ||
| 481 | } else std.c.environ; | ||
| 482 | |||
| 483 | const pid = try os.posix_spawn.spawnp(self.argv[0], actions, attr, argv_buf, envp); | ||
| 484 | |||
| 485 | if (self.stdin_behavior == StdIo.Pipe) { | ||
| 486 | self.stdin = File{ .handle = stdin_pipe[1] }; | ||
| 487 | } else { | ||
| 488 | self.stdin = null; | ||
| 489 | } | ||
| 490 | if (self.stdout_behavior == StdIo.Pipe) { | ||
| 491 | self.stdout = File{ .handle = stdout_pipe[0] }; | ||
| 492 | } else { | ||
| 493 | self.stdout = null; | ||
| 494 | } | ||
| 495 | if (self.stderr_behavior == StdIo.Pipe) { | ||
| 496 | self.stderr = File{ .handle = stderr_pipe[0] }; | ||
| 497 | } else { | ||
| 498 | self.stderr = null; | ||
| 499 | } | ||
| 500 | |||
| 501 | self.id = pid; | ||
| 502 | self.term = null; | ||
| 503 | |||
| 504 | if (self.stdin_behavior == StdIo.Pipe) { | ||
| 505 | os.close(stdin_pipe[0]); | ||
| 506 | } | ||
| 507 | if (self.stdout_behavior == StdIo.Pipe) { | ||
| 508 | os.close(stdout_pipe[1]); | ||
| 509 | } | ||
| 510 | if (self.stderr_behavior == StdIo.Pipe) { | ||
| 511 | os.close(stderr_pipe[1]); | ||
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | fn setUpChildIoPosixSpawn( | ||
| 516 | stdio: StdIo, | ||
| 517 | actions: *os.posix_spawn.Actions, | ||
| 518 | pipe_fd: [2]i32, | ||
| 519 | std_fileno: i32, | ||
| 520 | dev_null_fd: i32, | ||
| 521 | ) !void { | ||
| 522 | switch (stdio) { | ||
| 523 | .Pipe => { | ||
| 524 | const idx: usize = if (std_fileno == 0) 0 else 1; | ||
| 525 | try actions.dup2(pipe_fd[idx], std_fileno); | ||
| 526 | try actions.close(pipe_fd[1 - idx]); | ||
| 527 | }, | ||
| 528 | .Close => try actions.close(std_fileno), | ||
| 529 | .Inherit => {}, | ||
| 530 | .Ignore => try actions.dup2(dev_null_fd, std_fileno), | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 | fn spawnPosix(self: *ChildProcess) SpawnError!void { | 459 | fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 535 | const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; | 460 | const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0; |
| 536 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | 461 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; |
lib/std/crypto.zig+30| ... | @@ -1,3 +1,5 @@ | ... | @@ -1,3 +1,5 @@ |
| 1 | const root = @import("root"); | ||
| 2 | |||
| 1 | /// Authenticated Encryption with Associated Data | 3 | /// Authenticated Encryption with Associated Data |
| 2 | pub const aead = struct { | 4 | pub const aead = struct { |
| 3 | pub const aegis = struct { | 5 | pub const aegis = struct { |
| ... | @@ -66,6 +68,11 @@ pub const dh = struct { | ... | @@ -66,6 +68,11 @@ pub const dh = struct { |
| 66 | pub const X25519 = @import("crypto/25519/x25519.zig").X25519; | 68 | pub const X25519 = @import("crypto/25519/x25519.zig").X25519; |
| 67 | }; | 69 | }; |
| 68 | 70 | ||
| 71 | /// Key Encapsulation Mechanisms. | ||
| 72 | pub const kem = struct { | ||
| 73 | pub const kyber_d00 = @import("crypto/kyber_d00.zig"); | ||
| 74 | }; | ||
| 75 | |||
| 69 | /// Elliptic-curve arithmetic. | 76 | /// Elliptic-curve arithmetic. |
| 70 | pub const ecc = struct { | 77 | pub const ecc = struct { |
| 71 | pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; | 78 | pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; |
| ... | @@ -183,6 +190,27 @@ pub const errors = @import("crypto/errors.zig"); | ... | @@ -183,6 +190,27 @@ pub const errors = @import("crypto/errors.zig"); |
| 183 | pub const tls = @import("crypto/tls.zig"); | 190 | pub const tls = @import("crypto/tls.zig"); |
| 184 | pub const Certificate = @import("crypto/Certificate.zig"); | 191 | pub const Certificate = @import("crypto/Certificate.zig"); |
| 185 | 192 | ||
| 193 | /// Side-channels mitigations. | ||
| 194 | pub 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 | |||
| 212 | pub const default_side_channels_mitigations = .medium; | ||
| 213 | |||
| 186 | test { | 214 | test { |
| 187 | _ = aead.aegis.Aegis128L; | 215 | _ = aead.aegis.Aegis128L; |
| 188 | _ = aead.aegis.Aegis256; | 216 | _ = aead.aegis.Aegis256; |
| ... | @@ -217,6 +245,8 @@ test { | ... | @@ -217,6 +245,8 @@ test { |
| 217 | 245 | ||
| 218 | _ = dh.X25519; | 246 | _ = dh.X25519; |
| 219 | 247 | ||
| 248 | _ = kem.kyber_d00; | ||
| 249 | |||
| 220 | _ = ecc.Curve25519; | 250 | _ = ecc.Curve25519; |
| 221 | _ = ecc.Edwards25519; | 251 | _ = ecc.Edwards25519; |
| 222 | _ = ecc.P256; | 252 | _ = ecc.P256; |
lib/std/crypto/25519/field.zig+8-1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const crypto = std.crypto; | 3 | const crypto = std.crypto; |
| 3 | const readIntLittle = std.mem.readIntLittle; | 4 | const readIntLittle = std.mem.readIntLittle; |
| 4 | const writeIntLittle = std.mem.writeIntLittle; | 5 | const writeIntLittle = std.mem.writeIntLittle; |
| ... | @@ -6,6 +7,12 @@ const writeIntLittle = std.mem.writeIntLittle; | ... | @@ -6,6 +7,12 @@ const writeIntLittle = std.mem.writeIntLittle; |
| 6 | const NonCanonicalError = crypto.errors.NonCanonicalError; | 7 | const NonCanonicalError = crypto.errors.NonCanonicalError; |
| 7 | const NotSquareError = crypto.errors.NotSquareError; | 8 | const NotSquareError = crypto.errors.NotSquareError; |
| 8 | 9 | ||
| 10 | // Inline conditionally, when it can result in large code generation. | ||
| 11 | const bloaty_inline = switch (builtin.mode) { | ||
| 12 | .ReleaseSafe, .ReleaseFast => .Inline, | ||
| 13 | .Debug, .ReleaseSmall => .Unspecified, | ||
| 14 | }; | ||
| 15 | |||
| 9 | pub const Fe = struct { | 16 | pub const Fe = struct { |
| 10 | limbs: [5]u64, | 17 | limbs: [5]u64, |
| 11 | 18 | ||
| ... | @@ -264,7 +271,7 @@ pub const Fe = struct { | ... | @@ -264,7 +271,7 @@ pub const Fe = struct { |
| 264 | } | 271 | } |
| 265 | 272 | ||
| 266 | /// Multiply two field elements | 273 | /// Multiply two field elements |
| 267 | pub inline fn mul(a: Fe, b: Fe) Fe { | 274 | pub fn mul(a: Fe, b: Fe) callconv(bloaty_inline) Fe { |
| 268 | var ax: [5]u128 = undefined; | 275 | var ax: [5]u128 = undefined; |
| 269 | var bx: [5]u128 = undefined; | 276 | var bx: [5]u128 = undefined; |
| 270 | var a19: [5]u128 = undefined; | 277 | var a19: [5]u128 = undefined; |
lib/std/crypto/aes/armcrypto.zig+8-16| ... | @@ -32,62 +32,54 @@ pub const Block = struct { | ... | @@ -32,62 +32,54 @@ pub const Block = struct { |
| 32 | /// Encrypt a block with a round key. | 32 | /// Encrypt a block with a round key. |
| 33 | pub inline fn encrypt(block: Block, round_key: Block) Block { | 33 | pub inline fn encrypt(block: Block, round_key: Block) Block { |
| 34 | return Block{ | 34 | return Block{ |
| 35 | .repr = asm ( | 35 | .repr = (asm ( |
| 36 | \\ mov %[out].16b, %[in].16b | 36 | \\ mov %[out].16b, %[in].16b |
| 37 | \\ aese %[out].16b, %[zero].16b | 37 | \\ aese %[out].16b, %[zero].16b |
| 38 | \\ aesmc %[out].16b, %[out].16b | 38 | \\ aesmc %[out].16b, %[out].16b |
| 39 | \\ eor %[out].16b, %[out].16b, %[rk].16b | ||
| 40 | : [out] "=&x" (-> BlockVec), | 39 | : [out] "=&x" (-> BlockVec), |
| 41 | : [in] "x" (block.repr), | 40 | : [in] "x" (block.repr), |
| 42 | [rk] "x" (round_key.repr), | ||
| 43 | [zero] "x" (zero), | 41 | [zero] "x" (zero), |
| 44 | ), | 42 | )) ^ round_key.repr, |
| 45 | }; | 43 | }; |
| 46 | } | 44 | } |
| 47 | 45 | ||
| 48 | /// Encrypt a block with the last round key. | 46 | /// Encrypt a block with the last round key. |
| 49 | pub inline fn encryptLast(block: Block, round_key: Block) Block { | 47 | pub inline fn encryptLast(block: Block, round_key: Block) Block { |
| 50 | return Block{ | 48 | return Block{ |
| 51 | .repr = asm ( | 49 | .repr = (asm ( |
| 52 | \\ mov %[out].16b, %[in].16b | 50 | \\ mov %[out].16b, %[in].16b |
| 53 | \\ aese %[out].16b, %[zero].16b | 51 | \\ aese %[out].16b, %[zero].16b |
| 54 | \\ eor %[out].16b, %[out].16b, %[rk].16b | ||
| 55 | : [out] "=&x" (-> BlockVec), | 52 | : [out] "=&x" (-> BlockVec), |
| 56 | : [in] "x" (block.repr), | 53 | : [in] "x" (block.repr), |
| 57 | [rk] "x" (round_key.repr), | ||
| 58 | [zero] "x" (zero), | 54 | [zero] "x" (zero), |
| 59 | ), | 55 | )) ^ round_key.repr, |
| 60 | }; | 56 | }; |
| 61 | } | 57 | } |
| 62 | 58 | ||
| 63 | /// Decrypt a block with a round key. | 59 | /// Decrypt a block with a round key. |
| 64 | pub inline fn decrypt(block: Block, inv_round_key: Block) Block { | 60 | pub inline fn decrypt(block: Block, inv_round_key: Block) Block { |
| 65 | return Block{ | 61 | return Block{ |
| 66 | .repr = asm ( | 62 | .repr = (asm ( |
| 67 | \\ mov %[out].16b, %[in].16b | 63 | \\ mov %[out].16b, %[in].16b |
| 68 | \\ aesd %[out].16b, %[zero].16b | 64 | \\ aesd %[out].16b, %[zero].16b |
| 69 | \\ aesimc %[out].16b, %[out].16b | 65 | \\ aesimc %[out].16b, %[out].16b |
| 70 | \\ eor %[out].16b, %[out].16b, %[rk].16b | ||
| 71 | : [out] "=&x" (-> BlockVec), | 66 | : [out] "=&x" (-> BlockVec), |
| 72 | : [in] "x" (block.repr), | 67 | : [in] "x" (block.repr), |
| 73 | [rk] "x" (inv_round_key.repr), | ||
| 74 | [zero] "x" (zero), | 68 | [zero] "x" (zero), |
| 75 | ), | 69 | )) ^ inv_round_key.repr, |
| 76 | }; | 70 | }; |
| 77 | } | 71 | } |
| 78 | 72 | ||
| 79 | /// Decrypt a block with the last round key. | 73 | /// Decrypt a block with the last round key. |
| 80 | pub inline fn decryptLast(block: Block, inv_round_key: Block) Block { | 74 | pub inline fn decryptLast(block: Block, inv_round_key: Block) Block { |
| 81 | return Block{ | 75 | return Block{ |
| 82 | .repr = asm ( | 76 | .repr = (asm ( |
| 83 | \\ mov %[out].16b, %[in].16b | 77 | \\ mov %[out].16b, %[in].16b |
| 84 | \\ aesd %[out].16b, %[zero].16b | 78 | \\ aesd %[out].16b, %[zero].16b |
| 85 | \\ eor %[out].16b, %[out].16b, %[rk].16b | ||
| 86 | : [out] "=&x" (-> BlockVec), | 79 | : [out] "=&x" (-> BlockVec), |
| 87 | : [in] "x" (block.repr), | 80 | : [in] "x" (block.repr), |
| 88 | [rk] "x" (inv_round_key.repr), | ||
| 89 | [zero] "x" (zero), | 81 | [zero] "x" (zero), |
| 90 | ), | 82 | )) ^ inv_round_key.repr, |
| 91 | }; | 83 | }; |
| 92 | } | 84 | } |
| 93 | 85 |
lib/std/crypto/aes/soft.zig+302-63| ... | @@ -1,11 +1,11 @@ | ... | @@ -1,11 +1,11 @@ |
| 1 | // Based on Go stdlib implementation | ||
| 2 | |||
| 3 | const std = @import("../../std.zig"); | 1 | const std = @import("../../std.zig"); |
| 4 | const math = std.math; | 2 | const math = std.math; |
| 5 | const mem = std.mem; | 3 | const mem = std.mem; |
| 6 | 4 | ||
| 7 | const BlockVec = [4]u32; | 5 | const BlockVec = [4]u32; |
| 8 | 6 | ||
| 7 | const side_channels_mitigations = std.options.side_channels_mitigations; | ||
| 8 | |||
| 9 | /// A single AES block. | 9 | /// A single AES block. |
| 10 | pub const Block = struct { | 10 | pub const Block = struct { |
| 11 | pub const block_length: usize = 16; | 11 | pub const block_length: usize = 16; |
| ... | @@ -15,20 +15,20 @@ pub const Block = struct { | ... | @@ -15,20 +15,20 @@ pub const Block = struct { |
| 15 | 15 | ||
| 16 | /// Convert a byte sequence into an internal representation. | 16 | /// Convert a byte sequence into an internal representation. |
| 17 | pub inline fn fromBytes(bytes: *const [16]u8) Block { | 17 | pub inline fn fromBytes(bytes: *const [16]u8) Block { |
| 18 | const s0 = mem.readIntBig(u32, bytes[0..4]); | 18 | const s0 = mem.readIntLittle(u32, bytes[0..4]); |
| 19 | const s1 = mem.readIntBig(u32, bytes[4..8]); | 19 | const s1 = mem.readIntLittle(u32, bytes[4..8]); |
| 20 | const s2 = mem.readIntBig(u32, bytes[8..12]); | 20 | const s2 = mem.readIntLittle(u32, bytes[8..12]); |
| 21 | const s3 = mem.readIntBig(u32, bytes[12..16]); | 21 | const s3 = mem.readIntLittle(u32, bytes[12..16]); |
| 22 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; | 22 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | /// Convert the internal representation of a block into a byte sequence. | 25 | /// Convert the internal representation of a block into a byte sequence. |
| 26 | pub inline fn toBytes(block: Block) [16]u8 { | 26 | pub inline fn toBytes(block: Block) [16]u8 { |
| 27 | var bytes: [16]u8 = undefined; | 27 | var bytes: [16]u8 = undefined; |
| 28 | mem.writeIntBig(u32, bytes[0..4], block.repr[0]); | 28 | mem.writeIntLittle(u32, bytes[0..4], block.repr[0]); |
| 29 | mem.writeIntBig(u32, bytes[4..8], block.repr[1]); | 29 | mem.writeIntLittle(u32, bytes[4..8], block.repr[1]); |
| 30 | mem.writeIntBig(u32, bytes[8..12], block.repr[2]); | 30 | mem.writeIntLittle(u32, bytes[8..12], block.repr[2]); |
| 31 | mem.writeIntBig(u32, bytes[12..16], block.repr[3]); | 31 | mem.writeIntLittle(u32, bytes[12..16], block.repr[3]); |
| 32 | return bytes; | 32 | return bytes; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| ... | @@ -50,32 +50,93 @@ pub const Block = struct { | ... | @@ -50,32 +50,93 @@ pub const Block = struct { |
| 50 | const s2 = block.repr[2]; | 50 | const s2 = block.repr[2]; |
| 51 | const s3 = block.repr[3]; | 51 | const s3 = block.repr[3]; |
| 52 | 52 | ||
| 53 | const t0 = round_key.repr[0] ^ table_encrypt[0][@truncate(u8, s0 >> 24)] ^ table_encrypt[1][@truncate(u8, s1 >> 16)] ^ table_encrypt[2][@truncate(u8, s2 >> 8)] ^ table_encrypt[3][@truncate(u8, s3)]; | 53 | var x: [4]u32 = undefined; |
| 54 | const t1 = round_key.repr[1] ^ table_encrypt[0][@truncate(u8, s1 >> 24)] ^ table_encrypt[1][@truncate(u8, s2 >> 16)] ^ table_encrypt[2][@truncate(u8, s3 >> 8)] ^ table_encrypt[3][@truncate(u8, s0)]; | 54 | x = table_lookup(&table_encrypt, @truncate(u8, s0), @truncate(u8, s1 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 24)); |
| 55 | const t2 = round_key.repr[2] ^ table_encrypt[0][@truncate(u8, s2 >> 24)] ^ table_encrypt[1][@truncate(u8, s3 >> 16)] ^ table_encrypt[2][@truncate(u8, s0 >> 8)] ^ table_encrypt[3][@truncate(u8, s1)]; | 55 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| 56 | const t3 = round_key.repr[3] ^ table_encrypt[0][@truncate(u8, s3 >> 24)] ^ table_encrypt[1][@truncate(u8, s0 >> 16)] ^ table_encrypt[2][@truncate(u8, s1 >> 8)] ^ table_encrypt[3][@truncate(u8, s2)]; | 56 | x = table_lookup(&table_encrypt, @truncate(u8, s1), @truncate(u8, s2 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 24)); |
| 57 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 58 | x = table_lookup(&table_encrypt, @truncate(u8, s2), @truncate(u8, s3 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 24)); | ||
| 59 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 60 | x = table_lookup(&table_encrypt, @truncate(u8, s3), @truncate(u8, s0 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 24)); | ||
| 61 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 62 | |||
| 63 | t0 ^= round_key.repr[0]; | ||
| 64 | t1 ^= round_key.repr[1]; | ||
| 65 | t2 ^= round_key.repr[2]; | ||
| 66 | t3 ^= round_key.repr[3]; | ||
| 67 | |||
| 68 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Encrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS* | ||
| 72 | pub inline fn encryptUnprotected(block: Block, round_key: Block) Block { | ||
| 73 | const s0 = block.repr[0]; | ||
| 74 | const s1 = block.repr[1]; | ||
| 75 | const s2 = block.repr[2]; | ||
| 76 | const s3 = block.repr[3]; | ||
| 77 | |||
| 78 | var x: [4]u32 = undefined; | ||
| 79 | x = .{ | ||
| 80 | table_encrypt[0][@truncate(u8, s0)], | ||
| 81 | table_encrypt[1][@truncate(u8, s1 >> 8)], | ||
| 82 | table_encrypt[2][@truncate(u8, s2 >> 16)], | ||
| 83 | table_encrypt[3][@truncate(u8, s3 >> 24)], | ||
| 84 | }; | ||
| 85 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 86 | x = .{ | ||
| 87 | table_encrypt[0][@truncate(u8, s1)], | ||
| 88 | table_encrypt[1][@truncate(u8, s2 >> 8)], | ||
| 89 | table_encrypt[2][@truncate(u8, s3 >> 16)], | ||
| 90 | table_encrypt[3][@truncate(u8, s0 >> 24)], | ||
| 91 | }; | ||
| 92 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 93 | x = .{ | ||
| 94 | table_encrypt[0][@truncate(u8, s2)], | ||
| 95 | table_encrypt[1][@truncate(u8, s3 >> 8)], | ||
| 96 | table_encrypt[2][@truncate(u8, s0 >> 16)], | ||
| 97 | table_encrypt[3][@truncate(u8, s1 >> 24)], | ||
| 98 | }; | ||
| 99 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 100 | x = .{ | ||
| 101 | table_encrypt[0][@truncate(u8, s3)], | ||
| 102 | table_encrypt[1][@truncate(u8, s0 >> 8)], | ||
| 103 | table_encrypt[2][@truncate(u8, s1 >> 16)], | ||
| 104 | table_encrypt[3][@truncate(u8, s2 >> 24)], | ||
| 105 | }; | ||
| 106 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 107 | |||
| 108 | t0 ^= round_key.repr[0]; | ||
| 109 | t1 ^= round_key.repr[1]; | ||
| 110 | t2 ^= round_key.repr[2]; | ||
| 111 | t3 ^= round_key.repr[3]; | ||
| 57 | 112 | ||
| 58 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; | 113 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 59 | } | 114 | } |
| 60 | 115 | ||
| 61 | /// Encrypt a block with the last round key. | 116 | /// Encrypt a block with the last round key. |
| 62 | pub inline fn encryptLast(block: Block, round_key: Block) Block { | 117 | pub inline fn encryptLast(block: Block, round_key: Block) Block { |
| 63 | const t0 = block.repr[0]; | 118 | const s0 = block.repr[0]; |
| 64 | const t1 = block.repr[1]; | 119 | const s1 = block.repr[1]; |
| 65 | const t2 = block.repr[2]; | 120 | const s2 = block.repr[2]; |
| 66 | const t3 = block.repr[3]; | 121 | const s3 = block.repr[3]; |
| 67 | 122 | ||
| 68 | // Last round uses s-box directly and XORs to produce output. | 123 | // Last round uses s-box directly and XORs to produce output. |
| 69 | var s0 = @as(u32, sbox_encrypt[t0 >> 24]) << 24 | @as(u32, sbox_encrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t3 & 0xff]); | 124 | var x: [4]u8 = undefined; |
| 70 | var s1 = @as(u32, sbox_encrypt[t1 >> 24]) << 24 | @as(u32, sbox_encrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t0 & 0xff]); | 125 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s3 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s0)); |
| 71 | var s2 = @as(u32, sbox_encrypt[t2 >> 24]) << 24 | @as(u32, sbox_encrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t1 & 0xff]); | 126 | var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 72 | var s3 = @as(u32, sbox_encrypt[t3 >> 24]) << 24 | @as(u32, sbox_encrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t2 & 0xff]); | 127 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s0 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s1)); |
| 73 | s0 ^= round_key.repr[0]; | 128 | var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 74 | s1 ^= round_key.repr[1]; | 129 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s1 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s2)); |
| 75 | s2 ^= round_key.repr[2]; | 130 | var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 76 | s3 ^= round_key.repr[3]; | 131 | x = sbox_lookup(&sbox_encrypt, @truncate(u8, s2 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s3)); |
| 132 | var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); | ||
| 133 | |||
| 134 | t0 ^= round_key.repr[0]; | ||
| 135 | t1 ^= round_key.repr[1]; | ||
| 136 | t2 ^= round_key.repr[2]; | ||
| 137 | t3 ^= round_key.repr[3]; | ||
| 77 | 138 | ||
| 78 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; | 139 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 79 | } | 140 | } |
| 80 | 141 | ||
| 81 | /// Decrypt a block with a round key. | 142 | /// Decrypt a block with a round key. |
| ... | @@ -85,32 +146,93 @@ pub const Block = struct { | ... | @@ -85,32 +146,93 @@ pub const Block = struct { |
| 85 | const s2 = block.repr[2]; | 146 | const s2 = block.repr[2]; |
| 86 | const s3 = block.repr[3]; | 147 | const s3 = block.repr[3]; |
| 87 | 148 | ||
| 88 | const t0 = round_key.repr[0] ^ table_decrypt[0][@truncate(u8, s0 >> 24)] ^ table_decrypt[1][@truncate(u8, s3 >> 16)] ^ table_decrypt[2][@truncate(u8, s2 >> 8)] ^ table_decrypt[3][@truncate(u8, s1)]; | 149 | var x: [4]u32 = undefined; |
| 89 | const t1 = round_key.repr[1] ^ table_decrypt[0][@truncate(u8, s1 >> 24)] ^ table_decrypt[1][@truncate(u8, s0 >> 16)] ^ table_decrypt[2][@truncate(u8, s3 >> 8)] ^ table_decrypt[3][@truncate(u8, s2)]; | 150 | x = table_lookup(&table_decrypt, @truncate(u8, s0), @truncate(u8, s3 >> 8), @truncate(u8, s2 >> 16), @truncate(u8, s1 >> 24)); |
| 90 | const t2 = round_key.repr[2] ^ table_decrypt[0][@truncate(u8, s2 >> 24)] ^ table_decrypt[1][@truncate(u8, s1 >> 16)] ^ table_decrypt[2][@truncate(u8, s0 >> 8)] ^ table_decrypt[3][@truncate(u8, s3)]; | 151 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; |
| 91 | const t3 = round_key.repr[3] ^ table_decrypt[0][@truncate(u8, s3 >> 24)] ^ table_decrypt[1][@truncate(u8, s2 >> 16)] ^ table_decrypt[2][@truncate(u8, s1 >> 8)] ^ table_decrypt[3][@truncate(u8, s0)]; | 152 | x = table_lookup(&table_decrypt, @truncate(u8, s1), @truncate(u8, s0 >> 8), @truncate(u8, s3 >> 16), @truncate(u8, s2 >> 24)); |
| 153 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 154 | x = table_lookup(&table_decrypt, @truncate(u8, s2), @truncate(u8, s1 >> 8), @truncate(u8, s0 >> 16), @truncate(u8, s3 >> 24)); | ||
| 155 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 156 | x = table_lookup(&table_decrypt, @truncate(u8, s3), @truncate(u8, s2 >> 8), @truncate(u8, s1 >> 16), @truncate(u8, s0 >> 24)); | ||
| 157 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 158 | |||
| 159 | t0 ^= round_key.repr[0]; | ||
| 160 | t1 ^= round_key.repr[1]; | ||
| 161 | t2 ^= round_key.repr[2]; | ||
| 162 | t3 ^= round_key.repr[3]; | ||
| 163 | |||
| 164 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Decrypt a block with a round key *WITHOUT ANY PROTECTION AGAINST SIDE CHANNELS* | ||
| 168 | pub inline fn decryptUnprotected(block: Block, round_key: Block) Block { | ||
| 169 | const s0 = block.repr[0]; | ||
| 170 | const s1 = block.repr[1]; | ||
| 171 | const s2 = block.repr[2]; | ||
| 172 | const s3 = block.repr[3]; | ||
| 173 | |||
| 174 | var x: [4]u32 = undefined; | ||
| 175 | x = .{ | ||
| 176 | table_decrypt[0][@truncate(u8, s0)], | ||
| 177 | table_decrypt[1][@truncate(u8, s3 >> 8)], | ||
| 178 | table_decrypt[2][@truncate(u8, s2 >> 16)], | ||
| 179 | table_decrypt[3][@truncate(u8, s1 >> 24)], | ||
| 180 | }; | ||
| 181 | var t0 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 182 | x = .{ | ||
| 183 | table_decrypt[0][@truncate(u8, s1)], | ||
| 184 | table_decrypt[1][@truncate(u8, s0 >> 8)], | ||
| 185 | table_decrypt[2][@truncate(u8, s3 >> 16)], | ||
| 186 | table_decrypt[3][@truncate(u8, s2 >> 24)], | ||
| 187 | }; | ||
| 188 | var t1 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 189 | x = .{ | ||
| 190 | table_decrypt[0][@truncate(u8, s2)], | ||
| 191 | table_decrypt[1][@truncate(u8, s1 >> 8)], | ||
| 192 | table_decrypt[2][@truncate(u8, s0 >> 16)], | ||
| 193 | table_decrypt[3][@truncate(u8, s3 >> 24)], | ||
| 194 | }; | ||
| 195 | var t2 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 196 | x = .{ | ||
| 197 | table_decrypt[0][@truncate(u8, s3)], | ||
| 198 | table_decrypt[1][@truncate(u8, s2 >> 8)], | ||
| 199 | table_decrypt[2][@truncate(u8, s1 >> 16)], | ||
| 200 | table_decrypt[3][@truncate(u8, s0 >> 24)], | ||
| 201 | }; | ||
| 202 | var t3 = x[0] ^ x[1] ^ x[2] ^ x[3]; | ||
| 203 | |||
| 204 | t0 ^= round_key.repr[0]; | ||
| 205 | t1 ^= round_key.repr[1]; | ||
| 206 | t2 ^= round_key.repr[2]; | ||
| 207 | t3 ^= round_key.repr[3]; | ||
| 92 | 208 | ||
| 93 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; | 209 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 94 | } | 210 | } |
| 95 | 211 | ||
| 96 | /// Decrypt a block with the last round key. | 212 | /// Decrypt a block with the last round key. |
| 97 | pub inline fn decryptLast(block: Block, round_key: Block) Block { | 213 | pub inline fn decryptLast(block: Block, round_key: Block) Block { |
| 98 | const t0 = block.repr[0]; | 214 | const s0 = block.repr[0]; |
| 99 | const t1 = block.repr[1]; | 215 | const s1 = block.repr[1]; |
| 100 | const t2 = block.repr[2]; | 216 | const s2 = block.repr[2]; |
| 101 | const t3 = block.repr[3]; | 217 | const s3 = block.repr[3]; |
| 102 | 218 | ||
| 103 | // Last round uses s-box directly and XORs to produce output. | 219 | // Last round uses s-box directly and XORs to produce output. |
| 104 | var s0 = @as(u32, sbox_decrypt[t0 >> 24]) << 24 | @as(u32, sbox_decrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t1 & 0xff]); | 220 | var x: [4]u8 = undefined; |
| 105 | var s1 = @as(u32, sbox_decrypt[t1 >> 24]) << 24 | @as(u32, sbox_decrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t2 & 0xff]); | 221 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s1 >> 24), @truncate(u8, s2 >> 16), @truncate(u8, s3 >> 8), @truncate(u8, s0)); |
| 106 | var s2 = @as(u32, sbox_decrypt[t2 >> 24]) << 24 | @as(u32, sbox_decrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t3 & 0xff]); | 222 | var t0 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 107 | var s3 = @as(u32, sbox_decrypt[t3 >> 24]) << 24 | @as(u32, sbox_decrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t0 & 0xff]); | 223 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s2 >> 24), @truncate(u8, s3 >> 16), @truncate(u8, s0 >> 8), @truncate(u8, s1)); |
| 108 | s0 ^= round_key.repr[0]; | 224 | var t1 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 109 | s1 ^= round_key.repr[1]; | 225 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s3 >> 24), @truncate(u8, s0 >> 16), @truncate(u8, s1 >> 8), @truncate(u8, s2)); |
| 110 | s2 ^= round_key.repr[2]; | 226 | var t2 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); |
| 111 | s3 ^= round_key.repr[3]; | 227 | x = sbox_lookup(&sbox_decrypt, @truncate(u8, s0 >> 24), @truncate(u8, s1 >> 16), @truncate(u8, s2 >> 8), @truncate(u8, s3)); |
| 228 | var t3 = @as(u32, x[0]) << 24 | @as(u32, x[1]) << 16 | @as(u32, x[2]) << 8 | @as(u32, x[3]); | ||
| 229 | |||
| 230 | t0 ^= round_key.repr[0]; | ||
| 231 | t1 ^= round_key.repr[1]; | ||
| 232 | t2 ^= round_key.repr[2]; | ||
| 233 | t3 ^= round_key.repr[3]; | ||
| 112 | 234 | ||
| 113 | return Block{ .repr = BlockVec{ s0, s1, s2, s3 } }; | 235 | return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; |
| 114 | } | 236 | } |
| 115 | 237 | ||
| 116 | /// Apply the bitwise XOR operation to the content of two blocks. | 238 | /// Apply the bitwise XOR operation to the content of two blocks. |
| ... | @@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type { | ... | @@ -226,7 +348,8 @@ fn KeySchedule(comptime Aes: type) type { |
| 226 | const subw = struct { | 348 | const subw = struct { |
| 227 | // Apply sbox_encrypt to each byte in w. | 349 | // Apply sbox_encrypt to each byte in w. |
| 228 | fn func(w: u32) u32 { | 350 | fn func(w: u32) u32 { |
| 229 | return @as(u32, sbox_encrypt[w >> 24]) << 24 | @as(u32, sbox_encrypt[w >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[w >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[w & 0xff]); | 351 | const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, w), @truncate(u8, w >> 8), @truncate(u8, w >> 16), @truncate(u8, w >> 24)); |
| 352 | return @as(u32, x[3]) << 24 | @as(u32, x[2]) << 16 | @as(u32, x[1]) << 8 | @as(u32, x[0]); | ||
| 230 | } | 353 | } |
| 231 | }.func; | 354 | }.func; |
| 232 | 355 | ||
| ... | @@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type { | ... | @@ -244,6 +367,10 @@ fn KeySchedule(comptime Aes: type) type { |
| 244 | } | 367 | } |
| 245 | round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t; | 368 | round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t; |
| 246 | } | 369 | } |
| 370 | i = 0; | ||
| 371 | inline while (i < round_keys.len * 4) : (i += 1) { | ||
| 372 | round_keys[i / 4].repr[i % 4] = @byteSwap(round_keys[i / 4].repr[i % 4]); | ||
| 373 | } | ||
| 247 | return Self{ .round_keys = round_keys }; | 374 | return Self{ .round_keys = round_keys }; |
| 248 | } | 375 | } |
| 249 | 376 | ||
| ... | @@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type { | ... | @@ -257,11 +384,13 @@ fn KeySchedule(comptime Aes: type) type { |
| 257 | const ei = total_words - i - 4; | 384 | const ei = total_words - i - 4; |
| 258 | comptime var j: usize = 0; | 385 | comptime var j: usize = 0; |
| 259 | inline while (j < 4) : (j += 1) { | 386 | inline while (j < 4) : (j += 1) { |
| 260 | var x = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; | 387 | var rk = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; |
| 261 | if (i > 0 and i + 4 < total_words) { | 388 | if (i > 0 and i + 4 < total_words) { |
| 262 | x = table_decrypt[0][sbox_encrypt[x >> 24]] ^ table_decrypt[1][sbox_encrypt[x >> 16 & 0xff]] ^ table_decrypt[2][sbox_encrypt[x >> 8 & 0xff]] ^ table_decrypt[3][sbox_encrypt[x & 0xff]]; | 389 | const x = sbox_lookup(&sbox_key_schedule, @truncate(u8, rk >> 24), @truncate(u8, rk >> 16), @truncate(u8, rk >> 8), @truncate(u8, rk)); |
| 390 | const y = table_lookup(&table_decrypt, x[3], x[2], x[1], x[0]); | ||
| 391 | rk = y[0] ^ y[1] ^ y[2] ^ y[3]; | ||
| 263 | } | 392 | } |
| 264 | inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x; | 393 | inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = rk; |
| 265 | } | 394 | } |
| 266 | } | 395 | } |
| 267 | return Self{ .round_keys = inv_round_keys }; | 396 | return Self{ .round_keys = inv_round_keys }; |
| ... | @@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { | ... | @@ -293,7 +422,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { |
| 293 | const round_keys = ctx.key_schedule.round_keys; | 422 | const round_keys = ctx.key_schedule.round_keys; |
| 294 | var t = Block.fromBytes(src).xorBlocks(round_keys[0]); | 423 | var t = Block.fromBytes(src).xorBlocks(round_keys[0]); |
| 295 | comptime var i = 1; | 424 | comptime var i = 1; |
| 296 | inline while (i < rounds) : (i += 1) { | 425 | if (side_channels_mitigations == .full) { |
| 426 | inline while (i < rounds) : (i += 1) { | ||
| 427 | t = t.encrypt(round_keys[i]); | ||
| 428 | } | ||
| 429 | } else { | ||
| 430 | inline while (i < 5) : (i += 1) { | ||
| 431 | t = t.encrypt(round_keys[i]); | ||
| 432 | } | ||
| 433 | inline while (i < rounds - 1) : (i += 1) { | ||
| 434 | t = t.encryptUnprotected(round_keys[i]); | ||
| 435 | } | ||
| 297 | t = t.encrypt(round_keys[i]); | 436 | t = t.encrypt(round_keys[i]); |
| 298 | } | 437 | } |
| 299 | t = t.encryptLast(round_keys[rounds]); | 438 | t = t.encryptLast(round_keys[rounds]); |
| ... | @@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { | ... | @@ -305,7 +444,17 @@ pub fn AesEncryptCtx(comptime Aes: type) type { |
| 305 | const round_keys = ctx.key_schedule.round_keys; | 444 | const round_keys = ctx.key_schedule.round_keys; |
| 306 | var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]); | 445 | var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]); |
| 307 | comptime var i = 1; | 446 | comptime var i = 1; |
| 308 | inline while (i < rounds) : (i += 1) { | 447 | if (side_channels_mitigations == .full) { |
| 448 | inline while (i < rounds) : (i += 1) { | ||
| 449 | t = t.encrypt(round_keys[i]); | ||
| 450 | } | ||
| 451 | } else { | ||
| 452 | inline while (i < 5) : (i += 1) { | ||
| 453 | t = t.encrypt(round_keys[i]); | ||
| 454 | } | ||
| 455 | inline while (i < rounds - 1) : (i += 1) { | ||
| 456 | t = t.encryptUnprotected(round_keys[i]); | ||
| 457 | } | ||
| 309 | t = t.encrypt(round_keys[i]); | 458 | t = t.encrypt(round_keys[i]); |
| 310 | } | 459 | } |
| 311 | t = t.encryptLast(round_keys[rounds]); | 460 | t = t.encryptLast(round_keys[rounds]); |
| ... | @@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type { | ... | @@ -359,7 +508,17 @@ pub fn AesDecryptCtx(comptime Aes: type) type { |
| 359 | const inv_round_keys = ctx.key_schedule.round_keys; | 508 | const inv_round_keys = ctx.key_schedule.round_keys; |
| 360 | var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]); | 509 | var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]); |
| 361 | comptime var i = 1; | 510 | comptime var i = 1; |
| 362 | inline while (i < rounds) : (i += 1) { | 511 | if (side_channels_mitigations == .full) { |
| 512 | inline while (i < rounds) : (i += 1) { | ||
| 513 | t = t.decrypt(inv_round_keys[i]); | ||
| 514 | } | ||
| 515 | } else { | ||
| 516 | inline while (i < 5) : (i += 1) { | ||
| 517 | t = t.decrypt(inv_round_keys[i]); | ||
| 518 | } | ||
| 519 | inline while (i < rounds - 1) : (i += 1) { | ||
| 520 | t = t.decryptUnprotected(inv_round_keys[i]); | ||
| 521 | } | ||
| 363 | t = t.decrypt(inv_round_keys[i]); | 522 | t = t.decrypt(inv_round_keys[i]); |
| 364 | } | 523 | } |
| 365 | t = t.decryptLast(inv_round_keys[rounds]); | 524 | t = t.decryptLast(inv_round_keys[rounds]); |
| ... | @@ -428,10 +587,11 @@ const powx = init: { | ... | @@ -428,10 +587,11 @@ const powx = init: { |
| 428 | break :init array; | 587 | break :init array; |
| 429 | }; | 588 | }; |
| 430 | 589 | ||
| 431 | const sbox_encrypt align(64) = generateSbox(false); | 590 | const sbox_encrypt align(64) = generateSbox(false); // S-box for encryption |
| 432 | const sbox_decrypt align(64) = generateSbox(true); | 591 | const 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 |
| 433 | const table_encrypt align(64) = generateTable(false); | 592 | const sbox_decrypt align(64) = generateSbox(true); // S-box for decryption |
| 434 | const table_decrypt align(64) = generateTable(true); | 593 | const table_encrypt align(64) = generateTable(false); // 4-byte LUTs for encryption |
| 594 | const table_decrypt align(64) = generateTable(true); // 4-byte LUTs for decryption | ||
| 435 | 595 | ||
| 436 | // Generate S-box substitution values. | 596 | // Generate S-box substitution values. |
| 437 | fn generateSbox(invert: bool) [256]u8 { | 597 | fn generateSbox(invert: bool) [256]u8 { |
| ... | @@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 { | ... | @@ -472,14 +632,14 @@ fn generateTable(invert: bool) [4][256]u32 { |
| 472 | var table: [4][256]u32 = undefined; | 632 | var table: [4][256]u32 = undefined; |
| 473 | 633 | ||
| 474 | for (generateSbox(invert), 0..) |value, index| { | 634 | for (generateSbox(invert), 0..) |value, index| { |
| 475 | table[0][index] = mul(value, if (invert) 0xb else 0x3); | 635 | table[0][index] = math.shl(u32, mul(value, if (invert) 0xb else 0x3), 24); |
| 476 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 8); | 636 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 16); |
| 477 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 16); | 637 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 8); |
| 478 | table[0][index] |= math.shl(u32, mul(value, if (invert) 0xe else 0x2), 24); | 638 | table[0][index] |= mul(value, if (invert) 0xe else 0x2); |
| 479 | 639 | ||
| 480 | table[1][index] = math.rotr(u32, table[0][index], 8); | 640 | table[1][index] = math.rotl(u32, table[0][index], 8); |
| 481 | table[2][index] = math.rotr(u32, table[0][index], 16); | 641 | table[2][index] = math.rotl(u32, table[0][index], 16); |
| 482 | table[3][index] = math.rotr(u32, table[0][index], 24); | 642 | table[3][index] = math.rotl(u32, table[0][index], 24); |
| 483 | } | 643 | } |
| 484 | 644 | ||
| 485 | return table; | 645 | return table; |
| ... | @@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 { | ... | @@ -506,3 +666,82 @@ fn mul(a: u8, b: u8) u8 { |
| 506 | 666 | ||
| 507 | return @truncate(u8, s); | 667 | return @truncate(u8, s); |
| 508 | } | 668 | } |
| 669 | |||
| 670 | const cache_line_bytes = 64; | ||
| 671 | |||
| 672 | inline 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 | |||
| 710 | inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8, idx2: u8, idx3: u8) [4]u32 { | ||
| 711 | if (side_channels_mitigations == .none) { | ||
| 712 | return [4]u32{ | ||
| 713 | table[0][idx0], | ||
| 714 | table[1][idx1], | ||
| 715 | table[2][idx2], | ||
| 716 | table[3][idx3], | ||
| 717 | }; | ||
| 718 | } else { | ||
| 719 | const table_bytes = @sizeOf(@TypeOf(table[0])); | ||
| 720 | const stride = switch (side_channels_mitigations) { | ||
| 721 | .none => unreachable, | ||
| 722 | .basic => table[0].len / 4, | ||
| 723 | .medium => table[0].len / (table_bytes / cache_line_bytes) * 2, | ||
| 724 | .full => table[0].len / (table_bytes / cache_line_bytes), | ||
| 725 | }; | ||
| 726 | const of0 = idx0 % stride; | ||
| 727 | const of1 = idx1 % stride; | ||
| 728 | const of2 = idx2 % stride; | ||
| 729 | const of3 = idx3 % stride; | ||
| 730 | var t: [4][table[0].len / stride]u32 align(64) = undefined; | ||
| 731 | var i: usize = 0; | ||
| 732 | while (i < t[0].len) : (i += 1) { | ||
| 733 | const tx = table[0][i * stride ..]; | ||
| 734 | t[0][i] = tx[of0]; | ||
| 735 | t[1][i] = tx[of1]; | ||
| 736 | t[2][i] = tx[of2]; | ||
| 737 | t[3][i] = tx[of3]; | ||
| 738 | } | ||
| 739 | std.mem.doNotOptimizeAway(t); | ||
| 740 | return [4]u32{ | ||
| 741 | t[0][idx0 / stride], | ||
| 742 | math.rotl(u32, t[1][idx1 / stride], 8), | ||
| 743 | math.rotl(u32, t[2][idx2 / stride], 16), | ||
| 744 | math.rotl(u32, t[3][idx3 / stride], 24), | ||
| 745 | }; | ||
| 746 | } | ||
| 747 | } |
lib/std/crypto/argon2.zig+29-30| ... | @@ -138,40 +138,39 @@ fn initHash( | ... | @@ -138,40 +138,39 @@ fn initHash( |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | fn blake2bLong(out: []u8, in: []const u8) void { | 140 | fn blake2bLong(out: []u8, in: []const u8) void { |
| 141 | var b2 = Blake2b512.init(.{ .expected_out_bits = math.min(512, out.len * 8) }); | 141 | const H = Blake2b512; |
| 142 | 142 | var outlen_bytes: [4]u8 = undefined; | |
| 143 | var buffer: [Blake2b512.digest_length]u8 = undefined; | 143 | mem.writeIntLittle(u32, &outlen_bytes, @intCast(u32, out.len)); |
| 144 | mem.writeIntLittle(u32, buffer[0..4], @intCast(u32, out.len)); | 144 | |
| 145 | b2.update(buffer[0..4]); | 145 | var out_buf: [H.digest_length]u8 = undefined; |
| 146 | b2.update(in); | 146 | |
| 147 | b2.final(&buffer); | 147 | if (out.len <= H.digest_length) { |
| 148 | 148 | var h = H.init(.{ .expected_out_bits = out.len * 8 }); | |
| 149 | if (out.len <= Blake2b512.digest_length) { | 149 | h.update(&outlen_bytes); |
| 150 | mem.copy(u8, out, buffer[0..out.len]); | 150 | h.update(in); |
| 151 | h.final(&out_buf); | ||
| 152 | mem.copy(u8, out, out_buf[0..out.len]); | ||
| 151 | return; | 153 | return; |
| 152 | } | 154 | } |
| 153 | 155 | ||
| 154 | b2 = Blake2b512.init(.{}); | 156 | var h = H.init(.{}); |
| 155 | mem.copy(u8, out, buffer[0..32]); | 157 | h.update(&outlen_bytes); |
| 156 | var out_slice = out[32..]; | 158 | h.update(in); |
| 157 | while (out_slice.len > Blake2b512.digest_length) : ({ | 159 | h.final(&out_buf); |
| 158 | out_slice = out_slice[32..]; | 160 | var out_slice = out; |
| 159 | b2 = Blake2b512.init(.{}); | 161 | mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]); |
| 160 | }) { | 162 | out_slice = out_slice[H.digest_length / 2 ..]; |
| 161 | b2.update(&buffer); | 163 | |
| 162 | b2.final(&buffer); | 164 | var in_buf: [H.digest_length]u8 = undefined; |
| 163 | mem.copy(u8, out_slice, buffer[0..32]); | 165 | while (out_slice.len > H.digest_length) { |
| 164 | } | 166 | mem.copy(u8, &in_buf, &out_buf); |
| 165 | 167 | H.hash(&in_buf, &out_buf, .{}); | |
| 166 | var r = Blake2b512.digest_length; | 168 | mem.copy(u8, out_slice, out_buf[0 .. H.digest_length / 2]); |
| 167 | if (out.len % Blake2b512.digest_length > 0) { | 169 | out_slice = out_slice[H.digest_length / 2 ..]; |
| 168 | r = ((out.len + 31) / 32) - 2; | ||
| 169 | b2 = Blake2b512.init(.{ .expected_out_bits = r * 8 }); | ||
| 170 | } | 170 | } |
| 171 | 171 | mem.copy(u8, &in_buf, &out_buf); | |
| 172 | b2.update(&buffer); | 172 | H.hash(&in_buf, &out_buf, .{ .expected_out_bits = out_slice.len * 8 }); |
| 173 | b2.final(&buffer); | 173 | mem.copy(u8, out_slice, out_buf[0..out_slice.len]); |
| 174 | mem.copy(u8, out_slice, buffer[0..r]); | ||
| 175 | } | 174 | } |
| 176 | 175 | ||
| 177 | fn initBlocks( | 176 | fn initBlocks( |
lib/std/crypto/benchmark.zig+89| ... | @@ -27,6 +27,8 @@ const hashes = [_]Crypto{ | ... | @@ -27,6 +27,8 @@ const hashes = [_]Crypto{ |
| 27 | Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" }, | 27 | Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" }, |
| 28 | Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" }, | 28 | Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" }, |
| 29 | Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" }, | 29 | Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" }, |
| 30 | Crypto{ .ty = crypto.hash.sha3.TurboShake128(null), .name = "turboshake-128" }, | ||
| 31 | Crypto{ .ty = crypto.hash.sha3.TurboShake256(null), .name = "turboshake-256" }, | ||
| 30 | Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" }, | 32 | Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" }, |
| 31 | Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" }, | 33 | Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" }, |
| 32 | Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" }, | 34 | Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" }, |
| ... | @@ -201,6 +203,72 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime | ... | @@ -201,6 +203,72 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime |
| 201 | return throughput; | 203 | return throughput; |
| 202 | } | 204 | } |
| 203 | 205 | ||
| 206 | const 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 | |||
| 212 | pub 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 | |||
| 232 | pub 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 | |||
| 254 | pub 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 | |||
| 204 | const aeads = [_]Crypto{ | 272 | const aeads = [_]Crypto{ |
| 205 | Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" }, | 273 | Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" }, |
| 206 | Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" }, | 274 | Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" }, |
| ... | @@ -483,4 +551,25 @@ pub fn main() !void { | ... | @@ -483,4 +551,25 @@ pub fn main() !void { |
| 483 | try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); | 551 | try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); |
| 484 | } | 552 | } |
| 485 | } | 553 | } |
| 554 | |||
| 555 | inline for (kems) |E| { | ||
| 556 | if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { | ||
| 557 | const throughput = try benchmarkKem(E.ty, mode(1000)); | ||
| 558 | try stdout.print("{s:>17}: {:10} encaps/s\n", .{ E.name, throughput }); | ||
| 559 | } | ||
| 560 | } | ||
| 561 | |||
| 562 | inline for (kems) |E| { | ||
| 563 | if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { | ||
| 564 | const throughput = try benchmarkKemDecaps(E.ty, mode(25000)); | ||
| 565 | try stdout.print("{s:>17}: {:10} decaps/s\n", .{ E.name, throughput }); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | inline for (kems) |E| { | ||
| 570 | if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { | ||
| 571 | const throughput = try benchmarkKemKeyGen(E.ty, mode(25000)); | ||
| 572 | try stdout.print("{s:>17}: {:10} keygen/s\n", .{ E.name, throughput }); | ||
| 573 | } | ||
| 574 | } | ||
| 486 | } | 575 | } |
lib/std/crypto/blake3.zig+1-1| ... | @@ -200,7 +200,7 @@ const CompressGeneric = struct { | ... | @@ -200,7 +200,7 @@ const CompressGeneric = struct { |
| 200 | } | 200 | } |
| 201 | }; | 201 | }; |
| 202 | 202 | ||
| 203 | const compress = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c) | 203 | const compress = if (builtin.cpu.arch == .x86_64) |
| 204 | CompressVectorized.compress | 204 | CompressVectorized.compress |
| 205 | else | 205 | else |
| 206 | CompressGeneric.compress; | 206 | CompressGeneric.compress; |
lib/std/crypto/gimli.zig+1-1| ... | @@ -152,7 +152,7 @@ pub const State = struct { | ... | @@ -152,7 +152,7 @@ pub const State = struct { |
| 152 | self.endianSwap(); | 152 | self.endianSwap(); |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | pub const permute = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c) impl: { | 155 | pub const permute = if (builtin.cpu.arch == .x86_64) impl: { |
| 156 | break :impl permute_vectorized; | 156 | break :impl permute_vectorized; |
| 157 | } else if (builtin.mode == .ReleaseSmall) impl: { | 157 | } else if (builtin.mode == .ReleaseSmall) impl: { |
| 158 | break :impl permute_small; | 158 | break :impl permute_small; |
lib/std/crypto/keccak_p.zig+3-3| ... | @@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type { | ... | @@ -175,12 +175,12 @@ pub fn KeccakF(comptime f: u11) type { |
| 175 | /// Apply a (possibly) reduced-round permutation to the state. | 175 | /// Apply a (possibly) reduced-round permutation to the state. |
| 176 | pub fn permuteR(self: *Self, comptime rounds: u5) void { | 176 | pub fn permuteR(self: *Self, comptime rounds: u5) void { |
| 177 | var i = RC.len - rounds; | 177 | var i = RC.len - rounds; |
| 178 | while (i < rounds - rounds % 3) : (i += 3) { | 178 | while (i < RC.len - RC.len % 3) : (i += 3) { |
| 179 | self.round(RC[i]); | 179 | self.round(RC[i]); |
| 180 | self.round(RC[i + 1]); | 180 | self.round(RC[i + 1]); |
| 181 | self.round(RC[i + 2]); | 181 | self.round(RC[i + 2]); |
| 182 | } | 182 | } |
| 183 | while (i < rounds) : (i += 1) { | 183 | while (i < RC.len) : (i += 1) { |
| 184 | self.round(RC[i]); | 184 | self.round(RC[i]); |
| 185 | } | 185 | } |
| 186 | } | 186 | } |
| ... | @@ -231,7 +231,7 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti | ... | @@ -231,7 +231,7 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti |
| 231 | bytes = bytes[rate..]; | 231 | bytes = bytes[rate..]; |
| 232 | } | 232 | } |
| 233 | if (bytes.len > 0) { | 233 | if (bytes.len > 0) { |
| 234 | self.st.addBytes(bytes[0..]); | 234 | mem.copy(u8, &self.buf, bytes); |
| 235 | self.offset = bytes.len; | 235 | self.offset = bytes.len; |
| 236 | } | 236 | } |
| 237 | } | 237 | } |
lib/std/crypto/kyber_d00.zig created+1780| ... | @@ -0,0 +1,1780 @@ | ||
| 1 | //! Implementation of the IND-CCA2 post-quantum secure key encapsulation | ||
| 2 | //! mechanism (KEM) CRYSTALS-Kyber, as submitted to the third round of the NIST | ||
| 3 | //! Post-Quantum Cryptography (v3.02/"draft00"), and selected for standardisation. | ||
| 4 | //! | ||
| 5 | //! Kyber will likely change before final standardisation. | ||
| 6 | //! | ||
| 7 | //! The namespace suffix (currently `_d00`) refers to the version currently | ||
| 8 | //! implemented, in accordance with the draft. It may not be updated if new | ||
| 9 | //! versions of the draft only include editorial changes. | ||
| 10 | //! | ||
| 11 | //! The suffix will eventually be removed once Kyber is finalized. | ||
| 12 | //! | ||
| 13 | //! Quoting from the CFRG I-D: | ||
| 14 | //! | ||
| 15 | //! Kyber is not a Diffie-Hellman (DH) style non-interactive key | ||
| 16 | //! agreement, but instead, Kyber is a Key Encapsulation Method (KEM). | ||
| 17 | //! In essence, a KEM is a Public-Key Encryption (PKE) scheme where the | ||
| 18 | //! plaintext cannot be specified, but is generated as a random key as | ||
| 19 | //! part of the encryption. A KEM can be transformed into an unrestricted | ||
| 20 | //! PKE using HPKE (RFC9180). On its own, a KEM can be used as a key | ||
| 21 | //! agreement method in TLS. | ||
| 22 | //! | ||
| 23 | //! Kyber is an IND-CCA2 secure KEM. It is constructed by applying a | ||
| 24 | //! Fujisaki--Okamato style transformation on InnerPKE, which is the | ||
| 25 | //! underlying IND-CPA secure Public Key Encryption scheme. We cannot | ||
| 26 | //! use InnerPKE directly, as its ciphertexts are malleable. | ||
| 27 | //! | ||
| 28 | //! ``` | ||
| 29 | //! F.O. transform | ||
| 30 | //! InnerPKE ----------------------> Kyber | ||
| 31 | //! IND-CPA IND-CCA2 | ||
| 32 | //! ``` | ||
| 33 | //! | ||
| 34 | //! Kyber is a lattice-based scheme. More precisely, its security is | ||
| 35 | //! based on the learning-with-errors-and-rounding problem in module | ||
| 36 | //! lattices (MLWER). The underlying polynomial ring R (defined in | ||
| 37 | //! Section 5) is chosen such that multiplication is very fast using the | ||
| 38 | //! number theoretic transform (NTT, see Section 5.1.3). | ||
| 39 | //! | ||
| 40 | //! An InnerPKE private key is a vector _s_ over R of length k which is | ||
| 41 | //! _small_ in a particular way. Here k is a security parameter akin to | ||
| 42 | //! the size of a prime modulus. For Kyber512, which targets AES-128's | ||
| 43 | //! security level, the value of k is 2. | ||
| 44 | //! | ||
| 45 | //! The public key consists of two values: | ||
| 46 | //! | ||
| 47 | //! * _A_ a uniformly sampled k by k matrix over R _and_ | ||
| 48 | //! | ||
| 49 | //! * _t = A s + e_, where e is a suitably small masking vector. | ||
| 50 | //! | ||
| 51 | //! Distinguishing between such A s + e and a uniformly sampled t is the | ||
| 52 | //! module learning-with-errors (MLWE) problem. If that is hard, then it | ||
| 53 | //! is also hard to recover the private key from the public key as that | ||
| 54 | //! would allow you to distinguish between those two. | ||
| 55 | //! | ||
| 56 | //! To save space in the public key, A is recomputed deterministically | ||
| 57 | //! from a seed _rho_. | ||
| 58 | //! | ||
| 59 | //! A ciphertext for a message m under this public key is a pair (c_1, | ||
| 60 | //! c_2) computed roughly as follows: | ||
| 61 | //! | ||
| 62 | //! c_1 = Compress(A^T r + e_1, d_u) | ||
| 63 | //! c_2 = Compress(t^T r + e_2 + Decompress(m, 1), d_v) | ||
| 64 | //! | ||
| 65 | //! where | ||
| 66 | //! | ||
| 67 | //! * e_1, e_2 and r are small blinds; | ||
| 68 | //! | ||
| 69 | //! * Compress(-, d) removes some information, leaving d bits per | ||
| 70 | //! coefficient and Decompress is such that Compress after Decompress | ||
| 71 | //! does nothing and | ||
| 72 | //! | ||
| 73 | //! * d_u, d_v are scheme parameters. | ||
| 74 | //! | ||
| 75 | //! Distinguishing such a ciphertext and uniformly sampled (c_1, c_2) is | ||
| 76 | //! an example of the full MLWER problem, see section 4.4 of [KyberV302]. | ||
| 77 | //! | ||
| 78 | //! To decrypt the ciphertext, one computes | ||
| 79 | //! | ||
| 80 | //! m = Compress(Decompress(c_2, d_v) - s^T Decompress(c_1, d_u), 1). | ||
| 81 | //! | ||
| 82 | //! It it not straight-forward to see that this formula is correct. In | ||
| 83 | //! fact, there is negligable but non-zero probability that a ciphertext | ||
| 84 | //! does not decrypt correctly given by the DFP column in Table 4. This | ||
| 85 | //! failure probability can be computed by a careful automated analysis | ||
| 86 | //! of the probabilities involved, see kyber_failure.py of [SecEst]. | ||
| 87 | //! | ||
| 88 | //! [KyberV302](https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf) | ||
| 89 | //! [I-D](https://github.com/bwesterb/draft-schwabe-cfrg-kyber) | ||
| 90 | //! [SecEst](https://github.com/pq-crystals/security-estimates) | ||
| 91 | |||
| 92 | // TODO | ||
| 93 | // | ||
| 94 | // - The bottleneck in Kyber are the various hash/xof calls: | ||
| 95 | // - Optimize Zig's keccak implementation. | ||
| 96 | // - Use SIMD to compute keccak in parallel. | ||
| 97 | // - Can we track bounds of coefficients using comptime types without | ||
| 98 | // duplicating code? | ||
| 99 | // - Would be neater to have tests closer to the thing under test. | ||
| 100 | // - When generating a keypair, we have a copy of the inner public key with | ||
| 101 | // its large matrix A in both the public key and the private key. In Go we | ||
| 102 | // can just have a pointer in the private key to the public key, but | ||
| 103 | // how do we do this elegantly in Zig? | ||
| 104 | |||
| 105 | const std = @import("std"); | ||
| 106 | const builtin = @import("builtin"); | ||
| 107 | |||
| 108 | const testing = std.testing; | ||
| 109 | const assert = std.debug.assert; | ||
| 110 | const crypto = std.crypto; | ||
| 111 | const math = std.math; | ||
| 112 | const mem = std.mem; | ||
| 113 | const RndGen = std.rand.DefaultPrng; | ||
| 114 | const sha3 = crypto.hash.sha3; | ||
| 115 | |||
| 116 | // Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1. | ||
| 117 | const Q: i16 = 3329; | ||
| 118 | |||
| 119 | // Montgomery R | ||
| 120 | const R: i32 = 1 << 16; | ||
| 121 | |||
| 122 | // Parameter n, degree of polynomials. | ||
| 123 | const N: usize = 256; | ||
| 124 | |||
| 125 | // Size of "small" vectors used in encryption blinds. | ||
| 126 | const eta2: u8 = 2; | ||
| 127 | |||
| 128 | const 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 | |||
| 146 | pub const Kyber512 = Kyber(.{ | ||
| 147 | .name = "Kyber512", | ||
| 148 | .k = 2, | ||
| 149 | .eta1 = 3, | ||
| 150 | .du = 10, | ||
| 151 | .dv = 4, | ||
| 152 | }); | ||
| 153 | |||
| 154 | pub const Kyber768 = Kyber(.{ | ||
| 155 | .name = "Kyber768", | ||
| 156 | .k = 3, | ||
| 157 | .eta1 = 2, | ||
| 158 | .du = 10, | ||
| 159 | .dv = 4, | ||
| 160 | }); | ||
| 161 | |||
| 162 | pub const Kyber1024 = Kyber(.{ | ||
| 163 | .name = "Kyber1024", | ||
| 164 | .k = 4, | ||
| 165 | .eta1 = 2, | ||
| 166 | .du = 11, | ||
| 167 | .dv = 5, | ||
| 168 | }); | ||
| 169 | |||
| 170 | const modes = [_]type{ Kyber512, Kyber768, Kyber1024 }; | ||
| 171 | const h_length: usize = 32; | ||
| 172 | const inner_seed_length: usize = 32; | ||
| 173 | const common_encaps_seed_length: usize = 32; | ||
| 174 | const common_shared_key_size: usize = 32; | ||
| 175 | |||
| 176 | fn 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 | ||
| 491 | const r_mod_q: i32 = @rem(@as(i32, R), Q); | ||
| 492 | |||
| 493 | // R² mod q | ||
| 494 | const 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. | ||
| 497 | const zeta: i16 = 17; | ||
| 498 | |||
| 499 | // (128)⁻¹ R². Used in inverse NTT. | ||
| 500 | const 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. | ||
| 508 | const 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? | ||
| 519 | const 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 | |||
| 555 | test "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. | ||
| 591 | fn 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 | |||
| 599 | fn 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. | ||
| 604 | fn 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. | ||
| 610 | fn 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. | ||
| 617 | fn 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). | ||
| 626 | fn 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 | |||
| 658 | test "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¹⁶. | ||
| 670 | fn 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 | |||
| 676 | test "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. | ||
| 688 | fn 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 | |||
| 709 | test "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. | ||
| 722 | fn csubq(x: i16) i16 { | ||
| 723 | var r = x; | ||
| 724 | r -= Q; | ||
| 725 | r += (r >> 15) & Q; | ||
| 726 | return r; | ||
| 727 | } | ||
| 728 | |||
| 729 | test "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. | ||
| 742 | fn 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. | ||
| 761 | fn 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(). | ||
| 778 | const 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. | ||
| 1295 | fn 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 | ||
| 1425 | fn 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. | ||
| 1460 | fn 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. | ||
| 1465 | fn 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 | |||
| 1472 | test "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 | |||
| 1503 | test "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 | |||
| 1526 | test "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 | |||
| 1538 | test "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 | |||
| 1584 | test "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 | |||
| 1617 | test "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 | |||
| 1626 | test "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 | |||
| 1647 | test "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 | |||
| 1671 | const sha2 = crypto.hash.sha2; | ||
| 1672 | |||
| 1673 | test "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 | |||
| 1720 | const NistDRBG = struct { | ||
| 1721 | key: [32]u8, | ||
| 1722 | v: [16]u8, | ||
| 1723 | |||
| 1724 | fn incV(g: *NistDRBG) void { | ||
| 1725 | var j: usize = 15; | ||
| 1726 | while (j >= 0) : (j -= 1) { | ||
| 1727 | if (g.v[j] == 255) { | ||
| 1728 | g.v[j] = 0; | ||
| 1729 | } else { | ||
| 1730 | g.v[j] += 1; | ||
| 1731 | break; | ||
| 1732 | } | ||
| 1733 | } | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | // AES256_CTR_DRBG_Update(pd, &g.key, &g.v). | ||
| 1737 | fn update(g: *NistDRBG, pd: ?[48]u8) void { | ||
| 1738 | var buf: [48]u8 = undefined; | ||
| 1739 | const ctx = crypto.core.aes.Aes256.initEnc(g.key); | ||
| 1740 | var i: usize = 0; | ||
| 1741 | while (i < 3) : (i += 1) { | ||
| 1742 | g.incV(); | ||
| 1743 | var block: [16]u8 = undefined; | ||
| 1744 | ctx.encrypt(&block, &g.v); | ||
| 1745 | mem.copy(u8, buf[i * 16 .. (i + 1) * 16], &block); | ||
| 1746 | } | ||
| 1747 | if (pd) |p| { | ||
| 1748 | for (&buf, p) |*b, x| { | ||
| 1749 | b.* ^= x; | ||
| 1750 | } | ||
| 1751 | } | ||
| 1752 | mem.copy(u8, &g.key, buf[0..32]); | ||
| 1753 | mem.copy(u8, &g.v, buf[32..48]); | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | // randombytes. | ||
| 1757 | fn fill(g: *NistDRBG, out: []u8) void { | ||
| 1758 | var block: [16]u8 = undefined; | ||
| 1759 | var dst = out; | ||
| 1760 | |||
| 1761 | const ctx = crypto.core.aes.Aes256.initEnc(g.key); | ||
| 1762 | while (dst.len > 0) { | ||
| 1763 | g.incV(); | ||
| 1764 | ctx.encrypt(&block, &g.v); | ||
| 1765 | if (dst.len < 16) { | ||
| 1766 | mem.copy(u8, dst, block[0..dst.len]); | ||
| 1767 | break; | ||
| 1768 | } | ||
| 1769 | mem.copy(u8, dst, &block); | ||
| 1770 | dst = dst[16..dst.len]; | ||
| 1771 | } | ||
| 1772 | g.update(null); | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | fn init(seed: [48]u8) NistDRBG { | ||
| 1776 | var ret: NistDRBG = .{ .key = .{0} ** 32, .v = .{0} ** 16 }; | ||
| 1777 | ret.update(seed); | ||
| 1778 | return ret; | ||
| 1779 | } | ||
| 1780 | }; | ||
lib/std/crypto/sha3.zig+45-2| ... | @@ -18,6 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); | ... | @@ -18,6 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); |
| 18 | pub const Shake128 = Shake(128); | 18 | pub const Shake128 = Shake(128); |
| 19 | pub const Shake256 = Shake(256); | 19 | pub const Shake256 = Shake(256); |
| 20 | 20 | ||
| 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. | ||
| 24 | pub 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. | ||
| 31 | pub fn TurboShake256(comptime delim: ?u8) type { | ||
| 32 | return TurboShake(256, delim); | ||
| 33 | } | ||
| 34 | |||
| 21 | /// A generic Keccak hash function. | 35 | /// A generic Keccak hash function. |
| 22 | pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type { | 36 | pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type { |
| 23 | comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length | 37 | comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length |
| ... | @@ -76,9 +90,18 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, co | ... | @@ -76,9 +90,18 @@ pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, co |
| 76 | 90 | ||
| 77 | /// The SHAKE extendable output hash function. | 91 | /// The SHAKE extendable output hash function. |
| 78 | pub fn Shake(comptime security_level: u11) type { | 92 | pub 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/ | ||
| 98 | pub fn TurboShake(comptime security_level: u11, comptime delim: ?u8) type { | ||
| 99 | return ShakeLike(security_level, delim orelse 0x1f, 12); | ||
| 100 | } | ||
| 101 | |||
| 102 | fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { | ||
| 79 | const f = 1600; | 103 | const f = 1600; |
| 80 | const rounds = 24; | 104 | const State = KeccakState(f, security_level * 2, delim, rounds); |
| 81 | const State = KeccakState(f, security_level * 2, 0x1f, rounds); | ||
| 82 | 105 | ||
| 83 | return struct { | 106 | return struct { |
| 84 | const Self = @This(); | 107 | const Self = @This(); |
| ... | @@ -348,3 +371,23 @@ test "SHAKE-256 single" { | ... | @@ -348,3 +371,23 @@ test "SHAKE-256 single" { |
| 348 | Shake256.hash("hello123", &out, .{}); | 371 | Shake256.hash("hello123", &out, .{}); |
| 349 | try htest.assertEqual("ade612ba265f92de4a37", &out); | 372 | try htest.assertEqual("ade612ba265f92de4a37", &out); |
| 350 | } | 373 | } |
| 374 | |||
| 375 | test "TurboSHAKE-128" { | ||
| 376 | var out: [32]u8 = undefined; | ||
| 377 | TurboShake(128, 0x06).hash("\xff", &out, .{}); | ||
| 378 | try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out); | ||
| 379 | } | ||
| 380 | |||
| 381 | test "SHA-3 with streaming" { | ||
| 382 | var msg: [613]u8 = [613]u8{ 0x97, 0xd1, 0x2d, 0x1a, 0x16, 0x2d, 0x36, 0x4d, 0x20, 0x62, 0x19, 0x0b, 0x14, 0x93, 0xbb, 0xf8, 0x5b, 0xea, 0x04, 0xc2, 0x61, 0x8e, 0xd6, 0x08, 0x81, 0xa1, 0x1d, 0x73, 0x27, 0x48, 0xbf, 0xa4, 0xba, 0xb1, 0x9a, 0x48, 0x9c, 0xf9, 0x9b, 0xff, 0x34, 0x48, 0xa9, 0x75, 0xea, 0xc8, 0xa3, 0x48, 0x24, 0x9d, 0x75, 0x27, 0x48, 0xec, 0x03, 0xb0, 0xbb, 0xdf, 0x33, 0x90, 0xe3, 0x93, 0xed, 0x68, 0x24, 0x39, 0x12, 0xdf, 0xea, 0xee, 0x8c, 0x9f, 0x96, 0xde, 0x42, 0x46, 0x8c, 0x2b, 0x17, 0x83, 0x36, 0xfb, 0xf4, 0xf7, 0xff, 0x79, 0xb9, 0x45, 0x41, 0xc9, 0x56, 0x1a, 0x6b, 0x0c, 0xa4, 0x1a, 0xdd, 0x6b, 0x95, 0xe8, 0x03, 0x0f, 0x09, 0x29, 0x40, 0x1b, 0xea, 0x87, 0xfa, 0xb9, 0x18, 0xa9, 0x95, 0x07, 0x7c, 0x2f, 0x7c, 0x33, 0xfb, 0xc5, 0x11, 0x5e, 0x81, 0x0e, 0xbc, 0xae, 0xec, 0xb3, 0xe1, 0x4a, 0x26, 0x56, 0xe8, 0x5b, 0x11, 0x9d, 0x37, 0x06, 0x9b, 0x34, 0x31, 0x6e, 0xa3, 0xba, 0x41, 0xbc, 0x11, 0xd8, 0xc5, 0x15, 0xc9, 0x30, 0x2c, 0x9b, 0xb6, 0x71, 0xd8, 0x7c, 0xbc, 0x38, 0x2f, 0xd5, 0xbd, 0x30, 0x96, 0xd4, 0xa3, 0x00, 0x77, 0x9d, 0x55, 0x4a, 0x33, 0x53, 0xb6, 0xb3, 0x35, 0x1b, 0xae, 0xe5, 0xdc, 0x22, 0x23, 0x85, 0x95, 0x88, 0xf9, 0x3b, 0xbf, 0x74, 0x13, 0xaa, 0xcb, 0x0a, 0x60, 0x79, 0x13, 0x79, 0xc0, 0x4a, 0x02, 0xdb, 0x1c, 0xc9, 0xff, 0x60, 0x57, 0x9a, 0x70, 0x28, 0x58, 0x60, 0xbc, 0x57, 0x07, 0xc7, 0x47, 0x1a, 0x45, 0x71, 0x76, 0x94, 0xfb, 0x05, 0xad, 0xec, 0x12, 0x29, 0x5a, 0x44, 0x6a, 0x81, 0xd9, 0xc6, 0xf0, 0xb6, 0x9b, 0x97, 0x83, 0x69, 0xfb, 0xdc, 0x0d, 0x4a, 0x67, 0xbc, 0x72, 0xf5, 0x43, 0x5e, 0x9b, 0x13, 0xf2, 0xe4, 0x6d, 0x49, 0xdb, 0x76, 0xcb, 0x42, 0x6a, 0x3c, 0x9f, 0xa1, 0xfe, 0x5e, 0xca, 0x0a, 0xfc, 0xfa, 0x39, 0x27, 0xd1, 0x3c, 0xcb, 0x9a, 0xde, 0x4c, 0x6b, 0x09, 0x8b, 0x49, 0xfd, 0x1e, 0x3d, 0x5e, 0x67, 0x7c, 0x57, 0xad, 0x90, 0xcc, 0x46, 0x5f, 0x5c, 0xae, 0x6a, 0x9c, 0xb2, 0xcd, 0x2c, 0x89, 0x78, 0xcf, 0xf1, 0x49, 0x96, 0x55, 0x1e, 0x04, 0xef, 0x0e, 0x1c, 0xde, 0x6c, 0x96, 0x51, 0x00, 0xee, 0x9a, 0x1f, 0x8d, 0x61, 0xbc, 0xeb, 0xb1, 0xa6, 0xa5, 0x21, 0x8b, 0xa7, 0xf8, 0x25, 0x41, 0x48, 0x62, 0x5b, 0x01, 0x6c, 0x7c, 0x2a, 0xe8, 0xff, 0xf9, 0xf9, 0x1f, 0xe2, 0x79, 0x2e, 0xd1, 0xff, 0xa3, 0x2e, 0x1c, 0x3a, 0x1a, 0x5d, 0x2b, 0x7b, 0x87, 0x25, 0x22, 0xa4, 0x90, 0xea, 0x26, 0x9d, 0xdd, 0x13, 0x60, 0x4c, 0x10, 0x03, 0xf6, 0x99, 0xd3, 0x21, 0x0c, 0x69, 0xc6, 0xd8, 0xc8, 0x9e, 0x94, 0x89, 0x51, 0x21, 0xe3, 0x9a, 0xcd, 0xda, 0x54, 0x72, 0x64, 0xae, 0x94, 0x79, 0x36, 0x81, 0x44, 0x14, 0x6d, 0x3a, 0x0e, 0xa6, 0x30, 0xbf, 0x95, 0x99, 0xa6, 0xf5, 0x7f, 0x4f, 0xef, 0xc6, 0x71, 0x2f, 0x36, 0x13, 0x14, 0xa2, 0x9d, 0xc2, 0x0c, 0x0d, 0x4e, 0xc0, 0x02, 0xd3, 0x6f, 0xee, 0x98, 0x5e, 0x24, 0x31, 0x74, 0x11, 0x96, 0x6e, 0x43, 0x57, 0xe8, 0x8e, 0xa0, 0x8d, 0x3d, 0x79, 0x38, 0x20, 0xc2, 0x0f, 0xb4, 0x75, 0x99, 0x3b, 0xb1, 0xf0, 0xe8, 0xe1, 0xda, 0xf9, 0xd4, 0xe6, 0xd6, 0xf4, 0x8a, 0x32, 0x4a, 0x4a, 0x25, 0xa8, 0xd9, 0x60, 0xd6, 0x33, 0x31, 0x97, 0xb9, 0xb6, 0xed, 0x5f, 0xfc, 0x15, 0xbd, 0x13, 0xc0, 0x3a, 0x3f, 0x1f, 0x2d, 0x09, 0x1d, 0xeb, 0x69, 0x6a, 0xfe, 0xd7, 0x95, 0x3e, 0x8a, 0x4e, 0xe1, 0x6e, 0x61, 0xb2, 0x6c, 0xe3, 0x2b, 0x70, 0x60, 0x7e, 0x8c, 0xe4, 0xdd, 0x27, 0x30, 0x7e, 0x0d, 0xc7, 0xb7, 0x9a, 0x1a, 0x3c, 0xcc, 0xa7, 0x22, 0x77, 0x14, 0x05, 0x50, 0x57, 0x31, 0x1b, 0xc8, 0xbf, 0xce, 0x52, 0xaf, 0x9c, 0x8e, 0x10, 0x2e, 0xd2, 0x16, 0xb6, 0x6e, 0x43, 0x10, 0xaf, 0x8b, 0xde, 0x1d, 0x60, 0xb2, 0x7d, 0xe6, 0x2f, 0x08, 0x10, 0x12, 0x7e, 0xb4, 0x76, 0x45, 0xb6, 0xd8, 0x9b, 0x26, 0x40, 0xa1, 0x63, 0x5c, 0x7a, 0x2a, 0xb1, 0x8c, 0xd6, 0xa4, 0x6f, 0x5a, 0xae, 0x33, 0x7e, 0x6d, 0x71, 0xf5, 0xc8, 0x6d, 0x80, 0x1c, 0x35, 0xfc, 0x3f, 0xc1, 0xa6, 0xc6, 0x1a, 0x15, 0x04, 0x6d, 0x76, 0x38, 0x32, 0x95, 0xb2, 0x51, 0x1a, 0xe9, 0x3e, 0x89, 0x9f, 0x0c, 0x79 }; | ||
| 383 | var out: [Sha3_256.digest_length]u8 = undefined; | ||
| 384 | |||
| 385 | Sha3_256.hash(&msg, &out, .{}); | ||
| 386 | try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); | ||
| 387 | |||
| 388 | var h = Sha3_256.init(.{}); | ||
| 389 | h.update(msg[0..64]); | ||
| 390 | h.update(msg[64..613]); | ||
| 391 | h.final(&out); | ||
| 392 | try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); | ||
| 393 | } |
lib/std/crypto/tls/Client.zig+49-1| ... | @@ -88,11 +88,59 @@ pub const StreamInterface = struct { | ... | @@ -88,11 +88,59 @@ pub const StreamInterface = struct { |
| 88 | } | 88 | } |
| 89 | }; | 89 | }; |
| 90 | 90 | ||
| 91 | pub fn InitError(comptime Stream: type) type { | ||
| 92 | return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || error{ | ||
| 93 | InsufficientEntropy, | ||
| 94 | DiskQuota, | ||
| 95 | LockViolation, | ||
| 96 | NotOpenForWriting, | ||
| 97 | TlsAlert, | ||
| 98 | TlsUnexpectedMessage, | ||
| 99 | TlsIllegalParameter, | ||
| 100 | TlsDecryptFailure, | ||
| 101 | TlsRecordOverflow, | ||
| 102 | TlsBadRecordMac, | ||
| 103 | CertificateFieldHasInvalidLength, | ||
| 104 | CertificateHostMismatch, | ||
| 105 | CertificatePublicKeyInvalid, | ||
| 106 | CertificateExpired, | ||
| 107 | CertificateFieldHasWrongDataType, | ||
| 108 | CertificateIssuerMismatch, | ||
| 109 | CertificateNotYetValid, | ||
| 110 | CertificateSignatureAlgorithmMismatch, | ||
| 111 | CertificateSignatureAlgorithmUnsupported, | ||
| 112 | CertificateSignatureInvalid, | ||
| 113 | CertificateSignatureInvalidLength, | ||
| 114 | CertificateSignatureNamedCurveUnsupported, | ||
| 115 | CertificateSignatureUnsupportedBitCount, | ||
| 116 | TlsCertificateNotVerified, | ||
| 117 | TlsBadSignatureScheme, | ||
| 118 | TlsBadRsaSignatureBitCount, | ||
| 119 | InvalidEncoding, | ||
| 120 | IdentityElement, | ||
| 121 | SignatureVerificationFailed, | ||
| 122 | TlsDecryptError, | ||
| 123 | TlsConnectionTruncated, | ||
| 124 | TlsDecodeError, | ||
| 125 | UnsupportedCertificateVersion, | ||
| 126 | CertificateTimeInvalid, | ||
| 127 | CertificateHasUnrecognizedObjectId, | ||
| 128 | CertificateHasInvalidBitString, | ||
| 129 | MessageTooLong, | ||
| 130 | NegativeIntoUnsigned, | ||
| 131 | TargetTooSmall, | ||
| 132 | BufferTooSmall, | ||
| 133 | InvalidSignature, | ||
| 134 | NotSquare, | ||
| 135 | NonCanonical, | ||
| 136 | }; | ||
| 137 | } | ||
| 138 | |||
| 91 | /// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which | 139 | /// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which |
| 92 | /// must conform to `StreamInterface`. | 140 | /// must conform to `StreamInterface`. |
| 93 | /// | 141 | /// |
| 94 | /// `host` is only borrowed during this function call. | 142 | /// `host` is only borrowed during this function call. |
| 95 | pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) !Client { | 143 | pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client { |
| 96 | const host_len = @intCast(u16, host.len); | 144 | const host_len = @intCast(u16, host.len); |
| 97 | 145 | ||
| 98 | var random_buffer: [128]u8 = undefined; | 146 | var random_buffer: [128]u8 = undefined; |
lib/std/debug.zig+33| ... | @@ -635,6 +635,7 @@ pub const TTY = struct { | ... | @@ -635,6 +635,7 @@ pub const TTY = struct { |
| 635 | pub const Color = enum { | 635 | pub const Color = enum { |
| 636 | Red, | 636 | Red, |
| 637 | Green, | 637 | Green, |
| 638 | Yellow, | ||
| 638 | Cyan, | 639 | Cyan, |
| 639 | White, | 640 | White, |
| 640 | Dim, | 641 | Dim, |
| ... | @@ -659,6 +660,7 @@ pub const TTY = struct { | ... | @@ -659,6 +660,7 @@ pub const TTY = struct { |
| 659 | const color_string = switch (color) { | 660 | const color_string = switch (color) { |
| 660 | .Red => "\x1b[31;1m", | 661 | .Red => "\x1b[31;1m", |
| 661 | .Green => "\x1b[32;1m", | 662 | .Green => "\x1b[32;1m", |
| 663 | .Yellow => "\x1b[33;1m", | ||
| 662 | .Cyan => "\x1b[36;1m", | 664 | .Cyan => "\x1b[36;1m", |
| 663 | .White => "\x1b[37;1m", | 665 | .White => "\x1b[37;1m", |
| 664 | .Bold => "\x1b[1m", | 666 | .Bold => "\x1b[1m", |
| ... | @@ -671,6 +673,7 @@ pub const TTY = struct { | ... | @@ -671,6 +673,7 @@ pub const TTY = struct { |
| 671 | const attributes = switch (color) { | 673 | const attributes = switch (color) { |
| 672 | .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, | 674 | .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, |
| 673 | .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, | 675 | .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, |
| 676 | .Yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, | ||
| 674 | .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, | 677 | .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, |
| 675 | .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, | 678 | .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, |
| 676 | .Dim => windows.FOREGROUND_INTENSITY, | 679 | .Dim => windows.FOREGROUND_INTENSITY, |
| ... | @@ -682,6 +685,36 @@ pub const TTY = struct { | ... | @@ -682,6 +685,36 @@ pub const TTY = struct { |
| 682 | }, | 685 | }, |
| 683 | }; | 686 | }; |
| 684 | } | 687 | } |
| 688 | |||
| 689 | pub fn writeDEC(conf: Config, writer: anytype, codepoint: u8) !void { | ||
| 690 | const bytes = switch (conf) { | ||
| 691 | .no_color, .windows_api => switch (codepoint) { | ||
| 692 | 0x50...0x5e => @as(*const [1]u8, &codepoint), | ||
| 693 | 0x6a => "+", // ┘ | ||
| 694 | 0x6b => "+", // ┐ | ||
| 695 | 0x6c => "+", // ┌ | ||
| 696 | 0x6d => "+", // └ | ||
| 697 | 0x6e => "+", // ┼ | ||
| 698 | 0x71 => "-", // ─ | ||
| 699 | 0x74 => "+", // ├ | ||
| 700 | 0x75 => "+", // ┤ | ||
| 701 | 0x76 => "+", // ┴ | ||
| 702 | 0x77 => "+", // ┬ | ||
| 703 | 0x78 => "|", // │ | ||
| 704 | else => " ", // TODO | ||
| 705 | }, | ||
| 706 | .escape_codes => switch (codepoint) { | ||
| 707 | // Here we avoid writing the DEC beginning sequence and | ||
| 708 | // ending sequence in separate syscalls by putting the | ||
| 709 | // beginning and ending sequence into the same string | ||
| 710 | // literals, to prevent terminals ending up in bad states | ||
| 711 | // in case a crash happens between syscalls. | ||
| 712 | inline 0x50...0x7f => |x| "\x1B\x28\x30" ++ [1]u8{x} ++ "\x1B\x28\x42", | ||
| 713 | else => unreachable, | ||
| 714 | }, | ||
| 715 | }; | ||
| 716 | return writer.writeAll(bytes); | ||
| 717 | } | ||
| 685 | }; | 718 | }; |
| 686 | }; | 719 | }; |
| 687 | 720 |
lib/std/fifo.zig+27| ... | @@ -164,6 +164,17 @@ pub fn LinearFifo( | ... | @@ -164,6 +164,17 @@ pub fn LinearFifo( |
| 164 | return self.readableSliceMut(offset); | 164 | return self.readableSliceMut(offset); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | pub fn readableSliceOfLen(self: *Self, len: usize) []const T { | ||
| 168 | assert(len <= self.count); | ||
| 169 | const buf = self.readableSlice(0); | ||
| 170 | if (buf.len >= len) { | ||
| 171 | return buf[0..len]; | ||
| 172 | } else { | ||
| 173 | self.realign(); | ||
| 174 | return self.readableSlice(0)[0..len]; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 167 | /// Discard first `count` items in the fifo | 178 | /// Discard first `count` items in the fifo |
| 168 | pub fn discard(self: *Self, count: usize) void { | 179 | pub fn discard(self: *Self, count: usize) void { |
| 169 | assert(count <= self.count); | 180 | assert(count <= self.count); |
| ... | @@ -383,6 +394,22 @@ pub fn LinearFifo( | ... | @@ -383,6 +394,22 @@ pub fn LinearFifo( |
| 383 | self.discard(try dest_writer.write(self.readableSlice(0))); | 394 | self.discard(try dest_writer.write(self.readableSlice(0))); |
| 384 | } | 395 | } |
| 385 | } | 396 | } |
| 397 | |||
| 398 | pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { | ||
| 399 | if (self.head != 0) self.realign(); | ||
| 400 | assert(self.head == 0); | ||
| 401 | assert(self.count <= self.buf.len); | ||
| 402 | const allocator = self.allocator; | ||
| 403 | if (allocator.resize(self.buf, self.count)) { | ||
| 404 | const result = self.buf[0..self.count]; | ||
| 405 | self.* = Self.init(allocator); | ||
| 406 | return result; | ||
| 407 | } | ||
| 408 | const new_memory = try allocator.dupe(T, self.buf[0..self.count]); | ||
| 409 | allocator.free(self.buf); | ||
| 410 | self.* = Self.init(allocator); | ||
| 411 | return new_memory; | ||
| 412 | } | ||
| 386 | }; | 413 | }; |
| 387 | } | 414 | } |
| 388 | 415 |
lib/std/fmt.zig+22| ... | @@ -2555,6 +2555,21 @@ test "bytes.hex" { | ... | @@ -2555,6 +2555,21 @@ test "bytes.hex" { |
| 2555 | try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)}); | 2555 | try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)}); |
| 2556 | } | 2556 | } |
| 2557 | 2557 | ||
| 2558 | /// Encodes a sequence of bytes as hexadecimal digits. | ||
| 2559 | /// Returns an array containing the encoded bytes. | ||
| 2560 | pub fn bytesToHex(input: anytype, case: Case) [input.len * 2]u8 { | ||
| 2561 | if (input.len == 0) return [_]u8{}; | ||
| 2562 | comptime assert(@TypeOf(input[0]) == u8); // elements to encode must be unsigned bytes | ||
| 2563 | |||
| 2564 | const charset = "0123456789" ++ if (case == .upper) "ABCDEF" else "abcdef"; | ||
| 2565 | var result: [input.len * 2]u8 = undefined; | ||
| 2566 | for (input, 0..) |b, i| { | ||
| 2567 | result[i * 2 + 0] = charset[b >> 4]; | ||
| 2568 | result[i * 2 + 1] = charset[b & 15]; | ||
| 2569 | } | ||
| 2570 | return result; | ||
| 2571 | } | ||
| 2572 | |||
| 2558 | /// Decodes the sequence of bytes represented by the specified string of | 2573 | /// Decodes the sequence of bytes represented by the specified string of |
| 2559 | /// hexadecimal characters. | 2574 | /// hexadecimal characters. |
| 2560 | /// Returns a slice of the output buffer containing the decoded bytes. | 2575 | /// Returns a slice of the output buffer containing the decoded bytes. |
| ... | @@ -2575,6 +2590,13 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { | ... | @@ -2575,6 +2590,13 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { |
| 2575 | return out[0 .. in_i / 2]; | 2590 | return out[0 .. in_i / 2]; |
| 2576 | } | 2591 | } |
| 2577 | 2592 | ||
| 2593 | test "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 | |||
| 2578 | test "hexToBytes" { | 2600 | test "hexToBytes" { |
| 2579 | var buf: [32]u8 = undefined; | 2601 | var buf: [32]u8 = undefined; |
| 2580 | try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); | 2602 | try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); |
lib/std/fmt/parse_float.zig+1| ... | @@ -119,6 +119,7 @@ test "fmt.parseFloat hex.f16" { | ... | @@ -119,6 +119,7 @@ test "fmt.parseFloat hex.f16" { |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | test "fmt.parseFloat hex.f32" { | 121 | test "fmt.parseFloat hex.f32" { |
| 122 | try testing.expectError(error.InvalidCharacter, parseFloat(f32, "0x")); | ||
| 122 | try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0); | 123 | try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0); |
| 123 | try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5); | 124 | try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5); |
| 124 | try testing.expectEqual(try parseFloat(f32, "0x10p+10"), 16384.0); | 125 | try testing.expectEqual(try parseFloat(f32, "0x10p+10"), 16384.0); |
lib/std/fmt/parse_float/parse.zig+2| ... | @@ -107,6 +107,8 @@ fn parsePartialNumberBase(comptime T: type, stream: *FloatStream, negative: bool | ... | @@ -107,6 +107,8 @@ fn parsePartialNumberBase(comptime T: type, stream: *FloatStream, negative: bool |
| 107 | tryParseDigits(MantissaT, stream, &mantissa, info.base); | 107 | tryParseDigits(MantissaT, stream, &mantissa, info.base); |
| 108 | var int_end = stream.offsetTrue(); | 108 | var int_end = stream.offsetTrue(); |
| 109 | var n_digits = @intCast(isize, stream.offsetTrue()); | 109 | var n_digits = @intCast(isize, stream.offsetTrue()); |
| 110 | // the base being 16 implies a 0x prefix, which shouldn't be included in the digit count | ||
| 111 | if (info.base == 16) n_digits -= 2; | ||
| 110 | 112 | ||
| 111 | // handle dot with the following digits | 113 | // handle dot with the following digits |
| 112 | var exponent: i64 = 0; | 114 | var exponent: i64 = 0; |
lib/std/fs/file.zig+33-5| ... | @@ -1048,12 +1048,27 @@ pub const File = struct { | ... | @@ -1048,12 +1048,27 @@ pub const File = struct { |
| 1048 | /// Returns the number of bytes read. If the number read is smaller than the total bytes | 1048 | /// Returns the number of bytes read. If the number read is smaller than the total bytes |
| 1049 | /// from all the buffers, it means the file reached the end. Reaching the end of a file | 1049 | /// from all the buffers, it means the file reached the end. Reaching the end of a file |
| 1050 | /// is not an error condition. | 1050 | /// is not an error condition. |
| 1051 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 1051 | /// |
| 1052 | /// order to handle partial reads from the underlying OS layer. | 1052 | /// The `iovecs` parameter is mutable because: |
| 1053 | /// See https://github.com/ziglang/zig/issues/7699 | 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 | ||
| 1054 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { | 1061 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { |
| 1055 | if (iovecs.len == 0) return 0; | 1062 | if (iovecs.len == 0) return 0; |
| 1056 | 1063 | ||
| 1064 | // We use the address of this local variable for all zero-length | ||
| 1065 | // vectors so that the OS does not complain that we are giving it | ||
| 1066 | // addresses outside the application's address space. | ||
| 1067 | var garbage: [1]u8 = undefined; | ||
| 1068 | for (iovecs) |*v| { | ||
| 1069 | if (v.iov_len == 0) v.iov_base = &garbage; | ||
| 1070 | } | ||
| 1071 | |||
| 1057 | var i: usize = 0; | 1072 | var i: usize = 0; |
| 1058 | var off: usize = 0; | 1073 | var off: usize = 0; |
| 1059 | while (true) { | 1074 | while (true) { |
| ... | @@ -1181,13 +1196,26 @@ pub const File = struct { | ... | @@ -1181,13 +1196,26 @@ pub const File = struct { |
| 1181 | } | 1196 | } |
| 1182 | } | 1197 | } |
| 1183 | 1198 | ||
| 1184 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 1199 | /// The `iovecs` parameter is mutable because: |
| 1185 | /// order to handle partial writes from the underlying OS layer. | 1200 | /// * This function needs to mutate the fields in order to handle partial |
| 1201 | /// writes from the underlying OS layer. | ||
| 1202 | /// * The OS layer expects pointer addresses to be inside the application's address space | ||
| 1203 | /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer | ||
| 1204 | /// addresses when the length is zero. So this function modifies the iov_base fields | ||
| 1205 | /// when the length is zero. | ||
| 1186 | /// See https://github.com/ziglang/zig/issues/7699 | 1206 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1187 | /// See equivalent function: `std.net.Stream.writevAll`. | 1207 | /// See equivalent function: `std.net.Stream.writevAll`. |
| 1188 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { | 1208 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { |
| 1189 | if (iovecs.len == 0) return; | 1209 | if (iovecs.len == 0) return; |
| 1190 | 1210 | ||
| 1211 | // We use the address of this local variable for all zero-length | ||
| 1212 | // vectors so that the OS does not complain that we are giving it | ||
| 1213 | // addresses outside the application's address space. | ||
| 1214 | var garbage: [1]u8 = undefined; | ||
| 1215 | for (iovecs) |*v| { | ||
| 1216 | if (v.iov_len == 0) v.iov_base = &garbage; | ||
| 1217 | } | ||
| 1218 | |||
| 1191 | var i: usize = 0; | 1219 | var i: usize = 0; |
| 1192 | while (true) { | 1220 | while (true) { |
| 1193 | var amt = try self.writev(iovecs[i..]); | 1221 | var amt = try self.writev(iovecs[i..]); |
lib/std/fs/test.zig+22-8| ... | @@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" { | ... | @@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" { |
| 1124 | test "open file with exclusive nonblocking lock twice (absolute paths)" { | 1124 | test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 1125 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 1125 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 1126 | 1126 | ||
| 1127 | const allocator = testing.allocator; | 1127 | var random_bytes: [12]u8 = undefined; |
| 1128 | std.crypto.random.bytes(&random_bytes); | ||
| 1128 | 1129 | ||
| 1129 | const cwd = try std.process.getCwdAlloc(allocator); | 1130 | var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined; |
| 1130 | defer allocator.free(cwd); | 1131 | _ = fs.base64_encoder.encode(&random_b64, &random_bytes); |
| 1131 | const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" }; | ||
| 1132 | const filename = try fs.path.resolve(allocator, &file_paths); | ||
| 1133 | defer allocator.free(filename); | ||
| 1134 | 1132 | ||
| 1135 | const file1 = try fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); | 1133 | const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt"; |
| 1136 | 1134 | ||
| 1137 | const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); | 1135 | const gpa = testing.allocator; |
| 1136 | |||
| 1137 | const cwd = try std.process.getCwdAlloc(gpa); | ||
| 1138 | defer gpa.free(cwd); | ||
| 1139 | |||
| 1140 | const filename = try fs.path.resolve(gpa, &[_][]const u8{ cwd, sub_path }); | ||
| 1141 | defer gpa.free(filename); | ||
| 1142 | |||
| 1143 | const file1 = try fs.createFileAbsolute(filename, .{ | ||
| 1144 | .lock = .Exclusive, | ||
| 1145 | .lock_nonblocking = true, | ||
| 1146 | }); | ||
| 1147 | |||
| 1148 | const file2 = fs.createFileAbsolute(filename, .{ | ||
| 1149 | .lock = .Exclusive, | ||
| 1150 | .lock_nonblocking = true, | ||
| 1151 | }); | ||
| 1138 | file1.close(); | 1152 | file1.close(); |
| 1139 | try testing.expectError(error.WouldBlock, file2); | 1153 | try testing.expectError(error.WouldBlock, file2); |
| 1140 | 1154 |
lib/std/heap.zig+1| ... | @@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig" | ... | @@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig" |
| 19 | pub const WasmAllocator = @import("heap/WasmAllocator.zig"); | 19 | pub const WasmAllocator = @import("heap/WasmAllocator.zig"); |
| 20 | pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); | 20 | pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); |
| 21 | pub const PageAllocator = @import("heap/PageAllocator.zig"); | 21 | pub const PageAllocator = @import("heap/PageAllocator.zig"); |
| 22 | pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig"); | ||
| 22 | 23 | ||
| 23 | const memory_pool = @import("heap/memory_pool.zig"); | 24 | const memory_pool = @import("heap/memory_pool.zig"); |
| 24 | pub const MemoryPool = memory_pool.MemoryPool; | 25 | pub 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 | |||
| 3 | child_allocator: Allocator, | ||
| 4 | mutex: std.Thread.Mutex = .{}, | ||
| 5 | |||
| 6 | pub 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 | |||
| 17 | fn 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 | |||
| 25 | fn 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 | |||
| 34 | fn 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 | |||
| 43 | const std = @import("../std.zig"); | ||
| 44 | const ThreadSafeAllocator = @This(); | ||
| 45 | const Allocator = std.mem.Allocator; | ||
lib/std/http.zig+15| ... | @@ -248,9 +248,24 @@ pub const Status = enum(u10) { | ... | @@ -248,9 +248,24 @@ pub const Status = enum(u10) { |
| 248 | 248 | ||
| 249 | pub const TransferEncoding = enum { | 249 | pub const TransferEncoding = enum { |
| 250 | chunked, | 250 | chunked, |
| 251 | // compression is intentionally omitted here, as std.http.Client stores it as content-encoding | ||
| 252 | }; | ||
| 253 | |||
| 254 | pub const ContentEncoding = enum { | ||
| 251 | compress, | 255 | compress, |
| 252 | deflate, | 256 | deflate, |
| 253 | gzip, | 257 | gzip, |
| 258 | zstd, | ||
| 259 | }; | ||
| 260 | |||
| 261 | pub const Connection = enum { | ||
| 262 | keep_alive, | ||
| 263 | close, | ||
| 264 | }; | ||
| 265 | |||
| 266 | pub const CustomHeader = struct { | ||
| 267 | name: []const u8, | ||
| 268 | value: []const u8, | ||
| 254 | }; | 269 | }; |
| 255 | 270 | ||
| 256 | const std = @import("std.zig"); | 271 | const std = @import("std.zig"); |
lib/std/http/Client.zig+251-785| ... | @@ -13,6 +13,12 @@ const Uri = std.Uri; | ... | @@ -13,6 +13,12 @@ const Uri = std.Uri; |
| 13 | const Allocator = std.mem.Allocator; | 13 | const Allocator = std.mem.Allocator; |
| 14 | const testing = std.testing; | 14 | const testing = std.testing; |
| 15 | 15 | ||
| 16 | pub const Request = @import("Client/Request.zig"); | ||
| 17 | pub const Response = @import("Client/Response.zig"); | ||
| 18 | |||
| 19 | pub const default_connection_pool_size = 32; | ||
| 20 | const connection_pool_size = std.options.http_connection_pool_size; | ||
| 21 | |||
| 16 | /// Used for tcpConnectToHost and storing HTTP headers when an externally | 22 | /// Used for tcpConnectToHost and storing HTTP headers when an externally |
| 17 | /// managed buffer is not provided. | 23 | /// managed buffer is not provided. |
| 18 | allocator: Allocator, | 24 | allocator: Allocator, |
| ... | @@ -21,853 +27,256 @@ ca_bundle: std.crypto.Certificate.Bundle = .{}, | ... | @@ -21,853 +27,256 @@ ca_bundle: std.crypto.Certificate.Bundle = .{}, |
| 21 | /// it will first rescan the system for root certificates. | 27 | /// it will first rescan the system for root certificates. |
| 22 | next_https_rescan_certs: bool = true, | 28 | next_https_rescan_certs: bool = true, |
| 23 | 29 | ||
| 24 | pub const Connection = struct { | 30 | connection_pool: ConnectionPool = .{}, |
| 25 | stream: net.Stream, | ||
| 26 | /// undefined unless protocol is tls. | ||
| 27 | tls_client: std.crypto.tls.Client, | ||
| 28 | protocol: Protocol, | ||
| 29 | 31 | ||
| 30 | pub const Protocol = enum { plain, tls }; | 32 | pub const ConnectionPool = struct { |
| 33 | pub const Criteria = struct { | ||
| 34 | host: []const u8, | ||
| 35 | port: u16, | ||
| 36 | is_tls: bool, | ||
| 37 | }; | ||
| 31 | 38 | ||
| 32 | pub fn read(conn: *Connection, buffer: []u8) !usize { | 39 | const Queue = std.TailQueue(Connection); |
| 33 | switch (conn.protocol) { | 40 | pub const Node = Queue.Node; |
| 34 | .plain => return conn.stream.read(buffer), | 41 | |
| 35 | .tls => return conn.tls_client.read(conn.stream, buffer), | 42 | mutex: std.Thread.Mutex = .{}, |
| 43 | used: Queue = .{}, | ||
| 44 | free: Queue = .{}, | ||
| 45 | free_len: usize = 0, | ||
| 46 | free_size: usize = default_connection_pool_size, | ||
| 47 | |||
| 48 | /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. | ||
| 49 | /// If no connection is found, null is returned. | ||
| 50 | pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Node { | ||
| 51 | pool.mutex.lock(); | ||
| 52 | defer pool.mutex.unlock(); | ||
| 53 | |||
| 54 | var next = pool.free.last; | ||
| 55 | while (next) |node| : (next = node.prev) { | ||
| 56 | if ((node.data.protocol == .tls) != criteria.is_tls) continue; | ||
| 57 | if (node.data.port != criteria.port) continue; | ||
| 58 | if (std.mem.eql(u8, node.data.host, criteria.host)) continue; | ||
| 59 | |||
| 60 | pool.acquireUnsafe(node); | ||
| 61 | return node; | ||
| 36 | } | 62 | } |
| 37 | } | ||
| 38 | 63 | ||
| 39 | pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize { | 64 | return null; |
| 40 | switch (conn.protocol) { | ||
| 41 | .plain => return conn.stream.readAtLeast(buffer, len), | ||
| 42 | .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len), | ||
| 43 | } | ||
| 44 | } | 65 | } |
| 45 | 66 | ||
| 46 | pub fn writeAll(conn: *Connection, buffer: []const u8) !void { | 67 | /// Acquires an existing connection from the connection pool. This function is not threadsafe. |
| 47 | switch (conn.protocol) { | 68 | pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void { |
| 48 | .plain => return conn.stream.writeAll(buffer), | 69 | pool.free.remove(node); |
| 49 | .tls => return conn.tls_client.writeAll(conn.stream, buffer), | 70 | pool.free_len -= 1; |
| 50 | } | 71 | |
| 72 | pool.used.append(node); | ||
| 51 | } | 73 | } |
| 52 | 74 | ||
| 53 | pub fn write(conn: *Connection, buffer: []const u8) !usize { | 75 | /// Acquires an existing connection from the connection pool. This function is threadsafe. |
| 54 | switch (conn.protocol) { | 76 | pub fn acquire(pool: *ConnectionPool, node: *Node) void { |
| 55 | .plain => return conn.stream.write(buffer), | 77 | pool.mutex.lock(); |
| 56 | .tls => return conn.tls_client.write(conn.stream, buffer), | 78 | defer pool.mutex.unlock(); |
| 57 | } | 79 | |
| 80 | return pool.acquireUnsafe(node); | ||
| 58 | } | 81 | } |
| 59 | }; | ||
| 60 | 82 | ||
| 61 | /// TODO: emit error.UnexpectedEndOfStream or something like that when the read | 83 | /// Tries to release a connection back to the connection pool. This function is threadsafe. |
| 62 | /// data does not match the content length. This is necessary since HTTPS disables | 84 | /// If the connection is marked as closing, it will be closed instead. |
| 63 | /// close_notify protection on underlying TLS streams. | 85 | pub fn release(pool: *ConnectionPool, client: *Client, node: *Node) void { |
| 64 | pub const Request = struct { | 86 | pool.mutex.lock(); |
| 65 | client: *Client, | 87 | defer pool.mutex.unlock(); |
| 66 | connection: Connection, | ||
| 67 | redirects_left: u32, | ||
| 68 | response: Response, | ||
| 69 | /// These are stored in Request so that they are available when following | ||
| 70 | /// redirects. | ||
| 71 | headers: Headers, | ||
| 72 | |||
| 73 | pub const Response = struct { | ||
| 74 | headers: Response.Headers, | ||
| 75 | state: State, | ||
| 76 | header_bytes_owned: bool, | ||
| 77 | /// This could either be a fixed buffer provided by the API user or it | ||
| 78 | /// could be our own array list. | ||
| 79 | header_bytes: std.ArrayListUnmanaged(u8), | ||
| 80 | max_header_bytes: usize, | ||
| 81 | next_chunk_length: u64, | ||
| 82 | |||
| 83 | pub const Headers = struct { | ||
| 84 | status: http.Status, | ||
| 85 | version: http.Version, | ||
| 86 | location: ?[]const u8 = null, | ||
| 87 | content_length: ?u64 = null, | ||
| 88 | transfer_encoding: ?http.TransferEncoding = null, | ||
| 89 | |||
| 90 | pub fn parse(bytes: []const u8) !Response.Headers { | ||
| 91 | var it = mem.split(u8, bytes[0 .. bytes.len - 4], "\r\n"); | ||
| 92 | |||
| 93 | const first_line = it.first(); | ||
| 94 | if (first_line.len < 12) | ||
| 95 | return error.ShortHttpStatusLine; | ||
| 96 | |||
| 97 | const version: http.Version = switch (int64(first_line[0..8])) { | ||
| 98 | int64("HTTP/1.0") => .@"HTTP/1.0", | ||
| 99 | int64("HTTP/1.1") => .@"HTTP/1.1", | ||
| 100 | else => return error.BadHttpVersion, | ||
| 101 | }; | ||
| 102 | if (first_line[8] != ' ') return error.HttpHeadersInvalid; | ||
| 103 | const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*)); | ||
| 104 | |||
| 105 | var headers: Response.Headers = .{ | ||
| 106 | .version = version, | ||
| 107 | .status = status, | ||
| 108 | }; | ||
| 109 | |||
| 110 | while (it.next()) |line| { | ||
| 111 | if (line.len == 0) return error.HttpHeadersInvalid; | ||
| 112 | switch (line[0]) { | ||
| 113 | ' ', '\t' => return error.HttpHeaderContinuationsUnsupported, | ||
| 114 | else => {}, | ||
| 115 | } | ||
| 116 | var line_it = mem.split(u8, line, ": "); | ||
| 117 | const header_name = line_it.first(); | ||
| 118 | const header_value = line_it.rest(); | ||
| 119 | if (std.ascii.eqlIgnoreCase(header_name, "location")) { | ||
| 120 | if (headers.location != null) return error.HttpHeadersInvalid; | ||
| 121 | headers.location = header_value; | ||
| 122 | } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) { | ||
| 123 | if (headers.content_length != null) return error.HttpHeadersInvalid; | ||
| 124 | headers.content_length = try std.fmt.parseInt(u64, header_value, 10); | ||
| 125 | } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) { | ||
| 126 | if (headers.transfer_encoding != null) return error.HttpHeadersInvalid; | ||
| 127 | headers.transfer_encoding = std.meta.stringToEnum(http.TransferEncoding, header_value) orelse | ||
| 128 | return error.HttpTransferEncodingUnsupported; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | return headers; | ||
| 133 | } | ||
| 134 | |||
| 135 | test "parse headers" { | ||
| 136 | const example = | ||
| 137 | "HTTP/1.1 301 Moved Permanently\r\n" ++ | ||
| 138 | "Location: https://www.example.com/\r\n" ++ | ||
| 139 | "Content-Type: text/html; charset=UTF-8\r\n" ++ | ||
| 140 | "Content-Length: 220\r\n\r\n"; | ||
| 141 | const parsed = try Response.Headers.parse(example); | ||
| 142 | try testing.expectEqual(http.Version.@"HTTP/1.1", parsed.version); | ||
| 143 | try testing.expectEqual(http.Status.moved_permanently, parsed.status); | ||
| 144 | try testing.expectEqualStrings("https://www.example.com/", parsed.location orelse | ||
| 145 | return error.TestFailed); | ||
| 146 | try testing.expectEqual(@as(?u64, 220), parsed.content_length); | ||
| 147 | } | ||
| 148 | |||
| 149 | test "header continuation" { | ||
| 150 | const example = | ||
| 151 | "HTTP/1.0 200 OK\r\n" ++ | ||
| 152 | "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++ | ||
| 153 | "Content-Length: 220\r\n\r\n"; | ||
| 154 | try testing.expectError( | ||
| 155 | error.HttpHeaderContinuationsUnsupported, | ||
| 156 | Response.Headers.parse(example), | ||
| 157 | ); | ||
| 158 | } | ||
| 159 | |||
| 160 | test "extra content length" { | ||
| 161 | const example = | ||
| 162 | "HTTP/1.0 200 OK\r\n" ++ | ||
| 163 | "Content-Length: 220\r\n" ++ | ||
| 164 | "Content-Type: text/html; charset=UTF-8\r\n" ++ | ||
| 165 | "content-length: 220\r\n\r\n"; | ||
| 166 | try testing.expectError( | ||
| 167 | error.HttpHeadersInvalid, | ||
| 168 | Response.Headers.parse(example), | ||
| 169 | ); | ||
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | pub const State = enum { | ||
| 174 | /// Begin header parsing states. | ||
| 175 | invalid, | ||
| 176 | start, | ||
| 177 | seen_r, | ||
| 178 | seen_rn, | ||
| 179 | seen_rnr, | ||
| 180 | finished, | ||
| 181 | /// Begin transfer-encoding: chunked parsing states. | ||
| 182 | chunk_size_prefix_r, | ||
| 183 | chunk_size_prefix_n, | ||
| 184 | chunk_size, | ||
| 185 | chunk_r, | ||
| 186 | chunk_data, | ||
| 187 | |||
| 188 | pub fn zeroMeansEnd(state: State) bool { | ||
| 189 | return switch (state) { | ||
| 190 | .finished, .chunk_data => true, | ||
| 191 | else => false, | ||
| 192 | }; | ||
| 193 | } | ||
| 194 | }; | ||
| 195 | |||
| 196 | pub fn initDynamic(max: usize) Response { | ||
| 197 | return .{ | ||
| 198 | .state = .start, | ||
| 199 | .headers = undefined, | ||
| 200 | .header_bytes = .{}, | ||
| 201 | .max_header_bytes = max, | ||
| 202 | .header_bytes_owned = true, | ||
| 203 | .next_chunk_length = undefined, | ||
| 204 | }; | ||
| 205 | } | ||
| 206 | 88 | ||
| 207 | pub fn initStatic(buf: []u8) Response { | 89 | pool.used.remove(node); |
| 208 | return .{ | ||
| 209 | .state = .start, | ||
| 210 | .headers = undefined, | ||
| 211 | .header_bytes = .{ .items = buf[0..0], .capacity = buf.len }, | ||
| 212 | .max_header_bytes = buf.len, | ||
| 213 | .header_bytes_owned = false, | ||
| 214 | .next_chunk_length = undefined, | ||
| 215 | }; | ||
| 216 | } | ||
| 217 | 90 | ||
| 218 | /// Returns how many bytes are part of HTTP headers. Always less than or | 91 | if (node.data.closing) { |
| 219 | /// equal to bytes.len. If the amount returned is less than bytes.len, it | 92 | node.data.close(client); |
| 220 | /// means the headers ended and the first byte after the double \r\n\r\n is | ||
| 221 | /// located at `bytes[result]`. | ||
| 222 | pub fn findHeadersEnd(r: *Response, bytes: []const u8) usize { | ||
| 223 | var index: usize = 0; | ||
| 224 | |||
| 225 | // TODO: https://github.com/ziglang/zig/issues/8220 | ||
| 226 | state: while (true) { | ||
| 227 | switch (r.state) { | ||
| 228 | .invalid => unreachable, | ||
| 229 | .finished => unreachable, | ||
| 230 | .start => while (true) { | ||
| 231 | switch (bytes.len - index) { | ||
| 232 | 0 => return index, | ||
| 233 | 1 => { | ||
| 234 | if (bytes[index] == '\r') | ||
| 235 | r.state = .seen_r; | ||
| 236 | return index + 1; | ||
| 237 | }, | ||
| 238 | 2 => { | ||
| 239 | if (int16(bytes[index..][0..2]) == int16("\r\n")) { | ||
| 240 | r.state = .seen_rn; | ||
| 241 | } else if (bytes[index + 1] == '\r') { | ||
| 242 | r.state = .seen_r; | ||
| 243 | } | ||
| 244 | return index + 2; | ||
| 245 | }, | ||
| 246 | 3 => { | ||
| 247 | if (int16(bytes[index..][0..2]) == int16("\r\n") and | ||
| 248 | bytes[index + 2] == '\r') | ||
| 249 | { | ||
| 250 | r.state = .seen_rnr; | ||
| 251 | } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n")) { | ||
| 252 | r.state = .seen_rn; | ||
| 253 | } else if (bytes[index + 2] == '\r') { | ||
| 254 | r.state = .seen_r; | ||
| 255 | } | ||
| 256 | return index + 3; | ||
| 257 | }, | ||
| 258 | 4...15 => { | ||
| 259 | if (int32(bytes[index..][0..4]) == int32("\r\n\r\n")) { | ||
| 260 | r.state = .finished; | ||
| 261 | return index + 4; | ||
| 262 | } else if (int16(bytes[index + 1 ..][0..2]) == int16("\r\n") and | ||
| 263 | bytes[index + 3] == '\r') | ||
| 264 | { | ||
| 265 | r.state = .seen_rnr; | ||
| 266 | index += 4; | ||
| 267 | continue :state; | ||
| 268 | } else if (int16(bytes[index + 2 ..][0..2]) == int16("\r\n")) { | ||
| 269 | r.state = .seen_rn; | ||
| 270 | index += 4; | ||
| 271 | continue :state; | ||
| 272 | } else if (bytes[index + 3] == '\r') { | ||
| 273 | r.state = .seen_r; | ||
| 274 | index += 4; | ||
| 275 | continue :state; | ||
| 276 | } | ||
| 277 | index += 4; | ||
| 278 | continue; | ||
| 279 | }, | ||
| 280 | else => { | ||
| 281 | const chunk = bytes[index..][0..16]; | ||
| 282 | const v: @Vector(16, u8) = chunk.*; | ||
| 283 | const matches_r = v == @splat(16, @as(u8, '\r')); | ||
| 284 | const iota = std.simd.iota(u8, 16); | ||
| 285 | const default = @splat(16, @as(u8, 16)); | ||
| 286 | const sub_index = @reduce(.Min, @select(u8, matches_r, iota, default)); | ||
| 287 | switch (sub_index) { | ||
| 288 | 0...12 => { | ||
| 289 | index += sub_index + 4; | ||
| 290 | if (int32(chunk[sub_index..][0..4]) == int32("\r\n\r\n")) { | ||
| 291 | r.state = .finished; | ||
| 292 | return index; | ||
| 293 | } | ||
| 294 | continue; | ||
| 295 | }, | ||
| 296 | 13 => { | ||
| 297 | index += 16; | ||
| 298 | if (int16(chunk[14..][0..2]) == int16("\n\r")) { | ||
| 299 | r.state = .seen_rnr; | ||
| 300 | continue :state; | ||
| 301 | } | ||
| 302 | continue; | ||
| 303 | }, | ||
| 304 | 14 => { | ||
| 305 | index += 16; | ||
| 306 | if (chunk[15] == '\n') { | ||
| 307 | r.state = .seen_rn; | ||
| 308 | continue :state; | ||
| 309 | } | ||
| 310 | continue; | ||
| 311 | }, | ||
| 312 | 15 => { | ||
| 313 | r.state = .seen_r; | ||
| 314 | index += 16; | ||
| 315 | continue :state; | ||
| 316 | }, | ||
| 317 | 16 => { | ||
| 318 | index += 16; | ||
| 319 | continue; | ||
| 320 | }, | ||
| 321 | else => unreachable, | ||
| 322 | } | ||
| 323 | }, | ||
| 324 | } | ||
| 325 | }, | ||
| 326 | |||
| 327 | .seen_r => switch (bytes.len - index) { | ||
| 328 | 0 => return index, | ||
| 329 | 1 => { | ||
| 330 | switch (bytes[index]) { | ||
| 331 | '\n' => r.state = .seen_rn, | ||
| 332 | '\r' => r.state = .seen_r, | ||
| 333 | else => r.state = .start, | ||
| 334 | } | ||
| 335 | return index + 1; | ||
| 336 | }, | ||
| 337 | 2 => { | ||
| 338 | if (int16(bytes[index..][0..2]) == int16("\n\r")) { | ||
| 339 | r.state = .seen_rnr; | ||
| 340 | return index + 2; | ||
| 341 | } | ||
| 342 | r.state = .start; | ||
| 343 | return index + 2; | ||
| 344 | }, | ||
| 345 | else => { | ||
| 346 | if (int16(bytes[index..][0..2]) == int16("\n\r") and | ||
| 347 | bytes[index + 2] == '\n') | ||
| 348 | { | ||
| 349 | r.state = .finished; | ||
| 350 | return index + 3; | ||
| 351 | } | ||
| 352 | index += 3; | ||
| 353 | r.state = .start; | ||
| 354 | continue :state; | ||
| 355 | }, | ||
| 356 | }, | ||
| 357 | .seen_rn => switch (bytes.len - index) { | ||
| 358 | 0 => return index, | ||
| 359 | 1 => { | ||
| 360 | switch (bytes[index]) { | ||
| 361 | '\r' => r.state = .seen_rnr, | ||
| 362 | else => r.state = .start, | ||
| 363 | } | ||
| 364 | return index + 1; | ||
| 365 | }, | ||
| 366 | else => { | ||
| 367 | if (int16(bytes[index..][0..2]) == int16("\r\n")) { | ||
| 368 | r.state = .finished; | ||
| 369 | return index + 2; | ||
| 370 | } | ||
| 371 | index += 2; | ||
| 372 | r.state = .start; | ||
| 373 | continue :state; | ||
| 374 | }, | ||
| 375 | }, | ||
| 376 | .seen_rnr => switch (bytes.len - index) { | ||
| 377 | 0 => return index, | ||
| 378 | else => { | ||
| 379 | if (bytes[index] == '\n') { | ||
| 380 | r.state = .finished; | ||
| 381 | return index + 1; | ||
| 382 | } | ||
| 383 | index += 1; | ||
| 384 | r.state = .start; | ||
| 385 | continue :state; | ||
| 386 | }, | ||
| 387 | }, | ||
| 388 | .chunk_size_prefix_r => unreachable, | ||
| 389 | .chunk_size_prefix_n => unreachable, | ||
| 390 | .chunk_size => unreachable, | ||
| 391 | .chunk_r => unreachable, | ||
| 392 | .chunk_data => unreachable, | ||
| 393 | } | ||
| 394 | |||
| 395 | return index; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | 93 | ||
| 399 | pub fn findChunkedLen(r: *Response, bytes: []const u8) usize { | 94 | return client.allocator.destroy(node); |
| 400 | var i: usize = 0; | ||
| 401 | if (r.state == .chunk_size) { | ||
| 402 | while (i < bytes.len) : (i += 1) { | ||
| 403 | const digit = switch (bytes[i]) { | ||
| 404 | '0'...'9' => |b| b - '0', | ||
| 405 | 'A'...'Z' => |b| b - 'A' + 10, | ||
| 406 | 'a'...'z' => |b| b - 'a' + 10, | ||
| 407 | '\r' => { | ||
| 408 | r.state = .chunk_r; | ||
| 409 | i += 1; | ||
| 410 | break; | ||
| 411 | }, | ||
| 412 | else => { | ||
| 413 | r.state = .invalid; | ||
| 414 | return i; | ||
| 415 | }, | ||
| 416 | }; | ||
| 417 | const mul = @mulWithOverflow(r.next_chunk_length, 16); | ||
| 418 | if (mul[1] != 0) { | ||
| 419 | r.state = .invalid; | ||
| 420 | return i; | ||
| 421 | } | ||
| 422 | const add = @addWithOverflow(mul[0], digit); | ||
| 423 | if (add[1] != 0) { | ||
| 424 | r.state = .invalid; | ||
| 425 | return i; | ||
| 426 | } | ||
| 427 | r.next_chunk_length = add[0]; | ||
| 428 | } else { | ||
| 429 | return i; | ||
| 430 | } | ||
| 431 | } | ||
| 432 | assert(r.state == .chunk_r); | ||
| 433 | if (i == bytes.len) return i; | ||
| 434 | |||
| 435 | if (bytes[i] == '\n') { | ||
| 436 | r.state = .chunk_data; | ||
| 437 | return i + 1; | ||
| 438 | } else { | ||
| 439 | r.state = .invalid; | ||
| 440 | return i; | ||
| 441 | } | ||
| 442 | } | 95 | } |
| 443 | 96 | ||
| 444 | fn parseInt3(nnn: @Vector(3, u8)) u10 { | 97 | if (pool.free_len + 1 >= pool.free_size) { |
| 445 | const zero: @Vector(3, u8) = .{ '0', '0', '0' }; | 98 | const popped = pool.free.popFirst() orelse unreachable; |
| 446 | const mmm: @Vector(3, u10) = .{ 100, 10, 1 }; | ||
| 447 | return @reduce(.Add, @as(@Vector(3, u10), nnn -% zero) *% mmm); | ||
| 448 | } | ||
| 449 | 99 | ||
| 450 | test parseInt3 { | 100 | popped.data.close(client); |
| 451 | const expectEqual = std.testing.expectEqual; | ||
| 452 | try expectEqual(@as(u10, 0), parseInt3("000".*)); | ||
| 453 | try expectEqual(@as(u10, 418), parseInt3("418".*)); | ||
| 454 | try expectEqual(@as(u10, 999), parseInt3("999".*)); | ||
| 455 | } | ||
| 456 | 101 | ||
| 457 | test "find headers end basic" { | 102 | return client.allocator.destroy(popped); |
| 458 | var buffer: [1]u8 = undefined; | ||
| 459 | var r = Response.initStatic(&buffer); | ||
| 460 | try testing.expectEqual(@as(usize, 10), r.findHeadersEnd("HTTP/1.1 4")); | ||
| 461 | try testing.expectEqual(@as(usize, 2), r.findHeadersEnd("18")); | ||
| 462 | try testing.expectEqual(@as(usize, 8), r.findHeadersEnd(" lol\r\n\r\nblah blah")); | ||
| 463 | } | 103 | } |
| 464 | 104 | ||
| 465 | test "find headers end vectorized" { | 105 | pool.free.append(node); |
| 466 | var buffer: [1]u8 = undefined; | 106 | pool.free_len += 1; |
| 467 | var r = Response.initStatic(&buffer); | 107 | } |
| 468 | const example = | ||
| 469 | "HTTP/1.1 301 Moved Permanently\r\n" ++ | ||
| 470 | "Location: https://www.example.com/\r\n" ++ | ||
| 471 | "Content-Type: text/html; charset=UTF-8\r\n" ++ | ||
| 472 | "Content-Length: 220\r\n" ++ | ||
| 473 | "\r\ncontent"; | ||
| 474 | try testing.expectEqual(@as(usize, 131), r.findHeadersEnd(example)); | ||
| 475 | } | ||
| 476 | 108 | ||
| 477 | test "find headers end bug" { | 109 | /// Adds a newly created node to the pool of used connections. This function is threadsafe. |
| 478 | var buffer: [1]u8 = undefined; | 110 | pub fn addUsed(pool: *ConnectionPool, node: *Node) void { |
| 479 | var r = Response.initStatic(&buffer); | 111 | pool.mutex.lock(); |
| 480 | const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; | 112 | defer pool.mutex.unlock(); |
| 481 | const example = | ||
| 482 | "HTTP/1.1 200 OK\r\n" ++ | ||
| 483 | "Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++ | ||
| 484 | "content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++ | ||
| 485 | "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++ | ||
| 486 | "Content-Type: application/x-gzip\r\n" ++ | ||
| 487 | "ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++ | ||
| 488 | "Strict-Transport-Security: max-age=31536000\r\n" ++ | ||
| 489 | "Vary: Authorization,Accept-Encoding,Origin\r\n" ++ | ||
| 490 | "X-Content-Type-Options: nosniff\r\n" ++ | ||
| 491 | "X-Frame-Options: deny\r\n" ++ | ||
| 492 | "X-XSS-Protection: 1; mode=block\r\n" ++ | ||
| 493 | "Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++ | ||
| 494 | "Transfer-Encoding: chunked\r\n" ++ | ||
| 495 | "X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++ | ||
| 496 | "connection: close\r\n\r\n" ++ trail; | ||
| 497 | try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example)); | ||
| 498 | } | ||
| 499 | }; | ||
| 500 | 113 | ||
| 501 | pub const Headers = struct { | 114 | pool.used.append(node); |
| 502 | version: http.Version = .@"HTTP/1.1", | 115 | } |
| 503 | method: http.Method = .GET, | ||
| 504 | }; | ||
| 505 | 116 | ||
| 506 | pub const Options = struct { | 117 | pub fn deinit(pool: *ConnectionPool, client: *Client) void { |
| 507 | max_redirects: u32 = 3, | 118 | pool.mutex.lock(); |
| 508 | header_strategy: HeaderStrategy = .{ .dynamic = 16 * 1024 }, | ||
| 509 | |||
| 510 | pub const HeaderStrategy = union(enum) { | ||
| 511 | /// In this case, the client's Allocator will be used to store the | ||
| 512 | /// entire HTTP header. This value is the maximum total size of | ||
| 513 | /// HTTP headers allowed, otherwise | ||
| 514 | /// error.HttpHeadersExceededSizeLimit is returned from read(). | ||
| 515 | dynamic: usize, | ||
| 516 | /// This is used to store the entire HTTP header. If the HTTP | ||
| 517 | /// header is too big to fit, `error.HttpHeadersExceededSizeLimit` | ||
| 518 | /// is returned from read(). When this is used, `error.OutOfMemory` | ||
| 519 | /// cannot be returned from `read()`. | ||
| 520 | static: []u8, | ||
| 521 | }; | ||
| 522 | }; | ||
| 523 | 119 | ||
| 524 | /// May be skipped if header strategy is buffer. | 120 | var next = pool.free.first; |
| 525 | pub fn deinit(req: *Request) void { | 121 | while (next) |node| { |
| 526 | if (req.response.header_bytes_owned) { | 122 | defer client.allocator.destroy(node); |
| 527 | req.response.header_bytes.deinit(req.client.allocator); | 123 | next = node.next; |
| 124 | |||
| 125 | node.data.close(client); | ||
| 528 | } | 126 | } |
| 529 | req.* = undefined; | 127 | |
| 128 | next = pool.used.first; | ||
| 129 | while (next) |node| { | ||
| 130 | defer client.allocator.destroy(node); | ||
| 131 | next = node.next; | ||
| 132 | |||
| 133 | node.data.close(client); | ||
| 134 | } | ||
| 135 | |||
| 136 | pool.* = undefined; | ||
| 530 | } | 137 | } |
| 138 | }; | ||
| 139 | |||
| 140 | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.ReaderRaw); | ||
| 141 | pub const GzipDecompressor = std.compress.gzip.Decompress(Request.ReaderRaw); | ||
| 142 | pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Request.ReaderRaw, .{}); | ||
| 143 | |||
| 144 | pub 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, | ||
| 531 | 154 | ||
| 532 | pub const Reader = std.io.Reader(*Request, ReadError, read); | 155 | pub const Protocol = enum { plain, tls }; |
| 533 | 156 | ||
| 534 | pub fn reader(req: *Request) Reader { | 157 | pub fn read(conn: *Connection, buffer: []u8) !usize { |
| 535 | return .{ .context = req }; | 158 | switch (conn.protocol) { |
| 159 | .plain => return conn.stream.read(buffer), | ||
| 160 | .tls => return conn.tls_client.read(conn.stream, buffer), | ||
| 161 | } | ||
| 536 | } | 162 | } |
| 537 | 163 | ||
| 538 | pub fn readAll(req: *Request, buffer: []u8) !usize { | 164 | pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize { |
| 539 | return readAtLeast(req, buffer, buffer.len); | 165 | switch (conn.protocol) { |
| 166 | .plain => return conn.stream.readAtLeast(buffer, len), | ||
| 167 | .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len), | ||
| 168 | } | ||
| 540 | } | 169 | } |
| 541 | 170 | ||
| 542 | pub const ReadError = net.Stream.ReadError || error{ | 171 | pub const ReadError = net.Stream.ReadError || error{ |
| 543 | // From HTTP protocol | ||
| 544 | HttpHeadersInvalid, | ||
| 545 | HttpHeadersExceededSizeLimit, | ||
| 546 | HttpRedirectMissingLocation, | ||
| 547 | HttpTransferEncodingUnsupported, | ||
| 548 | HttpContentLengthUnknown, | ||
| 549 | TooManyHttpRedirects, | ||
| 550 | ShortHttpStatusLine, | ||
| 551 | BadHttpVersion, | ||
| 552 | HttpHeaderContinuationsUnsupported, | ||
| 553 | UnsupportedUrlScheme, | ||
| 554 | UriMissingHost, | ||
| 555 | UnknownHostName, | ||
| 556 | |||
| 557 | // Network problems | ||
| 558 | NetworkUnreachable, | ||
| 559 | HostLacksNetworkAddresses, | ||
| 560 | TemporaryNameServerFailure, | ||
| 561 | NameServerFailure, | ||
| 562 | ProtocolFamilyNotAvailable, | ||
| 563 | ProtocolNotSupported, | ||
| 564 | |||
| 565 | // System resource problems | ||
| 566 | ProcessFdQuotaExceeded, | ||
| 567 | SystemFdQuotaExceeded, | ||
| 568 | OutOfMemory, | ||
| 569 | |||
| 570 | // TLS problems | ||
| 571 | InsufficientEntropy, | ||
| 572 | TlsConnectionTruncated, | 172 | TlsConnectionTruncated, |
| 573 | TlsRecordOverflow, | 173 | TlsRecordOverflow, |
| 574 | TlsDecodeError, | 174 | TlsDecodeError, |
| 575 | TlsAlert, | 175 | TlsAlert, |
| 576 | TlsBadRecordMac, | 176 | TlsBadRecordMac, |
| 177 | Overflow, | ||
| 577 | TlsBadLength, | 178 | TlsBadLength, |
| 578 | TlsIllegalParameter, | 179 | TlsIllegalParameter, |
| 579 | TlsUnexpectedMessage, | 180 | TlsUnexpectedMessage, |
| 580 | TlsDecryptFailure, | ||
| 581 | CertificateFieldHasInvalidLength, | ||
| 582 | CertificateHostMismatch, | ||
| 583 | CertificatePublicKeyInvalid, | ||
| 584 | CertificateExpired, | ||
| 585 | CertificateFieldHasWrongDataType, | ||
| 586 | CertificateIssuerMismatch, | ||
| 587 | CertificateNotYetValid, | ||
| 588 | CertificateSignatureAlgorithmMismatch, | ||
| 589 | CertificateSignatureAlgorithmUnsupported, | ||
| 590 | CertificateSignatureInvalid, | ||
| 591 | CertificateSignatureInvalidLength, | ||
| 592 | CertificateSignatureNamedCurveUnsupported, | ||
| 593 | CertificateSignatureUnsupportedBitCount, | ||
| 594 | TlsCertificateNotVerified, | ||
| 595 | TlsBadSignatureScheme, | ||
| 596 | TlsBadRsaSignatureBitCount, | ||
| 597 | TlsDecryptError, | ||
| 598 | UnsupportedCertificateVersion, | ||
| 599 | CertificateTimeInvalid, | ||
| 600 | CertificateHasUnrecognizedObjectId, | ||
| 601 | CertificateHasInvalidBitString, | ||
| 602 | CertificateAuthorityBundleTooBig, | ||
| 603 | |||
| 604 | // TODO: convert to higher level errors | ||
| 605 | InvalidFormat, | ||
| 606 | InvalidPort, | ||
| 607 | UnexpectedCharacter, | ||
| 608 | Overflow, | ||
| 609 | InvalidCharacter, | ||
| 610 | AddressFamilyNotSupported, | ||
| 611 | AddressInUse, | ||
| 612 | AddressNotAvailable, | ||
| 613 | ConnectionPending, | ||
| 614 | ConnectionRefused, | ||
| 615 | FileNotFound, | ||
| 616 | PermissionDenied, | ||
| 617 | ServiceUnavailable, | ||
| 618 | SocketTypeNotSupported, | ||
| 619 | FileTooBig, | ||
| 620 | LockViolation, | ||
| 621 | NoSpaceLeft, | ||
| 622 | NotOpenForWriting, | ||
| 623 | InvalidEncoding, | ||
| 624 | IdentityElement, | ||
| 625 | NonCanonical, | ||
| 626 | SignatureVerificationFailed, | ||
| 627 | MessageTooLong, | ||
| 628 | NegativeIntoUnsigned, | ||
| 629 | TargetTooSmall, | ||
| 630 | BufferTooSmall, | ||
| 631 | InvalidSignature, | ||
| 632 | NotSquare, | ||
| 633 | DiskQuota, | ||
| 634 | InvalidEnd, | ||
| 635 | Incomplete, | ||
| 636 | InvalidIpv4Mapping, | ||
| 637 | InvalidIPAddressFormat, | ||
| 638 | BadPathName, | ||
| 639 | DeviceBusy, | ||
| 640 | FileBusy, | ||
| 641 | FileLocksNotSupported, | ||
| 642 | InvalidHandle, | ||
| 643 | InvalidUtf8, | ||
| 644 | NameTooLong, | ||
| 645 | NoDevice, | ||
| 646 | PathAlreadyExists, | ||
| 647 | PipeBusy, | ||
| 648 | SharingViolation, | ||
| 649 | SymLinkLoop, | ||
| 650 | FileSystem, | ||
| 651 | InterfaceNotFound, | ||
| 652 | AlreadyBound, | ||
| 653 | FileDescriptorNotASocket, | ||
| 654 | NetworkSubsystemFailed, | ||
| 655 | NotDir, | ||
| 656 | ReadOnlyFileSystem, | ||
| 657 | Unseekable, | ||
| 658 | MissingEndCertificateMarker, | ||
| 659 | InvalidPadding, | ||
| 660 | EndOfStream, | ||
| 661 | }; | 181 | }; |
| 662 | 182 | ||
| 663 | pub fn read(req: *Request, buffer: []u8) ReadError!usize { | 183 | pub const Reader = std.io.Reader(*Connection, ReadError, read); |
| 664 | return readAtLeast(req, buffer, 1); | 184 | |
| 185 | pub fn reader(conn: *Connection) Reader { | ||
| 186 | return Reader{ .context = conn }; | ||
| 665 | } | 187 | } |
| 666 | 188 | ||
| 667 | pub fn readAtLeast(req: *Request, buffer: []u8, len: usize) !usize { | 189 | pub fn writeAll(conn: *Connection, buffer: []const u8) !void { |
| 668 | assert(len <= buffer.len); | 190 | switch (conn.protocol) { |
| 669 | var index: usize = 0; | 191 | .plain => return conn.stream.writeAll(buffer), |
| 670 | while (index < len) { | 192 | .tls => return conn.tls_client.writeAll(conn.stream, buffer), |
| 671 | const zero_means_end = req.response.state.zeroMeansEnd(); | ||
| 672 | const amt = try readAdvanced(req, buffer[index..]); | ||
| 673 | if (amt == 0 and zero_means_end) break; | ||
| 674 | index += amt; | ||
| 675 | } | 193 | } |
| 676 | return index; | ||
| 677 | } | 194 | } |
| 678 | 195 | ||
| 679 | /// This one can return 0 without meaning EOF. | 196 | pub fn write(conn: *Connection, buffer: []const u8) !usize { |
| 680 | /// TODO change to readvAdvanced | 197 | switch (conn.protocol) { |
| 681 | pub fn readAdvanced(req: *Request, buffer: []u8) !usize { | 198 | .plain => return conn.stream.write(buffer), |
| 682 | var in = buffer[0..try req.connection.read(buffer)]; | 199 | .tls => return conn.tls_client.write(conn.stream, buffer), |
| 683 | var out_index: usize = 0; | ||
| 684 | while (true) { | ||
| 685 | switch (req.response.state) { | ||
| 686 | .invalid => unreachable, | ||
| 687 | .start, .seen_r, .seen_rn, .seen_rnr => { | ||
| 688 | const i = req.response.findHeadersEnd(in); | ||
| 689 | if (req.response.state == .invalid) return error.HttpHeadersInvalid; | ||
| 690 | |||
| 691 | const headers_data = in[0..i]; | ||
| 692 | if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) { | ||
| 693 | return error.HttpHeadersExceededSizeLimit; | ||
| 694 | } | ||
| 695 | try req.response.header_bytes.appendSlice(req.client.allocator, headers_data); | ||
| 696 | |||
| 697 | if (req.response.state == .finished) { | ||
| 698 | req.response.headers = try Response.Headers.parse(req.response.header_bytes.items); | ||
| 699 | |||
| 700 | if (req.response.headers.status.class() == .redirect) { | ||
| 701 | if (req.redirects_left == 0) return error.TooManyHttpRedirects; | ||
| 702 | const location = req.response.headers.location orelse | ||
| 703 | return error.HttpRedirectMissingLocation; | ||
| 704 | const new_url = try std.Uri.parse(location); | ||
| 705 | const new_req = try req.client.request(new_url, req.headers, .{ | ||
| 706 | .max_redirects = req.redirects_left - 1, | ||
| 707 | .header_strategy = if (req.response.header_bytes_owned) .{ | ||
| 708 | .dynamic = req.response.max_header_bytes, | ||
| 709 | } else .{ | ||
| 710 | .static = req.response.header_bytes.unusedCapacitySlice(), | ||
| 711 | }, | ||
| 712 | }); | ||
| 713 | req.deinit(); | ||
| 714 | req.* = new_req; | ||
| 715 | assert(out_index == 0); | ||
| 716 | in = buffer[0..try req.connection.read(buffer)]; | ||
| 717 | continue; | ||
| 718 | } | ||
| 719 | |||
| 720 | if (req.response.headers.transfer_encoding) |transfer_encoding| { | ||
| 721 | switch (transfer_encoding) { | ||
| 722 | .chunked => { | ||
| 723 | req.response.next_chunk_length = 0; | ||
| 724 | req.response.state = .chunk_size; | ||
| 725 | }, | ||
| 726 | .compress => return error.HttpTransferEncodingUnsupported, | ||
| 727 | .deflate => return error.HttpTransferEncodingUnsupported, | ||
| 728 | .gzip => return error.HttpTransferEncodingUnsupported, | ||
| 729 | } | ||
| 730 | } else if (req.response.headers.content_length) |content_length| { | ||
| 731 | req.response.next_chunk_length = content_length; | ||
| 732 | } else { | ||
| 733 | return error.HttpContentLengthUnknown; | ||
| 734 | } | ||
| 735 | |||
| 736 | in = in[i..]; | ||
| 737 | continue; | ||
| 738 | } | ||
| 739 | |||
| 740 | assert(out_index == 0); | ||
| 741 | return 0; | ||
| 742 | }, | ||
| 743 | .finished => { | ||
| 744 | if (in.ptr == buffer.ptr) { | ||
| 745 | return in.len; | ||
| 746 | } else { | ||
| 747 | mem.copy(u8, buffer[out_index..], in); | ||
| 748 | return out_index + in.len; | ||
| 749 | } | ||
| 750 | }, | ||
| 751 | .chunk_size_prefix_r => switch (in.len) { | ||
| 752 | 0 => return out_index, | ||
| 753 | 1 => switch (in[0]) { | ||
| 754 | '\r' => { | ||
| 755 | req.response.state = .chunk_size_prefix_n; | ||
| 756 | return out_index; | ||
| 757 | }, | ||
| 758 | else => { | ||
| 759 | req.response.state = .invalid; | ||
| 760 | return error.HttpHeadersInvalid; | ||
| 761 | }, | ||
| 762 | }, | ||
| 763 | else => switch (int16(in[0..2])) { | ||
| 764 | int16("\r\n") => { | ||
| 765 | in = in[2..]; | ||
| 766 | req.response.state = .chunk_size; | ||
| 767 | continue; | ||
| 768 | }, | ||
| 769 | else => { | ||
| 770 | req.response.state = .invalid; | ||
| 771 | return error.HttpHeadersInvalid; | ||
| 772 | }, | ||
| 773 | }, | ||
| 774 | }, | ||
| 775 | .chunk_size_prefix_n => switch (in.len) { | ||
| 776 | 0 => return out_index, | ||
| 777 | else => switch (in[0]) { | ||
| 778 | '\n' => { | ||
| 779 | in = in[1..]; | ||
| 780 | req.response.state = .chunk_size; | ||
| 781 | continue; | ||
| 782 | }, | ||
| 783 | else => { | ||
| 784 | req.response.state = .invalid; | ||
| 785 | return error.HttpHeadersInvalid; | ||
| 786 | }, | ||
| 787 | }, | ||
| 788 | }, | ||
| 789 | .chunk_size, .chunk_r => { | ||
| 790 | const i = req.response.findChunkedLen(in); | ||
| 791 | switch (req.response.state) { | ||
| 792 | .invalid => return error.HttpHeadersInvalid, | ||
| 793 | .chunk_data => { | ||
| 794 | if (req.response.next_chunk_length == 0) { | ||
| 795 | req.response.state = .start; | ||
| 796 | return out_index; | ||
| 797 | } | ||
| 798 | in = in[i..]; | ||
| 799 | continue; | ||
| 800 | }, | ||
| 801 | .chunk_size => return out_index, | ||
| 802 | else => unreachable, | ||
| 803 | } | ||
| 804 | }, | ||
| 805 | .chunk_data => { | ||
| 806 | // TODO https://github.com/ziglang/zig/issues/14039 | ||
| 807 | const sub_amt = @intCast(usize, @min(req.response.next_chunk_length, in.len)); | ||
| 808 | req.response.next_chunk_length -= sub_amt; | ||
| 809 | if (req.response.next_chunk_length > 0) { | ||
| 810 | if (in.ptr == buffer.ptr) { | ||
| 811 | return sub_amt; | ||
| 812 | } else { | ||
| 813 | mem.copy(u8, buffer[out_index..], in[0..sub_amt]); | ||
| 814 | out_index += sub_amt; | ||
| 815 | return out_index; | ||
| 816 | } | ||
| 817 | } | ||
| 818 | mem.copy(u8, buffer[out_index..], in[0..sub_amt]); | ||
| 819 | out_index += sub_amt; | ||
| 820 | req.response.state = .chunk_size_prefix_r; | ||
| 821 | in = in[sub_amt..]; | ||
| 822 | continue; | ||
| 823 | }, | ||
| 824 | } | ||
| 825 | } | 200 | } |
| 826 | } | 201 | } |
| 827 | 202 | ||
| 828 | inline fn int16(array: *const [2]u8) u16 { | 203 | pub const WriteError = net.Stream.WriteError || error{}; |
| 829 | return @bitCast(u16, array.*); | 204 | pub const Writer = std.io.Writer(*Connection, WriteError, write); |
| 830 | } | ||
| 831 | 205 | ||
| 832 | inline fn int32(array: *const [4]u8) u32 { | 206 | pub fn writer(conn: *Connection) Writer { |
| 833 | return @bitCast(u32, array.*); | 207 | return Writer{ .context = conn }; |
| 834 | } | 208 | } |
| 835 | 209 | ||
| 836 | inline fn int64(array: *const [8]u8) u64 { | 210 | pub fn close(conn: *Connection, client: *const Client) void { |
| 837 | return @bitCast(u64, array.*); | 211 | if (conn.protocol == .tls) { |
| 838 | } | 212 | // try to cleanly close the TLS connection, for any server that cares. |
| 213 | _ = conn.tls_client.writeEnd(conn.stream, "", true) catch {}; | ||
| 214 | client.allocator.destroy(conn.tls_client); | ||
| 215 | } | ||
| 839 | 216 | ||
| 840 | test { | 217 | conn.stream.close(); |
| 841 | _ = Response; | 218 | |
| 219 | client.allocator.free(conn.host); | ||
| 842 | } | 220 | } |
| 843 | }; | 221 | }; |
| 844 | 222 | ||
| 845 | pub fn deinit(client: *Client) void { | 223 | pub fn deinit(client: *Client) void { |
| 224 | client.connection_pool.deinit(client); | ||
| 225 | |||
| 846 | client.ca_bundle.deinit(client.allocator); | 226 | client.ca_bundle.deinit(client.allocator); |
| 847 | client.* = undefined; | 227 | client.* = undefined; |
| 848 | } | 228 | } |
| 849 | 229 | ||
| 850 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !Connection { | 230 | pub const ConnectError = std.mem.Allocator.Error || net.TcpConnectToHostError || std.crypto.tls.Client.InitError(net.Stream); |
| 851 | var conn: Connection = .{ | 231 | |
| 232 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node { | ||
| 233 | if (client.connection_pool.findConnection(.{ | ||
| 234 | .host = host, | ||
| 235 | .port = port, | ||
| 236 | .is_tls = protocol == .tls, | ||
| 237 | })) |node| | ||
| 238 | return node; | ||
| 239 | |||
| 240 | const conn = try client.allocator.create(ConnectionPool.Node); | ||
| 241 | errdefer client.allocator.destroy(conn); | ||
| 242 | conn.* = .{ .data = undefined }; | ||
| 243 | |||
| 244 | conn.data = .{ | ||
| 852 | .stream = try net.tcpConnectToHost(client.allocator, host, port), | 245 | .stream = try net.tcpConnectToHost(client.allocator, host, port), |
| 853 | .tls_client = undefined, | 246 | .tls_client = undefined, |
| 854 | .protocol = protocol, | 247 | .protocol = protocol, |
| 248 | .host = try client.allocator.dupe(u8, host), | ||
| 249 | .port = port, | ||
| 855 | }; | 250 | }; |
| 856 | 251 | ||
| 857 | switch (protocol) { | 252 | switch (protocol) { |
| 858 | .plain => {}, | 253 | .plain => {}, |
| 859 | .tls => { | 254 | .tls => { |
| 860 | conn.tls_client = try std.crypto.tls.Client.init(conn.stream, client.ca_bundle, host); | 255 | conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client); |
| 256 | conn.data.tls_client.* = try std.crypto.tls.Client.init(conn.data.stream, client.ca_bundle, host); | ||
| 861 | // This is appropriate for HTTPS because the HTTP headers contain | 257 | // This is appropriate for HTTPS because the HTTP headers contain |
| 862 | // the content length which is used to detect truncation attacks. | 258 | // the content length which is used to detect truncation attacks. |
| 863 | conn.tls_client.allow_truncation_attacks = true; | 259 | conn.data.tls_client.allow_truncation_attacks = true; |
| 864 | }, | 260 | }, |
| 865 | } | 261 | } |
| 866 | 262 | ||
| 263 | client.connection_pool.addUsed(conn); | ||
| 264 | |||
| 867 | return conn; | 265 | return conn; |
| 868 | } | 266 | } |
| 869 | 267 | ||
| 870 | pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) !Request { | 268 | pub const RequestError = ConnectError || Connection.WriteError || error{ |
| 269 | UnsupportedUrlScheme, | ||
| 270 | UriMissingHost, | ||
| 271 | |||
| 272 | CertificateAuthorityBundleTooBig, | ||
| 273 | InvalidPadding, | ||
| 274 | MissingEndCertificateMarker, | ||
| 275 | Unseekable, | ||
| 276 | EndOfStream, | ||
| 277 | }; | ||
| 278 | |||
| 279 | pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Request.Options) RequestError!Request { | ||
| 871 | const protocol: Connection.Protocol = if (mem.eql(u8, uri.scheme, "http")) | 280 | const protocol: Connection.Protocol = if (mem.eql(u8, uri.scheme, "http")) |
| 872 | .plain | 281 | .plain |
| 873 | else if (mem.eql(u8, uri.scheme, "https")) | 282 | else if (mem.eql(u8, uri.scheme, "https")) |
| ... | @@ -883,34 +292,89 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req | ... | @@ -883,34 +292,89 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req |
| 883 | const host = uri.host orelse return error.UriMissingHost; | 292 | const host = uri.host orelse return error.UriMissingHost; |
| 884 | 293 | ||
| 885 | if (client.next_https_rescan_certs and protocol == .tls) { | 294 | if (client.next_https_rescan_certs and protocol == .tls) { |
| 886 | try client.ca_bundle.rescan(client.allocator); | 295 | client.connection_pool.mutex.lock(); // TODO: this could be so much better than reusing the connection pool mutex. |
| 887 | client.next_https_rescan_certs = false; | 296 | defer client.connection_pool.mutex.unlock(); |
| 297 | |||
| 298 | if (client.next_https_rescan_certs) { | ||
| 299 | try client.ca_bundle.rescan(client.allocator); | ||
| 300 | client.next_https_rescan_certs = false; | ||
| 301 | } | ||
| 888 | } | 302 | } |
| 889 | 303 | ||
| 890 | var req: Request = .{ | 304 | var req: Request = .{ |
| 305 | .uri = uri, | ||
| 891 | .client = client, | 306 | .client = client, |
| 892 | .headers = headers, | 307 | .headers = headers, |
| 893 | .connection = try client.connect(host, port, protocol), | 308 | .connection = try client.connect(host, port, protocol), |
| 894 | .redirects_left = options.max_redirects, | 309 | .redirects_left = options.max_redirects, |
| 310 | .handle_redirects = options.handle_redirects, | ||
| 311 | .compression_init = false, | ||
| 895 | .response = switch (options.header_strategy) { | 312 | .response = switch (options.header_strategy) { |
| 896 | .dynamic => |max| Request.Response.initDynamic(max), | 313 | .dynamic => |max| Response.initDynamic(max), |
| 897 | .static => |buf| Request.Response.initStatic(buf), | 314 | .static => |buf| Response.initStatic(buf), |
| 898 | }, | 315 | }, |
| 316 | .arena = undefined, | ||
| 899 | }; | 317 | }; |
| 900 | 318 | ||
| 319 | req.arena = std.heap.ArenaAllocator.init(client.allocator); | ||
| 320 | |||
| 901 | { | 321 | { |
| 902 | var h = try std.BoundedArray(u8, 1000).init(0); | 322 | var buffered = std.io.bufferedWriter(req.connection.data.writer()); |
| 903 | try h.appendSlice(@tagName(headers.method)); | 323 | const writer = buffered.writer(); |
| 904 | try h.appendSlice(" "); | 324 | |
| 905 | try h.appendSlice(uri.path); | 325 | const escaped_path = try Uri.escapePath(client.allocator, uri.path); |
| 906 | try h.appendSlice(" "); | 326 | defer client.allocator.free(escaped_path); |
| 907 | try h.appendSlice(@tagName(headers.version)); | 327 | |
| 908 | try h.appendSlice("\r\nHost: "); | 328 | const escaped_query = if (uri.query) |q| try Uri.escapeQuery(client.allocator, q) else null; |
| 909 | try h.appendSlice(host); | 329 | defer if (escaped_query) |q| client.allocator.free(q); |
| 910 | try h.appendSlice("\r\nConnection: close\r\n\r\n"); | 330 | |
| 911 | 331 | const escaped_fragment = if (uri.fragment) |f| try Uri.escapeQuery(client.allocator, f) else null; | |
| 912 | const header_bytes = h.slice(); | 332 | defer if (escaped_fragment) |f| client.allocator.free(f); |
| 913 | try req.connection.writeAll(header_bytes); | 333 | |
| 334 | try writer.writeAll(@tagName(headers.method)); | ||
| 335 | try writer.writeByte(' '); | ||
| 336 | if (escaped_path.len == 0) { | ||
| 337 | try writer.writeByte('/'); | ||
| 338 | } else { | ||
| 339 | try writer.writeAll(escaped_path); | ||
| 340 | } | ||
| 341 | if (escaped_query) |q| { | ||
| 342 | try writer.writeByte('?'); | ||
| 343 | try writer.writeAll(q); | ||
| 344 | } | ||
| 345 | if (escaped_fragment) |f| { | ||
| 346 | try writer.writeByte('#'); | ||
| 347 | try writer.writeAll(f); | ||
| 348 | } | ||
| 349 | try writer.writeByte(' '); | ||
| 350 | try writer.writeAll(@tagName(headers.version)); | ||
| 351 | try writer.writeAll("\r\nHost: "); | ||
| 352 | try writer.writeAll(host); | ||
| 353 | try writer.writeAll("\r\nUser-Agent: "); | ||
| 354 | try writer.writeAll(headers.user_agent); | ||
| 355 | if (headers.connection == .close) { | ||
| 356 | try writer.writeAll("\r\nConnection: close"); | ||
| 357 | } else { | ||
| 358 | try writer.writeAll("\r\nConnection: keep-alive"); | ||
| 359 | } | ||
| 360 | try writer.writeAll("\r\nAccept-Encoding: gzip, deflate, zstd"); | ||
| 361 | |||
| 362 | switch (headers.transfer_encoding) { | ||
| 363 | .chunked => try writer.writeAll("\r\nTransfer-Encoding: chunked"), | ||
| 364 | .content_length => |content_length| try writer.print("\r\nContent-Length: {d}", .{content_length}), | ||
| 365 | .none => {}, | ||
| 366 | } | ||
| 367 | |||
| 368 | for (headers.custom) |header| { | ||
| 369 | try writer.writeAll("\r\n"); | ||
| 370 | try writer.writeAll(header.name); | ||
| 371 | try writer.writeAll(": "); | ||
| 372 | try writer.writeAll(header.value); | ||
| 373 | } | ||
| 374 | |||
| 375 | try writer.writeAll("\r\n\r\n"); | ||
| 376 | |||
| 377 | try buffered.flush(); | ||
| 914 | } | 378 | } |
| 915 | 379 | ||
| 916 | return req; | 380 | return req; |
| ... | @@ -924,5 +388,7 @@ test { | ... | @@ -924,5 +388,7 @@ test { |
| 924 | return error.SkipZigTest; | 388 | return error.SkipZigTest; |
| 925 | } | 389 | } |
| 926 | 390 | ||
| 391 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 392 | |||
| 927 | _ = Request; | 393 | _ = Request; |
| 928 | } | 394 | } |
lib/std/http/Client/Request.zig created+482| ... | @@ -0,0 +1,482 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const http = std.http; | ||
| 3 | const Uri = std.Uri; | ||
| 4 | const mem = std.mem; | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | |||
| 7 | const Client = @import("../Client.zig"); | ||
| 8 | const Connection = Client.Connection; | ||
| 9 | const ConnectionNode = Client.ConnectionPool.Node; | ||
| 10 | const Response = @import("Response.zig"); | ||
| 11 | |||
| 12 | const Request = @This(); | ||
| 13 | |||
| 14 | const read_buffer_size = 8192; | ||
| 15 | const ReadBufferIndex = std.math.IntFittingRange(0, read_buffer_size); | ||
| 16 | |||
| 17 | uri: Uri, | ||
| 18 | client: *Client, | ||
| 19 | connection: *ConnectionNode, | ||
| 20 | response: Response, | ||
| 21 | /// These are stored in Request so that they are available when following | ||
| 22 | /// redirects. | ||
| 23 | headers: Headers, | ||
| 24 | |||
| 25 | redirects_left: u32, | ||
| 26 | handle_redirects: bool, | ||
| 27 | compression_init: bool, | ||
| 28 | |||
| 29 | /// Used as a allocator for resolving redirects locations. | ||
| 30 | arena: 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. | ||
| 33 | read_buffer: [read_buffer_size]u8 = undefined, | ||
| 34 | read_buffer_start: ReadBufferIndex = 0, | ||
| 35 | read_buffer_len: ReadBufferIndex = 0, | ||
| 36 | |||
| 37 | pub const RequestTransfer = union(enum) { | ||
| 38 | content_length: u64, | ||
| 39 | chunked: void, | ||
| 40 | none: void, | ||
| 41 | }; | ||
| 42 | |||
| 43 | pub 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 | |||
| 53 | pub 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. | ||
| 73 | pub 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 | |||
| 95 | pub const ReadRawError = Connection.ReadError || Uri.ParseError || Client.RequestError || error{ | ||
| 96 | UnexpectedEndOfStream, | ||
| 97 | TooManyHttpRedirects, | ||
| 98 | HttpRedirectMissingLocation, | ||
| 99 | HttpHeadersInvalid, | ||
| 100 | }; | ||
| 101 | |||
| 102 | pub 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. | ||
| 106 | pub 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 | |||
| 119 | fn 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 | |||
| 171 | pub 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. | ||
| 183 | pub 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. | ||
| 201 | fn 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 | |||
| 348 | pub const ReadError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error || WaitForCompleteHeadError || error{ BadHeader, InvalidCompression, StreamTooLong, InvalidWindowSize, CompressionNotSupported }; | ||
| 349 | |||
| 350 | pub const Reader = std.io.Reader(*Request, ReadError, read); | ||
| 351 | |||
| 352 | pub fn reader(req: *Request) Reader { | ||
| 353 | return .{ .context = req }; | ||
| 354 | } | ||
| 355 | |||
| 356 | pub 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 | |||
| 416 | pub 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 | |||
| 426 | pub const WriteError = Connection.WriteError || error{MessageTooLong}; | ||
| 427 | |||
| 428 | pub const Writer = std.io.Writer(*Request, WriteError, write); | ||
| 429 | |||
| 430 | pub 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. | ||
| 435 | pub 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. | ||
| 456 | pub 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 | |||
| 464 | inline fn int16(array: *const [2]u8) u16 { | ||
| 465 | return @bitCast(u16, array.*); | ||
| 466 | } | ||
| 467 | |||
| 468 | inline fn int32(array: *const [4]u8) u32 { | ||
| 469 | return @bitCast(u32, array.*); | ||
| 470 | } | ||
| 471 | |||
| 472 | inline fn int64(array: *const [8]u8) u64 { | ||
| 473 | return @bitCast(u64, array.*); | ||
| 474 | } | ||
| 475 | |||
| 476 | test { | ||
| 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const http = std.http; | ||
| 3 | const mem = std.mem; | ||
| 4 | const testing = std.testing; | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | |||
| 7 | const Client = @import("../Client.zig"); | ||
| 8 | const Response = @This(); | ||
| 9 | |||
| 10 | headers: Headers, | ||
| 11 | state: State, | ||
| 12 | header_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. | ||
| 15 | header_bytes: std.ArrayListUnmanaged(u8), | ||
| 16 | max_header_bytes: usize, | ||
| 17 | next_chunk_length: u64, | ||
| 18 | done: bool = false, | ||
| 19 | |||
| 20 | compression: union(enum) { | ||
| 21 | deflate: Client.DeflateDecompressor, | ||
| 22 | gzip: Client.GzipDecompressor, | ||
| 23 | zstd: Client.ZstdDecompressor, | ||
| 24 | none: void, | ||
| 25 | } = .none, | ||
| 26 | |||
| 27 | pub 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 | |||
| 172 | inline fn int16(array: *const [2]u8) u16 { | ||
| 173 | return @bitCast(u16, array.*); | ||
| 174 | } | ||
| 175 | |||
| 176 | inline fn int32(array: *const [4]u8) u32 { | ||
| 177 | return @bitCast(u32, array.*); | ||
| 178 | } | ||
| 179 | |||
| 180 | inline fn int64(array: *const [8]u8) u64 { | ||
| 181 | return @bitCast(u64, array.*); | ||
| 182 | } | ||
| 183 | |||
| 184 | pub 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 | |||
| 207 | pub 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 | |||
| 218 | pub 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]`. | ||
| 233 | pub 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 | |||
| 410 | pub 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 | |||
| 455 | fn 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 | |||
| 461 | test 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 | |||
| 468 | test "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 | |||
| 476 | test "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 | |||
| 488 | test "find headers end bug" { | ||
| 489 | var buffer: [1]u8 = undefined; | ||
| 490 | var r = Response.initStatic(&buffer); | ||
| 491 | const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; | ||
| 492 | const example = | ||
| 493 | "HTTP/1.1 200 OK\r\n" ++ | ||
| 494 | "Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++ | ||
| 495 | "content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++ | ||
| 496 | "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++ | ||
| 497 | "Content-Type: application/x-gzip\r\n" ++ | ||
| 498 | "ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++ | ||
| 499 | "Strict-Transport-Security: max-age=31536000\r\n" ++ | ||
| 500 | "Vary: Authorization,Accept-Encoding,Origin\r\n" ++ | ||
| 501 | "X-Content-Type-Options: nosniff\r\n" ++ | ||
| 502 | "X-Frame-Options: deny\r\n" ++ | ||
| 503 | "X-XSS-Protection: 1; mode=block\r\n" ++ | ||
| 504 | "Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++ | ||
| 505 | "Transfer-Encoding: chunked\r\n" ++ | ||
| 506 | "X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++ | ||
| 507 | "connection: close\r\n\r\n" ++ trail; | ||
| 508 | try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example)); | ||
| 509 | } | ||
lib/std/math/big/int.zig+1| ... | @@ -1674,6 +1674,7 @@ pub const Mutable = struct { | ... | @@ -1674,6 +1674,7 @@ pub const Mutable = struct { |
| 1674 | 1674 | ||
| 1675 | /// If a is positive, this passes through to truncate. | 1675 | /// If a is positive, this passes through to truncate. |
| 1676 | /// If a is negative, then r is set to positive with the bit pattern ~(a - 1). | 1676 | /// If a is negative, then r is set to positive with the bit pattern ~(a - 1). |
| 1677 | /// r may alias a. | ||
| 1677 | /// | 1678 | /// |
| 1678 | /// Asserts `r` has enough storage to store the result. | 1679 | /// Asserts `r` has enough storage to store the result. |
| 1679 | /// The upper bound is `calcTwosCompLimbCount(a.len)`. | 1680 | /// The upper bound is `calcTwosCompLimbCount(a.len)`. |
lib/std/mem.zig+4-9| ... | @@ -196,13 +196,8 @@ test "Allocator.resize" { | ... | @@ -196,13 +196,8 @@ test "Allocator.resize" { |
| 196 | /// dest.len must be >= source.len. | 196 | /// dest.len must be >= source.len. |
| 197 | /// If the slices overlap, dest.ptr must be <= src.ptr. | 197 | /// If the slices overlap, dest.ptr must be <= src.ptr. |
| 198 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { | 198 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { |
| 199 | // TODO instead of manually doing this check for the whole array | 199 | for (dest[0..source.len], source) |*d, s| |
| 200 | // and turning off runtime safety, the compiler should detect loops like | 200 | d.* = s; |
| 201 | // this and automatically omit safety checks for loops | ||
| 202 | @setRuntimeSafety(false); | ||
| 203 | assert(dest.len >= source.len); | ||
| 204 | for (source, 0..) |s, i| | ||
| 205 | dest[i] = s; | ||
| 206 | } | 201 | } |
| 207 | 202 | ||
| 208 | /// Copy all of source into dest at position 0. | 203 | /// Copy all of source into dest at position 0. |
| ... | @@ -611,8 +606,8 @@ test "lessThan" { | ... | @@ -611,8 +606,8 @@ test "lessThan" { |
| 611 | pub fn eql(comptime T: type, a: []const T, b: []const T) bool { | 606 | pub fn eql(comptime T: type, a: []const T, b: []const T) bool { |
| 612 | if (a.len != b.len) return false; | 607 | if (a.len != b.len) return false; |
| 613 | if (a.ptr == b.ptr) return true; | 608 | if (a.ptr == b.ptr) return true; |
| 614 | for (a, 0..) |item, index| { | 609 | for (a, b) |a_elem, b_elem| { |
| 615 | if (b[index] != item) return false; | 610 | if (a_elem != b_elem) return false; |
| 616 | } | 611 | } |
| 617 | return true; | 612 | return true; |
| 618 | } | 613 | } |
lib/std/multi_array_list.zig+13-2| ... | @@ -68,6 +68,15 @@ pub fn MultiArrayList(comptime S: type) type { | ... | @@ -68,6 +68,15 @@ pub fn MultiArrayList(comptime S: type) type { |
| 68 | other.deinit(gpa); | 68 | other.deinit(gpa); |
| 69 | self.* = undefined; | 69 | self.* = undefined; |
| 70 | } | 70 | } |
| 71 | |||
| 72 | /// This function is used in the debugger pretty formatters in tools/ to fetch the | ||
| 73 | /// child field order and entry type to facilitate fancy debug printing for this type. | ||
| 74 | fn dbHelper(self: *Slice, child: *S, field: *Field, entry: *Entry) void { | ||
| 75 | _ = self; | ||
| 76 | _ = child; | ||
| 77 | _ = field; | ||
| 78 | _ = entry; | ||
| 79 | } | ||
| 71 | }; | 80 | }; |
| 72 | 81 | ||
| 73 | const Self = @This(); | 82 | const Self = @This(); |
| ... | @@ -463,16 +472,18 @@ pub fn MultiArrayList(comptime S: type) type { | ... | @@ -463,16 +472,18 @@ pub fn MultiArrayList(comptime S: type) type { |
| 463 | } }); | 472 | } }); |
| 464 | }; | 473 | }; |
| 465 | /// This function is used in the debugger pretty formatters in tools/ to fetch the | 474 | /// This function is used in the debugger pretty formatters in tools/ to fetch the |
| 466 | /// child type to facilitate fancy debug printing for this type. | 475 | /// child field order and entry type to facilitate fancy debug printing for this type. |
| 467 | fn dbHelper(self: *Self, child: *S, entry: *Entry) void { | 476 | fn dbHelper(self: *Self, child: *S, field: *Field, entry: *Entry) void { |
| 468 | _ = self; | 477 | _ = self; |
| 469 | _ = child; | 478 | _ = child; |
| 479 | _ = field; | ||
| 470 | _ = entry; | 480 | _ = entry; |
| 471 | } | 481 | } |
| 472 | 482 | ||
| 473 | comptime { | 483 | comptime { |
| 474 | if (builtin.mode == .Debug) { | 484 | if (builtin.mode == .Debug) { |
| 475 | _ = dbHelper; | 485 | _ = dbHelper; |
| 486 | _ = Slice.dbHelper; | ||
| 476 | } | 487 | } |
| 477 | } | 488 | } |
| 478 | }; | 489 | }; |
lib/std/net.zig+31-3| ... | @@ -702,8 +702,10 @@ pub const AddressList = struct { | ... | @@ -702,8 +702,10 @@ pub const AddressList = struct { |
| 702 | } | 702 | } |
| 703 | }; | 703 | }; |
| 704 | 704 | ||
| 705 | pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError; | ||
| 706 | |||
| 705 | /// All memory allocated with `allocator` will be freed before this function returns. | 707 | /// All memory allocated with `allocator` will be freed before this function returns. |
| 706 | pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) !Stream { | 708 | pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream { |
| 707 | const list = try getAddressList(allocator, name, port); | 709 | const list = try getAddressList(allocator, name, port); |
| 708 | defer list.deinit(); | 710 | defer list.deinit(); |
| 709 | 711 | ||
| ... | @@ -720,7 +722,9 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) ! | ... | @@ -720,7 +722,9 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) ! |
| 720 | return std.os.ConnectError.ConnectionRefused; | 722 | return std.os.ConnectError.ConnectionRefused; |
| 721 | } | 723 | } |
| 722 | 724 | ||
| 723 | pub fn tcpConnectToAddress(address: Address) !Stream { | 725 | pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError; |
| 726 | |||
| 727 | pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { | ||
| 724 | const nonblock = if (std.io.is_async) os.SOCK.NONBLOCK else 0; | 728 | const nonblock = if (std.io.is_async) os.SOCK.NONBLOCK else 0; |
| 725 | const sock_flags = os.SOCK.STREAM | nonblock | | 729 | const sock_flags = os.SOCK.STREAM | nonblock | |
| 726 | (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC); | 730 | (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC); |
| ... | @@ -737,8 +741,32 @@ pub fn tcpConnectToAddress(address: Address) !Stream { | ... | @@ -737,8 +741,32 @@ pub fn tcpConnectToAddress(address: Address) !Stream { |
| 737 | return Stream{ .handle = sockfd }; | 741 | return Stream{ .handle = sockfd }; |
| 738 | } | 742 | } |
| 739 | 743 | ||
| 744 | const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || error{ | ||
| 745 | // TODO: break this up into error sets from the various underlying functions | ||
| 746 | |||
| 747 | TemporaryNameServerFailure, | ||
| 748 | NameServerFailure, | ||
| 749 | AddressFamilyNotSupported, | ||
| 750 | UnknownHostName, | ||
| 751 | ServiceUnavailable, | ||
| 752 | Unexpected, | ||
| 753 | |||
| 754 | HostLacksNetworkAddresses, | ||
| 755 | |||
| 756 | InvalidCharacter, | ||
| 757 | InvalidEnd, | ||
| 758 | NonCanonical, | ||
| 759 | Overflow, | ||
| 760 | Incomplete, | ||
| 761 | InvalidIpv4Mapping, | ||
| 762 | InvalidIPAddressFormat, | ||
| 763 | |||
| 764 | InterfaceNotFound, | ||
| 765 | FileSystem, | ||
| 766 | }; | ||
| 767 | |||
| 740 | /// Call `AddressList.deinit` on the result. | 768 | /// Call `AddressList.deinit` on the result. |
| 741 | pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*AddressList { | 769 | pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList { |
| 742 | const result = blk: { | 770 | const result = blk: { |
| 743 | var arena = std.heap.ArenaAllocator.init(allocator); | 771 | var arena = std.heap.ArenaAllocator.init(allocator); |
| 744 | errdefer arena.deinit(); | 772 | errdefer arena.deinit(); |
lib/std/os.zig+84-39| ... | @@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator; |
| 29 | const Preopen = std.fs.wasi.Preopen; | 29 | const Preopen = std.fs.wasi.Preopen; |
| 30 | const PreopenList = std.fs.wasi.PreopenList; | 30 | const PreopenList = std.fs.wasi.PreopenList; |
| 31 | 31 | ||
| 32 | pub const darwin = @import("os/darwin.zig"); | 32 | pub const darwin = std.c; |
| 33 | pub const dragonfly = std.c; | 33 | pub const dragonfly = std.c; |
| 34 | pub const freebsd = std.c; | 34 | pub const freebsd = std.c; |
| 35 | pub const haiku = std.c; | 35 | pub const haiku = std.c; |
| ... | @@ -41,8 +41,6 @@ pub const plan9 = @import("os/plan9.zig"); | ... | @@ -41,8 +41,6 @@ pub const plan9 = @import("os/plan9.zig"); |
| 41 | pub const uefi = @import("os/uefi.zig"); | 41 | pub const uefi = @import("os/uefi.zig"); |
| 42 | pub const wasi = @import("os/wasi.zig"); | 42 | pub const wasi = @import("os/wasi.zig"); |
| 43 | pub const windows = @import("os/windows.zig"); | 43 | pub const windows = @import("os/windows.zig"); |
| 44 | pub const posix_spawn = @import("os/posix_spawn.zig"); | ||
| 45 | pub const ptrace = @import("os/ptrace.zig"); | ||
| 46 | 44 | ||
| 47 | comptime { | 45 | comptime { |
| 48 | assert(@import("std") == std); // std lib tests require --zig-lib-dir | 46 | assert(@import("std") == std); // std lib tests require --zig-lib-dir |
| ... | @@ -56,7 +54,6 @@ test { | ... | @@ -56,7 +54,6 @@ test { |
| 56 | } | 54 | } |
| 57 | _ = wasi; | 55 | _ = wasi; |
| 58 | _ = windows; | 56 | _ = windows; |
| 59 | _ = posix_spawn; | ||
| 60 | 57 | ||
| 61 | _ = @import("os/test.zig"); | 58 | _ = @import("os/test.zig"); |
| 62 | } | 59 | } |
| ... | @@ -253,6 +250,25 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin. | ... | @@ -253,6 +250,25 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin. |
| 253 | else => undefined, | 250 | else => undefined, |
| 254 | }; | 251 | }; |
| 255 | 252 | ||
| 253 | pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); | ||
| 254 | |||
| 255 | fn noopSigHandler(_: c_int) callconv(.C) void {} | ||
| 256 | |||
| 257 | /// On default executed by posix startup code before main(), if SIGPIPE is supported. | ||
| 258 | pub fn maybeIgnoreSigpipe() void { | ||
| 259 | if (have_sigpipe_support and !std.options.keep_sigpipe) { | ||
| 260 | const act = Sigaction{ | ||
| 261 | // We set handler to a noop function instead of SIG.IGN so we don't leak our | ||
| 262 | // signal disposition to a child process | ||
| 263 | .handler = .{ .handler = noopSigHandler }, | ||
| 264 | .mask = empty_sigset, | ||
| 265 | .flags = 0, | ||
| 266 | }; | ||
| 267 | sigaction(SIG.PIPE, &act, null) catch |err| | ||
| 268 | std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)}); | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 256 | /// To obtain errno, call this function with the return value of the | 272 | /// To obtain errno, call this function with the return value of the |
| 257 | /// system function call. For some systems this will obtain the value directly | 273 | /// system function call. For some systems this will obtain the value directly |
| 258 | /// from the return code; for others it will use a thread-local errno variable. | 274 | /// from the return code; for others it will use a thread-local errno variable. |
| ... | @@ -575,22 +591,12 @@ pub fn abort() noreturn { | ... | @@ -575,22 +591,12 @@ pub fn abort() noreturn { |
| 575 | raise(SIG.KILL) catch {}; | 591 | raise(SIG.KILL) catch {}; |
| 576 | exit(127); // Pid 1 might not be signalled in some containers. | 592 | exit(127); // Pid 1 might not be signalled in some containers. |
| 577 | } | 593 | } |
| 578 | if (builtin.os.tag == .uefi) { | 594 | switch (builtin.os.tag) { |
| 579 | exit(0); // TODO choose appropriate exit code | 595 | .uefi, .wasi, .cuda => @trap(), |
| 580 | } | 596 | else => system.abort(), |
| 581 | if (builtin.os.tag == .wasi) { | ||
| 582 | exit(1); | ||
| 583 | } | ||
| 584 | if (builtin.os.tag == .cuda) { | ||
| 585 | // TODO: introduce `@trap` instead of abusing https://github.com/ziglang/zig/issues/2291 | ||
| 586 | @"llvm.trap"(); | ||
| 587 | } | 597 | } |
| 588 | |||
| 589 | system.abort(); | ||
| 590 | } | 598 | } |
| 591 | 599 | ||
| 592 | extern fn @"llvm.trap"() noreturn; | ||
| 593 | |||
| 594 | pub const RaiseError = UnexpectedError; | 600 | pub const RaiseError = UnexpectedError; |
| 595 | 601 | ||
| 596 | pub fn raise(sig: u8) RaiseError!void { | 602 | pub fn raise(sig: u8) RaiseError!void { |
| ... | @@ -759,6 +765,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -759,6 +765,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 759 | /// This operation is non-atomic on the following systems: | 765 | /// This operation is non-atomic on the following systems: |
| 760 | /// * Windows | 766 | /// * Windows |
| 761 | /// On these systems, the read races with concurrent writes to the same file descriptor. | 767 | /// On these systems, the read races with concurrent writes to the same file descriptor. |
| 768 | /// | ||
| 769 | /// This function assumes that all vectors, including zero-length vectors, have | ||
| 770 | /// a pointer within the address space of the application. | ||
| 762 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { | 771 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 763 | if (builtin.os.tag == .windows) { | 772 | if (builtin.os.tag == .windows) { |
| 764 | // TODO improve this to use ReadFileScatter | 773 | // TODO improve this to use ReadFileScatter |
| ... | @@ -1036,6 +1045,8 @@ pub const WriteError = error{ | ... | @@ -1036,6 +1045,8 @@ pub const WriteError = error{ |
| 1036 | FileTooBig, | 1045 | FileTooBig, |
| 1037 | InputOutput, | 1046 | InputOutput, |
| 1038 | NoSpaceLeft, | 1047 | NoSpaceLeft, |
| 1048 | DeviceBusy, | ||
| 1049 | InvalidArgument, | ||
| 1039 | 1050 | ||
| 1040 | /// In WASI, this error may occur when the file descriptor does | 1051 | /// In WASI, this error may occur when the file descriptor does |
| 1041 | /// not hold the required rights to write to it. | 1052 | /// not hold the required rights to write to it. |
| ... | @@ -1122,7 +1133,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | ... | @@ -1122,7 +1133,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 1122 | switch (errno(rc)) { | 1133 | switch (errno(rc)) { |
| 1123 | .SUCCESS => return @intCast(usize, rc), | 1134 | .SUCCESS => return @intCast(usize, rc), |
| 1124 | .INTR => continue, | 1135 | .INTR => continue, |
| 1125 | .INVAL => unreachable, | 1136 | .INVAL => return error.InvalidArgument, |
| 1126 | .FAULT => unreachable, | 1137 | .FAULT => unreachable, |
| 1127 | .AGAIN => return error.WouldBlock, | 1138 | .AGAIN => return error.WouldBlock, |
| 1128 | .BADF => return error.NotOpenForWriting, // can be a race condition. | 1139 | .BADF => return error.NotOpenForWriting, // can be a race condition. |
| ... | @@ -1134,6 +1145,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | ... | @@ -1134,6 +1145,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 1134 | .PERM => return error.AccessDenied, | 1145 | .PERM => return error.AccessDenied, |
| 1135 | .PIPE => return error.BrokenPipe, | 1146 | .PIPE => return error.BrokenPipe, |
| 1136 | .CONNRESET => return error.ConnectionResetByPeer, | 1147 | .CONNRESET => return error.ConnectionResetByPeer, |
| 1148 | .BUSY => return error.DeviceBusy, | ||
| 1137 | else => |err| return unexpectedErrno(err), | 1149 | else => |err| return unexpectedErrno(err), |
| 1138 | } | 1150 | } |
| 1139 | } | 1151 | } |
| ... | @@ -1157,6 +1169,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | ... | @@ -1157,6 +1169,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 1157 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. | 1169 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 1158 | /// | 1170 | /// |
| 1159 | /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. | 1171 | /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. |
| 1172 | /// | ||
| 1173 | /// This function assumes that all vectors, including zero-length vectors, have | ||
| 1174 | /// a pointer within the address space of the application. | ||
| 1160 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { | 1175 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { |
| 1161 | if (builtin.os.tag == .windows) { | 1176 | if (builtin.os.tag == .windows) { |
| 1162 | // TODO improve this to use WriteFileScatter | 1177 | // TODO improve this to use WriteFileScatter |
| ... | @@ -1191,7 +1206,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { | ... | @@ -1191,7 +1206,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { |
| 1191 | switch (errno(rc)) { | 1206 | switch (errno(rc)) { |
| 1192 | .SUCCESS => return @intCast(usize, rc), | 1207 | .SUCCESS => return @intCast(usize, rc), |
| 1193 | .INTR => continue, | 1208 | .INTR => continue, |
| 1194 | .INVAL => unreachable, | 1209 | .INVAL => return error.InvalidArgument, |
| 1195 | .FAULT => unreachable, | 1210 | .FAULT => unreachable, |
| 1196 | .AGAIN => return error.WouldBlock, | 1211 | .AGAIN => return error.WouldBlock, |
| 1197 | .BADF => return error.NotOpenForWriting, // Can be a race condition. | 1212 | .BADF => return error.NotOpenForWriting, // Can be a race condition. |
| ... | @@ -1203,6 +1218,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { | ... | @@ -1203,6 +1218,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { |
| 1203 | .PERM => return error.AccessDenied, | 1218 | .PERM => return error.AccessDenied, |
| 1204 | .PIPE => return error.BrokenPipe, | 1219 | .PIPE => return error.BrokenPipe, |
| 1205 | .CONNRESET => return error.ConnectionResetByPeer, | 1220 | .CONNRESET => return error.ConnectionResetByPeer, |
| 1221 | .BUSY => return error.DeviceBusy, | ||
| 1206 | else => |err| return unexpectedErrno(err), | 1222 | else => |err| return unexpectedErrno(err), |
| 1207 | } | 1223 | } |
| 1208 | } | 1224 | } |
| ... | @@ -1285,7 +1301,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { | ... | @@ -1285,7 +1301,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1285 | switch (errno(rc)) { | 1301 | switch (errno(rc)) { |
| 1286 | .SUCCESS => return @intCast(usize, rc), | 1302 | .SUCCESS => return @intCast(usize, rc), |
| 1287 | .INTR => continue, | 1303 | .INTR => continue, |
| 1288 | .INVAL => unreachable, | 1304 | .INVAL => return error.InvalidArgument, |
| 1289 | .FAULT => unreachable, | 1305 | .FAULT => unreachable, |
| 1290 | .AGAIN => return error.WouldBlock, | 1306 | .AGAIN => return error.WouldBlock, |
| 1291 | .BADF => return error.NotOpenForWriting, // Can be a race condition. | 1307 | .BADF => return error.NotOpenForWriting, // Can be a race condition. |
| ... | @@ -1299,6 +1315,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { | ... | @@ -1299,6 +1315,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1299 | .NXIO => return error.Unseekable, | 1315 | .NXIO => return error.Unseekable, |
| 1300 | .SPIPE => return error.Unseekable, | 1316 | .SPIPE => return error.Unseekable, |
| 1301 | .OVERFLOW => return error.Unseekable, | 1317 | .OVERFLOW => return error.Unseekable, |
| 1318 | .BUSY => return error.DeviceBusy, | ||
| 1302 | else => |err| return unexpectedErrno(err), | 1319 | else => |err| return unexpectedErrno(err), |
| 1303 | } | 1320 | } |
| 1304 | } | 1321 | } |
| ... | @@ -1374,7 +1391,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz | ... | @@ -1374,7 +1391,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz |
| 1374 | switch (errno(rc)) { | 1391 | switch (errno(rc)) { |
| 1375 | .SUCCESS => return @intCast(usize, rc), | 1392 | .SUCCESS => return @intCast(usize, rc), |
| 1376 | .INTR => continue, | 1393 | .INTR => continue, |
| 1377 | .INVAL => unreachable, | 1394 | .INVAL => return error.InvalidArgument, |
| 1378 | .FAULT => unreachable, | 1395 | .FAULT => unreachable, |
| 1379 | .AGAIN => return error.WouldBlock, | 1396 | .AGAIN => return error.WouldBlock, |
| 1380 | .BADF => return error.NotOpenForWriting, // Can be a race condition. | 1397 | .BADF => return error.NotOpenForWriting, // Can be a race condition. |
| ... | @@ -1388,6 +1405,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz | ... | @@ -1388,6 +1405,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz |
| 1388 | .NXIO => return error.Unseekable, | 1405 | .NXIO => return error.Unseekable, |
| 1389 | .SPIPE => return error.Unseekable, | 1406 | .SPIPE => return error.Unseekable, |
| 1390 | .OVERFLOW => return error.Unseekable, | 1407 | .OVERFLOW => return error.Unseekable, |
| 1408 | .BUSY => return error.DeviceBusy, | ||
| 1391 | else => |err| return unexpectedErrno(err), | 1409 | else => |err| return unexpectedErrno(err), |
| 1392 | } | 1410 | } |
| 1393 | } | 1411 | } |
| ... | @@ -3456,7 +3474,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi | ... | @@ -3456,7 +3474,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi |
| 3456 | const rc = system.bind(sock, addr, len); | 3474 | const rc = system.bind(sock, addr, len); |
| 3457 | switch (errno(rc)) { | 3475 | switch (errno(rc)) { |
| 3458 | .SUCCESS => return, | 3476 | .SUCCESS => return, |
| 3459 | .ACCES => return error.AccessDenied, | 3477 | .ACCES, .PERM => return error.AccessDenied, |
| 3460 | .ADDRINUSE => return error.AddressInUse, | 3478 | .ADDRINUSE => return error.AddressInUse, |
| 3461 | .BADF => unreachable, // always a race condition if this error is returned | 3479 | .BADF => unreachable, // always a race condition if this error is returned |
| 3462 | .INVAL => unreachable, // invalid parameters | 3480 | .INVAL => unreachable, // invalid parameters |
| ... | @@ -3983,13 +4001,32 @@ pub const WaitPidResult = struct { | ... | @@ -3983,13 +4001,32 @@ pub const WaitPidResult = struct { |
| 3983 | }; | 4001 | }; |
| 3984 | 4002 | ||
| 3985 | /// Use this version of the `waitpid` wrapper if you spawned your child process using explicit | 4003 | /// Use this version of the `waitpid` wrapper if you spawned your child process using explicit |
| 3986 | /// `fork` and `execve` method. If you spawned your child process using `posix_spawn` method, | 4004 | /// `fork` and `execve` method. |
| 3987 | /// use `std.os.posix_spawn.waitpid` instead. | ||
| 3988 | pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { | 4005 | pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { |
| 3989 | const Status = if (builtin.link_libc) c_int else u32; | 4006 | const Status = if (builtin.link_libc) c_int else u32; |
| 3990 | var status: Status = undefined; | 4007 | var status: Status = undefined; |
| 4008 | const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags; | ||
| 4009 | while (true) { | ||
| 4010 | const rc = system.waitpid(pid, &status, coerced_flags); | ||
| 4011 | switch (errno(rc)) { | ||
| 4012 | .SUCCESS => return .{ | ||
| 4013 | .pid = @intCast(pid_t, rc), | ||
| 4014 | .status = @bitCast(u32, status), | ||
| 4015 | }, | ||
| 4016 | .INTR => continue, | ||
| 4017 | .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 4018 | .INVAL => unreachable, // Invalid flags. | ||
| 4019 | else => unreachable, | ||
| 4020 | } | ||
| 4021 | } | ||
| 4022 | } | ||
| 4023 | |||
| 4024 | pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult { | ||
| 4025 | const Status = if (builtin.link_libc) c_int else u32; | ||
| 4026 | var status: Status = undefined; | ||
| 4027 | const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags; | ||
| 3991 | while (true) { | 4028 | while (true) { |
| 3992 | const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags); | 4029 | const rc = system.wait4(pid, &status, coerced_flags, ru); |
| 3993 | switch (errno(rc)) { | 4030 | switch (errno(rc)) { |
| 3994 | .SUCCESS => return .{ | 4031 | .SUCCESS => return .{ |
| 3995 | .pid = @intCast(pid_t, rc), | 4032 | .pid = @intCast(pid_t, rc), |
| ... | @@ -4310,6 +4347,8 @@ pub const MMapError = error{ | ... | @@ -4310,6 +4347,8 @@ pub const MMapError = error{ |
| 4310 | /// a filesystem that was mounted no-exec. | 4347 | /// a filesystem that was mounted no-exec. |
| 4311 | PermissionDenied, | 4348 | PermissionDenied, |
| 4312 | LockedMemoryLimitExceeded, | 4349 | LockedMemoryLimitExceeded, |
| 4350 | ProcessFdQuotaExceeded, | ||
| 4351 | SystemFdQuotaExceeded, | ||
| 4313 | OutOfMemory, | 4352 | OutOfMemory, |
| 4314 | } || UnexpectedError; | 4353 | } || UnexpectedError; |
| 4315 | 4354 | ||
| ... | @@ -4351,6 +4390,8 @@ pub fn mmap( | ... | @@ -4351,6 +4390,8 @@ pub fn mmap( |
| 4351 | .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow. | 4390 | .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow. |
| 4352 | .NODEV => return error.MemoryMappingNotSupported, | 4391 | .NODEV => return error.MemoryMappingNotSupported, |
| 4353 | .INVAL => unreachable, // Invalid parameters to mmap() | 4392 | .INVAL => unreachable, // Invalid parameters to mmap() |
| 4393 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 4394 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 4354 | .NOMEM => return error.OutOfMemory, | 4395 | .NOMEM => return error.OutOfMemory, |
| 4355 | else => return unexpectedErrno(err), | 4396 | else => return unexpectedErrno(err), |
| 4356 | } | 4397 | } |
| ... | @@ -7086,20 +7127,24 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { | ... | @@ -7086,20 +7127,24 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { |
| 7086 | }; | 7127 | }; |
| 7087 | } | 7128 | } |
| 7088 | 7129 | ||
| 7089 | pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); | 7130 | pub const PtraceError = error{ |
| 7090 | 7131 | DeviceBusy, | |
| 7091 | fn noopSigHandler(_: c_int) callconv(.C) void {} | 7132 | ProcessNotFound, |
| 7133 | PermissionDenied, | ||
| 7134 | } || UnexpectedError; | ||
| 7092 | 7135 | ||
| 7093 | pub fn maybeIgnoreSigpipe() void { | 7136 | /// TODO on other OSes |
| 7094 | if (have_sigpipe_support and !std.options.keep_sigpipe) { | 7137 | pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void { |
| 7095 | const act = Sigaction{ | 7138 | switch (builtin.os.tag) { |
| 7096 | // We set handler to a noop function instead of SIG.IGN so we don't leak our | 7139 | .macos, .ios, .tvos, .watchos => {}, |
| 7097 | // signal disposition to a child process | 7140 | else => @compileError("TODO implement ptrace"), |
| 7098 | .handler = .{ .handler = noopSigHandler }, | ||
| 7099 | .mask = empty_sigset, | ||
| 7100 | .flags = 0, | ||
| 7101 | }; | ||
| 7102 | sigaction(SIG.PIPE, &act, null) catch |err| | ||
| 7103 | std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)}); | ||
| 7104 | } | 7141 | } |
| 7142 | return switch (errno(system.ptrace(request, pid, addr, signal))) { | ||
| 7143 | .SUCCESS => {}, | ||
| 7144 | .SRCH => error.ProcessNotFound, | ||
| 7145 | .INVAL => unreachable, | ||
| 7146 | .PERM => error.PermissionDenied, | ||
| 7147 | .BUSY => error.DeviceBusy, | ||
| 7148 | else => |err| return unexpectedErrno(err), | ||
| 7149 | }; | ||
| 7105 | } | 7150 | } |
lib/std/os/darwin.zig deleted-540| ... | @@ -1,540 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const log = std.log; | ||
| 4 | const mem = std.mem; | ||
| 5 | |||
| 6 | pub const cssm = @import("darwin/cssm.zig"); | ||
| 7 | |||
| 8 | pub usingnamespace std.c; | ||
| 9 | pub usingnamespace mach_task; | ||
| 10 | |||
| 11 | const 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 | ||
| 5 | pub const DB_RECORDTYPE_SCHEMA_START = 0x00000000; | ||
| 6 | pub const DB_RECORDTYPE_SCHEMA_END = DB_RECORDTYPE_SCHEMA_START + 4; | ||
| 7 | |||
| 8 | // Open Group Application Name Space Range Definition | ||
| 9 | pub const DB_RECORDTYPE_OPEN_GROUP_START = 0x0000000A; | ||
| 10 | pub const DB_RECORDTYPE_OPEN_GROUP_END = DB_RECORDTYPE_OPEN_GROUP_START + 8; | ||
| 11 | |||
| 12 | // Industry At Large Application Name Space Range Definition | ||
| 13 | pub const DB_RECORDTYPE_APP_DEFINED_START = 0x80000000; | ||
| 14 | pub const DB_RECORDTYPE_APP_DEFINED_END = 0xffffffff; | ||
| 15 | |||
| 16 | pub const DB_RECORDTYPE = enum(u32) { | ||
| 17 | // Record Types defined in the Schema Management Name Space | ||
| 18 | SCHEMA_INFO = DB_RECORDTYPE_SCHEMA_START + 0, | ||
| 19 | SCHEMA_INDEXES = DB_RECORDTYPE_SCHEMA_START + 1, | ||
| 20 | SCHEMA_ATTRIBUTES = DB_RECORDTYPE_SCHEMA_START + 2, | ||
| 21 | SCHEMA_PARSING_MODULE = DB_RECORDTYPE_SCHEMA_START + 3, | ||
| 22 | |||
| 23 | // Record Types defined in the Open Group Application Name Space | ||
| 24 | ANY = DB_RECORDTYPE_OPEN_GROUP_START + 0, | ||
| 25 | CERT = DB_RECORDTYPE_OPEN_GROUP_START + 1, | ||
| 26 | CRL = DB_RECORDTYPE_OPEN_GROUP_START + 2, | ||
| 27 | POLICY = DB_RECORDTYPE_OPEN_GROUP_START + 3, | ||
| 28 | GENERIC = DB_RECORDTYPE_OPEN_GROUP_START + 4, | ||
| 29 | PUBLIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 5, | ||
| 30 | PRIVATE_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 6, | ||
| 31 | SYMMETRIC_KEY = DB_RECORDTYPE_OPEN_GROUP_START + 7, | ||
| 32 | ALL_KEYS = DB_RECORDTYPE_OPEN_GROUP_START + 8, | ||
| 33 | |||
| 34 | // AppleFileDL record types | ||
| 35 | GENERIC_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 0, | ||
| 36 | INTERNET_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 1, | ||
| 37 | APPLESHARE_PASSWORD = DB_RECORDTYPE_APP_DEFINED_START + 2, | ||
| 38 | |||
| 39 | X509_CERTIFICATE = DB_RECORDTYPE_APP_DEFINED_START + 0x1000, | ||
| 40 | USER_TRUST, | ||
| 41 | X509_CRL, | ||
| 42 | UNLOCK_REFERRAL, | ||
| 43 | EXTENDED_ATTRIBUTE, | ||
| 44 | METADATA = DB_RECORDTYPE_APP_DEFINED_START + 0x8000, | ||
| 45 | |||
| 46 | _, | ||
| 47 | }; | ||
lib/std/os/linux.zig+74-10| ... | @@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { | ... | @@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { |
| 944 | return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); | 944 | return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); |
| 945 | } | 945 | } |
| 946 | 946 | ||
| 947 | pub 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 | |||
| 947 | pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { | 957 | pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { |
| 948 | return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0); | 958 | return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0); |
| 949 | } | 959 | } |
| ... | @@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u | ... | @@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u |
| 1716 | ); | 1726 | ); |
| 1717 | } | 1727 | } |
| 1718 | 1728 | ||
| 1719 | pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { | 1729 | pub fn process_vm_readv(pid: pid_t, local: []iovec, remote: []const iovec_const, flags: usize) usize { |
| 1720 | return syscall6( | 1730 | return syscall6( |
| 1721 | .process_vm_readv, | 1731 | .process_vm_readv, |
| 1722 | @bitCast(usize, @as(isize, pid)), | 1732 | @bitCast(usize, @as(isize, pid)), |
| 1723 | @ptrToInt(local), | 1733 | @ptrToInt(local.ptr), |
| 1724 | local_count, | 1734 | local.len, |
| 1725 | @ptrToInt(remote), | 1735 | @ptrToInt(remote.ptr), |
| 1726 | remote_count, | 1736 | remote.len, |
| 1727 | flags, | 1737 | flags, |
| 1728 | ); | 1738 | ); |
| 1729 | } | 1739 | } |
| 1730 | 1740 | ||
| 1731 | pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { | 1741 | pub fn process_vm_writev(pid: pid_t, local: []const iovec_const, remote: []const iovec_const, flags: usize) usize { |
| 1732 | return syscall6( | 1742 | return syscall6( |
| 1733 | .process_vm_writev, | 1743 | .process_vm_writev, |
| 1734 | @bitCast(usize, @as(isize, pid)), | 1744 | @bitCast(usize, @as(isize, pid)), |
| 1735 | @ptrToInt(local), | 1745 | @ptrToInt(local.ptr), |
| 1736 | local_count, | 1746 | local.len, |
| 1737 | @ptrToInt(remote), | 1747 | @ptrToInt(remote.ptr), |
| 1738 | remote_count, | 1748 | remote.len, |
| 1739 | flags, | 1749 | flags, |
| 1740 | ); | 1750 | ); |
| 1741 | } | 1751 | } |
| ... | @@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize { | ... | @@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize { |
| 1820 | return syscall3(.seccomp, operation, flags, @ptrToInt(args)); | 1830 | return syscall3(.seccomp, operation, flags, @ptrToInt(args)); |
| 1821 | } | 1831 | } |
| 1822 | 1832 | ||
| 1833 | pub 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 | |||
| 1823 | pub const E = switch (native_arch) { | 1850 | pub const E = switch (native_arch) { |
| 1824 | .mips, .mipsel => @import("linux/errno/mips.zig").E, | 1851 | .mips, .mipsel => @import("linux/errno/mips.zig").E, |
| 1825 | .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, | 1852 | .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, |
| ... | @@ -5721,3 +5748,40 @@ pub const AUDIT = struct { | ... | @@ -5721,3 +5748,40 @@ pub const AUDIT = struct { |
| 5721 | } | 5748 | } |
| 5722 | }; | 5749 | }; |
| 5723 | }; | 5750 | }; |
| 5751 | |||
| 5752 | pub const PTRACE = struct { | ||
| 5753 | pub const TRACEME = 0; | ||
| 5754 | pub const PEEKTEXT = 1; | ||
| 5755 | pub const PEEKDATA = 2; | ||
| 5756 | pub const PEEKUSER = 3; | ||
| 5757 | pub const POKETEXT = 4; | ||
| 5758 | pub const POKEDATA = 5; | ||
| 5759 | pub const POKEUSER = 6; | ||
| 5760 | pub const CONT = 7; | ||
| 5761 | pub const KILL = 8; | ||
| 5762 | pub const SINGLESTEP = 9; | ||
| 5763 | pub const GETREGS = 12; | ||
| 5764 | pub const SETREGS = 13; | ||
| 5765 | pub const GETFPREGS = 14; | ||
| 5766 | pub const SETFPREGS = 15; | ||
| 5767 | pub const ATTACH = 16; | ||
| 5768 | pub const DETACH = 17; | ||
| 5769 | pub const GETFPXREGS = 18; | ||
| 5770 | pub const SETFPXREGS = 19; | ||
| 5771 | pub const SYSCALL = 24; | ||
| 5772 | pub const SETOPTIONS = 0x4200; | ||
| 5773 | pub const GETEVENTMSG = 0x4201; | ||
| 5774 | pub const GETSIGINFO = 0x4202; | ||
| 5775 | pub const SETSIGINFO = 0x4203; | ||
| 5776 | pub const GETREGSET = 0x4204; | ||
| 5777 | pub const SETREGSET = 0x4205; | ||
| 5778 | pub const SEIZE = 0x4206; | ||
| 5779 | pub const INTERRUPT = 0x4207; | ||
| 5780 | pub const LISTEN = 0x4208; | ||
| 5781 | pub const PEEKSIGINFO = 0x4209; | ||
| 5782 | pub const GETSIGMASK = 0x420a; | ||
| 5783 | pub const SETSIGMASK = 0x420b; | ||
| 5784 | pub const SECCOMP_GET_FILTER = 0x420c; | ||
| 5785 | pub const SECCOMP_GET_METADATA = 0x420d; | ||
| 5786 | pub const GET_SYSCALL_INFO = 0x420e; | ||
| 5787 | }; |
lib/std/os/linux/io_uring.zig+104-66| ... | @@ -1728,10 +1728,12 @@ test "writev/fsync/readv" { | ... | @@ -1728,10 +1728,12 @@ test "writev/fsync/readv" { |
| 1728 | }; | 1728 | }; |
| 1729 | defer ring.deinit(); | 1729 | defer ring.deinit(); |
| 1730 | 1730 | ||
| 1731 | var tmp = std.testing.tmpDir(.{}); | ||
| 1732 | defer tmp.cleanup(); | ||
| 1733 | |||
| 1731 | const path = "test_io_uring_writev_fsync_readv"; | 1734 | const path = "test_io_uring_writev_fsync_readv"; |
| 1732 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | 1735 | const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); |
| 1733 | defer file.close(); | 1736 | defer file.close(); |
| 1734 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1735 | const fd = file.handle; | 1737 | const fd = file.handle; |
| 1736 | 1738 | ||
| 1737 | const buffer_write = [_]u8{42} ** 128; | 1739 | const buffer_write = [_]u8{42} ** 128; |
| ... | @@ -1796,10 +1798,11 @@ test "write/read" { | ... | @@ -1796,10 +1798,11 @@ test "write/read" { |
| 1796 | }; | 1798 | }; |
| 1797 | defer ring.deinit(); | 1799 | defer ring.deinit(); |
| 1798 | 1800 | ||
| 1801 | var tmp = std.testing.tmpDir(.{}); | ||
| 1802 | defer tmp.cleanup(); | ||
| 1799 | const path = "test_io_uring_write_read"; | 1803 | const path = "test_io_uring_write_read"; |
| 1800 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | 1804 | const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); |
| 1801 | defer file.close(); | 1805 | defer file.close(); |
| 1802 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1803 | const fd = file.handle; | 1806 | const fd = file.handle; |
| 1804 | 1807 | ||
| 1805 | const buffer_write = [_]u8{97} ** 20; | 1808 | const buffer_write = [_]u8{97} ** 20; |
| ... | @@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" { | ... | @@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" { |
| 1842 | }; | 1845 | }; |
| 1843 | defer ring.deinit(); | 1846 | defer ring.deinit(); |
| 1844 | 1847 | ||
| 1848 | var tmp = std.testing.tmpDir(.{}); | ||
| 1849 | defer tmp.cleanup(); | ||
| 1850 | |||
| 1845 | const path = "test_io_uring_write_read_fixed"; | 1851 | const path = "test_io_uring_write_read_fixed"; |
| 1846 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | 1852 | const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); |
| 1847 | defer file.close(); | 1853 | defer file.close(); |
| 1848 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1849 | const fd = file.handle; | 1854 | const fd = file.handle; |
| 1850 | 1855 | ||
| 1851 | var raw_buffers: [2][11]u8 = undefined; | 1856 | var raw_buffers: [2][11]u8 = undefined; |
| ... | @@ -1899,8 +1904,10 @@ test "openat" { | ... | @@ -1899,8 +1904,10 @@ test "openat" { |
| 1899 | }; | 1904 | }; |
| 1900 | defer ring.deinit(); | 1905 | defer ring.deinit(); |
| 1901 | 1906 | ||
| 1907 | var tmp = std.testing.tmpDir(.{}); | ||
| 1908 | defer tmp.cleanup(); | ||
| 1909 | |||
| 1902 | const path = "test_io_uring_openat"; | 1910 | const path = "test_io_uring_openat"; |
| 1903 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1904 | 1911 | ||
| 1905 | // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014 | 1912 | // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014 |
| 1906 | const path_addr = if (builtin.zig_backend == .stage2_llvm) p: { | 1913 | const path_addr = if (builtin.zig_backend == .stage2_llvm) p: { |
| ... | @@ -1910,12 +1917,12 @@ test "openat" { | ... | @@ -1910,12 +1917,12 @@ test "openat" { |
| 1910 | 1917 | ||
| 1911 | const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; | 1918 | const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; |
| 1912 | const mode: os.mode_t = 0o666; | 1919 | const mode: os.mode_t = 0o666; |
| 1913 | const sqe_openat = try ring.openat(0x33333333, linux.AT.FDCWD, path, flags, mode); | 1920 | const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode); |
| 1914 | try testing.expectEqual(linux.io_uring_sqe{ | 1921 | try testing.expectEqual(linux.io_uring_sqe{ |
| 1915 | .opcode = .OPENAT, | 1922 | .opcode = .OPENAT, |
| 1916 | .flags = 0, | 1923 | .flags = 0, |
| 1917 | .ioprio = 0, | 1924 | .ioprio = 0, |
| 1918 | .fd = linux.AT.FDCWD, | 1925 | .fd = tmp.dir.fd, |
| 1919 | .off = 0, | 1926 | .off = 0, |
| 1920 | .addr = path_addr, | 1927 | .addr = path_addr, |
| 1921 | .len = mode, | 1928 | .len = mode, |
| ... | @@ -1931,12 +1938,6 @@ test "openat" { | ... | @@ -1931,12 +1938,6 @@ test "openat" { |
| 1931 | const cqe_openat = try ring.copy_cqe(); | 1938 | const cqe_openat = try ring.copy_cqe(); |
| 1932 | try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data); | 1939 | try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data); |
| 1933 | if (cqe_openat.err() == .INVAL) return error.SkipZigTest; | 1940 | if (cqe_openat.err() == .INVAL) return error.SkipZigTest; |
| 1934 | // AT.FDCWD is not fully supported before kernel 5.6: | ||
| 1935 | // See https://lore.kernel.org/io-uring/20200207155039.12819-1-axboe@kernel.dk/T/ | ||
| 1936 | // We use IORING_FEAT_RW_CUR_POS to know if we are pre-5.6 since that feature was added in 5.6. | ||
| 1937 | if (cqe_openat.err() == .BADF and (ring.features & linux.IORING_FEAT_RW_CUR_POS) == 0) { | ||
| 1938 | return error.SkipZigTest; | ||
| 1939 | } | ||
| 1940 | if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res}); | 1941 | if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res}); |
| 1941 | try testing.expect(cqe_openat.res > 0); | 1942 | try testing.expect(cqe_openat.res > 0); |
| 1942 | try testing.expectEqual(@as(u32, 0), cqe_openat.flags); | 1943 | try testing.expectEqual(@as(u32, 0), cqe_openat.flags); |
| ... | @@ -1954,10 +1955,12 @@ test "close" { | ... | @@ -1954,10 +1955,12 @@ test "close" { |
| 1954 | }; | 1955 | }; |
| 1955 | defer ring.deinit(); | 1956 | defer ring.deinit(); |
| 1956 | 1957 | ||
| 1958 | var tmp = std.testing.tmpDir(.{}); | ||
| 1959 | defer tmp.cleanup(); | ||
| 1960 | |||
| 1957 | const path = "test_io_uring_close"; | 1961 | const path = "test_io_uring_close"; |
| 1958 | const file = try std.fs.cwd().createFile(path, .{}); | 1962 | const file = try tmp.dir.createFile(path, .{}); |
| 1959 | errdefer file.close(); | 1963 | errdefer file.close(); |
| 1960 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1961 | 1964 | ||
| 1962 | const sqe_close = try ring.close(0x44444444, file.handle); | 1965 | const sqe_close = try ring.close(0x44444444, file.handle); |
| 1963 | try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); | 1966 | try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); |
| ... | @@ -1976,6 +1979,11 @@ test "close" { | ... | @@ -1976,6 +1979,11 @@ test "close" { |
| 1976 | test "accept/connect/send/recv" { | 1979 | test "accept/connect/send/recv" { |
| 1977 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 1980 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1978 | 1981 | ||
| 1982 | if (true) { | ||
| 1983 | // https://github.com/ziglang/zig/issues/14907 | ||
| 1984 | return error.SkipZigTest; | ||
| 1985 | } | ||
| 1986 | |||
| 1979 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 1987 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 1980 | error.SystemOutdated => return error.SkipZigTest, | 1988 | error.SystemOutdated => return error.SkipZigTest, |
| 1981 | error.PermissionDenied => return error.SkipZigTest, | 1989 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" { | ... | @@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" { |
| 2017 | test "sendmsg/recvmsg" { | 2025 | test "sendmsg/recvmsg" { |
| 2018 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2026 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2019 | 2027 | ||
| 2028 | if (true) { | ||
| 2029 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2030 | return error.SkipZigTest; | ||
| 2031 | } | ||
| 2032 | |||
| 2020 | var ring = IO_Uring.init(2, 0) catch |err| switch (err) { | 2033 | var ring = IO_Uring.init(2, 0) catch |err| switch (err) { |
| 2021 | error.SystemOutdated => return error.SkipZigTest, | 2034 | error.SystemOutdated => return error.SkipZigTest, |
| 2022 | error.PermissionDenied => return error.SkipZigTest, | 2035 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" { | ... | @@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" { |
| 2024 | }; | 2037 | }; |
| 2025 | defer ring.deinit(); | 2038 | defer ring.deinit(); |
| 2026 | 2039 | ||
| 2040 | if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 | ||
| 2027 | const address_server = try net.Address.parseIp4("127.0.0.1", 3131); | 2041 | const address_server = try net.Address.parseIp4("127.0.0.1", 3131); |
| 2028 | 2042 | ||
| 2029 | const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); | 2043 | const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); |
| ... | @@ -2223,6 +2237,11 @@ test "timeout_remove" { | ... | @@ -2223,6 +2237,11 @@ test "timeout_remove" { |
| 2223 | test "accept/connect/recv/link_timeout" { | 2237 | test "accept/connect/recv/link_timeout" { |
| 2224 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2238 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2225 | 2239 | ||
| 2240 | if (true) { | ||
| 2241 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2242 | return error.SkipZigTest; | ||
| 2243 | } | ||
| 2244 | |||
| 2226 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 2245 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2227 | error.SystemOutdated => return error.SkipZigTest, | 2246 | error.SystemOutdated => return error.SkipZigTest, |
| 2228 | error.PermissionDenied => return error.SkipZigTest, | 2247 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2279,10 +2298,12 @@ test "fallocate" { | ... | @@ -2279,10 +2298,12 @@ test "fallocate" { |
| 2279 | }; | 2298 | }; |
| 2280 | defer ring.deinit(); | 2299 | defer ring.deinit(); |
| 2281 | 2300 | ||
| 2301 | var tmp = std.testing.tmpDir(.{}); | ||
| 2302 | defer tmp.cleanup(); | ||
| 2303 | |||
| 2282 | const path = "test_io_uring_fallocate"; | 2304 | const path = "test_io_uring_fallocate"; |
| 2283 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2305 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2284 | defer file.close(); | 2306 | defer file.close(); |
| 2285 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 2286 | 2307 | ||
| 2287 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); | 2308 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); |
| 2288 | 2309 | ||
| ... | @@ -2323,10 +2344,11 @@ test "statx" { | ... | @@ -2323,10 +2344,11 @@ test "statx" { |
| 2323 | }; | 2344 | }; |
| 2324 | defer ring.deinit(); | 2345 | defer ring.deinit(); |
| 2325 | 2346 | ||
| 2347 | var tmp = std.testing.tmpDir(.{}); | ||
| 2348 | defer tmp.cleanup(); | ||
| 2326 | const path = "test_io_uring_statx"; | 2349 | const path = "test_io_uring_statx"; |
| 2327 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2350 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2328 | defer file.close(); | 2351 | defer file.close(); |
| 2329 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 2330 | 2352 | ||
| 2331 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); | 2353 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); |
| 2332 | 2354 | ||
| ... | @@ -2335,14 +2357,14 @@ test "statx" { | ... | @@ -2335,14 +2357,14 @@ test "statx" { |
| 2335 | var buf: linux.Statx = undefined; | 2357 | var buf: linux.Statx = undefined; |
| 2336 | const sqe = try ring.statx( | 2358 | const sqe = try ring.statx( |
| 2337 | 0xaaaaaaaa, | 2359 | 0xaaaaaaaa, |
| 2338 | linux.AT.FDCWD, | 2360 | tmp.dir.fd, |
| 2339 | path, | 2361 | path, |
| 2340 | 0, | 2362 | 0, |
| 2341 | linux.STATX_SIZE, | 2363 | linux.STATX_SIZE, |
| 2342 | &buf, | 2364 | &buf, |
| 2343 | ); | 2365 | ); |
| 2344 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); | 2366 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); |
| 2345 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2367 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2346 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2368 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2347 | 2369 | ||
| 2348 | const cqe = try ring.copy_cqe(); | 2370 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2355,8 +2377,6 @@ test "statx" { | ... | @@ -2355,8 +2377,6 @@ test "statx" { |
| 2355 | // The filesystem containing the file referred to by fd does not support this operation; | 2377 | // The filesystem containing the file referred to by fd does not support this operation; |
| 2356 | // or the mode is not supported by the filesystem containing the file referred to by fd: | 2378 | // or the mode is not supported by the filesystem containing the file referred to by fd: |
| 2357 | .OPNOTSUPP => return error.SkipZigTest, | 2379 | .OPNOTSUPP => return error.SkipZigTest, |
| 2358 | // The kernel is too old to support FDCWD for dir_fd | ||
| 2359 | .BADF => return error.SkipZigTest, | ||
| 2360 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), | 2380 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2361 | } | 2381 | } |
| 2362 | try testing.expectEqual(linux.io_uring_cqe{ | 2382 | try testing.expectEqual(linux.io_uring_cqe{ |
| ... | @@ -2372,6 +2392,11 @@ test "statx" { | ... | @@ -2372,6 +2392,11 @@ test "statx" { |
| 2372 | test "accept/connect/recv/cancel" { | 2392 | test "accept/connect/recv/cancel" { |
| 2373 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2393 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2374 | 2394 | ||
| 2395 | if (true) { | ||
| 2396 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2397 | return error.SkipZigTest; | ||
| 2398 | } | ||
| 2399 | |||
| 2375 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 2400 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2376 | error.SystemOutdated => return error.SkipZigTest, | 2401 | error.SystemOutdated => return error.SkipZigTest, |
| 2377 | error.PermissionDenied => return error.SkipZigTest, | 2402 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2509,6 +2534,11 @@ test "register_files_update" { | ... | @@ -2509,6 +2534,11 @@ test "register_files_update" { |
| 2509 | test "shutdown" { | 2534 | test "shutdown" { |
| 2510 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2535 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2511 | 2536 | ||
| 2537 | if (true) { | ||
| 2538 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2539 | return error.SkipZigTest; | ||
| 2540 | } | ||
| 2541 | |||
| 2512 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 2542 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2513 | error.SystemOutdated => return error.SkipZigTest, | 2543 | error.SystemOutdated => return error.SkipZigTest, |
| 2514 | error.PermissionDenied => return error.SkipZigTest, | 2544 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2516,6 +2546,7 @@ test "shutdown" { | ... | @@ -2516,6 +2546,7 @@ test "shutdown" { |
| 2516 | }; | 2546 | }; |
| 2517 | defer ring.deinit(); | 2547 | defer ring.deinit(); |
| 2518 | 2548 | ||
| 2549 | if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 | ||
| 2519 | const address = try net.Address.parseIp4("127.0.0.1", 3131); | 2550 | const address = try net.Address.parseIp4("127.0.0.1", 3131); |
| 2520 | 2551 | ||
| 2521 | // Socket bound, expect shutdown to work | 2552 | // Socket bound, expect shutdown to work |
| ... | @@ -2579,28 +2610,28 @@ test "renameat" { | ... | @@ -2579,28 +2610,28 @@ test "renameat" { |
| 2579 | const old_path = "test_io_uring_renameat_old"; | 2610 | const old_path = "test_io_uring_renameat_old"; |
| 2580 | const new_path = "test_io_uring_renameat_new"; | 2611 | const new_path = "test_io_uring_renameat_new"; |
| 2581 | 2612 | ||
| 2613 | var tmp = std.testing.tmpDir(.{}); | ||
| 2614 | defer tmp.cleanup(); | ||
| 2615 | |||
| 2582 | // Write old file with data | 2616 | // Write old file with data |
| 2583 | 2617 | ||
| 2584 | const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 }); | 2618 | const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 }); |
| 2585 | defer { | 2619 | defer old_file.close(); |
| 2586 | old_file.close(); | ||
| 2587 | std.fs.cwd().deleteFile(new_path) catch {}; | ||
| 2588 | } | ||
| 2589 | try old_file.writeAll("hello"); | 2620 | try old_file.writeAll("hello"); |
| 2590 | 2621 | ||
| 2591 | // Submit renameat | 2622 | // Submit renameat |
| 2592 | 2623 | ||
| 2593 | var sqe = try ring.renameat( | 2624 | var sqe = try ring.renameat( |
| 2594 | 0x12121212, | 2625 | 0x12121212, |
| 2595 | linux.AT.FDCWD, | 2626 | tmp.dir.fd, |
| 2596 | old_path, | 2627 | old_path, |
| 2597 | linux.AT.FDCWD, | 2628 | tmp.dir.fd, |
| 2598 | new_path, | 2629 | new_path, |
| 2599 | 0, | 2630 | 0, |
| 2600 | ); | 2631 | ); |
| 2601 | try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); | 2632 | try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); |
| 2602 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2633 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2603 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); | 2634 | try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len)); |
| 2604 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2635 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2605 | 2636 | ||
| 2606 | const cqe = try ring.copy_cqe(); | 2637 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2618,7 +2649,7 @@ test "renameat" { | ... | @@ -2618,7 +2649,7 @@ test "renameat" { |
| 2618 | 2649 | ||
| 2619 | // Validate that the old file doesn't exist anymore | 2650 | // Validate that the old file doesn't exist anymore |
| 2620 | { | 2651 | { |
| 2621 | _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) { | 2652 | _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) { |
| 2622 | error.FileNotFound => {}, | 2653 | error.FileNotFound => {}, |
| 2623 | else => std.debug.panic("unexpected error: {}", .{err}), | 2654 | else => std.debug.panic("unexpected error: {}", .{err}), |
| 2624 | }; | 2655 | }; |
| ... | @@ -2626,7 +2657,7 @@ test "renameat" { | ... | @@ -2626,7 +2657,7 @@ test "renameat" { |
| 2626 | 2657 | ||
| 2627 | // Validate that the new file exists with the proper content | 2658 | // Validate that the new file exists with the proper content |
| 2628 | { | 2659 | { |
| 2629 | const new_file = try std.fs.cwd().openFile(new_path, .{}); | 2660 | const new_file = try tmp.dir.openFile(new_path, .{}); |
| 2630 | defer new_file.close(); | 2661 | defer new_file.close(); |
| 2631 | 2662 | ||
| 2632 | var new_file_data: [16]u8 = undefined; | 2663 | var new_file_data: [16]u8 = undefined; |
| ... | @@ -2647,22 +2678,24 @@ test "unlinkat" { | ... | @@ -2647,22 +2678,24 @@ test "unlinkat" { |
| 2647 | 2678 | ||
| 2648 | const path = "test_io_uring_unlinkat"; | 2679 | const path = "test_io_uring_unlinkat"; |
| 2649 | 2680 | ||
| 2681 | var tmp = std.testing.tmpDir(.{}); | ||
| 2682 | defer tmp.cleanup(); | ||
| 2683 | |||
| 2650 | // Write old file with data | 2684 | // Write old file with data |
| 2651 | 2685 | ||
| 2652 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2686 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2653 | defer file.close(); | 2687 | defer file.close(); |
| 2654 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 2655 | 2688 | ||
| 2656 | // Submit unlinkat | 2689 | // Submit unlinkat |
| 2657 | 2690 | ||
| 2658 | var sqe = try ring.unlinkat( | 2691 | var sqe = try ring.unlinkat( |
| 2659 | 0x12121212, | 2692 | 0x12121212, |
| 2660 | linux.AT.FDCWD, | 2693 | tmp.dir.fd, |
| 2661 | path, | 2694 | path, |
| 2662 | 0, | 2695 | 0, |
| 2663 | ); | 2696 | ); |
| 2664 | try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode); | 2697 | try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode); |
| 2665 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2698 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2666 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2699 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2667 | 2700 | ||
| 2668 | const cqe = try ring.copy_cqe(); | 2701 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2679,7 +2712,7 @@ test "unlinkat" { | ... | @@ -2679,7 +2712,7 @@ test "unlinkat" { |
| 2679 | }, cqe); | 2712 | }, cqe); |
| 2680 | 2713 | ||
| 2681 | // Validate that the file doesn't exist anymore | 2714 | // Validate that the file doesn't exist anymore |
| 2682 | _ = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { | 2715 | _ = tmp.dir.openFile(path, .{}) catch |err| switch (err) { |
| 2683 | error.FileNotFound => {}, | 2716 | error.FileNotFound => {}, |
| 2684 | else => std.debug.panic("unexpected error: {}", .{err}), | 2717 | else => std.debug.panic("unexpected error: {}", .{err}), |
| 2685 | }; | 2718 | }; |
| ... | @@ -2695,20 +2728,21 @@ test "mkdirat" { | ... | @@ -2695,20 +2728,21 @@ test "mkdirat" { |
| 2695 | }; | 2728 | }; |
| 2696 | defer ring.deinit(); | 2729 | defer ring.deinit(); |
| 2697 | 2730 | ||
| 2698 | const path = "test_io_uring_mkdirat"; | 2731 | var tmp = std.testing.tmpDir(.{}); |
| 2732 | defer tmp.cleanup(); | ||
| 2699 | 2733 | ||
| 2700 | defer std.fs.cwd().deleteDir(path) catch {}; | 2734 | const path = "test_io_uring_mkdirat"; |
| 2701 | 2735 | ||
| 2702 | // Submit mkdirat | 2736 | // Submit mkdirat |
| 2703 | 2737 | ||
| 2704 | var sqe = try ring.mkdirat( | 2738 | var sqe = try ring.mkdirat( |
| 2705 | 0x12121212, | 2739 | 0x12121212, |
| 2706 | linux.AT.FDCWD, | 2740 | tmp.dir.fd, |
| 2707 | path, | 2741 | path, |
| 2708 | 0o0755, | 2742 | 0o0755, |
| 2709 | ); | 2743 | ); |
| 2710 | try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode); | 2744 | try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode); |
| 2711 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2745 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2712 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2746 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2713 | 2747 | ||
| 2714 | const cqe = try ring.copy_cqe(); | 2748 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2725,7 +2759,7 @@ test "mkdirat" { | ... | @@ -2725,7 +2759,7 @@ test "mkdirat" { |
| 2725 | }, cqe); | 2759 | }, cqe); |
| 2726 | 2760 | ||
| 2727 | // Validate that the directory exist | 2761 | // Validate that the directory exist |
| 2728 | _ = try std.fs.cwd().openDir(path, .{}); | 2762 | _ = try tmp.dir.openDir(path, .{}); |
| 2729 | } | 2763 | } |
| 2730 | 2764 | ||
| 2731 | test "symlinkat" { | 2765 | test "symlinkat" { |
| ... | @@ -2738,26 +2772,25 @@ test "symlinkat" { | ... | @@ -2738,26 +2772,25 @@ test "symlinkat" { |
| 2738 | }; | 2772 | }; |
| 2739 | defer ring.deinit(); | 2773 | defer ring.deinit(); |
| 2740 | 2774 | ||
| 2775 | var tmp = std.testing.tmpDir(.{}); | ||
| 2776 | defer tmp.cleanup(); | ||
| 2777 | |||
| 2741 | const path = "test_io_uring_symlinkat"; | 2778 | const path = "test_io_uring_symlinkat"; |
| 2742 | const link_path = "test_io_uring_symlinkat_link"; | 2779 | const link_path = "test_io_uring_symlinkat_link"; |
| 2743 | 2780 | ||
| 2744 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2781 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2745 | defer { | 2782 | defer file.close(); |
| 2746 | file.close(); | ||
| 2747 | std.fs.cwd().deleteFile(path) catch {}; | ||
| 2748 | std.fs.cwd().deleteFile(link_path) catch {}; | ||
| 2749 | } | ||
| 2750 | 2783 | ||
| 2751 | // Submit symlinkat | 2784 | // Submit symlinkat |
| 2752 | 2785 | ||
| 2753 | var sqe = try ring.symlinkat( | 2786 | var sqe = try ring.symlinkat( |
| 2754 | 0x12121212, | 2787 | 0x12121212, |
| 2755 | path, | 2788 | path, |
| 2756 | linux.AT.FDCWD, | 2789 | tmp.dir.fd, |
| 2757 | link_path, | 2790 | link_path, |
| 2758 | ); | 2791 | ); |
| 2759 | try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode); | 2792 | try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode); |
| 2760 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2793 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2761 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2794 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2762 | 2795 | ||
| 2763 | const cqe = try ring.copy_cqe(); | 2796 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2774,7 +2807,7 @@ test "symlinkat" { | ... | @@ -2774,7 +2807,7 @@ test "symlinkat" { |
| 2774 | }, cqe); | 2807 | }, cqe); |
| 2775 | 2808 | ||
| 2776 | // Validate that the symlink exist | 2809 | // Validate that the symlink exist |
| 2777 | _ = try std.fs.cwd().openFile(link_path, .{}); | 2810 | _ = try tmp.dir.openFile(link_path, .{}); |
| 2778 | } | 2811 | } |
| 2779 | 2812 | ||
| 2780 | test "linkat" { | 2813 | test "linkat" { |
| ... | @@ -2787,32 +2820,31 @@ test "linkat" { | ... | @@ -2787,32 +2820,31 @@ test "linkat" { |
| 2787 | }; | 2820 | }; |
| 2788 | defer ring.deinit(); | 2821 | defer ring.deinit(); |
| 2789 | 2822 | ||
| 2823 | var tmp = std.testing.tmpDir(.{}); | ||
| 2824 | defer tmp.cleanup(); | ||
| 2825 | |||
| 2790 | const first_path = "test_io_uring_linkat_first"; | 2826 | const first_path = "test_io_uring_linkat_first"; |
| 2791 | const second_path = "test_io_uring_linkat_second"; | 2827 | const second_path = "test_io_uring_linkat_second"; |
| 2792 | 2828 | ||
| 2793 | // Write file with data | 2829 | // Write file with data |
| 2794 | 2830 | ||
| 2795 | const first_file = try std.fs.cwd().createFile(first_path, .{ .truncate = true, .mode = 0o666 }); | 2831 | const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 }); |
| 2796 | defer { | 2832 | defer first_file.close(); |
| 2797 | first_file.close(); | ||
| 2798 | std.fs.cwd().deleteFile(first_path) catch {}; | ||
| 2799 | std.fs.cwd().deleteFile(second_path) catch {}; | ||
| 2800 | } | ||
| 2801 | try first_file.writeAll("hello"); | 2833 | try first_file.writeAll("hello"); |
| 2802 | 2834 | ||
| 2803 | // Submit linkat | 2835 | // Submit linkat |
| 2804 | 2836 | ||
| 2805 | var sqe = try ring.linkat( | 2837 | var sqe = try ring.linkat( |
| 2806 | 0x12121212, | 2838 | 0x12121212, |
| 2807 | linux.AT.FDCWD, | 2839 | tmp.dir.fd, |
| 2808 | first_path, | 2840 | first_path, |
| 2809 | linux.AT.FDCWD, | 2841 | tmp.dir.fd, |
| 2810 | second_path, | 2842 | second_path, |
| 2811 | 0, | 2843 | 0, |
| 2812 | ); | 2844 | ); |
| 2813 | try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode); | 2845 | try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode); |
| 2814 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2846 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2815 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); | 2847 | try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len)); |
| 2816 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2848 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2817 | 2849 | ||
| 2818 | const cqe = try ring.copy_cqe(); | 2850 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2829,7 +2861,7 @@ test "linkat" { | ... | @@ -2829,7 +2861,7 @@ test "linkat" { |
| 2829 | }, cqe); | 2861 | }, cqe); |
| 2830 | 2862 | ||
| 2831 | // Validate the second file | 2863 | // Validate the second file |
| 2832 | const second_file = try std.fs.cwd().openFile(second_path, .{}); | 2864 | const second_file = try tmp.dir.openFile(second_path, .{}); |
| 2833 | defer second_file.close(); | 2865 | defer second_file.close(); |
| 2834 | 2866 | ||
| 2835 | var second_file_data: [16]u8 = undefined; | 2867 | var second_file_data: [16]u8 = undefined; |
| ... | @@ -3060,6 +3092,11 @@ test "remove_buffers" { | ... | @@ -3060,6 +3092,11 @@ test "remove_buffers" { |
| 3060 | test "provide_buffers: accept/connect/send/recv" { | 3092 | test "provide_buffers: accept/connect/send/recv" { |
| 3061 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 3093 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 3062 | 3094 | ||
| 3095 | if (true) { | ||
| 3096 | // https://github.com/ziglang/zig/issues/14907 | ||
| 3097 | return error.SkipZigTest; | ||
| 3098 | } | ||
| 3099 | |||
| 3063 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 3100 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 3064 | error.SystemOutdated => return error.SkipZigTest, | 3101 | error.SystemOutdated => return error.SkipZigTest, |
| 3065 | error.PermissionDenied => return error.SkipZigTest, | 3102 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -3236,6 +3273,7 @@ const SocketTestHarness = struct { | ... | @@ -3236,6 +3273,7 @@ const SocketTestHarness = struct { |
| 3236 | fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness { | 3273 | fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness { |
| 3237 | // Create a TCP server socket | 3274 | // Create a TCP server socket |
| 3238 | 3275 | ||
| 3276 | if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 | ||
| 3239 | const address = try net.Address.parseIp4("127.0.0.1", 3131); | 3277 | const address = try net.Address.parseIp4("127.0.0.1", 3131); |
| 3240 | const kernel_backlog = 1; | 3278 | const kernel_backlog = 1; |
| 3241 | const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | 3279 | const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); |
lib/std/os/linux/test.zig+15-18| ... | @@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual; | ... | @@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual; |
| 8 | const fs = std.fs; | 8 | const fs = std.fs; |
| 9 | 9 | ||
| 10 | test "fallocate" { | 10 | test "fallocate" { |
| 11 | var tmp = std.testing.tmpDir(.{}); | ||
| 12 | defer tmp.cleanup(); | ||
| 13 | |||
| 11 | const path = "test_fallocate"; | 14 | const path = "test_fallocate"; |
| 12 | const file = try fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 15 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 13 | defer file.close(); | 16 | defer file.close(); |
| 14 | defer fs.cwd().deleteFile(path) catch {}; | ||
| 15 | 17 | ||
| 16 | try expect((try file.stat()).size == 0); | 18 | try expect((try file.stat()).size == 0); |
| 17 | 19 | ||
| ... | @@ -67,12 +69,12 @@ test "timer" { | ... | @@ -67,12 +69,12 @@ test "timer" { |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | test "statx" { | 71 | test "statx" { |
| 72 | var tmp = std.testing.tmpDir(.{}); | ||
| 73 | defer tmp.cleanup(); | ||
| 74 | |||
| 70 | const tmp_file_name = "just_a_temporary_file.txt"; | 75 | const tmp_file_name = "just_a_temporary_file.txt"; |
| 71 | var file = try fs.cwd().createFile(tmp_file_name, .{}); | 76 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 72 | defer { | 77 | defer file.close(); |
| 73 | file.close(); | ||
| 74 | fs.cwd().deleteFile(tmp_file_name) catch {}; | ||
| 75 | } | ||
| 76 | 78 | ||
| 77 | var statx_buf: linux.Statx = undefined; | 79 | var statx_buf: linux.Statx = undefined; |
| 78 | switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { | 80 | switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { |
| ... | @@ -105,21 +107,16 @@ test "user and group ids" { | ... | @@ -105,21 +107,16 @@ test "user and group ids" { |
| 105 | } | 107 | } |
| 106 | 108 | ||
| 107 | test "fadvise" { | 109 | test "fadvise" { |
| 110 | var tmp = std.testing.tmpDir(.{}); | ||
| 111 | defer tmp.cleanup(); | ||
| 112 | |||
| 108 | const tmp_file_name = "temp_posix_fadvise.txt"; | 113 | const tmp_file_name = "temp_posix_fadvise.txt"; |
| 109 | var file = try fs.cwd().createFile(tmp_file_name, .{}); | 114 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 110 | defer { | 115 | defer file.close(); |
| 111 | file.close(); | ||
| 112 | fs.cwd().deleteFile(tmp_file_name) catch {}; | ||
| 113 | } | ||
| 114 | 116 | ||
| 115 | var buf: [2048]u8 = undefined; | 117 | var buf: [2048]u8 = undefined; |
| 116 | try file.writeAll(&buf); | 118 | try file.writeAll(&buf); |
| 117 | 119 | ||
| 118 | const ret = linux.fadvise( | 120 | const ret = linux.fadvise(file.handle, 0, 0, linux.POSIX_FADV.SEQUENTIAL); |
| 119 | file.handle, | ||
| 120 | 0, | ||
| 121 | 0, | ||
| 122 | linux.POSIX_FADV.SEQUENTIAL, | ||
| 123 | ); | ||
| 124 | try expectEqual(@as(usize, 0), ret); | 121 | try expectEqual(@as(usize, 0), ret); |
| 125 | } | 122 | } |
lib/std/os/posix_spawn.zig deleted-290| ... | @@ -1,290 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const os = @import("../os.zig"); | ||
| 5 | const system = os.system; | ||
| 6 | const errno = system.getErrno; | ||
| 7 | const fd_t = system.fd_t; | ||
| 8 | const mode_t = system.mode_t; | ||
| 9 | const pid_t = system.pid_t; | ||
| 10 | const unexpectedErrno = os.unexpectedErrno; | ||
| 11 | const UnexpectedError = os.UnexpectedError; | ||
| 12 | const toPosixPath = os.toPosixPath; | ||
| 13 | const WaitPidResult = os.WaitPidResult; | ||
| 14 | |||
| 15 | pub usingnamespace posix_spawn; | ||
| 16 | |||
| 17 | pub 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 | |||
| 35 | const 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const os = @import("../os.zig"); | ||
| 5 | const system = os.system; | ||
| 6 | const errno = system.getErrno; | ||
| 7 | const pid_t = system.pid_t; | ||
| 8 | const unexpectedErrno = os.unexpectedErrno; | ||
| 9 | const UnexpectedError = os.UnexpectedError; | ||
| 10 | |||
| 11 | pub usingnamespace ptrace; | ||
| 12 | |||
| 13 | const ptrace = if (builtin.target.isDarwin()) struct { | ||
| 14 | pub const PtraceError = error{ | ||
| 15 | ProcessNotFound, | ||
| 16 | PermissionDenied, | ||
| 17 | } || UnexpectedError; | ||
| 18 | |||
| 19 | pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void { | ||
| 20 | switch (errno(system.ptrace(request, pid, addr, signal))) { | ||
| 21 | .SUCCESS => return, | ||
| 22 | .SRCH => return error.ProcessNotFound, | ||
| 23 | .INVAL => unreachable, | ||
| 24 | .BUSY, .PERM => return error.PermissionDenied, | ||
| 25 | else => |err| return unexpectedErrno(err), | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } else struct {}; | ||
lib/std/os/windows.zig+47-35| ... | @@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 105 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. | 105 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. |
| 106 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; | 106 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; |
| 107 | 107 | ||
| 108 | const rc = ntdll.NtCreateFile( | 108 | while (true) { |
| 109 | &result, | 109 | const rc = ntdll.NtCreateFile( |
| 110 | options.access_mask, | 110 | &result, |
| 111 | &attr, | 111 | options.access_mask, |
| 112 | &io, | 112 | &attr, |
| 113 | null, | 113 | &io, |
| 114 | FILE_ATTRIBUTE_NORMAL, | 114 | null, |
| 115 | options.share_access, | 115 | FILE_ATTRIBUTE_NORMAL, |
| 116 | options.creation, | 116 | options.share_access, |
| 117 | flags, | 117 | options.creation, |
| 118 | null, | 118 | flags, |
| 119 | 0, | 119 | null, |
| 120 | ); | 120 | 0, |
| 121 | switch (rc) { | 121 | ); |
| 122 | .SUCCESS => { | 122 | switch (rc) { |
| 123 | if (std.io.is_async and options.io_mode == .evented) { | 123 | .SUCCESS => { |
| 124 | _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; | 124 | if (std.io.is_async and options.io_mode == .evented) { |
| 125 | } | 125 | _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; |
| 126 | return result; | 126 | } |
| 127 | }, | 127 | return result; |
| 128 | .OBJECT_NAME_INVALID => unreachable, | 128 | }, |
| 129 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | 129 | .OBJECT_NAME_INVALID => unreachable, |
| 130 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | 130 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 131 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | 131 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 132 | .INVALID_PARAMETER => unreachable, | 132 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 133 | .SHARING_VIOLATION => return error.AccessDenied, | 133 | .INVALID_PARAMETER => unreachable, |
| 134 | .ACCESS_DENIED => return error.AccessDenied, | 134 | .SHARING_VIOLATION => return error.AccessDenied, |
| 135 | .PIPE_BUSY => return error.PipeBusy, | 135 | .ACCESS_DENIED => return error.AccessDenied, |
| 136 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | 136 | .PIPE_BUSY => return error.PipeBusy, |
| 137 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, | 137 | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 138 | .FILE_IS_A_DIRECTORY => return error.IsDir, | 138 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 139 | .NOT_A_DIRECTORY => return error.NotDir, | 139 | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 140 | .USER_MAPPED_FILE => return error.AccessDenied, | 140 | .NOT_A_DIRECTORY => return error.NotDir, |
| 141 | .INVALID_HANDLE => unreachable, | 141 | .USER_MAPPED_FILE => return error.AccessDenied, |
| 142 | else => return unexpectedStatus(rc), | 142 | .INVALID_HANDLE => unreachable, |
| 143 | .DELETE_PENDING => { | ||
| 144 | // This error means that there *was* a file in this location on | ||
| 145 | // the file system, but it was deleted. However, the OS is not | ||
| 146 | // finished with the deletion operation, and so this CreateFile | ||
| 147 | // call has failed. There is not really a sane way to handle | ||
| 148 | // this other than retrying the creation after the OS finishes | ||
| 149 | // the deletion. | ||
| 150 | std.time.sleep(std.time.ns_per_ms); | ||
| 151 | continue; | ||
| 152 | }, | ||
| 153 | else => return unexpectedStatus(rc), | ||
| 154 | } | ||
| 143 | } | 155 | } |
| 144 | } | 156 | } |
| 145 | 157 |
lib/std/os/windows/advapi32.zig+2| ... | @@ -27,6 +27,8 @@ pub extern "advapi32" fn RegQueryValueExW( | ... | @@ -27,6 +27,8 @@ pub extern "advapi32" fn RegQueryValueExW( |
| 27 | lpcbData: ?*DWORD, | 27 | lpcbData: ?*DWORD, |
| 28 | ) callconv(WINAPI) LSTATUS; | 28 | ) callconv(WINAPI) LSTATUS; |
| 29 | 29 | ||
| 30 | pub extern "advapi32" fn RegCloseKey(hKey: HKEY) callconv(WINAPI) LSTATUS; | ||
| 31 | |||
| 30 | // RtlGenRandom is known as SystemFunction036 under advapi32 | 32 | // RtlGenRandom is known as SystemFunction036 under advapi32 |
| 31 | // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ | 33 | // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ |
| 32 | pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(WINAPI) BOOL; | 34 | pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(WINAPI) BOOL; |
lib/std/os/windows/kernel32.zig+3| ... | @@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; | ... | @@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; |
| 67 | const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; | 67 | const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; |
| 68 | const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE; | 68 | const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE; |
| 69 | const MODULEENTRY32 = windows.MODULEENTRY32; | 69 | const MODULEENTRY32 = windows.MODULEENTRY32; |
| 70 | const ULONGLONG = windows.ULONGLONG; | ||
| 70 | 71 | ||
| 71 | pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; | 72 | pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; |
| 72 | pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; | 73 | pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; |
| ... | @@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW( | ... | @@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW( |
| 457 | samDesired: REGSAM, | 458 | samDesired: REGSAM, |
| 458 | phkResult: *HKEY, | 459 | phkResult: *HKEY, |
| 459 | ) callconv(WINAPI) LSTATUS; | 460 | ) callconv(WINAPI) LSTATUS; |
| 461 | |||
| 462 | pub extern "kernel32" fn GetPhysicallyInstalledSystemMemory(TotalMemoryInKilobytes: *ULONGLONG) BOOL; |
lib/std/process.zig+48-18| ... | @@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator | ... | @@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator |
| 828 | return ArgIterator.initWithAllocator(allocator); | 828 | return ArgIterator.initWithAllocator(allocator); |
| 829 | } | 829 | } |
| 830 | 830 | ||
| 831 | test "args iterator" { | ||
| 832 | var ga = std.testing.allocator; | ||
| 833 | var it = try argsWithAllocator(ga); | ||
| 834 | defer it.deinit(); // no-op unless WASI or Windows | ||
| 835 | |||
| 836 | const prog_name = it.next() orelse unreachable; | ||
| 837 | const expected_suffix = switch (builtin.os.tag) { | ||
| 838 | .wasi => "test.wasm", | ||
| 839 | .windows => "test.exe", | ||
| 840 | else => "test", | ||
| 841 | }; | ||
| 842 | const given_suffix = std.fs.path.basename(prog_name); | ||
| 843 | |||
| 844 | try testing.expect(mem.eql(u8, expected_suffix, given_suffix)); | ||
| 845 | try testing.expect(it.next() == null); | ||
| 846 | try testing.expect(!it.skip()); | ||
| 847 | } | ||
| 848 | |||
| 849 | /// Caller must call argsFree on result. | 831 | /// Caller must call argsFree on result. |
| 850 | pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { | 832 | pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { |
| 851 | // TODO refactor to only make 1 allocation. | 833 | // TODO refactor to only make 1 allocation. |
| ... | @@ -1169,3 +1151,51 @@ pub fn execve( | ... | @@ -1169,3 +1151,51 @@ pub fn execve( |
| 1169 | 1151 | ||
| 1170 | return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); | 1152 | return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); |
| 1171 | } | 1153 | } |
| 1154 | |||
| 1155 | pub const TotalSystemMemoryError = error{ | ||
| 1156 | UnknownTotalSystemMemory, | ||
| 1157 | }; | ||
| 1158 | |||
| 1159 | /// Returns the total system memory, in bytes. | ||
| 1160 | pub 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 | |||
| 1174 | fn 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. | ||
| 1195 | pub fn cleanExit() void { | ||
| 1196 | if (builtin.mode == .Debug) { | ||
| 1197 | return; | ||
| 1198 | } else { | ||
| 1199 | exit(0); | ||
| 1200 | } | ||
| 1201 | } |
lib/std/std.zig+10| ... | @@ -185,6 +185,16 @@ pub const options = struct { | ... | @@ -185,6 +185,16 @@ pub const options = struct { |
| 185 | options_override.keep_sigpipe | 185 | options_override.keep_sigpipe |
| 186 | else | 186 | else |
| 187 | false; | 187 | false; |
| 188 | |||
| 189 | pub const http_connection_pool_size = if (@hasDecl(options_override, "http_connection_pool_size")) | ||
| 190 | options_override.http_connection_pool_size | ||
| 191 | else | ||
| 192 | http.Client.default_connection_pool_size; | ||
| 193 | |||
| 194 | pub const side_channels_mitigations: crypto.SideChannelsMitigations = if (@hasDecl(options_override, "side_channels_mitigations")) | ||
| 195 | options_override.side_channels_mitigations | ||
| 196 | else | ||
| 197 | crypto.default_side_channels_mitigations; | ||
| 188 | }; | 198 | }; |
| 189 | 199 | ||
| 190 | // This forces the start.zig file to be imported, and the comptime logic inside that | 200 | // This forces the start.zig file to be imported, and the comptime logic inside that |
lib/std/target.zig+2-10| ... | @@ -724,11 +724,7 @@ pub const Target = struct { | ... | @@ -724,11 +724,7 @@ pub const Target = struct { |
| 724 | 724 | ||
| 725 | /// Adds the specified feature set but not its dependencies. | 725 | /// Adds the specified feature set but not its dependencies. |
| 726 | pub fn addFeatureSet(set: *Set, other_set: Set) void { | 726 | pub fn addFeatureSet(set: *Set, other_set: Set) void { |
| 727 | if (builtin.zig_backend == .stage2_c) { | 727 | set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); |
| 728 | for (&set.ints, 0..) |*int, i| int.* |= other_set.ints[i]; | ||
| 729 | } else { | ||
| 730 | set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); | ||
| 731 | } | ||
| 732 | } | 728 | } |
| 733 | 729 | ||
| 734 | /// Removes the specified feature but not its dependents. | 730 | /// Removes the specified feature but not its dependents. |
| ... | @@ -740,11 +736,7 @@ pub const Target = struct { | ... | @@ -740,11 +736,7 @@ pub const Target = struct { |
| 740 | 736 | ||
| 741 | /// Removes the specified feature but not its dependents. | 737 | /// Removes the specified feature but not its dependents. |
| 742 | pub fn removeFeatureSet(set: *Set, other_set: Set) void { | 738 | pub fn removeFeatureSet(set: *Set, other_set: Set) void { |
| 743 | if (builtin.zig_backend == .stage2_c) { | 739 | set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); |
| 744 | for (&set.ints, 0..) |*int, i| int.* &= ~other_set.ints[i]; | ||
| 745 | } else { | ||
| 746 | set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); | ||
| 747 | } | ||
| 748 | } | 740 | } |
| 749 | 741 | ||
| 750 | pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { | 742 | pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { |
lib/std/zig.zig+3| ... | @@ -3,6 +3,9 @@ const tokenizer = @import("zig/tokenizer.zig"); | ... | @@ -3,6 +3,9 @@ const tokenizer = @import("zig/tokenizer.zig"); |
| 3 | const fmt = @import("zig/fmt.zig"); | 3 | const fmt = @import("zig/fmt.zig"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | 5 | ||
| 6 | pub const ErrorBundle = @import("zig/ErrorBundle.zig"); | ||
| 7 | pub const Server = @import("zig/Server.zig"); | ||
| 8 | pub const Client = @import("zig/Client.zig"); | ||
| 6 | pub const Token = tokenizer.Token; | 9 | pub const Token = tokenizer.Token; |
| 7 | pub const Tokenizer = tokenizer.Tokenizer; | 10 | pub const Tokenizer = tokenizer.Tokenizer; |
| 8 | pub const fmtId = fmt.fmtId; | 11 | pub const fmtId = fmt.fmtId; |
lib/std/zig/Ast.zig+6-3| ... | @@ -1407,7 +1407,8 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { | ... | @@ -1407,7 +1407,8 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { |
| 1407 | .type_expr = data.lhs, | 1407 | .type_expr = data.lhs, |
| 1408 | .value_expr = extra.value_expr, | 1408 | .value_expr = extra.value_expr, |
| 1409 | .align_expr = extra.align_expr, | 1409 | .align_expr = extra.align_expr, |
| 1410 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | 1410 | .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or |
| 1411 | tree.tokens.items(.tag)[main_token + 1] != .colon, | ||
| 1411 | }); | 1412 | }); |
| 1412 | } | 1413 | } |
| 1413 | 1414 | ||
| ... | @@ -1420,7 +1421,8 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { | ... | @@ -1420,7 +1421,8 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { |
| 1420 | .type_expr = data.lhs, | 1421 | .type_expr = data.lhs, |
| 1421 | .value_expr = data.rhs, | 1422 | .value_expr = data.rhs, |
| 1422 | .align_expr = 0, | 1423 | .align_expr = 0, |
| 1423 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | 1424 | .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or |
| 1425 | tree.tokens.items(.tag)[main_token + 1] != .colon, | ||
| 1424 | }); | 1426 | }); |
| 1425 | } | 1427 | } |
| 1426 | 1428 | ||
| ... | @@ -1433,7 +1435,8 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { | ... | @@ -1433,7 +1435,8 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { |
| 1433 | .type_expr = data.lhs, | 1435 | .type_expr = data.lhs, |
| 1434 | .value_expr = 0, | 1436 | .value_expr = 0, |
| 1435 | .align_expr = data.rhs, | 1437 | .align_expr = data.rhs, |
| 1436 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | 1438 | .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or |
| 1439 | tree.tokens.items(.tag)[main_token + 1] != .colon, | ||
| 1437 | }); | 1440 | }); |
| 1438 | } | 1441 | } |
| 1439 | 1442 |
lib/std/zig/Client.zig created+39| ... | @@ -0,0 +1,39 @@ | ||
| 1 | pub 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 | |||
| 10 | string_bytes: []const u8, | ||
| 11 | /// The first thing in this array is an `ErrorMessageList`. | ||
| 12 | extra: []const u32, | ||
| 13 | |||
| 14 | /// Special encoding when there are no errors. | ||
| 15 | pub const empty: ErrorBundle = .{ | ||
| 16 | .string_bytes = &.{}, | ||
| 17 | .extra = &.{}, | ||
| 18 | }; | ||
| 19 | |||
| 20 | // An index into `extra` pointing at an `ErrorMessage`. | ||
| 21 | pub const MessageIndex = enum(u32) { | ||
| 22 | _, | ||
| 23 | }; | ||
| 24 | |||
| 25 | // An index into `extra` pointing at an `SourceLocation`. | ||
| 26 | pub const SourceLocationIndex = enum(u32) { | ||
| 27 | none = 0, | ||
| 28 | _, | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// There will be a MessageIndex for each len at start. | ||
| 32 | pub 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 | ||
| 41 | pub 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. | ||
| 60 | pub 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 | |||
| 69 | pub 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 | |||
| 80 | pub fn deinit(eb: *ErrorBundle, gpa: Allocator) void { | ||
| 81 | gpa.free(eb.string_bytes); | ||
| 82 | gpa.free(eb.extra); | ||
| 83 | eb.* = undefined; | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn errorMessageCount(eb: ErrorBundle) u32 { | ||
| 87 | if (eb.extra.len == 0) return 0; | ||
| 88 | return eb.getErrorMessageList().len; | ||
| 89 | } | ||
| 90 | |||
| 91 | pub fn getErrorMessageList(eb: ErrorBundle) ErrorMessageList { | ||
| 92 | return eb.extraData(ErrorMessageList, 0).data; | ||
| 93 | } | ||
| 94 | |||
| 95 | pub 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 | |||
| 100 | pub fn getErrorMessage(eb: ErrorBundle, index: MessageIndex) ErrorMessage { | ||
| 101 | return eb.extraData(ErrorMessage, @enumToInt(index)).data; | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn getSourceLocation(eb: ErrorBundle, index: SourceLocationIndex) SourceLocation { | ||
| 105 | assert(index != .none); | ||
| 106 | return eb.extraData(SourceLocation, @enumToInt(index)).data; | ||
| 107 | } | ||
| 108 | |||
| 109 | pub 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 | |||
| 115 | pub 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. | ||
| 121 | fn 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. | ||
| 141 | pub 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 | |||
| 150 | pub 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 | |||
| 157 | pub 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 | |||
| 164 | pub 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 | |||
| 178 | fn 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. | ||
| 296 | fn 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 | |||
| 306 | const std = @import("std"); | ||
| 307 | const ErrorBundle = @This(); | ||
| 308 | const Allocator = std.mem.Allocator; | ||
| 309 | const assert = std.debug.assert; | ||
| 310 | |||
| 311 | pub 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 @@ | ||
| 1 | in: std.fs.File, | ||
| 2 | out: std.fs.File, | ||
| 3 | receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), | ||
| 4 | |||
| 5 | pub 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 | |||
| 77 | pub const Options = struct { | ||
| 78 | gpa: Allocator, | ||
| 79 | in: std.fs.File, | ||
| 80 | out: std.fs.File, | ||
| 81 | zig_version: []const u8, | ||
| 82 | }; | ||
| 83 | |||
| 84 | pub 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 | |||
| 94 | pub fn deinit(s: *Server) void { | ||
| 95 | s.receive_fifo.deinit(); | ||
| 96 | s.* = undefined; | ||
| 97 | } | ||
| 98 | |||
| 99 | pub 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 | |||
| 134 | pub 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 | |||
| 142 | pub 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 | |||
| 149 | pub 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 | |||
| 169 | pub 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 | |||
| 183 | pub 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 | |||
| 196 | pub 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 | |||
| 214 | pub const TestMetadata = struct { | ||
| 215 | names: []u32, | ||
| 216 | async_frame_sizes: []u32, | ||
| 217 | expected_panic_msgs: []u32, | ||
| 218 | string_bytes: []const u8, | ||
| 219 | }; | ||
| 220 | |||
| 221 | pub 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 | |||
| 253 | fn 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 | |||
| 278 | fn 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 | ||
| 284 | fn 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 | ||
| 290 | fn 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 | |||
| 296 | const OutMessage = std.zig.Server.Message; | ||
| 297 | const InMessage = std.zig.Client.Message; | ||
| 298 | |||
| 299 | const Server = @This(); | ||
| 300 | const builtin = @import("builtin"); | ||
| 301 | const std = @import("std"); | ||
| 302 | const Allocator = std.mem.Allocator; | ||
| 303 | const assert = std.debug.assert; | ||
| 304 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 305 | const need_bswap = native_endian != .Little; | ||
lib/std/zig/system/NativeTargetInfo.zig+5| ... | @@ -1090,6 +1090,11 @@ pub fn getExternalExecutor( | ... | @@ -1090,6 +1090,11 @@ pub fn getExternalExecutor( |
| 1090 | switch (candidate.target.os.tag) { | 1090 | switch (candidate.target.os.tag) { |
| 1091 | .windows => { | 1091 | .windows => { |
| 1092 | if (options.allow_wine) { | 1092 | if (options.allow_wine) { |
| 1093 | // x86_64 wine does not support emulating aarch64-windows and | ||
| 1094 | // vice versa. | ||
| 1095 | if (candidate.target.cpu.arch != builtin.cpu.arch) { | ||
| 1096 | return bad_result; | ||
| 1097 | } | ||
| 1093 | switch (candidate.target.cpu.arch.ptrBitWidth()) { | 1098 | switch (candidate.target.cpu.arch.ptrBitWidth()) { |
| 1094 | 32 => return Executor{ .wine = "wine" }, | 1099 | 32 => return Executor{ .wine = "wine" }, |
| 1095 | 64 => return Executor{ .wine = "wine64" }, | 1100 | 64 => return Executor{ .wine = "wine64" }, |
lib/test_runner.zig+123-45| ... | @@ -8,14 +8,126 @@ pub const std_options = struct { | ... | @@ -8,14 +8,126 @@ pub const std_options = struct { |
| 8 | }; | 8 | }; |
| 9 | 9 | ||
| 10 | var log_err_count: usize = 0; | 10 | var log_err_count: usize = 0; |
| 11 | var cmdline_buffer: [4096]u8 = undefined; | ||
| 12 | var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); | ||
| 11 | 13 | ||
| 12 | pub fn main() void { | 14 | pub fn main() void { |
| 13 | if (builtin.zig_backend != .stage1 and | 15 | if (builtin.zig_backend == .stage2_wasm or |
| 14 | builtin.zig_backend != .stage2_llvm and | 16 | builtin.zig_backend == .stage2_x86_64 or |
| 15 | builtin.zig_backend != .stage2_c) | 17 | builtin.zig_backend == .stage2_aarch64) |
| 16 | { | 18 | { |
| 17 | return main2() catch @panic("test failure"); | 19 | return mainSimple() catch @panic("test failure"); |
| 20 | } | ||
| 21 | |||
| 22 | const args = std.process.argsAlloc(fba.allocator()) catch | ||
| 23 | @panic("unable to parse command line args"); | ||
| 24 | |||
| 25 | var listen = false; | ||
| 26 | |||
| 27 | for (args[1..]) |arg| { | ||
| 28 | if (std.mem.eql(u8, arg, "--listen=-")) { | ||
| 29 | listen = true; | ||
| 30 | } else { | ||
| 31 | @panic("unrecognized command line argument"); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | if (listen) { | ||
| 36 | return mainServer() catch @panic("internal test runner failure"); | ||
| 37 | } else { | ||
| 38 | return mainTerminal(); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | fn mainServer() !void { | ||
| 43 | var server = try std.zig.Server.init(.{ | ||
| 44 | .gpa = fba.allocator(), | ||
| 45 | .in = std.io.getStdIn(), | ||
| 46 | .out = std.io.getStdOut(), | ||
| 47 | .zig_version = builtin.zig_version_string, | ||
| 48 | }); | ||
| 49 | defer server.deinit(); | ||
| 50 | |||
| 51 | while (true) { | ||
| 52 | const hdr = try server.receiveMessage(); | ||
| 53 | switch (hdr.tag) { | ||
| 54 | .exit => { | ||
| 55 | return std.process.exit(0); | ||
| 56 | }, | ||
| 57 | .query_test_metadata => { | ||
| 58 | std.testing.allocator_instance = .{}; | ||
| 59 | defer if (std.testing.allocator_instance.deinit()) { | ||
| 60 | @panic("internal test runner memory leak"); | ||
| 61 | }; | ||
| 62 | |||
| 63 | var string_bytes: std.ArrayListUnmanaged(u8) = .{}; | ||
| 64 | defer string_bytes.deinit(std.testing.allocator); | ||
| 65 | try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null. | ||
| 66 | |||
| 67 | const test_fns = builtin.test_functions; | ||
| 68 | const names = try std.testing.allocator.alloc(u32, test_fns.len); | ||
| 69 | defer std.testing.allocator.free(names); | ||
| 70 | const async_frame_sizes = try std.testing.allocator.alloc(u32, test_fns.len); | ||
| 71 | defer std.testing.allocator.free(async_frame_sizes); | ||
| 72 | const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len); | ||
| 73 | defer std.testing.allocator.free(expected_panic_msgs); | ||
| 74 | |||
| 75 | for (test_fns, names, async_frame_sizes, expected_panic_msgs) |test_fn, *name, *async_frame_size, *expected_panic_msg| { | ||
| 76 | name.* = @intCast(u32, string_bytes.items.len); | ||
| 77 | try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1); | ||
| 78 | string_bytes.appendSliceAssumeCapacity(test_fn.name); | ||
| 79 | string_bytes.appendAssumeCapacity(0); | ||
| 80 | |||
| 81 | async_frame_size.* = @intCast(u32, test_fn.async_frame_size orelse 0); | ||
| 82 | expected_panic_msg.* = 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | try server.serveTestMetadata(.{ | ||
| 86 | .names = names, | ||
| 87 | .async_frame_sizes = async_frame_sizes, | ||
| 88 | .expected_panic_msgs = expected_panic_msgs, | ||
| 89 | .string_bytes = string_bytes.items, | ||
| 90 | }); | ||
| 91 | }, | ||
| 92 | |||
| 93 | .run_test => { | ||
| 94 | std.testing.allocator_instance = .{}; | ||
| 95 | const index = try server.receiveBody_u32(); | ||
| 96 | const test_fn = builtin.test_functions[index]; | ||
| 97 | if (test_fn.async_frame_size != null) | ||
| 98 | @panic("TODO test runner implement async tests"); | ||
| 99 | var fail = false; | ||
| 100 | var skip = false; | ||
| 101 | var leak = false; | ||
| 102 | test_fn.func() catch |err| switch (err) { | ||
| 103 | error.SkipZigTest => skip = true, | ||
| 104 | else => { | ||
| 105 | fail = true; | ||
| 106 | if (@errorReturnTrace()) |trace| { | ||
| 107 | std.debug.dumpStackTrace(trace.*); | ||
| 108 | } | ||
| 109 | }, | ||
| 110 | }; | ||
| 111 | leak = std.testing.allocator_instance.deinit(); | ||
| 112 | try server.serveTestResults(.{ | ||
| 113 | .index = index, | ||
| 114 | .flags = .{ | ||
| 115 | .fail = fail, | ||
| 116 | .skip = skip, | ||
| 117 | .leak = leak, | ||
| 118 | }, | ||
| 119 | }); | ||
| 120 | }, | ||
| 121 | |||
| 122 | else => { | ||
| 123 | std.debug.print("unsupported message: {x}", .{@enumToInt(hdr.tag)}); | ||
| 124 | std.process.exit(1); | ||
| 125 | }, | ||
| 126 | } | ||
| 18 | } | 127 | } |
| 128 | } | ||
| 129 | |||
| 130 | fn mainTerminal() void { | ||
| 19 | const test_fn_list = builtin.test_functions; | 131 | const test_fn_list = builtin.test_functions; |
| 20 | var ok_count: usize = 0; | 132 | var ok_count: usize = 0; |
| 21 | var skip_count: usize = 0; | 133 | var skip_count: usize = 0; |
| ... | @@ -118,51 +230,17 @@ pub fn log( | ... | @@ -118,51 +230,17 @@ pub fn log( |
| 118 | } | 230 | } |
| 119 | } | 231 | } |
| 120 | 232 | ||
| 121 | pub fn main2() anyerror!void { | 233 | /// Simpler main(), exercising fewer language features, so that |
| 122 | var skipped: usize = 0; | 234 | /// work-in-progress backends can handle it. |
| 123 | var failed: usize = 0; | 235 | pub fn mainSimple() anyerror!void { |
| 124 | // Simpler main(), exercising fewer language features, so that stage2 can handle it. | 236 | //const stderr = std.io.getStdErr(); |
| 125 | for (builtin.test_functions) |test_fn| { | 237 | for (builtin.test_functions) |test_fn| { |
| 126 | test_fn.func() catch |err| { | 238 | test_fn.func() catch |err| { |
| 127 | if (err != error.SkipZigTest) { | 239 | if (err != error.SkipZigTest) { |
| 128 | failed += 1; | 240 | //stderr.writeAll(test_fn.name) catch {}; |
| 129 | } else { | 241 | //stderr.writeAll("\n") catch {}; |
| 130 | skipped += 1; | 242 | return err; |
| 131 | } | 243 | } |
| 132 | }; | 244 | }; |
| 133 | } | 245 | } |
| 134 | if (builtin.zig_backend == .stage2_wasm or | ||
| 135 | builtin.zig_backend == .stage2_x86_64 or | ||
| 136 | builtin.zig_backend == .stage2_aarch64 or | ||
| 137 | builtin.zig_backend == .stage2_llvm or | ||
| 138 | builtin.zig_backend == .stage2_c) | ||
| 139 | { | ||
| 140 | const passed = builtin.test_functions.len - skipped - failed; | ||
| 141 | const stderr = std.io.getStdErr(); | ||
| 142 | writeInt(stderr, passed) catch {}; | ||
| 143 | stderr.writeAll(" passed; ") catch {}; | ||
| 144 | writeInt(stderr, skipped) catch {}; | ||
| 145 | stderr.writeAll(" skipped; ") catch {}; | ||
| 146 | writeInt(stderr, failed) catch {}; | ||
| 147 | stderr.writeAll(" failed.\n") catch {}; | ||
| 148 | } | ||
| 149 | if (failed != 0) { | ||
| 150 | return error.TestsFailed; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | fn writeInt(stderr: std.fs.File, int: usize) anyerror!void { | ||
| 155 | const base = 10; | ||
| 156 | var buf: [100]u8 = undefined; | ||
| 157 | var a: usize = int; | ||
| 158 | var index: usize = buf.len; | ||
| 159 | while (true) { | ||
| 160 | const digit = a % base; | ||
| 161 | index -= 1; | ||
| 162 | buf[index] = std.fmt.digitToChar(@intCast(u8, digit), .lower); | ||
| 163 | a /= base; | ||
| 164 | if (a == 0) break; | ||
| 165 | } | ||
| 166 | const slice = buf[index..]; | ||
| 167 | try stderr.writeAll(slice); | ||
| 168 | } | 246 | } |
lib/zig.h+1097-214| ... | @@ -37,6 +37,14 @@ typedef char bool; | ... | @@ -37,6 +37,14 @@ typedef char bool; |
| 37 | #define zig_has_attribute(attribute) 0 | 37 | #define zig_has_attribute(attribute) 0 |
| 38 | #endif | 38 | #endif |
| 39 | 39 | ||
| 40 | #if __LITTLE_ENDIAN__ || _MSC_VER | ||
| 41 | #define zig_little_endian 1 | ||
| 42 | #define zig_big_endian 0 | ||
| 43 | #else | ||
| 44 | #define zig_little_endian 0 | ||
| 45 | #define zig_big_endian 1 | ||
| 46 | #endif | ||
| 47 | |||
| 40 | #if __STDC_VERSION__ >= 201112L | 48 | #if __STDC_VERSION__ >= 201112L |
| 41 | #define zig_threadlocal _Thread_local | 49 | #define zig_threadlocal _Thread_local |
| 42 | #elif defined(__GNUC__) | 50 | #elif defined(__GNUC__) |
| ... | @@ -180,16 +188,38 @@ typedef char bool; | ... | @@ -180,16 +188,38 @@ typedef char bool; |
| 180 | #define zig_export(sig, symbol, name) __asm(name " = " symbol) | 188 | #define zig_export(sig, symbol, name) __asm(name " = " symbol) |
| 181 | #endif | 189 | #endif |
| 182 | 190 | ||
| 191 | #if zig_has_builtin(trap) | ||
| 192 | #define zig_trap() __builtin_trap() | ||
| 193 | #elif _MSC_VER && (_M_IX86 || _M_X64) | ||
| 194 | #define zig_trap() __ud2() | ||
| 195 | #elif _MSC_VER | ||
| 196 | #define zig_trap() __fastfail(0) | ||
| 197 | #elif defined(__i386__) || defined(__x86_64__) | ||
| 198 | #define zig_trap() __asm__ volatile("ud2"); | ||
| 199 | #elif defined(__arm__) || defined(__aarch64__) | ||
| 200 | #define zig_breakpoint() __asm__ volatile("udf #0"); | ||
| 201 | #else | ||
| 202 | #include <stdlib.h> | ||
| 203 | #define zig_trap() abort() | ||
| 204 | #endif | ||
| 205 | |||
| 183 | #if zig_has_builtin(debugtrap) | 206 | #if zig_has_builtin(debugtrap) |
| 184 | #define zig_breakpoint() __builtin_debugtrap() | 207 | #define zig_breakpoint() __builtin_debugtrap() |
| 185 | #elif zig_has_builtin(trap) || defined(zig_gnuc) | ||
| 186 | #define zig_breakpoint() __builtin_trap() | ||
| 187 | #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) | 208 | #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) |
| 188 | #define zig_breakpoint() __debugbreak() | 209 | #define zig_breakpoint() __debugbreak() |
| 189 | #elif defined(__i386__) || defined(__x86_64__) | 210 | #elif defined(__i386__) || defined(__x86_64__) |
| 190 | #define zig_breakpoint() __asm__ volatile("int $0x03"); | 211 | #define zig_breakpoint() __asm__ volatile("int $0x03"); |
| 212 | #elif defined(__arm__) | ||
| 213 | #define zig_breakpoint() __asm__ volatile("bkpt #0"); | ||
| 214 | #elif defined(__aarch64__) | ||
| 215 | #define zig_breakpoint() __asm__ volatile("brk #0"); | ||
| 191 | #else | 216 | #else |
| 217 | #include <signal.h> | ||
| 218 | #if defined(SIGTRAP) | ||
| 192 | #define zig_breakpoint() raise(SIGTRAP) | 219 | #define zig_breakpoint() raise(SIGTRAP) |
| 220 | #else | ||
| 221 | #define zig_breakpoint() zig_breakpoint_unavailable | ||
| 222 | #endif | ||
| 193 | #endif | 223 | #endif |
| 194 | 224 | ||
| 195 | #if zig_has_builtin(return_address) || defined(zig_gnuc) | 225 | #if zig_has_builtin(return_address) || defined(zig_gnuc) |
| ... | @@ -598,12 +628,6 @@ static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 | ... | @@ -598,12 +628,6 @@ static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 |
| 598 | #endif | 628 | #endif |
| 599 | } | 629 | } |
| 600 | 630 | ||
| 601 | static 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 | |||
| 607 | zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); | 631 | zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 608 | static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { | 632 | static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 609 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 633 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| ... | @@ -618,12 +642,6 @@ static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t | ... | @@ -618,12 +642,6 @@ static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t |
| 618 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); | 642 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 619 | } | 643 | } |
| 620 | 644 | ||
| 621 | static 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 | |||
| 627 | static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { | 645 | static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 628 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 646 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 629 | uint64_t full_res; | 647 | uint64_t full_res; |
| ... | @@ -636,12 +654,6 @@ static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 | ... | @@ -636,12 +654,6 @@ static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 |
| 636 | #endif | 654 | #endif |
| 637 | } | 655 | } |
| 638 | 656 | ||
| 639 | static 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 | |||
| 645 | zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); | 657 | zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 646 | static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { | 658 | static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 647 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 659 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| ... | @@ -656,12 +668,6 @@ static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t | ... | @@ -656,12 +668,6 @@ static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t |
| 656 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); | 668 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 657 | } | 669 | } |
| 658 | 670 | ||
| 659 | static 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 | |||
| 665 | static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { | 671 | static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 666 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 672 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 667 | uint8_t full_res; | 673 | uint8_t full_res; |
| ... | @@ -676,12 +682,6 @@ static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b | ... | @@ -676,12 +682,6 @@ static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b |
| 676 | #endif | 682 | #endif |
| 677 | } | 683 | } |
| 678 | 684 | ||
| 679 | static 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 | |||
| 685 | static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { | 685 | static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 686 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 686 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 687 | int8_t full_res; | 687 | int8_t full_res; |
| ... | @@ -696,12 +696,6 @@ static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits | ... | @@ -696,12 +696,6 @@ static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits |
| 696 | #endif | 696 | #endif |
| 697 | } | 697 | } |
| 698 | 698 | ||
| 699 | static 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 | |||
| 705 | static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { | 699 | static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 706 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 700 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 707 | uint16_t full_res; | 701 | uint16_t full_res; |
| ... | @@ -716,12 +710,6 @@ static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 | ... | @@ -716,12 +710,6 @@ static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 |
| 716 | #endif | 710 | #endif |
| 717 | } | 711 | } |
| 718 | 712 | ||
| 719 | static 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 | |||
| 725 | static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { | 713 | static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 726 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 714 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 727 | int16_t full_res; | 715 | int16_t full_res; |
| ... | @@ -736,12 +724,6 @@ static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t | ... | @@ -736,12 +724,6 @@ static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t |
| 736 | #endif | 724 | #endif |
| 737 | } | 725 | } |
| 738 | 726 | ||
| 739 | static 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 | |||
| 745 | static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { | 727 | static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 746 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 728 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 747 | uint32_t full_res; | 729 | uint32_t full_res; |
| ... | @@ -754,12 +736,6 @@ static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 | ... | @@ -754,12 +736,6 @@ static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 |
| 754 | #endif | 736 | #endif |
| 755 | } | 737 | } |
| 756 | 738 | ||
| 757 | static 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 | |||
| 763 | zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); | 739 | zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 764 | static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { | 740 | static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 765 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 741 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| ... | @@ -774,12 +750,6 @@ static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t | ... | @@ -774,12 +750,6 @@ static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t |
| 774 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); | 750 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 775 | } | 751 | } |
| 776 | 752 | ||
| 777 | static 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 | |||
| 783 | static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { | 753 | static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 784 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 754 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 785 | uint64_t full_res; | 755 | uint64_t full_res; |
| ... | @@ -792,12 +762,6 @@ static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 | ... | @@ -792,12 +762,6 @@ static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 |
| 792 | #endif | 762 | #endif |
| 793 | } | 763 | } |
| 794 | 764 | ||
| 795 | static 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 | |||
| 801 | zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); | 765 | zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 802 | static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { | 766 | static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 803 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 767 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| ... | @@ -812,12 +776,6 @@ static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t | ... | @@ -812,12 +776,6 @@ static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t |
| 812 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); | 776 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 813 | } | 777 | } |
| 814 | 778 | ||
| 815 | static 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 | |||
| 821 | static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { | 779 | static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 822 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 780 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 823 | uint8_t full_res; | 781 | uint8_t full_res; |
| ... | @@ -832,12 +790,6 @@ static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b | ... | @@ -832,12 +790,6 @@ static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b |
| 832 | #endif | 790 | #endif |
| 833 | } | 791 | } |
| 834 | 792 | ||
| 835 | static 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 | |||
| 841 | static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { | 793 | static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 842 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 794 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 843 | int8_t full_res; | 795 | int8_t full_res; |
| ... | @@ -852,13 +804,6 @@ static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits | ... | @@ -852,13 +804,6 @@ static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits |
| 852 | #endif | 804 | #endif |
| 853 | } | 805 | } |
| 854 | 806 | ||
| 855 | static 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 | |||
| 862 | static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { | 807 | static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 863 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 808 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 864 | uint16_t full_res; | 809 | uint16_t full_res; |
| ... | @@ -873,13 +818,6 @@ static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 | ... | @@ -873,13 +818,6 @@ static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 |
| 873 | #endif | 818 | #endif |
| 874 | } | 819 | } |
| 875 | 820 | ||
| 876 | static 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 | |||
| 883 | static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { | 821 | static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 884 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 822 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 885 | int16_t full_res; | 823 | int16_t full_res; |
| ... | @@ -894,12 +832,6 @@ static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t | ... | @@ -894,12 +832,6 @@ static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t |
| 894 | #endif | 832 | #endif |
| 895 | } | 833 | } |
| 896 | 834 | ||
| 897 | static 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 | |||
| 903 | static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { | 835 | static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 904 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 836 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 905 | uint32_t full_res; | 837 | uint32_t full_res; |
| ... | @@ -912,12 +844,6 @@ static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 | ... | @@ -912,12 +844,6 @@ static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8 |
| 912 | #endif | 844 | #endif |
| 913 | } | 845 | } |
| 914 | 846 | ||
| 915 | static 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 | |||
| 921 | zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); | 847 | zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 922 | static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { | 848 | static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 923 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 849 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| ... | @@ -932,12 +858,6 @@ static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t | ... | @@ -932,12 +858,6 @@ static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t |
| 932 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); | 858 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 933 | } | 859 | } |
| 934 | 860 | ||
| 935 | static 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 | |||
| 941 | static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { | 861 | static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 942 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 862 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 943 | uint64_t full_res; | 863 | uint64_t full_res; |
| ... | @@ -950,12 +870,6 @@ static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 | ... | @@ -950,12 +870,6 @@ static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8 |
| 950 | #endif | 870 | #endif |
| 951 | } | 871 | } |
| 952 | 872 | ||
| 953 | static 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 | |||
| 959 | zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); | 873 | zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 960 | static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { | 874 | static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 961 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 875 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| ... | @@ -970,12 +884,6 @@ static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t | ... | @@ -970,12 +884,6 @@ static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t |
| 970 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); | 884 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 971 | } | 885 | } |
| 972 | 886 | ||
| 973 | static 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 | |||
| 979 | static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { | 887 | static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 980 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 888 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 981 | uint8_t full_res; | 889 | uint8_t full_res; |
| ... | @@ -990,12 +898,6 @@ static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b | ... | @@ -990,12 +898,6 @@ static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t b |
| 990 | #endif | 898 | #endif |
| 991 | } | 899 | } |
| 992 | 900 | ||
| 993 | static 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 | |||
| 999 | static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { | 901 | static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 1000 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 902 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 1001 | int8_t full_res; | 903 | int8_t full_res; |
| ... | @@ -1010,12 +912,6 @@ static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits | ... | @@ -1010,12 +912,6 @@ static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits |
| 1010 | #endif | 912 | #endif |
| 1011 | } | 913 | } |
| 1012 | 914 | ||
| 1013 | static 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 | |||
| 1019 | static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { | 915 | static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 1020 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 916 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 1021 | uint16_t full_res; | 917 | uint16_t full_res; |
| ... | @@ -1030,12 +926,6 @@ static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 | ... | @@ -1030,12 +926,6 @@ static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8 |
| 1030 | #endif | 926 | #endif |
| 1031 | } | 927 | } |
| 1032 | 928 | ||
| 1033 | static 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 | |||
| 1039 | static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { | 929 | static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 1040 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 930 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 1041 | int16_t full_res; | 931 | int16_t full_res; |
| ... | @@ -1050,12 +940,6 @@ static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t | ... | @@ -1050,12 +940,6 @@ static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t |
| 1050 | #endif | 940 | #endif |
| 1051 | } | 941 | } |
| 1052 | 942 | ||
| 1053 | static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n, | ||
| 1054 | const int16_t *lhs, const int16_t *rhs, uint8_t bits) | ||
| 1055 | { | ||
| 1056 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | #define zig_int_builtins(w) \ | 943 | #define zig_int_builtins(w) \ |
| 1060 | static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ | 944 | static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 1061 | *res = zig_shlw_u##w(lhs, rhs, bits); \ | 945 | *res = zig_shlw_u##w(lhs, rhs, bits); \ |
| ... | @@ -1354,8 +1238,8 @@ typedef signed __int128 zig_i128; | ... | @@ -1354,8 +1238,8 @@ typedef signed __int128 zig_i128; |
| 1354 | 1238 | ||
| 1355 | #define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) | 1239 | #define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) |
| 1356 | #define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) | 1240 | #define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) |
| 1357 | #define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) | 1241 | #define zig_init_u128(hi, lo) zig_make_u128(hi, lo) |
| 1358 | #define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) | 1242 | #define zig_init_i128(hi, lo) zig_make_i128(hi, lo) |
| 1359 | #define zig_hi_u128(val) ((uint64_t)((val) >> 64)) | 1243 | #define zig_hi_u128(val) ((uint64_t)((val) >> 64)) |
| 1360 | #define zig_lo_u128(val) ((uint64_t)((val) >> 0)) | 1244 | #define zig_lo_u128(val) ((uint64_t)((val) >> 0)) |
| 1361 | #define zig_hi_i128(val) (( int64_t)((val) >> 64)) | 1245 | #define zig_hi_i128(val) (( int64_t)((val) >> 64)) |
| ... | @@ -1373,7 +1257,7 @@ typedef signed __int128 zig_i128; | ... | @@ -1373,7 +1257,7 @@ typedef signed __int128 zig_i128; |
| 1373 | 1257 | ||
| 1374 | #else /* zig_has_int128 */ | 1258 | #else /* zig_has_int128 */ |
| 1375 | 1259 | ||
| 1376 | #if __LITTLE_ENDIAN__ || _MSC_VER | 1260 | #if zig_little_endian |
| 1377 | typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; | 1261 | typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; |
| 1378 | typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; | 1262 | typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; |
| 1379 | #else | 1263 | #else |
| ... | @@ -1385,11 +1269,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; | ... | @@ -1385,11 +1269,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; |
| 1385 | #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) | 1269 | #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) |
| 1386 | 1270 | ||
| 1387 | #if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ | 1271 | #if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ |
| 1388 | #define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } | 1272 | #define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } |
| 1389 | #define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } | 1273 | #define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } |
| 1390 | #else /* But non-MSVC doesn't like the unprotected commas */ | 1274 | #else /* But non-MSVC doesn't like the unprotected commas */ |
| 1391 | #define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) | 1275 | #define zig_init_u128(hi, lo) zig_make_u128(hi, lo) |
| 1392 | #define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) | 1276 | #define zig_init_i128(hi, lo) zig_make_i128(hi, lo) |
| 1393 | #endif | 1277 | #endif |
| 1394 | #define zig_hi_u128(val) ((val).hi) | 1278 | #define zig_hi_u128(val) ((val).hi) |
| 1395 | #define zig_lo_u128(val) ((val).lo) | 1279 | #define zig_lo_u128(val) ((val).lo) |
| ... | @@ -1632,7 +1516,9 @@ static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { | ... | @@ -1632,7 +1516,9 @@ static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { |
| 1632 | } | 1516 | } |
| 1633 | 1517 | ||
| 1634 | static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { | 1518 | static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { |
| 1635 | return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); | 1519 | if (bits > UINT8_C(64)) return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); |
| 1520 | int64_t lo = zig_wrap_i64((int64_t)zig_lo_i128(val), bits); | ||
| 1521 | return zig_make_i128(zig_shr_i64(lo, 63), (uint64_t)lo); | ||
| 1636 | } | 1522 | } |
| 1637 | 1523 | ||
| 1638 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { | 1524 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { |
| ... | @@ -1903,6 +1789,1010 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { | ... | @@ -1903,6 +1789,1010 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { |
| 1903 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); | 1789 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); |
| 1904 | } | 1790 | } |
| 1905 | 1791 | ||
| 1792 | /* ========================== Big Integer Support =========================== */ | ||
| 1793 | |||
| 1794 | static 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 | |||
| 1801 | static 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 | |||
| 1963 | static 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 | |||
| 2179 | static 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 | |||
| 2395 | static 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 | |||
| 2399 | static 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 | |||
| 2403 | zig_extern void __udivei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); | ||
| 2404 | static 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 | |||
| 2413 | static 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 | |||
| 2422 | zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); | ||
| 2423 | static 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 | |||
| 2432 | static 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 | |||
| 2441 | static 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 | |||
| 2567 | static 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 | |||
| 2687 | static inline uint16_t zig_popcount_big(const void *val, bool is_signed, uint16_t bits) { | ||
| 2688 | const uint8_t *val_bytes = val; | ||
| 2689 | uint16_t byte_offset = 0; | ||
| 2690 | uint16_t remaining_bytes = zig_int_bytes(bits); | ||
| 2691 | uint16_t total_pc = 0; | ||
| 2692 | (void)is_signed; | ||
| 2693 | |||
| 2694 | #if zig_big_endian | ||
| 2695 | byte_offset = remaining_bytes; | ||
| 2696 | #endif | ||
| 2697 | |||
| 2698 | while (remaining_bytes >= 128 / CHAR_BIT) { | ||
| 2699 | #if zig_big_endian | ||
| 2700 | byte_offset -= 128 / CHAR_BIT; | ||
| 2701 | #endif | ||
| 2702 | |||
| 2703 | { | ||
| 2704 | zig_u128 val_limb; | ||
| 2705 | |||
| 2706 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2707 | total_pc += zig_popcount_u128(val_limb, 128); | ||
| 2708 | } | ||
| 2709 | |||
| 2710 | remaining_bytes -= 128 / CHAR_BIT; | ||
| 2711 | |||
| 2712 | #if zig_little_endian | ||
| 2713 | byte_offset += 128 / CHAR_BIT; | ||
| 2714 | #endif | ||
| 2715 | } | ||
| 2716 | |||
| 2717 | while (remaining_bytes >= 64 / CHAR_BIT) { | ||
| 2718 | #if zig_big_endian | ||
| 2719 | byte_offset -= 64 / CHAR_BIT; | ||
| 2720 | #endif | ||
| 2721 | |||
| 2722 | { | ||
| 2723 | uint64_t val_limb; | ||
| 2724 | |||
| 2725 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2726 | total_pc += zig_popcount_u64(val_limb, 64); | ||
| 2727 | } | ||
| 2728 | |||
| 2729 | remaining_bytes -= 64 / CHAR_BIT; | ||
| 2730 | |||
| 2731 | #if zig_little_endian | ||
| 2732 | byte_offset += 64 / CHAR_BIT; | ||
| 2733 | #endif | ||
| 2734 | } | ||
| 2735 | |||
| 2736 | while (remaining_bytes >= 32 / CHAR_BIT) { | ||
| 2737 | #if zig_big_endian | ||
| 2738 | byte_offset -= 32 / CHAR_BIT; | ||
| 2739 | #endif | ||
| 2740 | |||
| 2741 | { | ||
| 2742 | uint32_t val_limb; | ||
| 2743 | |||
| 2744 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2745 | total_pc += zig_popcount_u32(val_limb, 32); | ||
| 2746 | } | ||
| 2747 | |||
| 2748 | remaining_bytes -= 32 / CHAR_BIT; | ||
| 2749 | |||
| 2750 | #if zig_little_endian | ||
| 2751 | byte_offset += 32 / CHAR_BIT; | ||
| 2752 | #endif | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | while (remaining_bytes >= 16 / CHAR_BIT) { | ||
| 2756 | #if zig_big_endian | ||
| 2757 | byte_offset -= 16 / CHAR_BIT; | ||
| 2758 | #endif | ||
| 2759 | |||
| 2760 | { | ||
| 2761 | uint16_t val_limb; | ||
| 2762 | |||
| 2763 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2764 | total_pc = zig_popcount_u16(val_limb, 16); | ||
| 2765 | } | ||
| 2766 | |||
| 2767 | remaining_bytes -= 16 / CHAR_BIT; | ||
| 2768 | |||
| 2769 | #if zig_little_endian | ||
| 2770 | byte_offset += 16 / CHAR_BIT; | ||
| 2771 | #endif | ||
| 2772 | } | ||
| 2773 | |||
| 2774 | while (remaining_bytes >= 8 / CHAR_BIT) { | ||
| 2775 | #if zig_big_endian | ||
| 2776 | byte_offset -= 8 / CHAR_BIT; | ||
| 2777 | #endif | ||
| 2778 | |||
| 2779 | { | ||
| 2780 | uint8_t val_limb; | ||
| 2781 | |||
| 2782 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2783 | total_pc = zig_popcount_u8(val_limb, 8); | ||
| 2784 | } | ||
| 2785 | |||
| 2786 | remaining_bytes -= 8 / CHAR_BIT; | ||
| 2787 | |||
| 2788 | #if zig_little_endian | ||
| 2789 | byte_offset += 8 / CHAR_BIT; | ||
| 2790 | #endif | ||
| 2791 | } | ||
| 2792 | |||
| 2793 | return total_pc; | ||
| 2794 | } | ||
| 2795 | |||
| 1906 | /* ========================= Floating Point Support ========================= */ | 2796 | /* ========================= Floating Point Support ========================= */ |
| 1907 | 2797 | ||
| 1908 | #if _MSC_VER | 2798 | #if _MSC_VER |
| ... | @@ -1927,7 +2817,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { | ... | @@ -1927,7 +2817,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { |
| 1927 | #define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) | 2817 | #define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) |
| 1928 | #define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) | 2818 | #define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) |
| 1929 | #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) | 2819 | #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) |
| 1930 | #define zig_make_special_c_longdouble(sign, name, arg, repr) sign zig_make_c_longdouble(__builtin_##name, )(arg) | ||
| 1931 | #else | 2820 | #else |
| 1932 | #define zig_has_float_builtins 0 | 2821 | #define zig_has_float_builtins 0 |
| 1933 | #define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) | 2822 | #define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) |
| ... | @@ -1935,13 +2824,13 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { | ... | @@ -1935,13 +2824,13 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { |
| 1935 | #define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) | 2824 | #define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) |
| 1936 | #define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) | 2825 | #define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) |
| 1937 | #define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) | 2826 | #define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) |
| 1938 | #define zig_make_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) | ||
| 1939 | #endif | 2827 | #endif |
| 1940 | 2828 | ||
| 1941 | #define zig_has_f16 1 | 2829 | #define zig_has_f16 1 |
| 1942 | #define zig_bitSizeOf_f16 16 | 2830 | #define zig_bitSizeOf_f16 16 |
| 2831 | typedef int16_t zig_repr_f16; | ||
| 1943 | #define zig_libc_name_f16(name) __##name##h | 2832 | #define zig_libc_name_f16(name) __##name##h |
| 1944 | #define zig_make_special_constant_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) | 2833 | #define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) |
| 1945 | #if FLT_MANT_DIG == 11 | 2834 | #if FLT_MANT_DIG == 11 |
| 1946 | typedef float zig_f16; | 2835 | typedef float zig_f16; |
| 1947 | #define zig_make_f16(fp, repr) fp##f | 2836 | #define zig_make_f16(fp, repr) fp##f |
| ... | @@ -1950,7 +2839,9 @@ typedef double zig_f16; | ... | @@ -1950,7 +2839,9 @@ typedef double zig_f16; |
| 1950 | #define zig_make_f16(fp, repr) fp | 2839 | #define zig_make_f16(fp, repr) fp |
| 1951 | #elif LDBL_MANT_DIG == 11 | 2840 | #elif LDBL_MANT_DIG == 11 |
| 1952 | #define zig_bitSizeOf_c_longdouble 16 | 2841 | #define zig_bitSizeOf_c_longdouble 16 |
| 1953 | typedef uint16_t zig_repr_c_longdouble; | 2842 | #ifndef ZIG_TARGET_ABI_MSVC |
| 2843 | typedef zig_repr_f16 zig_repr_c_longdouble; | ||
| 2844 | #endif | ||
| 1954 | typedef long double zig_f16; | 2845 | typedef long double zig_f16; |
| 1955 | #define zig_make_f16(fp, repr) fp##l | 2846 | #define zig_make_f16(fp, repr) fp##l |
| 1956 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) | 2847 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) |
| ... | @@ -1967,17 +2858,18 @@ typedef int16_t zig_f16; | ... | @@ -1967,17 +2858,18 @@ typedef int16_t zig_f16; |
| 1967 | #define zig_make_f16(fp, repr) repr | 2858 | #define zig_make_f16(fp, repr) repr |
| 1968 | #undef zig_make_special_f16 | 2859 | #undef zig_make_special_f16 |
| 1969 | #define zig_make_special_f16(sign, name, arg, repr) repr | 2860 | #define zig_make_special_f16(sign, name, arg, repr) repr |
| 1970 | #undef zig_make_special_constant_f16 | 2861 | #undef zig_init_special_f16 |
| 1971 | #define zig_make_special_constant_f16(sign, name, arg, repr) repr | 2862 | #define zig_init_special_f16(sign, name, arg, repr) repr |
| 1972 | #endif | 2863 | #endif |
| 1973 | 2864 | ||
| 1974 | #define zig_has_f32 1 | 2865 | #define zig_has_f32 1 |
| 1975 | #define zig_bitSizeOf_f32 32 | 2866 | #define zig_bitSizeOf_f32 32 |
| 2867 | typedef int32_t zig_repr_f32; | ||
| 1976 | #define zig_libc_name_f32(name) name##f | 2868 | #define zig_libc_name_f32(name) name##f |
| 1977 | #if _MSC_VER | 2869 | #if _MSC_VER |
| 1978 | #define zig_make_special_constant_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) | 2870 | #define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) |
| 1979 | #else | 2871 | #else |
| 1980 | #define zig_make_special_constant_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) | 2872 | #define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) |
| 1981 | #endif | 2873 | #endif |
| 1982 | #if FLT_MANT_DIG == 24 | 2874 | #if FLT_MANT_DIG == 24 |
| 1983 | typedef float zig_f32; | 2875 | typedef float zig_f32; |
| ... | @@ -1987,7 +2879,9 @@ typedef double zig_f32; | ... | @@ -1987,7 +2879,9 @@ typedef double zig_f32; |
| 1987 | #define zig_make_f32(fp, repr) fp | 2879 | #define zig_make_f32(fp, repr) fp |
| 1988 | #elif LDBL_MANT_DIG == 24 | 2880 | #elif LDBL_MANT_DIG == 24 |
| 1989 | #define zig_bitSizeOf_c_longdouble 32 | 2881 | #define zig_bitSizeOf_c_longdouble 32 |
| 1990 | typedef uint32_t zig_repr_c_longdouble; | 2882 | #ifndef ZIG_TARGET_ABI_MSVC |
| 2883 | typedef zig_repr_f32 zig_repr_c_longdouble; | ||
| 2884 | #endif | ||
| 1991 | typedef long double zig_f32; | 2885 | typedef long double zig_f32; |
| 1992 | #define zig_make_f32(fp, repr) fp##l | 2886 | #define zig_make_f32(fp, repr) fp##l |
| 1993 | #elif FLT32_MANT_DIG == 24 | 2887 | #elif FLT32_MANT_DIG == 24 |
| ... | @@ -2001,21 +2895,24 @@ typedef int32_t zig_f32; | ... | @@ -2001,21 +2895,24 @@ typedef int32_t zig_f32; |
| 2001 | #define zig_make_f32(fp, repr) repr | 2895 | #define zig_make_f32(fp, repr) repr |
| 2002 | #undef zig_make_special_f32 | 2896 | #undef zig_make_special_f32 |
| 2003 | #define zig_make_special_f32(sign, name, arg, repr) repr | 2897 | #define zig_make_special_f32(sign, name, arg, repr) repr |
| 2004 | #undef zig_make_special_constant_f32 | 2898 | #undef zig_init_special_f32 |
| 2005 | #define zig_make_special_constant_f32(sign, name, arg, repr) repr | 2899 | #define zig_init_special_f32(sign, name, arg, repr) repr |
| 2006 | #endif | 2900 | #endif |
| 2007 | 2901 | ||
| 2008 | #define zig_has_f64 1 | 2902 | #define zig_has_f64 1 |
| 2009 | #define zig_bitSizeOf_f64 64 | 2903 | #define zig_bitSizeOf_f64 64 |
| 2904 | typedef int64_t zig_repr_f64; | ||
| 2010 | #define zig_libc_name_f64(name) name | 2905 | #define zig_libc_name_f64(name) name |
| 2011 | #if _MSC_VER | 2906 | #if _MSC_VER |
| 2012 | #ifdef ZIG_TARGET_ABI_MSVC | 2907 | #ifdef ZIG_TARGET_ABI_MSVC |
| 2013 | #define zig_bitSizeOf_c_longdouble 64 | 2908 | #define zig_bitSizeOf_c_longdouble 64 |
| 2014 | typedef uint64_t zig_repr_c_longdouble; | 2909 | #ifndef ZIG_TARGET_ABI_MSVC |
| 2910 | typedef zig_repr_f64 zig_repr_c_longdouble; | ||
| 2015 | #endif | 2911 | #endif |
| 2016 | #define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) | 2912 | #endif |
| 2913 | #define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) | ||
| 2017 | #else /* _MSC_VER */ | 2914 | #else /* _MSC_VER */ |
| 2018 | #define zig_make_special_constant_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) | 2915 | #define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) |
| 2019 | #endif /* _MSC_VER */ | 2916 | #endif /* _MSC_VER */ |
| 2020 | #if FLT_MANT_DIG == 53 | 2917 | #if FLT_MANT_DIG == 53 |
| 2021 | typedef float zig_f64; | 2918 | typedef float zig_f64; |
| ... | @@ -2025,7 +2922,9 @@ typedef double zig_f64; | ... | @@ -2025,7 +2922,9 @@ typedef double zig_f64; |
| 2025 | #define zig_make_f64(fp, repr) fp | 2922 | #define zig_make_f64(fp, repr) fp |
| 2026 | #elif LDBL_MANT_DIG == 53 | 2923 | #elif LDBL_MANT_DIG == 53 |
| 2027 | #define zig_bitSizeOf_c_longdouble 64 | 2924 | #define zig_bitSizeOf_c_longdouble 64 |
| 2028 | typedef uint64_t zig_repr_c_longdouble; | 2925 | #ifndef ZIG_TARGET_ABI_MSVC |
| 2926 | typedef zig_repr_f64 zig_repr_c_longdouble; | ||
| 2927 | #endif | ||
| 2029 | typedef long double zig_f64; | 2928 | typedef long double zig_f64; |
| 2030 | #define zig_make_f64(fp, repr) fp##l | 2929 | #define zig_make_f64(fp, repr) fp##l |
| 2031 | #elif FLT64_MANT_DIG == 53 | 2930 | #elif FLT64_MANT_DIG == 53 |
| ... | @@ -2042,14 +2941,15 @@ typedef int64_t zig_f64; | ... | @@ -2042,14 +2941,15 @@ typedef int64_t zig_f64; |
| 2042 | #define zig_make_f64(fp, repr) repr | 2941 | #define zig_make_f64(fp, repr) repr |
| 2043 | #undef zig_make_special_f64 | 2942 | #undef zig_make_special_f64 |
| 2044 | #define zig_make_special_f64(sign, name, arg, repr) repr | 2943 | #define zig_make_special_f64(sign, name, arg, repr) repr |
| 2045 | #undef zig_make_special_constant_f64 | 2944 | #undef zig_init_special_f64 |
| 2046 | #define zig_make_special_constant_f64(sign, name, arg, repr) repr | 2945 | #define zig_init_special_f64(sign, name, arg, repr) repr |
| 2047 | #endif | 2946 | #endif |
| 2048 | 2947 | ||
| 2049 | #define zig_has_f80 1 | 2948 | #define zig_has_f80 1 |
| 2050 | #define zig_bitSizeOf_f80 80 | 2949 | #define zig_bitSizeOf_f80 80 |
| 2950 | typedef zig_i128 zig_repr_f80; | ||
| 2051 | #define zig_libc_name_f80(name) __##name##x | 2951 | #define zig_libc_name_f80(name) __##name##x |
| 2052 | #define zig_make_special_constant_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) | 2952 | #define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) |
| 2053 | #if FLT_MANT_DIG == 64 | 2953 | #if FLT_MANT_DIG == 64 |
| 2054 | typedef float zig_f80; | 2954 | typedef float zig_f80; |
| 2055 | #define zig_make_f80(fp, repr) fp##f | 2955 | #define zig_make_f80(fp, repr) fp##f |
| ... | @@ -2058,7 +2958,9 @@ typedef double zig_f80; | ... | @@ -2058,7 +2958,9 @@ typedef double zig_f80; |
| 2058 | #define zig_make_f80(fp, repr) fp | 2958 | #define zig_make_f80(fp, repr) fp |
| 2059 | #elif LDBL_MANT_DIG == 64 | 2959 | #elif LDBL_MANT_DIG == 64 |
| 2060 | #define zig_bitSizeOf_c_longdouble 80 | 2960 | #define zig_bitSizeOf_c_longdouble 80 |
| 2061 | typedef zig_u128 zig_repr_c_longdouble; | 2961 | #ifndef ZIG_TARGET_ABI_MSVC |
| 2962 | typedef zig_repr_f80 zig_repr_c_longdouble; | ||
| 2963 | #endif | ||
| 2062 | typedef long double zig_f80; | 2964 | typedef long double zig_f80; |
| 2063 | #define zig_make_f80(fp, repr) fp##l | 2965 | #define zig_make_f80(fp, repr) fp##l |
| 2064 | #elif FLT80_MANT_DIG == 64 | 2966 | #elif FLT80_MANT_DIG == 64 |
| ... | @@ -2078,14 +2980,15 @@ typedef zig_i128 zig_f80; | ... | @@ -2078,14 +2980,15 @@ typedef zig_i128 zig_f80; |
| 2078 | #define zig_make_f80(fp, repr) repr | 2980 | #define zig_make_f80(fp, repr) repr |
| 2079 | #undef zig_make_special_f80 | 2981 | #undef zig_make_special_f80 |
| 2080 | #define zig_make_special_f80(sign, name, arg, repr) repr | 2982 | #define zig_make_special_f80(sign, name, arg, repr) repr |
| 2081 | #undef zig_make_special_constant_f80 | 2983 | #undef zig_init_special_f80 |
| 2082 | #define zig_make_special_constant_f80(sign, name, arg, repr) repr | 2984 | #define zig_init_special_f80(sign, name, arg, repr) repr |
| 2083 | #endif | 2985 | #endif |
| 2084 | 2986 | ||
| 2085 | #define zig_has_f128 1 | 2987 | #define zig_has_f128 1 |
| 2086 | #define zig_bitSizeOf_f128 128 | 2988 | #define zig_bitSizeOf_f128 128 |
| 2989 | typedef zig_i128 zig_repr_f128; | ||
| 2087 | #define zig_libc_name_f128(name) name##q | 2990 | #define zig_libc_name_f128(name) name##q |
| 2088 | #define zig_make_special_constant_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) | 2991 | #define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) |
| 2089 | #if FLT_MANT_DIG == 113 | 2992 | #if FLT_MANT_DIG == 113 |
| 2090 | typedef float zig_f128; | 2993 | typedef float zig_f128; |
| 2091 | #define zig_make_f128(fp, repr) fp##f | 2994 | #define zig_make_f128(fp, repr) fp##f |
| ... | @@ -2094,7 +2997,9 @@ typedef double zig_f128; | ... | @@ -2094,7 +2997,9 @@ typedef double zig_f128; |
| 2094 | #define zig_make_f128(fp, repr) fp | 2997 | #define zig_make_f128(fp, repr) fp |
| 2095 | #elif LDBL_MANT_DIG == 113 | 2998 | #elif LDBL_MANT_DIG == 113 |
| 2096 | #define zig_bitSizeOf_c_longdouble 128 | 2999 | #define zig_bitSizeOf_c_longdouble 128 |
| 2097 | typedef zig_u128 zig_repr_c_longdouble; | 3000 | #ifndef ZIG_TARGET_ABI_MSVC |
| 3001 | typedef zig_repr_f128 zig_repr_c_longdouble; | ||
| 3002 | #endif | ||
| 2098 | typedef long double zig_f128; | 3003 | typedef long double zig_f128; |
| 2099 | #define zig_make_f128(fp, repr) fp##l | 3004 | #define zig_make_f128(fp, repr) fp##l |
| 2100 | #elif FLT128_MANT_DIG == 113 | 3005 | #elif FLT128_MANT_DIG == 113 |
| ... | @@ -2116,63 +3021,44 @@ typedef zig_i128 zig_f128; | ... | @@ -2116,63 +3021,44 @@ typedef zig_i128 zig_f128; |
| 2116 | #define zig_make_f128(fp, repr) repr | 3021 | #define zig_make_f128(fp, repr) repr |
| 2117 | #undef zig_make_special_f128 | 3022 | #undef zig_make_special_f128 |
| 2118 | #define zig_make_special_f128(sign, name, arg, repr) repr | 3023 | #define zig_make_special_f128(sign, name, arg, repr) repr |
| 2119 | #undef zig_make_special_constant_f128 | 3024 | #undef zig_init_special_f128 |
| 2120 | #define zig_make_special_constant_f128(sign, name, arg, repr) repr | 3025 | #define zig_init_special_f128(sign, name, arg, repr) repr |
| 2121 | #endif | ||
| 2122 | |||
| 2123 | #define zig_has_c_longdouble 1 | ||
| 2124 | |||
| 2125 | #ifdef ZIG_TARGET_ABI_MSVC | ||
| 2126 | #define zig_libc_name_c_longdouble(name) name | ||
| 2127 | #else | ||
| 2128 | #define zig_libc_name_c_longdouble(name) name##l | ||
| 2129 | #endif | 3026 | #endif |
| 2130 | 3027 | ||
| 2131 | #define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr) | ||
| 2132 | #ifdef zig_bitSizeOf_c_longdouble | 3028 | #ifdef zig_bitSizeOf_c_longdouble |
| 2133 | 3029 | ||
| 3030 | #define zig_has_c_longdouble 1 | ||
| 2134 | #ifdef ZIG_TARGET_ABI_MSVC | 3031 | #ifdef ZIG_TARGET_ABI_MSVC |
| 2135 | #undef zig_bitSizeOf_c_longdouble | 3032 | #undef zig_bitSizeOf_c_longdouble |
| 2136 | #define zig_bitSizeOf_c_longdouble 64 | 3033 | #define zig_bitSizeOf_c_longdouble 64 |
| 2137 | typedef uint64_t zig_repr_c_longdouble; | ||
| 2138 | typedef zig_f64 zig_c_longdouble; | 3034 | typedef zig_f64 zig_c_longdouble; |
| 2139 | #define zig_make_c_longdouble(fp, repr) fp | 3035 | typedef zig_repr_f64 zig_repr_c_longdouble; |
| 2140 | #else | 3036 | #else |
| 2141 | typedef long double zig_c_longdouble; | 3037 | typedef long double zig_c_longdouble; |
| 2142 | #define zig_make_c_longdouble(fp, repr) fp##l | ||
| 2143 | #endif | 3038 | #endif |
| 2144 | 3039 | ||
| 2145 | #else /* zig_bitSizeOf_c_longdouble */ | 3040 | #else /* zig_bitSizeOf_c_longdouble */ |
| 2146 | 3041 | ||
| 2147 | #undef zig_has_c_longdouble | ||
| 2148 | #define zig_has_c_longdouble 0 | 3042 | #define zig_has_c_longdouble 0 |
| 2149 | #define zig_bitSizeOf_c_longdouble 80 | ||
| 2150 | typedef zig_u128 zig_repr_c_longdouble; | ||
| 2151 | #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 | ||
| 2152 | #define zig_bitSizeOf_repr_c_longdouble 128 | 3043 | #define zig_bitSizeOf_repr_c_longdouble 128 |
| 2153 | typedef zig_i128 zig_c_longdouble; | 3044 | typedef zig_f128 zig_c_longdouble; |
| 2154 | #define zig_make_c_longdouble(fp, repr) repr | 3045 | typedef zig_repr_f128 zig_repr_c_longdouble; |
| 2155 | #undef zig_make_special_c_longdouble | ||
| 2156 | #define zig_make_special_c_longdouble(sign, name, arg, repr) repr | ||
| 2157 | #undef zig_make_special_constant_c_longdouble | ||
| 2158 | #define zig_make_special_constant_c_longdouble(sign, name, arg, repr) repr | ||
| 2159 | 3046 | ||
| 2160 | #endif /* zig_bitSizeOf_c_longdouble */ | 3047 | #endif /* zig_bitSizeOf_c_longdouble */ |
| 2161 | 3048 | ||
| 2162 | #if !zig_has_float_builtins | 3049 | #if !zig_has_float_builtins |
| 2163 | #define zig_float_from_repr(Type, ReprType) \ | 3050 | #define zig_float_from_repr(Type) \ |
| 2164 | static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \ | 3051 | static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \ |
| 2165 | zig_##Type result; \ | 3052 | zig_##Type result; \ |
| 2166 | memcpy(&result, &repr, sizeof(result)); \ | 3053 | memcpy(&result, &repr, sizeof(result)); \ |
| 2167 | return result; \ | 3054 | return result; \ |
| 2168 | } | 3055 | } |
| 2169 | 3056 | ||
| 2170 | zig_float_from_repr(f16, uint16_t) | 3057 | zig_float_from_repr(f16) |
| 2171 | zig_float_from_repr(f32, uint32_t) | 3058 | zig_float_from_repr(f32) |
| 2172 | zig_float_from_repr(f64, uint64_t) | 3059 | zig_float_from_repr(f64) |
| 2173 | zig_float_from_repr(f80, zig_u128) | 3060 | zig_float_from_repr(f80) |
| 2174 | zig_float_from_repr(f128, zig_u128) | 3061 | zig_float_from_repr(f128) |
| 2175 | zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) | ||
| 2176 | #endif | 3062 | #endif |
| 2177 | 3063 | ||
| 2178 | #define zig_cast_f16 (zig_f16) | 3064 | #define zig_cast_f16 (zig_f16) |
| ... | @@ -2181,11 +3067,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) | ... | @@ -2181,11 +3067,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) |
| 2181 | 3067 | ||
| 2182 | #if _MSC_VER && !zig_has_f128 | 3068 | #if _MSC_VER && !zig_has_f128 |
| 2183 | #define zig_cast_f80 | 3069 | #define zig_cast_f80 |
| 2184 | #define zig_cast_c_longdouble | ||
| 2185 | #define zig_cast_f128 | 3070 | #define zig_cast_f128 |
| 2186 | #else | 3071 | #else |
| 2187 | #define zig_cast_f80 (zig_f80) | 3072 | #define zig_cast_f80 (zig_f80) |
| 2188 | #define zig_cast_c_longdouble (zig_c_longdouble) | ||
| 2189 | #define zig_cast_f128 (zig_f128) | 3073 | #define zig_cast_f128 (zig_f128) |
| 2190 | #endif | 3074 | #endif |
| 2191 | 3075 | ||
| ... | @@ -2314,7 +3198,6 @@ zig_float_builtins(f32) | ... | @@ -2314,7 +3198,6 @@ zig_float_builtins(f32) |
| 2314 | zig_float_builtins(f64) | 3198 | zig_float_builtins(f64) |
| 2315 | zig_float_builtins(f80) | 3199 | zig_float_builtins(f80) |
| 2316 | zig_float_builtins(f128) | 3200 | zig_float_builtins(f128) |
| 2317 | zig_float_builtins(c_longdouble) | ||
| 2318 | 3201 | ||
| 2319 | #if _MSC_VER && (_M_IX86 || _M_X64) | 3202 | #if _MSC_VER && (_M_IX86 || _M_X64) |
| 2320 | 3203 |
src/Air.zig+9-1| ... | @@ -232,7 +232,14 @@ pub const Inst = struct { | ... | @@ -232,7 +232,14 @@ pub const Inst = struct { |
| 232 | /// Result type is always noreturn; no instructions in a block follow this one. | 232 | /// Result type is always noreturn; no instructions in a block follow this one. |
| 233 | /// Uses the `br` field. | 233 | /// Uses the `br` field. |
| 234 | br, | 234 | br, |
| 235 | /// Lowers to a hardware trap instruction, or the next best thing. | 235 | /// Lowers to a trap/jam instruction causing program abortion. |
| 236 | /// This may lower to an instruction known to be invalid. | ||
| 237 | /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code. | ||
| 238 | /// Result type is always noreturn; no instructions in a block follow this one. | ||
| 239 | trap, | ||
| 240 | /// Lowers to a trap instruction causing debuggers to break here, or the next best thing. | ||
| 241 | /// The debugger or something else may allow the program to resume after this point. | ||
| 242 | /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code. | ||
| 236 | /// Result type is always void. | 243 | /// Result type is always void. |
| 237 | breakpoint, | 244 | breakpoint, |
| 238 | /// Yields the return address of the current function. | 245 | /// Yields the return address of the current function. |
| ... | @@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1186 | .ret, | 1193 | .ret, |
| 1187 | .ret_load, | 1194 | .ret_load, |
| 1188 | .unreach, | 1195 | .unreach, |
| 1196 | .trap, | ||
| 1189 | => return Type.initTag(.noreturn), | 1197 | => return Type.initTag(.noreturn), |
| 1190 | 1198 | ||
| 1191 | .breakpoint, | 1199 | .breakpoint, |
src/AstGen.zig+151-92| ... | @@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { | ... | @@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { |
| 148 | }; | 148 | }; |
| 149 | defer gz_instructions.deinit(gpa); | 149 | defer gz_instructions.deinit(gpa); |
| 150 | 150 | ||
| 151 | if (AstGen.structDeclInner( | 151 | // The AST -> ZIR lowering process assumes an AST that does not have any |
| 152 | &gen_scope, | 152 | // parse errors. |
| 153 | &gen_scope.base, | 153 | if (tree.errors.len == 0) { |
| 154 | 0, | 154 | if (AstGen.structDeclInner( |
| 155 | tree.containerDeclRoot(), | 155 | &gen_scope, |
| 156 | .Auto, | 156 | &gen_scope.base, |
| 157 | 0, | 157 | 0, |
| 158 | )) |struct_decl_ref| { | 158 | tree.containerDeclRoot(), |
| 159 | assert(refToIndex(struct_decl_ref).? == 0); | 159 | .Auto, |
| 160 | } else |err| switch (err) { | 160 | 0, |
| 161 | error.OutOfMemory => return error.OutOfMemory, | 161 | )) |struct_decl_ref| { |
| 162 | error.AnalysisFail => {}, // Handled via compile_errors below. | 162 | assert(refToIndex(struct_decl_ref).? == 0); |
| 163 | } else |err| switch (err) { | ||
| 164 | error.OutOfMemory => return error.OutOfMemory, | ||
| 165 | error.AnalysisFail => {}, // Handled via compile_errors below. | ||
| 166 | } | ||
| 167 | } else { | ||
| 168 | try lowerAstErrors(&astgen); | ||
| 163 | } | 169 | } |
| 164 | 170 | ||
| 165 | const err_index = @enumToInt(Zir.ExtraIndex.compile_errors); | 171 | const err_index = @enumToInt(Zir.ExtraIndex.compile_errors); |
| ... | @@ -205,7 +211,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { | ... | @@ -205,7 +211,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { |
| 205 | }; | 211 | }; |
| 206 | } | 212 | } |
| 207 | 213 | ||
| 208 | pub fn deinit(astgen: *AstGen, gpa: Allocator) void { | 214 | fn deinit(astgen: *AstGen, gpa: Allocator) void { |
| 209 | astgen.instructions.deinit(gpa); | 215 | astgen.instructions.deinit(gpa); |
| 210 | astgen.extra.deinit(gpa); | 216 | astgen.extra.deinit(gpa); |
| 211 | astgen.string_table.deinit(gpa); | 217 | astgen.string_table.deinit(gpa); |
| ... | @@ -216,7 +222,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void { | ... | @@ -216,7 +222,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void { |
| 216 | astgen.ref_table.deinit(gpa); | 222 | astgen.ref_table.deinit(gpa); |
| 217 | } | 223 | } |
| 218 | 224 | ||
| 219 | pub const ResultInfo = struct { | 225 | const ResultInfo = struct { |
| 220 | /// The semantics requested for the result location | 226 | /// The semantics requested for the result location |
| 221 | rl: Loc, | 227 | rl: Loc, |
| 222 | 228 | ||
| ... | @@ -245,7 +251,7 @@ pub const ResultInfo = struct { | ... | @@ -245,7 +251,7 @@ pub const ResultInfo = struct { |
| 245 | } | 251 | } |
| 246 | } | 252 | } |
| 247 | 253 | ||
| 248 | pub const Loc = union(enum) { | 254 | const Loc = union(enum) { |
| 249 | /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the | 255 | /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the |
| 250 | /// expression should be generated. The result instruction from the expression must | 256 | /// expression should be generated. The result instruction from the expression must |
| 251 | /// be ignored. | 257 | /// be ignored. |
| ... | @@ -277,11 +283,11 @@ pub const ResultInfo = struct { | ... | @@ -277,11 +283,11 @@ pub const ResultInfo = struct { |
| 277 | src_node: ?Ast.Node.Index = null, | 283 | src_node: ?Ast.Node.Index = null, |
| 278 | }; | 284 | }; |
| 279 | 285 | ||
| 280 | pub const Strategy = struct { | 286 | const Strategy = struct { |
| 281 | elide_store_to_block_ptr_instructions: bool, | 287 | elide_store_to_block_ptr_instructions: bool, |
| 282 | tag: Tag, | 288 | tag: Tag, |
| 283 | 289 | ||
| 284 | pub const Tag = enum { | 290 | const Tag = enum { |
| 285 | /// Both branches will use break_void; result location is used to communicate the | 291 | /// Both branches will use break_void; result location is used to communicate the |
| 286 | /// result instruction. | 292 | /// result instruction. |
| 287 | break_void, | 293 | break_void, |
| ... | @@ -331,7 +337,7 @@ pub const ResultInfo = struct { | ... | @@ -331,7 +337,7 @@ pub const ResultInfo = struct { |
| 331 | } | 337 | } |
| 332 | }; | 338 | }; |
| 333 | 339 | ||
| 334 | pub const Context = enum { | 340 | const Context = enum { |
| 335 | /// The expression is the operand to a return expression. | 341 | /// The expression is the operand to a return expression. |
| 336 | @"return", | 342 | @"return", |
| 337 | /// The expression is the input to an error-handling operator (if-else, try, or catch). | 343 | /// The expression is the input to an error-handling operator (if-else, try, or catch). |
| ... | @@ -349,11 +355,11 @@ pub const ResultInfo = struct { | ... | @@ -349,11 +355,11 @@ pub const ResultInfo = struct { |
| 349 | }; | 355 | }; |
| 350 | }; | 356 | }; |
| 351 | 357 | ||
| 352 | pub const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } }; | 358 | const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } }; |
| 353 | pub const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; | 359 | const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; |
| 354 | pub const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } }; | 360 | const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } }; |
| 355 | pub const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } }; | 361 | const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } }; |
| 356 | pub const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; | 362 | const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; |
| 357 | 363 | ||
| 358 | fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref { | 364 | fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref { |
| 359 | const prev_force_comptime = gz.force_comptime; | 365 | const prev_force_comptime = gz.force_comptime; |
| ... | @@ -1960,7 +1966,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn | ... | @@ -1960,7 +1966,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1960 | else | 1966 | else |
| 1961 | .@"break"; | 1967 | .@"break"; |
| 1962 | 1968 | ||
| 1969 | block_gz.break_count += 1; | ||
| 1963 | if (rhs == 0) { | 1970 | if (rhs == 0) { |
| 1971 | _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); | ||
| 1972 | |||
| 1964 | try genDefers(parent_gz, scope, parent_scope, .normal_only); | 1973 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 1965 | 1974 | ||
| 1966 | // As our last action before the break, "pop" the error trace if needed | 1975 | // As our last action before the break, "pop" the error trace if needed |
| ... | @@ -1970,7 +1979,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn | ... | @@ -1970,7 +1979,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1970 | _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); | 1979 | _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); |
| 1971 | return Zir.Inst.Ref.unreachable_value; | 1980 | return Zir.Inst.Ref.unreachable_value; |
| 1972 | } | 1981 | } |
| 1973 | block_gz.break_count += 1; | ||
| 1974 | 1982 | ||
| 1975 | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); | 1983 | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); |
| 1976 | const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len); | 1984 | const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len); |
| ... | @@ -2609,8 +2617,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2609,8 +2617,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2609 | .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { | 2617 | .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { |
| 2610 | .breakpoint, | 2618 | .breakpoint, |
| 2611 | .fence, | 2619 | .fence, |
| 2612 | .set_align_stack, | ||
| 2613 | .set_float_mode, | 2620 | .set_float_mode, |
| 2621 | .set_align_stack, | ||
| 2622 | .set_cold, | ||
| 2614 | => break :b true, | 2623 | => break :b true, |
| 2615 | else => break :b false, | 2624 | else => break :b false, |
| 2616 | }, | 2625 | }, |
| ... | @@ -2630,6 +2639,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2630,6 +2639,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2630 | .repeat_inline, | 2639 | .repeat_inline, |
| 2631 | .panic, | 2640 | .panic, |
| 2632 | .panic_comptime, | 2641 | .panic_comptime, |
| 2642 | .trap, | ||
| 2633 | .check_comptime_control_flow, | 2643 | .check_comptime_control_flow, |
| 2634 | => { | 2644 | => { |
| 2635 | noreturn_src_node = statement; | 2645 | noreturn_src_node = statement; |
| ... | @@ -2658,7 +2668,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2658,7 +2668,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2658 | .validate_struct_init_comptime, | 2668 | .validate_struct_init_comptime, |
| 2659 | .validate_array_init, | 2669 | .validate_array_init, |
| 2660 | .validate_array_init_comptime, | 2670 | .validate_array_init_comptime, |
| 2661 | .set_cold, | ||
| 2662 | .set_runtime_safety, | 2671 | .set_runtime_safety, |
| 2663 | .closure_capture, | 2672 | .closure_capture, |
| 2664 | .memcpy, | 2673 | .memcpy, |
| ... | @@ -3506,7 +3515,7 @@ const WipMembers = struct { | ... | @@ -3506,7 +3515,7 @@ const WipMembers = struct { |
| 3506 | /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space ) | 3515 | /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space ) |
| 3507 | const max_decl_size = 11; | 3516 | const max_decl_size = 11; |
| 3508 | 3517 | ||
| 3509 | pub fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { | 3518 | fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { |
| 3510 | const payload_top = @intCast(u32, payload.items.len); | 3519 | const payload_top = @intCast(u32, payload.items.len); |
| 3511 | const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32; | 3520 | const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32; |
| 3512 | const field_bits_start = decls_start + decl_count * max_decl_size; | 3521 | const field_bits_start = decls_start + decl_count * max_decl_size; |
| ... | @@ -3527,7 +3536,7 @@ const WipMembers = struct { | ... | @@ -3527,7 +3536,7 @@ const WipMembers = struct { |
| 3527 | }; | 3536 | }; |
| 3528 | } | 3537 | } |
| 3529 | 3538 | ||
| 3530 | pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void { | 3539 | fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void { |
| 3531 | const index = self.payload_top + self.decl_index / decls_per_u32; | 3540 | const index = self.payload_top + self.decl_index / decls_per_u32; |
| 3532 | assert(index < self.decls_start); | 3541 | assert(index < self.decls_start); |
| 3533 | const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index]; | 3542 | const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index]; |
| ... | @@ -3539,7 +3548,7 @@ const WipMembers = struct { | ... | @@ -3539,7 +3548,7 @@ const WipMembers = struct { |
| 3539 | self.decl_index += 1; | 3548 | self.decl_index += 1; |
| 3540 | } | 3549 | } |
| 3541 | 3550 | ||
| 3542 | pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { | 3551 | fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { |
| 3543 | const fields_per_u32 = 32 / bits_per_field; | 3552 | const fields_per_u32 = 32 / bits_per_field; |
| 3544 | const index = self.field_bits_start + self.field_index / fields_per_u32; | 3553 | const index = self.field_bits_start + self.field_index / fields_per_u32; |
| 3545 | assert(index < self.fields_start); | 3554 | assert(index < self.fields_start); |
| ... | @@ -3553,25 +3562,25 @@ const WipMembers = struct { | ... | @@ -3553,25 +3562,25 @@ const WipMembers = struct { |
| 3553 | self.field_index += 1; | 3562 | self.field_index += 1; |
| 3554 | } | 3563 | } |
| 3555 | 3564 | ||
| 3556 | pub fn appendToDecl(self: *Self, data: u32) void { | 3565 | fn appendToDecl(self: *Self, data: u32) void { |
| 3557 | assert(self.decls_end < self.field_bits_start); | 3566 | assert(self.decls_end < self.field_bits_start); |
| 3558 | self.payload.items[self.decls_end] = data; | 3567 | self.payload.items[self.decls_end] = data; |
| 3559 | self.decls_end += 1; | 3568 | self.decls_end += 1; |
| 3560 | } | 3569 | } |
| 3561 | 3570 | ||
| 3562 | pub fn appendToDeclSlice(self: *Self, data: []const u32) void { | 3571 | fn appendToDeclSlice(self: *Self, data: []const u32) void { |
| 3563 | assert(self.decls_end + data.len <= self.field_bits_start); | 3572 | assert(self.decls_end + data.len <= self.field_bits_start); |
| 3564 | mem.copy(u32, self.payload.items[self.decls_end..], data); | 3573 | mem.copy(u32, self.payload.items[self.decls_end..], data); |
| 3565 | self.decls_end += @intCast(u32, data.len); | 3574 | self.decls_end += @intCast(u32, data.len); |
| 3566 | } | 3575 | } |
| 3567 | 3576 | ||
| 3568 | pub fn appendToField(self: *Self, data: u32) void { | 3577 | fn appendToField(self: *Self, data: u32) void { |
| 3569 | assert(self.fields_end < self.payload.items.len); | 3578 | assert(self.fields_end < self.payload.items.len); |
| 3570 | self.payload.items[self.fields_end] = data; | 3579 | self.payload.items[self.fields_end] = data; |
| 3571 | self.fields_end += 1; | 3580 | self.fields_end += 1; |
| 3572 | } | 3581 | } |
| 3573 | 3582 | ||
| 3574 | pub fn finishBits(self: *Self, comptime bits_per_field: u32) void { | 3583 | fn finishBits(self: *Self, comptime bits_per_field: u32) void { |
| 3575 | const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32); | 3584 | const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32); |
| 3576 | if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) { | 3585 | if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) { |
| 3577 | const index = self.payload_top + self.decl_index / decls_per_u32; | 3586 | const index = self.payload_top + self.decl_index / decls_per_u32; |
| ... | @@ -3587,15 +3596,15 @@ const WipMembers = struct { | ... | @@ -3587,15 +3596,15 @@ const WipMembers = struct { |
| 3587 | } | 3596 | } |
| 3588 | } | 3597 | } |
| 3589 | 3598 | ||
| 3590 | pub fn declsSlice(self: *Self) []u32 { | 3599 | fn declsSlice(self: *Self) []u32 { |
| 3591 | return self.payload.items[self.payload_top..self.decls_end]; | 3600 | return self.payload.items[self.payload_top..self.decls_end]; |
| 3592 | } | 3601 | } |
| 3593 | 3602 | ||
| 3594 | pub fn fieldsSlice(self: *Self) []u32 { | 3603 | fn fieldsSlice(self: *Self) []u32 { |
| 3595 | return self.payload.items[self.field_bits_start..self.fields_end]; | 3604 | return self.payload.items[self.field_bits_start..self.fields_end]; |
| 3596 | } | 3605 | } |
| 3597 | 3606 | ||
| 3598 | pub fn deinit(self: *Self) void { | 3607 | fn deinit(self: *Self) void { |
| 3599 | self.payload.items.len = self.payload_top; | 3608 | self.payload.items.len = self.payload_top; |
| 3600 | } | 3609 | } |
| 3601 | }; | 3610 | }; |
| ... | @@ -6583,6 +6592,9 @@ fn forExpr( | ... | @@ -6583,6 +6592,9 @@ fn forExpr( |
| 6583 | cond_block, | 6592 | cond_block, |
| 6584 | break_tag, | 6593 | break_tag, |
| 6585 | ); | 6594 | ); |
| 6595 | if (ri.rl.strategy(&loop_scope).tag == .break_void and loop_scope.break_count == 0) { | ||
| 6596 | _ = try rvalue(parent_gz, ri, .void_value, node); | ||
| 6597 | } | ||
| 6586 | if (is_statement) { | 6598 | if (is_statement) { |
| 6587 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); | 6599 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); |
| 6588 | } | 6600 | } |
| ... | @@ -7976,6 +7988,9 @@ fn builtinCall( | ... | @@ -7976,6 +7988,9 @@ fn builtinCall( |
| 7976 | switch (node_tags[params[0]]) { | 7988 | switch (node_tags[params[0]]) { |
| 7977 | .identifier => { | 7989 | .identifier => { |
| 7978 | const ident_token = main_tokens[params[0]]; | 7990 | const ident_token = main_tokens[params[0]]; |
| 7991 | if (isPrimitive(tree.tokenSlice(ident_token))) { | ||
| 7992 | return astgen.failTok(ident_token, "unable to export primitive value", .{}); | ||
| 7993 | } | ||
| 7979 | decl_name = try astgen.identAsString(ident_token); | 7994 | decl_name = try astgen.identAsString(ident_token); |
| 7980 | 7995 | ||
| 7981 | var s = scope; | 7996 | var s = scope; |
| ... | @@ -8056,27 +8071,35 @@ fn builtinCall( | ... | @@ -8056,27 +8071,35 @@ fn builtinCall( |
| 8056 | }, | 8071 | }, |
| 8057 | .fence => { | 8072 | .fence => { |
| 8058 | const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]); | 8073 | const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]); |
| 8059 | const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{ | 8074 | _ = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{ |
| 8060 | .node = gz.nodeIndexToRelative(node), | 8075 | .node = gz.nodeIndexToRelative(node), |
| 8061 | .operand = order, | 8076 | .operand = order, |
| 8062 | }); | 8077 | }); |
| 8063 | return rvalue(gz, ri, result, node); | 8078 | return rvalue(gz, ri, .void_value, node); |
| 8064 | }, | 8079 | }, |
| 8065 | .set_float_mode => { | 8080 | .set_float_mode => { |
| 8066 | const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]); | 8081 | const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]); |
| 8067 | const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ | 8082 | _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ |
| 8068 | .node = gz.nodeIndexToRelative(node), | 8083 | .node = gz.nodeIndexToRelative(node), |
| 8069 | .operand = order, | 8084 | .operand = order, |
| 8070 | }); | 8085 | }); |
| 8071 | return rvalue(gz, ri, result, node); | 8086 | return rvalue(gz, ri, .void_value, node); |
| 8072 | }, | 8087 | }, |
| 8073 | .set_align_stack => { | 8088 | .set_align_stack => { |
| 8074 | const order = try expr(gz, scope, align_ri, params[0]); | 8089 | const order = try expr(gz, scope, align_ri, params[0]); |
| 8075 | const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{ | 8090 | _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{ |
| 8076 | .node = gz.nodeIndexToRelative(node), | 8091 | .node = gz.nodeIndexToRelative(node), |
| 8077 | .operand = order, | 8092 | .operand = order, |
| 8078 | }); | 8093 | }); |
| 8079 | return rvalue(gz, ri, result, node); | 8094 | return rvalue(gz, ri, .void_value, node); |
| 8095 | }, | ||
| 8096 | .set_cold => { | ||
| 8097 | const order = try expr(gz, scope, ri, params[0]); | ||
| 8098 | _ = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{ | ||
| 8099 | .node = gz.nodeIndexToRelative(node), | ||
| 8100 | .operand = order, | ||
| 8101 | }); | ||
| 8102 | return rvalue(gz, ri, .void_value, node); | ||
| 8080 | }, | 8103 | }, |
| 8081 | 8104 | ||
| 8082 | .src => { | 8105 | .src => { |
| ... | @@ -8097,7 +8120,7 @@ fn builtinCall( | ... | @@ -8097,7 +8120,7 @@ fn builtinCall( |
| 8097 | .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), | 8120 | .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), |
| 8098 | .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), | 8121 | .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), |
| 8099 | .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), | 8122 | .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), |
| 8100 | .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), | 8123 | .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), |
| 8101 | 8124 | ||
| 8102 | .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), | 8125 | .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), |
| 8103 | .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), | 8126 | .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), |
| ... | @@ -8111,7 +8134,6 @@ fn builtinCall( | ... | @@ -8111,7 +8134,6 @@ fn builtinCall( |
| 8111 | .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int), | 8134 | .bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int), |
| 8112 | .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file), | 8135 | .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file), |
| 8113 | .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name), | 8136 | .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name), |
| 8114 | .set_cold => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_cold), | ||
| 8115 | .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety), | 8137 | .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety), |
| 8116 | .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), | 8138 | .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), |
| 8117 | .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin), | 8139 | .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin), |
| ... | @@ -8171,6 +8193,11 @@ fn builtinCall( | ... | @@ -8171,6 +8193,11 @@ fn builtinCall( |
| 8171 | try emitDbgNode(gz, node); | 8193 | try emitDbgNode(gz, node); |
| 8172 | return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic); | 8194 | return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic); |
| 8173 | }, | 8195 | }, |
| 8196 | .trap => { | ||
| 8197 | try emitDbgNode(gz, node); | ||
| 8198 | _ = try gz.addNode(.trap, node); | ||
| 8199 | return rvalue(gz, ri, .void_value, node); | ||
| 8200 | }, | ||
| 8174 | .error_to_int => { | 8201 | .error_to_int => { |
| 8175 | const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); | 8202 | const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); |
| 8176 | const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{ | 8203 | const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{ |
| ... | @@ -8357,14 +8384,14 @@ fn builtinCall( | ... | @@ -8357,14 +8384,14 @@ fn builtinCall( |
| 8357 | }, | 8384 | }, |
| 8358 | .atomic_store => { | 8385 | .atomic_store => { |
| 8359 | const int_type = try typeExpr(gz, scope, params[0]); | 8386 | const int_type = try typeExpr(gz, scope, params[0]); |
| 8360 | const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ | 8387 | _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ |
| 8361 | // zig fmt: off | 8388 | // zig fmt: off |
| 8362 | .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), | 8389 | .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), |
| 8363 | .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), | 8390 | .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), |
| 8364 | .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]), | 8391 | .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]), |
| 8365 | // zig fmt: on | 8392 | // zig fmt: on |
| 8366 | }); | 8393 | }); |
| 8367 | return rvalue(gz, ri, result, node); | 8394 | return rvalue(gz, ri, .void_value, node); |
| 8368 | }, | 8395 | }, |
| 8369 | .mul_add => { | 8396 | .mul_add => { |
| 8370 | const float_type = try typeExpr(gz, scope, params[0]); | 8397 | const float_type = try typeExpr(gz, scope, params[0]); |
| ... | @@ -8405,20 +8432,20 @@ fn builtinCall( | ... | @@ -8405,20 +8432,20 @@ fn builtinCall( |
| 8405 | return rvalue(gz, ri, result, node); | 8432 | return rvalue(gz, ri, result, node); |
| 8406 | }, | 8433 | }, |
| 8407 | .memcpy => { | 8434 | .memcpy => { |
| 8408 | const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ | 8435 | _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ |
| 8409 | .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), | 8436 | .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), |
| 8410 | .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]), | 8437 | .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]), |
| 8411 | .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), | 8438 | .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), |
| 8412 | }); | 8439 | }); |
| 8413 | return rvalue(gz, ri, result, node); | 8440 | return rvalue(gz, ri, .void_value, node); |
| 8414 | }, | 8441 | }, |
| 8415 | .memset => { | 8442 | .memset => { |
| 8416 | const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ | 8443 | _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ |
| 8417 | .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), | 8444 | .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), |
| 8418 | .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]), | 8445 | .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]), |
| 8419 | .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), | 8446 | .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), |
| 8420 | }); | 8447 | }); |
| 8421 | return rvalue(gz, ri, result, node); | 8448 | return rvalue(gz, ri, .void_value, node); |
| 8422 | }, | 8449 | }, |
| 8423 | .shuffle => { | 8450 | .shuffle => { |
| 8424 | const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ | 8451 | const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ |
| ... | @@ -8459,12 +8486,12 @@ fn builtinCall( | ... | @@ -8459,12 +8486,12 @@ fn builtinCall( |
| 8459 | .prefetch => { | 8486 | .prefetch => { |
| 8460 | const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); | 8487 | const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); |
| 8461 | const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]); | 8488 | const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]); |
| 8462 | const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ | 8489 | _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ |
| 8463 | .node = gz.nodeIndexToRelative(node), | 8490 | .node = gz.nodeIndexToRelative(node), |
| 8464 | .lhs = ptr, | 8491 | .lhs = ptr, |
| 8465 | .rhs = options, | 8492 | .rhs = options, |
| 8466 | }); | 8493 | }); |
| 8467 | return rvalue(gz, ri, result, node); | 8494 | return rvalue(gz, ri, .void_value, node); |
| 8468 | }, | 8495 | }, |
| 8469 | .c_va_arg => { | 8496 | .c_va_arg => { |
| 8470 | if (astgen.fn_block == null) { | 8497 | if (astgen.fn_block == null) { |
| ... | @@ -8509,16 +8536,6 @@ fn builtinCall( | ... | @@ -8509,16 +8536,6 @@ fn builtinCall( |
| 8509 | } | 8536 | } |
| 8510 | } | 8537 | } |
| 8511 | 8538 | ||
| 8512 | fn 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 | |||
| 8522 | fn hasDeclOrField( | 8539 | fn hasDeclOrField( |
| 8523 | gz: *GenZir, | 8540 | gz: *GenZir, |
| 8524 | scope: *Scope, | 8541 | scope: *Scope, |
| ... | @@ -8988,7 +9005,7 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{ | ... | @@ -8988,7 +9005,7 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{ |
| 8988 | }); | 9005 | }); |
| 8989 | 9006 | ||
| 8990 | comptime { | 9007 | comptime { |
| 8991 | // These checks ensure that std.zig.primitives stays in synce with the primitive->Zir map. | 9008 | // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map. |
| 8992 | const primitives = std.zig.primitives; | 9009 | const primitives = std.zig.primitives; |
| 8993 | for (primitive_instrs.kvs) |kv| { | 9010 | for (primitive_instrs.kvs) |kv| { |
| 8994 | if (!primitives.isPrimitive(kv.key)) { | 9011 | if (!primitives.isPrimitive(kv.key)) { |
| ... | @@ -10369,7 +10386,7 @@ fn appendErrorTok( | ... | @@ -10369,7 +10386,7 @@ fn appendErrorTok( |
| 10369 | comptime format: []const u8, | 10386 | comptime format: []const u8, |
| 10370 | args: anytype, | 10387 | args: anytype, |
| 10371 | ) !void { | 10388 | ) !void { |
| 10372 | try astgen.appendErrorTokNotes(token, format, args, &[0]u32{}); | 10389 | try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{}); |
| 10373 | } | 10390 | } |
| 10374 | 10391 | ||
| 10375 | fn failTokNotes( | 10392 | fn failTokNotes( |
| ... | @@ -10379,7 +10396,7 @@ fn failTokNotes( | ... | @@ -10379,7 +10396,7 @@ fn failTokNotes( |
| 10379 | args: anytype, | 10396 | args: anytype, |
| 10380 | notes: []const u32, | 10397 | notes: []const u32, |
| 10381 | ) InnerError { | 10398 | ) InnerError { |
| 10382 | try appendErrorTokNotes(astgen, token, format, args, notes); | 10399 | try appendErrorTokNotesOff(astgen, token, 0, format, args, notes); |
| 10383 | return error.AnalysisFail; | 10400 | return error.AnalysisFail; |
| 10384 | } | 10401 | } |
| 10385 | 10402 | ||
| ... | @@ -10390,27 +10407,11 @@ fn appendErrorTokNotes( | ... | @@ -10390,27 +10407,11 @@ fn appendErrorTokNotes( |
| 10390 | args: anytype, | 10407 | args: anytype, |
| 10391 | notes: []const u32, | 10408 | notes: []const u32, |
| 10392 | ) !void { | 10409 | ) !void { |
| 10393 | @setCold(true); | 10410 | return appendErrorTokNotesOff(astgen, token, 0, format, args, notes); |
| 10394 | const string_bytes = &astgen.string_bytes; | ||
| 10395 | const msg = @intCast(u32, string_bytes.items.len); | ||
| 10396 | try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); | ||
| 10397 | const notes_index: u32 = if (notes.len != 0) blk: { | ||
| 10398 | const notes_start = astgen.extra.items.len; | ||
| 10399 | try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); | ||
| 10400 | astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); | ||
| 10401 | astgen.extra.appendSliceAssumeCapacity(notes); | ||
| 10402 | break :blk @intCast(u32, notes_start); | ||
| 10403 | } else 0; | ||
| 10404 | try astgen.compile_errors.append(astgen.gpa, .{ | ||
| 10405 | .msg = msg, | ||
| 10406 | .node = 0, | ||
| 10407 | .token = token, | ||
| 10408 | .byte_offset = 0, | ||
| 10409 | .notes = notes_index, | ||
| 10410 | }); | ||
| 10411 | } | 10411 | } |
| 10412 | 10412 | ||
| 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. | ||
| 10414 | fn failOff( | 10415 | fn failOff( |
| 10415 | astgen: *AstGen, | 10416 | astgen: *AstGen, |
| 10416 | token: Ast.TokenIndex, | 10417 | token: Ast.TokenIndex, |
| ... | @@ -10418,27 +10419,36 @@ fn failOff( | ... | @@ -10418,27 +10419,36 @@ fn failOff( |
| 10418 | comptime format: []const u8, | 10419 | comptime format: []const u8, |
| 10419 | args: anytype, | 10420 | args: anytype, |
| 10420 | ) InnerError { | 10421 | ) InnerError { |
| 10421 | try appendErrorOff(astgen, token, byte_offset, format, args); | 10422 | try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{}); |
| 10422 | return error.AnalysisFail; | 10423 | return error.AnalysisFail; |
| 10423 | } | 10424 | } |
| 10424 | 10425 | ||
| 10425 | fn appendErrorOff( | 10426 | fn appendErrorTokNotesOff( |
| 10426 | astgen: *AstGen, | 10427 | astgen: *AstGen, |
| 10427 | token: Ast.TokenIndex, | 10428 | token: Ast.TokenIndex, |
| 10428 | byte_offset: u32, | 10429 | byte_offset: u32, |
| 10429 | comptime format: []const u8, | 10430 | comptime format: []const u8, |
| 10430 | args: anytype, | 10431 | args: anytype, |
| 10431 | ) Allocator.Error!void { | 10432 | notes: []const u32, |
| 10433 | ) !void { | ||
| 10432 | @setCold(true); | 10434 | @setCold(true); |
| 10435 | const gpa = astgen.gpa; | ||
| 10433 | const string_bytes = &astgen.string_bytes; | 10436 | const string_bytes = &astgen.string_bytes; |
| 10434 | const msg = @intCast(u32, string_bytes.items.len); | 10437 | const msg = @intCast(u32, string_bytes.items.len); |
| 10435 | try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); | 10438 | try string_bytes.writer(gpa).print(format ++ "\x00", args); |
| 10436 | try astgen.compile_errors.append(astgen.gpa, .{ | 10439 | const notes_index: u32 = if (notes.len != 0) blk: { |
| 10440 | const notes_start = astgen.extra.items.len; | ||
| 10441 | try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); | ||
| 10442 | astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); | ||
| 10443 | astgen.extra.appendSliceAssumeCapacity(notes); | ||
| 10444 | break :blk @intCast(u32, notes_start); | ||
| 10445 | } else 0; | ||
| 10446 | try astgen.compile_errors.append(gpa, .{ | ||
| 10437 | .msg = msg, | 10447 | .msg = msg, |
| 10438 | .node = 0, | 10448 | .node = 0, |
| 10439 | .token = token, | 10449 | .token = token, |
| 10440 | .byte_offset = byte_offset, | 10450 | .byte_offset = byte_offset, |
| 10441 | .notes = 0, | 10451 | .notes = notes_index, |
| 10442 | }); | 10452 | }); |
| 10443 | } | 10453 | } |
| 10444 | 10454 | ||
| ... | @@ -10447,6 +10457,16 @@ fn errNoteTok( | ... | @@ -10447,6 +10457,16 @@ fn errNoteTok( |
| 10447 | token: Ast.TokenIndex, | 10457 | token: Ast.TokenIndex, |
| 10448 | comptime format: []const u8, | 10458 | comptime format: []const u8, |
| 10449 | args: anytype, | 10459 | args: anytype, |
| 10460 | ) Allocator.Error!u32 { | ||
| 10461 | return errNoteTokOff(astgen, token, 0, format, args); | ||
| 10462 | } | ||
| 10463 | |||
| 10464 | fn errNoteTokOff( | ||
| 10465 | astgen: *AstGen, | ||
| 10466 | token: Ast.TokenIndex, | ||
| 10467 | byte_offset: u32, | ||
| 10468 | comptime format: []const u8, | ||
| 10469 | args: anytype, | ||
| 10450 | ) Allocator.Error!u32 { | 10470 | ) Allocator.Error!u32 { |
| 10451 | @setCold(true); | 10471 | @setCold(true); |
| 10452 | const string_bytes = &astgen.string_bytes; | 10472 | const string_bytes = &astgen.string_bytes; |
| ... | @@ -10456,7 +10476,7 @@ fn errNoteTok( | ... | @@ -10456,7 +10476,7 @@ fn errNoteTok( |
| 10456 | .msg = msg, | 10476 | .msg = msg, |
| 10457 | .node = 0, | 10477 | .node = 0, |
| 10458 | .token = token, | 10478 | .token = token, |
| 10459 | .byte_offset = 0, | 10479 | .byte_offset = byte_offset, |
| 10460 | .notes = 0, | 10480 | .notes = 0, |
| 10461 | }); | 10481 | }); |
| 10462 | } | 10482 | } |
| ... | @@ -10787,7 +10807,7 @@ const Scope = struct { | ... | @@ -10787,7 +10807,7 @@ const Scope = struct { |
| 10787 | /// ref of the capture for decls in this namespace | 10807 | /// ref of the capture for decls in this namespace |
| 10788 | captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, | 10808 | captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, |
| 10789 | 10809 | ||
| 10790 | pub fn deinit(self: *Namespace, gpa: Allocator) void { | 10810 | fn deinit(self: *Namespace, gpa: Allocator) void { |
| 10791 | self.decls.deinit(gpa); | 10811 | self.decls.deinit(gpa); |
| 10792 | self.captures.deinit(gpa); | 10812 | self.captures.deinit(gpa); |
| 10793 | self.* = undefined; | 10813 | self.* = undefined; |
| ... | @@ -12623,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { | ... | @@ -12623,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { |
| 12623 | }, | 12643 | }, |
| 12624 | } }); | 12644 | } }); |
| 12625 | } | 12645 | } |
| 12646 | |||
| 12647 | fn lowerAstErrors(astgen: *AstGen) !void { | ||
| 12648 | const tree = astgen.tree; | ||
| 12649 | assert(tree.errors.len > 0); | ||
| 12650 | |||
| 12651 | const gpa = astgen.gpa; | ||
| 12652 | const parse_err = tree.errors[0]; | ||
| 12653 | |||
| 12654 | var msg: std.ArrayListUnmanaged(u8) = .{}; | ||
| 12655 | defer msg.deinit(gpa); | ||
| 12656 | |||
| 12657 | const token_starts = tree.tokens.items(.start); | ||
| 12658 | const token_tags = tree.tokens.items(.tag); | ||
| 12659 | |||
| 12660 | var notes: std.ArrayListUnmanaged(u32) = .{}; | ||
| 12661 | defer notes.deinit(gpa); | ||
| 12662 | |||
| 12663 | if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { | ||
| 12664 | const tok = parse_err.token + @boolToInt(parse_err.token_is_prev); | ||
| 12665 | const bad_off = @intCast(u32, tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); | ||
| 12666 | const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; | ||
| 12667 | try notes.append(gpa, try astgen.errNoteTokOff(tok, bad_off, "invalid byte: '{'}'", .{ | ||
| 12668 | std.zig.fmtEscapes(tree.source[byte_abs..][0..1]), | ||
| 12669 | })); | ||
| 12670 | } | ||
| 12671 | |||
| 12672 | for (tree.errors[1..]) |note| { | ||
| 12673 | if (!note.is_note) break; | ||
| 12674 | |||
| 12675 | msg.clearRetainingCapacity(); | ||
| 12676 | try tree.renderError(note, msg.writer(gpa)); | ||
| 12677 | try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items})); | ||
| 12678 | } | ||
| 12679 | |||
| 12680 | const extra_offset = tree.errorOffset(parse_err); | ||
| 12681 | msg.clearRetainingCapacity(); | ||
| 12682 | try tree.renderError(parse_err, msg.writer(gpa)); | ||
| 12683 | try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items); | ||
| 12684 | } |
src/Autodoc.zig-1| ... | @@ -1338,7 +1338,6 @@ fn walkInstruction( | ... | @@ -1338,7 +1338,6 @@ fn walkInstruction( |
| 1338 | .embed_file, | 1338 | .embed_file, |
| 1339 | .error_name, | 1339 | .error_name, |
| 1340 | .panic, | 1340 | .panic, |
| 1341 | .set_cold, // @check | ||
| 1342 | .set_runtime_safety, // @check | 1341 | .set_runtime_safety, // @check |
| 1343 | .sqrt, | 1342 | .sqrt, |
| 1344 | .sin, | 1343 | .sin, |
src/BuiltinFn.zig+8| ... | @@ -109,6 +109,7 @@ pub const Tag = enum { | ... | @@ -109,6 +109,7 @@ pub const Tag = enum { |
| 109 | sub_with_overflow, | 109 | sub_with_overflow, |
| 110 | tag_name, | 110 | tag_name, |
| 111 | This, | 111 | This, |
| 112 | trap, | ||
| 112 | truncate, | 113 | truncate, |
| 113 | Type, | 114 | Type, |
| 114 | type_info, | 115 | type_info, |
| ... | @@ -915,6 +916,13 @@ pub const list = list: { | ... | @@ -915,6 +916,13 @@ pub const list = list: { |
| 915 | .param_count = 0, | 916 | .param_count = 0, |
| 916 | }, | 917 | }, |
| 917 | }, | 918 | }, |
| 919 | .{ | ||
| 920 | "@trap", | ||
| 921 | .{ | ||
| 922 | .tag = .trap, | ||
| 923 | .param_count = 0, | ||
| 924 | }, | ||
| 925 | }, | ||
| 918 | .{ | 926 | .{ |
| 919 | "@truncate", | 927 | "@truncate", |
| 920 | .{ | 928 | .{ |
src/Compilation.zig+424-608| ... | @@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator; | ... | @@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator; |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const log = std.log.scoped(.compilation); | 8 | const log = std.log.scoped(.compilation); |
| 9 | const Target = std.Target; | 9 | const Target = std.Target; |
| 10 | const ThreadPool = std.Thread.Pool; | ||
| 11 | const WaitGroup = std.Thread.WaitGroup; | ||
| 12 | const ErrorBundle = std.zig.ErrorBundle; | ||
| 10 | 13 | ||
| 11 | const Value = @import("value.zig").Value; | 14 | const Value = @import("value.zig").Value; |
| 12 | const Type = @import("type.zig").Type; | 15 | const Type = @import("type.zig").Type; |
| ... | @@ -30,8 +33,6 @@ const Cache = std.Build.Cache; | ... | @@ -30,8 +33,6 @@ const Cache = std.Build.Cache; |
| 30 | const translate_c = @import("translate_c.zig"); | 33 | const translate_c = @import("translate_c.zig"); |
| 31 | const clang = @import("clang.zig"); | 34 | const clang = @import("clang.zig"); |
| 32 | const c_codegen = @import("codegen/c.zig"); | 35 | const c_codegen = @import("codegen/c.zig"); |
| 33 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 34 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 35 | const libtsan = @import("libtsan.zig"); | 36 | const libtsan = @import("libtsan.zig"); |
| 36 | const Zir = @import("Zir.zig"); | 37 | const Zir = @import("Zir.zig"); |
| 37 | const Autodoc = @import("Autodoc.zig"); | 38 | const Autodoc = @import("Autodoc.zig"); |
| ... | @@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false, | ... | @@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false, |
| 99 | job_queued_compiler_rt_obj: bool = false, | 100 | job_queued_compiler_rt_obj: bool = false, |
| 100 | alloc_failure_occurred: bool = false, | 101 | alloc_failure_occurred: bool = false, |
| 101 | formatted_panics: bool = false, | 102 | formatted_panics: bool = false, |
| 103 | last_update_was_cache_hit: bool = false, | ||
| 102 | 104 | ||
| 103 | c_source_files: []const CSourceFile, | 105 | c_source_files: []const CSourceFile, |
| 104 | clang_argv: []const []const u8, | 106 | clang_argv: []const []const u8, |
| ... | @@ -334,12 +336,41 @@ pub const MiscTask = enum { | ... | @@ -334,12 +336,41 @@ pub const MiscTask = enum { |
| 334 | libssp, | 336 | libssp, |
| 335 | zig_libc, | 337 | zig_libc, |
| 336 | analyze_pkg, | 338 | analyze_pkg, |
| 339 | |||
| 340 | @"musl crti.o", | ||
| 341 | @"musl crtn.o", | ||
| 342 | @"musl crt1.o", | ||
| 343 | @"musl rcrt1.o", | ||
| 344 | @"musl Scrt1.o", | ||
| 345 | @"musl libc.a", | ||
| 346 | @"musl libc.so", | ||
| 347 | |||
| 348 | @"wasi crt1-reactor.o", | ||
| 349 | @"wasi crt1-command.o", | ||
| 350 | @"wasi libc.a", | ||
| 351 | @"libwasi-emulated-process-clocks.a", | ||
| 352 | @"libwasi-emulated-getpid.a", | ||
| 353 | @"libwasi-emulated-mman.a", | ||
| 354 | @"libwasi-emulated-signal.a", | ||
| 355 | |||
| 356 | @"glibc crti.o", | ||
| 357 | @"glibc crtn.o", | ||
| 358 | @"glibc Scrt1.o", | ||
| 359 | @"glibc libc_nonshared.a", | ||
| 360 | @"glibc shared object", | ||
| 361 | |||
| 362 | @"mingw-w64 crt2.o", | ||
| 363 | @"mingw-w64 dllcrt2.o", | ||
| 364 | @"mingw-w64 mingw32.lib", | ||
| 365 | @"mingw-w64 msvcrt-os.lib", | ||
| 366 | @"mingw-w64 mingwex.lib", | ||
| 367 | @"mingw-w64 uuid.lib", | ||
| 337 | }; | 368 | }; |
| 338 | 369 | ||
| 339 | pub const MiscError = struct { | 370 | pub const MiscError = struct { |
| 340 | /// Allocated with gpa. | 371 | /// Allocated with gpa. |
| 341 | msg: []u8, | 372 | msg: []u8, |
| 342 | children: ?AllErrors = null, | 373 | children: ?ErrorBundle = null, |
| 343 | 374 | ||
| 344 | pub fn deinit(misc_err: *MiscError, gpa: Allocator) void { | 375 | pub fn deinit(misc_err: *MiscError, gpa: Allocator) void { |
| 345 | gpa.free(misc_err.msg); | 376 | gpa.free(misc_err.msg); |
| ... | @@ -365,448 +396,6 @@ pub const LldError = struct { | ... | @@ -365,448 +396,6 @@ pub const LldError = struct { |
| 365 | } | 396 | } |
| 366 | }; | 397 | }; |
| 367 | 398 | ||
| 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. | ||
| 373 | pub 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 | |||
| 810 | pub const Directory = Cache.Directory; | 399 | pub const Directory = Cache.Directory; |
| 811 | 400 | ||
| 812 | pub const EmitLoc = struct { | 401 | pub const EmitLoc = struct { |
| ... | @@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory( | ... | @@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory( |
| 2259 | } | 1848 | } |
| 2260 | } | 1849 | } |
| 2261 | 1850 | ||
| 1851 | pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void { | ||
| 1852 | comp.bin_file.child_pid = pid; | ||
| 1853 | try comp.makeBinFileWritable(); | ||
| 1854 | try comp.update(prog_node); | ||
| 1855 | try comp.makeBinFileExecutable(); | ||
| 1856 | } | ||
| 1857 | |||
| 2262 | /// Detect changes to source files, perform semantic analysis, and update the output files. | 1858 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 2263 | pub fn update(comp: *Compilation) !void { | 1859 | pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { |
| 2264 | const tracy_trace = trace(@src()); | 1860 | const tracy_trace = trace(@src()); |
| 2265 | defer tracy_trace.end(); | 1861 | defer tracy_trace.end(); |
| 2266 | 1862 | ||
| 2267 | comp.clearMiscFailures(); | 1863 | comp.clearMiscFailures(); |
| 1864 | comp.last_update_was_cache_hit = false; | ||
| 2268 | 1865 | ||
| 2269 | var man: Cache.Manifest = undefined; | 1866 | var man: Cache.Manifest = undefined; |
| 2270 | defer if (comp.whole_cache_manifest != null) man.deinit(); | 1867 | defer if (comp.whole_cache_manifest != null) man.deinit(); |
| ... | @@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void { |
| 2292 | return err; | 1889 | return err; |
| 2293 | }; | 1890 | }; |
| 2294 | if (is_hit) { | 1891 | if (is_hit) { |
| 1892 | comp.last_update_was_cache_hit = true; | ||
| 2295 | log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); | 1893 | log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); |
| 2296 | const digest = man.final(); | 1894 | const digest = man.final(); |
| 2297 | 1895 | ||
| ... | @@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void { |
| 2407 | } | 2005 | } |
| 2408 | } | 2006 | } |
| 2409 | 2007 | ||
| 2410 | // If the terminal is dumb, we dont want to show the user all the output. | ||
| 2411 | var progress: std.Progress = .{ .dont_print_on_dumb = true }; | ||
| 2412 | const main_progress_node = progress.start("", 0); | ||
| 2413 | defer main_progress_node.end(); | ||
| 2414 | switch (comp.color) { | ||
| 2415 | .off => { | ||
| 2416 | progress.terminal = null; | ||
| 2417 | }, | ||
| 2418 | .on => { | ||
| 2419 | progress.terminal = std.io.getStdErr(); | ||
| 2420 | progress.supports_ansi_escape_codes = true; | ||
| 2421 | }, | ||
| 2422 | .auto => {}, | ||
| 2423 | } | ||
| 2424 | |||
| 2425 | try comp.performAllTheWork(main_progress_node); | 2008 | try comp.performAllTheWork(main_progress_node); |
| 2426 | 2009 | ||
| 2427 | if (comp.bin_file.options.module) |module| { | 2010 | if (comp.bin_file.options.module) |module| { |
| ... | @@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void { | ... | @@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void { |
| 2891 | } | 2474 | } |
| 2892 | 2475 | ||
| 2893 | /// This function is temporally single-threaded. | 2476 | /// This function is temporally single-threaded. |
| 2894 | pub fn totalErrorCount(self: *Compilation) usize { | 2477 | pub fn totalErrorCount(self: *Compilation) u32 { |
| 2895 | var total: usize = self.failed_c_objects.count() + self.misc_failures.count() + | 2478 | var total: usize = self.failed_c_objects.count() + self.misc_failures.count() + |
| 2896 | @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len; | 2479 | @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len; |
| 2897 | 2480 | ||
| ... | @@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize { | ... | @@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 2951 | } | 2534 | } |
| 2952 | } | 2535 | } |
| 2953 | 2536 | ||
| 2954 | return total; | 2537 | return @intCast(u32, total); |
| 2955 | } | 2538 | } |
| 2956 | 2539 | ||
| 2957 | /// This function is temporally single-threaded. | 2540 | /// This function is temporally single-threaded. |
| 2958 | pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | 2541 | pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { |
| 2959 | var arena = std.heap.ArenaAllocator.init(self.gpa); | 2542 | const gpa = self.gpa; |
| 2960 | errdefer arena.deinit(); | ||
| 2961 | const arena_allocator = arena.allocator(); | ||
| 2962 | 2543 | ||
| 2963 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); | 2544 | var bundle: ErrorBundle.Wip = undefined; |
| 2964 | defer errors.deinit(); | 2545 | try bundle.init(gpa); |
| 2546 | defer bundle.deinit(); | ||
| 2965 | 2547 | ||
| 2966 | { | 2548 | { |
| 2967 | var it = self.failed_c_objects.iterator(); | 2549 | var it = self.failed_c_objects.iterator(); |
| ... | @@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 2970 | const err_msg = entry.value_ptr.*; | 2552 | const err_msg = entry.value_ptr.*; |
| 2971 | // TODO these fields will need to be adjusted when we have proper | 2553 | // TODO these fields will need to be adjusted when we have proper |
| 2972 | // C error reporting bubbling up. | 2554 | // C error reporting bubbling up. |
| 2973 | try errors.append(.{ | 2555 | try bundle.addRootErrorMessage(.{ |
| 2974 | .src = .{ | 2556 | .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}), |
| 2975 | .src_path = try arena_allocator.dupe(u8, c_object.src.src_path), | 2557 | .src_loc = try bundle.addSourceLocation(.{ |
| 2976 | .msg = try std.fmt.allocPrint(arena_allocator, "unable to build C object: {s}", .{ | 2558 | .src_path = try bundle.addString(c_object.src.src_path), |
| 2977 | err_msg.msg, | 2559 | .span_start = 0, |
| 2978 | }), | 2560 | .span_main = 0, |
| 2979 | .span = .{ .start = 0, .end = 1, .main = 0 }, | 2561 | .span_end = 1, |
| 2980 | .line = err_msg.line, | 2562 | .line = err_msg.line, |
| 2981 | .column = err_msg.column, | 2563 | .column = err_msg.column, |
| 2982 | .source_line = null, // TODO | 2564 | .source_line = 0, // TODO |
| 2983 | }, | 2565 | }), |
| 2984 | }); | 2566 | }); |
| 2985 | } | 2567 | } |
| 2986 | } | 2568 | } |
| 2569 | |||
| 2987 | for (self.lld_errors.items) |lld_error| { | 2570 | for (self.lld_errors.items) |lld_error| { |
| 2988 | const notes = try arena_allocator.alloc(AllErrors.Message, lld_error.context_lines.len); | 2571 | const notes_len = @intCast(u32, lld_error.context_lines.len); |
| 2989 | for (lld_error.context_lines, 0..) |context_line, i| { | ||
| 2990 | notes[i] = .{ .plain = .{ | ||
| 2991 | .msg = try arena_allocator.dupe(u8, context_line), | ||
| 2992 | } }; | ||
| 2993 | } | ||
| 2994 | 2572 | ||
| 2995 | try errors.append(.{ | 2573 | try bundle.addRootErrorMessage(.{ |
| 2996 | .plain = .{ | 2574 | .msg = try bundle.addString(lld_error.msg), |
| 2997 | .msg = try arena_allocator.dupe(u8, lld_error.msg), | 2575 | .notes_len = notes_len, |
| 2998 | .notes = notes, | ||
| 2999 | }, | ||
| 3000 | }); | 2576 | }); |
| 2577 | const notes_start = try bundle.reserveNotes(notes_len); | ||
| 2578 | for (notes_start.., lld_error.context_lines) |note, context_line| { | ||
| 2579 | bundle.extra.items[note] = @enumToInt(bundle.addErrorMessageAssumeCapacity(.{ | ||
| 2580 | .msg = try bundle.addString(context_line), | ||
| 2581 | })); | ||
| 2582 | } | ||
| 3001 | } | 2583 | } |
| 3002 | for (self.misc_failures.values()) |*value| { | 2584 | for (self.misc_failures.values()) |*value| { |
| 3003 | try AllErrors.addPlainWithChildren(&arena, &errors, value.msg, value.children); | 2585 | try bundle.addRootErrorMessage(.{ |
| 2586 | .msg = try bundle.addString(value.msg), | ||
| 2587 | .notes_len = if (value.children) |b| b.errorMessageCount() else 0, | ||
| 2588 | }); | ||
| 2589 | if (value.children) |b| try bundle.addBundle(b); | ||
| 3004 | } | 2590 | } |
| 3005 | if (self.alloc_failure_occurred) { | 2591 | if (self.alloc_failure_occurred) { |
| 3006 | try AllErrors.addPlain(&arena, &errors, "memory allocation failure"); | 2592 | try bundle.addRootErrorMessage(.{ |
| 2593 | .msg = try bundle.addString("memory allocation failure"), | ||
| 2594 | }); | ||
| 3007 | } | 2595 | } |
| 3008 | if (self.bin_file.options.module) |module| { | 2596 | if (self.bin_file.options.module) |module| { |
| 3009 | { | 2597 | { |
| 3010 | var it = module.failed_files.iterator(); | 2598 | var it = module.failed_files.iterator(); |
| 3011 | while (it.next()) |entry| { | 2599 | while (it.next()) |entry| { |
| 3012 | if (entry.value_ptr.*) |msg| { | 2600 | if (entry.value_ptr.*) |msg| { |
| 3013 | try AllErrors.add(module, &arena, &errors, msg.*); | 2601 | try addModuleErrorMsg(&bundle, msg.*); |
| 3014 | } else { | 2602 | } else { |
| 3015 | // Must be ZIR errors. In order for ZIR errors to exist, the parsing | 2603 | // Must be ZIR errors. Note that this may include AST errors. |
| 3016 | // must have completed successfully. | 2604 | // addZirErrorMessages asserts that the tree is loaded. |
| 3017 | const tree = try entry.key_ptr.*.getTree(module.gpa); | 2605 | _ = try entry.key_ptr.*.getTree(gpa); |
| 3018 | assert(tree.errors.len == 0); | 2606 | try addZirErrorMessages(&bundle, entry.key_ptr.*); |
| 3019 | try AllErrors.addZir(arena_allocator, &errors, entry.key_ptr.*); | ||
| 3020 | } | 2607 | } |
| 3021 | } | 2608 | } |
| 3022 | } | 2609 | } |
| ... | @@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3024 | var it = module.failed_embed_files.iterator(); | 2611 | var it = module.failed_embed_files.iterator(); |
| 3025 | while (it.next()) |entry| { | 2612 | while (it.next()) |entry| { |
| 3026 | const msg = entry.value_ptr.*; | 2613 | const msg = entry.value_ptr.*; |
| 3027 | try AllErrors.add(module, &arena, &errors, msg.*); | 2614 | try addModuleErrorMsg(&bundle, msg.*); |
| 3028 | } | 2615 | } |
| 3029 | } | 2616 | } |
| 3030 | { | 2617 | { |
| ... | @@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3034 | // Skip errors for Decls within files that had a parse failure. | 2621 | // Skip errors for Decls within files that had a parse failure. |
| 3035 | // We'll try again once parsing succeeds. | 2622 | // We'll try again once parsing succeeds. |
| 3036 | if (decl.getFileScope().okToReportErrors()) { | 2623 | if (decl.getFileScope().okToReportErrors()) { |
| 3037 | try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); | 2624 | try addModuleErrorMsg(&bundle, entry.value_ptr.*.*); |
| 3038 | if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { | 2625 | if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { |
| 3039 | if (c_error.path) |some| | 2626 | try bundle.addRootErrorMessage(.{ |
| 3040 | try errors.append(.{ | 2627 | .msg = try bundle.addString(std.mem.span(c_error.msg)), |
| 3041 | .src = .{ | 2628 | .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(.{ |
| 3042 | .src_path = try arena_allocator.dupe(u8, std.mem.span(some)), | 2629 | .src_path = try bundle.addString(std.mem.span(some)), |
| 3043 | .span = .{ .start = c_error.offset, .end = c_error.offset + 1, .main = c_error.offset }, | 2630 | .span_start = c_error.offset, |
| 3044 | .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)), | 2631 | .span_main = c_error.offset, |
| 3045 | .line = c_error.line, | 2632 | .span_end = c_error.offset + 1, |
| 3046 | .column = c_error.column, | 2633 | .line = c_error.line, |
| 3047 | .source_line = if (c_error.source_line) |line| try arena_allocator.dupe(u8, std.mem.span(line)) else null, | 2634 | .column = c_error.column, |
| 3048 | }, | 2635 | .source_line = if (c_error.source_line) |line| try bundle.addString(std.mem.span(line)) else 0, |
| 3049 | }) | 2636 | }) else .none, |
| 3050 | else | 2637 | }); |
| 3051 | try errors.append(.{ | ||
| 3052 | .plain = .{ .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)) }, | ||
| 3053 | }); | ||
| 3054 | }; | 2638 | }; |
| 3055 | } | 2639 | } |
| 3056 | } | 2640 | } |
| ... | @@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3062 | // Skip errors for Decls within files that had a parse failure. | 2646 | // Skip errors for Decls within files that had a parse failure. |
| 3063 | // We'll try again once parsing succeeds. | 2647 | // We'll try again once parsing succeeds. |
| 3064 | if (decl.getFileScope().okToReportErrors()) { | 2648 | if (decl.getFileScope().okToReportErrors()) { |
| 3065 | try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); | 2649 | try addModuleErrorMsg(&bundle, entry.value_ptr.*.*); |
| 3066 | } | 2650 | } |
| 3067 | } | 2651 | } |
| 3068 | } | 2652 | } |
| 3069 | for (module.failed_exports.values()) |value| { | 2653 | for (module.failed_exports.values()) |value| { |
| 3070 | try AllErrors.add(module, &arena, &errors, value.*); | 2654 | try addModuleErrorMsg(&bundle, value.*); |
| 3071 | } | 2655 | } |
| 3072 | } | 2656 | } |
| 3073 | 2657 | ||
| 3074 | if (errors.items.len == 0) { | 2658 | if (bundle.root_list.items.len == 0) { |
| 3075 | if (self.link_error_flags.no_entry_point_found) { | 2659 | if (self.link_error_flags.no_entry_point_found) { |
| 3076 | try errors.append(.{ | 2660 | try bundle.addRootErrorMessage(.{ |
| 3077 | .plain = .{ | 2661 | .msg = try bundle.addString("no entry point found"), |
| 3078 | .msg = try std.fmt.allocPrint(arena_allocator, "no entry point found", .{}), | ||
| 3079 | }, | ||
| 3080 | }); | 2662 | }); |
| 3081 | } | 2663 | } |
| 3082 | } | 2664 | } |
| 3083 | 2665 | ||
| 3084 | if (self.link_error_flags.missing_libc) { | 2666 | if (self.link_error_flags.missing_libc) { |
| 3085 | const notes = try arena_allocator.create([2]AllErrors.Message); | 2667 | try bundle.addRootErrorMessage(.{ |
| 3086 | notes.* = .{ | 2668 | .msg = try bundle.addString("libc not available"), |
| 3087 | .{ .plain = .{ | 2669 | .notes_len = 2, |
| 3088 | .msg = try arena_allocator.dupe(u8, "run 'zig libc -h' to learn about libc installations"), | ||
| 3089 | } }, | ||
| 3090 | .{ .plain = .{ | ||
| 3091 | .msg = try arena_allocator.dupe(u8, "run 'zig targets' to see the targets for which zig can always provide libc"), | ||
| 3092 | } }, | ||
| 3093 | }; | ||
| 3094 | try errors.append(.{ | ||
| 3095 | .plain = .{ | ||
| 3096 | .msg = try std.fmt.allocPrint(arena_allocator, "libc not available", .{}), | ||
| 3097 | .notes = notes, | ||
| 3098 | }, | ||
| 3099 | }); | 2670 | }); |
| 2671 | const notes_start = try bundle.reserveNotes(2); | ||
| 2672 | bundle.extra.items[notes_start + 0] = @enumToInt(try bundle.addErrorMessage(.{ | ||
| 2673 | .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"), | ||
| 2674 | })); | ||
| 2675 | bundle.extra.items[notes_start + 1] = @enumToInt(try bundle.addErrorMessage(.{ | ||
| 2676 | .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"), | ||
| 2677 | })); | ||
| 3100 | } | 2678 | } |
| 3101 | 2679 | ||
| 3102 | if (self.bin_file.options.module) |module| { | 2680 | if (self.bin_file.options.module) |module| { |
| 3103 | if (errors.items.len == 0 and module.compile_log_decls.count() != 0) { | 2681 | if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) { |
| 3104 | const keys = module.compile_log_decls.keys(); | 2682 | const keys = module.compile_log_decls.keys(); |
| 3105 | const values = module.compile_log_decls.values(); | 2683 | const values = module.compile_log_decls.values(); |
| 3106 | // First one will be the error; subsequent ones will be notes. | 2684 | // First one will be the error; subsequent ones will be notes. |
| ... | @@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3109 | const err_msg = Module.ErrorMsg{ | 2687 | const err_msg = Module.ErrorMsg{ |
| 3110 | .src_loc = src_loc, | 2688 | .src_loc = src_loc, |
| 3111 | .msg = "found compile log statement", | 2689 | .msg = "found compile log statement", |
| 3112 | .notes = try self.gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), | 2690 | .notes = try gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), |
| 3113 | }; | 2691 | }; |
| 3114 | defer self.gpa.free(err_msg.notes); | 2692 | defer gpa.free(err_msg.notes); |
| 3115 | 2693 | ||
| 3116 | for (keys[1..], 0..) |key, i| { | 2694 | for (keys[1..], 0..) |key, i| { |
| 3117 | const note_decl = module.declPtr(key); | 2695 | const note_decl = module.declPtr(key); |
| ... | @@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3121 | }; | 2699 | }; |
| 3122 | } | 2700 | } |
| 3123 | 2701 | ||
| 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 | |||
| 2712 | pub const ErrorNoteHashContext = struct { | ||
| 2713 | eb: *const ErrorBundle.Wip, | ||
| 2714 | |||
| 2715 | pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 { | ||
| 2716 | var hasher = std.hash.Wyhash.init(0); | ||
| 2717 | const eb = ctx.eb.tmpBundle(); | ||
| 2718 | |||
| 2719 | hasher.update(eb.nullTerminatedString(key.msg)); | ||
| 2720 | if (key.src_loc != .none) { | ||
| 2721 | const src = eb.getSourceLocation(key.src_loc); | ||
| 2722 | hasher.update(eb.nullTerminatedString(src.src_path)); | ||
| 2723 | std.hash.autoHash(&hasher, src.line); | ||
| 2724 | std.hash.autoHash(&hasher, src.column); | ||
| 2725 | std.hash.autoHash(&hasher, src.span_main); | ||
| 3125 | } | 2726 | } |
| 2727 | |||
| 2728 | return @truncate(u32, hasher.final()); | ||
| 3126 | } | 2729 | } |
| 3127 | 2730 | ||
| 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 | }; | ||
| 3129 | 2757 | ||
| 3130 | return AllErrors{ | 2758 | pub fn addModuleErrorMsg(eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void { |
| 3131 | .list = try arena_allocator.dupe(AllErrors.Message, errors.items), | 2759 | const gpa = eb.gpa; |
| 3132 | .arena = arena.state, | 2760 | const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| { |
| 2761 | const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); | ||
| 2762 | defer gpa.free(file_path); | ||
| 2763 | try eb.addRootErrorMessage(.{ | ||
| 2764 | .msg = try eb.printString("unable to load '{s}': {s}", .{ | ||
| 2765 | file_path, @errorName(err), | ||
| 2766 | }), | ||
| 2767 | }); | ||
| 2768 | return; | ||
| 3133 | }; | 2769 | }; |
| 2770 | const err_span = try module_err_msg.src_loc.span(gpa); | ||
| 2771 | const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); | ||
| 2772 | const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); | ||
| 2773 | defer gpa.free(file_path); | ||
| 2774 | |||
| 2775 | var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{}; | ||
| 2776 | defer ref_traces.deinit(gpa); | ||
| 2777 | |||
| 2778 | for (module_err_msg.reference_trace) |module_reference| { | ||
| 2779 | if (module_reference.hidden != 0) { | ||
| 2780 | try ref_traces.append(gpa, .{ | ||
| 2781 | .decl_name = module_reference.hidden, | ||
| 2782 | .src_loc = .none, | ||
| 2783 | }); | ||
| 2784 | break; | ||
| 2785 | } else if (module_reference.decl == null) { | ||
| 2786 | try ref_traces.append(gpa, .{ | ||
| 2787 | .decl_name = 0, | ||
| 2788 | .src_loc = .none, | ||
| 2789 | }); | ||
| 2790 | break; | ||
| 2791 | } | ||
| 2792 | const source = try module_reference.src_loc.file_scope.getSource(gpa); | ||
| 2793 | const span = try module_reference.src_loc.span(gpa); | ||
| 2794 | const loc = std.zig.findLineColumn(source.bytes, span.main); | ||
| 2795 | const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa); | ||
| 2796 | defer gpa.free(rt_file_path); | ||
| 2797 | try ref_traces.append(gpa, .{ | ||
| 2798 | .decl_name = try eb.addString(std.mem.sliceTo(module_reference.decl.?, 0)), | ||
| 2799 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2800 | .src_path = try eb.addString(rt_file_path), | ||
| 2801 | .span_start = span.start, | ||
| 2802 | .span_main = span.main, | ||
| 2803 | .span_end = span.end, | ||
| 2804 | .line = @intCast(u32, loc.line), | ||
| 2805 | .column = @intCast(u32, loc.column), | ||
| 2806 | .source_line = 0, | ||
| 2807 | }), | ||
| 2808 | }); | ||
| 2809 | } | ||
| 2810 | |||
| 2811 | const src_loc = try eb.addSourceLocation(.{ | ||
| 2812 | .src_path = try eb.addString(file_path), | ||
| 2813 | .span_start = err_span.start, | ||
| 2814 | .span_main = err_span.main, | ||
| 2815 | .span_end = err_span.end, | ||
| 2816 | .line = @intCast(u32, err_loc.line), | ||
| 2817 | .column = @intCast(u32, err_loc.column), | ||
| 2818 | .source_line = if (module_err_msg.src_loc.lazy == .entire_file) | ||
| 2819 | 0 | ||
| 2820 | else | ||
| 2821 | try eb.addString(err_loc.source_line), | ||
| 2822 | .reference_trace_len = @intCast(u32, ref_traces.items.len), | ||
| 2823 | }); | ||
| 2824 | |||
| 2825 | for (ref_traces.items) |rt| { | ||
| 2826 | try eb.addReferenceTrace(rt); | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | // De-duplicate error notes. The main use case in mind for this is | ||
| 2830 | // too many "note: called from here" notes when eval branch quota is reached. | ||
| 2831 | var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .{}; | ||
| 2832 | defer notes.deinit(gpa); | ||
| 2833 | |||
| 2834 | for (module_err_msg.notes) |module_note| { | ||
| 2835 | const source = try module_note.src_loc.file_scope.getSource(gpa); | ||
| 2836 | const span = try module_note.src_loc.span(gpa); | ||
| 2837 | const loc = std.zig.findLineColumn(source.bytes, span.main); | ||
| 2838 | const note_file_path = try module_note.src_loc.file_scope.fullPath(gpa); | ||
| 2839 | defer gpa.free(note_file_path); | ||
| 2840 | |||
| 2841 | const gop = try notes.getOrPutContext(gpa, .{ | ||
| 2842 | .msg = try eb.addString(module_note.msg), | ||
| 2843 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2844 | .src_path = try eb.addString(note_file_path), | ||
| 2845 | .span_start = span.start, | ||
| 2846 | .span_main = span.main, | ||
| 2847 | .span_end = span.end, | ||
| 2848 | .line = @intCast(u32, loc.line), | ||
| 2849 | .column = @intCast(u32, loc.column), | ||
| 2850 | .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(loc.source_line), | ||
| 2851 | }), | ||
| 2852 | }, .{ .eb = eb }); | ||
| 2853 | if (gop.found_existing) { | ||
| 2854 | gop.key_ptr.count += 1; | ||
| 2855 | } | ||
| 2856 | } | ||
| 2857 | |||
| 2858 | const notes_len = @intCast(u32, notes.entries.len); | ||
| 2859 | |||
| 2860 | try eb.addRootErrorMessage(.{ | ||
| 2861 | .msg = try eb.addString(module_err_msg.msg), | ||
| 2862 | .src_loc = src_loc, | ||
| 2863 | .notes_len = notes_len, | ||
| 2864 | }); | ||
| 2865 | |||
| 2866 | const notes_start = try eb.reserveNotes(notes_len); | ||
| 2867 | |||
| 2868 | for (notes_start.., notes.keys()) |i, note| { | ||
| 2869 | eb.extra.items[i] = @enumToInt(try eb.addErrorMessage(note)); | ||
| 2870 | } | ||
| 3134 | } | 2871 | } |
| 3135 | 2872 | ||
| 3136 | pub fn getCompileLogOutput(self: *Compilation) []const u8 { | 2873 | pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void { |
| 3137 | const module = self.bin_file.options.module orelse return &[0]u8{}; | 2874 | assert(file.zir_loaded); |
| 3138 | return module.compile_log_text.items; | 2875 | assert(file.tree_loaded); |
| 2876 | assert(file.source_loaded); | ||
| 2877 | const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; | ||
| 2878 | assert(payload_index != 0); | ||
| 2879 | const gpa = eb.gpa; | ||
| 2880 | |||
| 2881 | const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); | ||
| 2882 | const items_len = header.data.items_len; | ||
| 2883 | var extra_index = header.end; | ||
| 2884 | for (0..items_len) |_| { | ||
| 2885 | const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); | ||
| 2886 | extra_index = item.end; | ||
| 2887 | const err_span = blk: { | ||
| 2888 | if (item.data.node != 0) { | ||
| 2889 | break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node); | ||
| 2890 | } | ||
| 2891 | const token_starts = file.tree.tokens.items(.start); | ||
| 2892 | const start = token_starts[item.data.token] + item.data.byte_offset; | ||
| 2893 | const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset; | ||
| 2894 | break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; | ||
| 2895 | }; | ||
| 2896 | const err_loc = std.zig.findLineColumn(file.source, err_span.main); | ||
| 2897 | |||
| 2898 | { | ||
| 2899 | const msg = file.zir.nullTerminatedString(item.data.msg); | ||
| 2900 | const src_path = try file.fullPath(gpa); | ||
| 2901 | defer gpa.free(src_path); | ||
| 2902 | try eb.addRootErrorMessage(.{ | ||
| 2903 | .msg = try eb.addString(msg), | ||
| 2904 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2905 | .src_path = try eb.addString(src_path), | ||
| 2906 | .span_start = err_span.start, | ||
| 2907 | .span_main = err_span.main, | ||
| 2908 | .span_end = err_span.end, | ||
| 2909 | .line = @intCast(u32, err_loc.line), | ||
| 2910 | .column = @intCast(u32, err_loc.column), | ||
| 2911 | .source_line = try eb.addString(err_loc.source_line), | ||
| 2912 | }), | ||
| 2913 | .notes_len = item.data.notesLen(file.zir), | ||
| 2914 | }); | ||
| 2915 | } | ||
| 2916 | |||
| 2917 | if (item.data.notes != 0) { | ||
| 2918 | const notes_start = try eb.reserveNotes(item.data.notes); | ||
| 2919 | const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); | ||
| 2920 | const body = file.zir.extra[block.end..][0..block.data.body_len]; | ||
| 2921 | for (notes_start.., body) |note_i, body_elem| { | ||
| 2922 | const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem); | ||
| 2923 | const msg = file.zir.nullTerminatedString(note_item.data.msg); | ||
| 2924 | const span = blk: { | ||
| 2925 | if (note_item.data.node != 0) { | ||
| 2926 | break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node); | ||
| 2927 | } | ||
| 2928 | const token_starts = file.tree.tokens.items(.start); | ||
| 2929 | const start = token_starts[note_item.data.token] + note_item.data.byte_offset; | ||
| 2930 | const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset; | ||
| 2931 | break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; | ||
| 2932 | }; | ||
| 2933 | const loc = std.zig.findLineColumn(file.source, span.main); | ||
| 2934 | const src_path = try file.fullPath(gpa); | ||
| 2935 | defer gpa.free(src_path); | ||
| 2936 | |||
| 2937 | eb.extra.items[note_i] = @enumToInt(try eb.addErrorMessage(.{ | ||
| 2938 | .msg = try eb.addString(msg), | ||
| 2939 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2940 | .src_path = try eb.addString(src_path), | ||
| 2941 | .span_start = span.start, | ||
| 2942 | .span_main = span.main, | ||
| 2943 | .span_end = span.end, | ||
| 2944 | .line = @intCast(u32, loc.line), | ||
| 2945 | .column = @intCast(u32, loc.column), | ||
| 2946 | .source_line = if (loc.eql(err_loc)) | ||
| 2947 | 0 | ||
| 2948 | else | ||
| 2949 | try eb.addString(loc.source_line), | ||
| 2950 | }), | ||
| 2951 | .notes_len = 0, // TODO rework this function to be recursive | ||
| 2952 | })); | ||
| 2953 | } | ||
| 2954 | } | ||
| 2955 | } | ||
| 3139 | } | 2956 | } |
| 3140 | 2957 | ||
| 3141 | pub fn performAllTheWork( | 2958 | pub fn performAllTheWork( |
| ... | @@ -3231,11 +3048,11 @@ pub fn performAllTheWork( | ... | @@ -3231,11 +3048,11 @@ pub fn performAllTheWork( |
| 3231 | // backend, preventing anonymous Decls from being prematurely destroyed. | 3048 | // backend, preventing anonymous Decls from being prematurely destroyed. |
| 3232 | while (true) { | 3049 | while (true) { |
| 3233 | if (comp.work_queue.readItem()) |work_item| { | 3050 | if (comp.work_queue.readItem()) |work_item| { |
| 3234 | try processOneJob(comp, work_item); | 3051 | try processOneJob(comp, work_item, main_progress_node); |
| 3235 | continue; | 3052 | continue; |
| 3236 | } | 3053 | } |
| 3237 | if (comp.anon_work_queue.readItem()) |work_item| { | 3054 | if (comp.anon_work_queue.readItem()) |work_item| { |
| 3238 | try processOneJob(comp, work_item); | 3055 | try processOneJob(comp, work_item, main_progress_node); |
| 3239 | continue; | 3056 | continue; |
| 3240 | } | 3057 | } |
| 3241 | break; | 3058 | break; |
| ... | @@ -3243,16 +3060,16 @@ pub fn performAllTheWork( | ... | @@ -3243,16 +3060,16 @@ pub fn performAllTheWork( |
| 3243 | 3060 | ||
| 3244 | if (comp.job_queued_compiler_rt_lib) { | 3061 | if (comp.job_queued_compiler_rt_lib) { |
| 3245 | comp.job_queued_compiler_rt_lib = false; | 3062 | comp.job_queued_compiler_rt_lib = false; |
| 3246 | buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); | 3063 | buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node); |
| 3247 | } | 3064 | } |
| 3248 | 3065 | ||
| 3249 | if (comp.job_queued_compiler_rt_obj) { | 3066 | if (comp.job_queued_compiler_rt_obj) { |
| 3250 | comp.job_queued_compiler_rt_obj = false; | 3067 | comp.job_queued_compiler_rt_obj = false; |
| 3251 | buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj); | 3068 | buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node); |
| 3252 | } | 3069 | } |
| 3253 | } | 3070 | } |
| 3254 | 3071 | ||
| 3255 | fn processOneJob(comp: *Compilation, job: Job) !void { | 3072 | fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void { |
| 3256 | switch (job) { | 3073 | switch (job) { |
| 3257 | .codegen_decl => |decl_index| { | 3074 | .codegen_decl => |decl_index| { |
| 3258 | const module = comp.bin_file.options.module.?; | 3075 | const module = comp.bin_file.options.module.?; |
| ... | @@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3404 | const named_frame = tracy.namedFrame("glibc_crt_file"); | 3221 | const named_frame = tracy.namedFrame("glibc_crt_file"); |
| 3405 | defer named_frame.end(); | 3222 | defer named_frame.end(); |
| 3406 | 3223 | ||
| 3407 | glibc.buildCRTFile(comp, crt_file) catch |err| { | 3224 | glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3408 | // TODO Surface more error details. | 3225 | // TODO Surface more error details. |
| 3409 | comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ | 3226 | comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ |
| 3410 | @errorName(err), | 3227 | @errorName(err), |
| ... | @@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3415 | const named_frame = tracy.namedFrame("glibc_shared_objects"); | 3232 | const named_frame = tracy.namedFrame("glibc_shared_objects"); |
| 3416 | defer named_frame.end(); | 3233 | defer named_frame.end(); |
| 3417 | 3234 | ||
| 3418 | glibc.buildSharedObjects(comp) catch |err| { | 3235 | glibc.buildSharedObjects(comp, prog_node) catch |err| { |
| 3419 | // TODO Surface more error details. | 3236 | // TODO Surface more error details. |
| 3420 | comp.lockAndSetMiscFailure( | 3237 | comp.lockAndSetMiscFailure( |
| 3421 | .glibc_shared_objects, | 3238 | .glibc_shared_objects, |
| ... | @@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3428 | const named_frame = tracy.namedFrame("musl_crt_file"); | 3245 | const named_frame = tracy.namedFrame("musl_crt_file"); |
| 3429 | defer named_frame.end(); | 3246 | defer named_frame.end(); |
| 3430 | 3247 | ||
| 3431 | musl.buildCRTFile(comp, crt_file) catch |err| { | 3248 | musl.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3432 | // TODO Surface more error details. | 3249 | // TODO Surface more error details. |
| 3433 | comp.lockAndSetMiscFailure( | 3250 | comp.lockAndSetMiscFailure( |
| 3434 | .musl_crt_file, | 3251 | .musl_crt_file, |
| ... | @@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3441 | const named_frame = tracy.namedFrame("mingw_crt_file"); | 3258 | const named_frame = tracy.namedFrame("mingw_crt_file"); |
| 3442 | defer named_frame.end(); | 3259 | defer named_frame.end(); |
| 3443 | 3260 | ||
| 3444 | mingw.buildCRTFile(comp, crt_file) catch |err| { | 3261 | mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3445 | // TODO Surface more error details. | 3262 | // TODO Surface more error details. |
| 3446 | comp.lockAndSetMiscFailure( | 3263 | comp.lockAndSetMiscFailure( |
| 3447 | .mingw_crt_file, | 3264 | .mingw_crt_file, |
| ... | @@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3468 | const named_frame = tracy.namedFrame("libunwind"); | 3285 | const named_frame = tracy.namedFrame("libunwind"); |
| 3469 | defer named_frame.end(); | 3286 | defer named_frame.end(); |
| 3470 | 3287 | ||
| 3471 | libunwind.buildStaticLib(comp) catch |err| { | 3288 | libunwind.buildStaticLib(comp, prog_node) catch |err| { |
| 3472 | // TODO Surface more error details. | 3289 | // TODO Surface more error details. |
| 3473 | comp.lockAndSetMiscFailure( | 3290 | comp.lockAndSetMiscFailure( |
| 3474 | .libunwind, | 3291 | .libunwind, |
| ... | @@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3481 | const named_frame = tracy.namedFrame("libcxx"); | 3298 | const named_frame = tracy.namedFrame("libcxx"); |
| 3482 | defer named_frame.end(); | 3299 | defer named_frame.end(); |
| 3483 | 3300 | ||
| 3484 | libcxx.buildLibCXX(comp) catch |err| { | 3301 | libcxx.buildLibCXX(comp, prog_node) catch |err| { |
| 3485 | // TODO Surface more error details. | 3302 | // TODO Surface more error details. |
| 3486 | comp.lockAndSetMiscFailure( | 3303 | comp.lockAndSetMiscFailure( |
| 3487 | .libcxx, | 3304 | .libcxx, |
| ... | @@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3494 | const named_frame = tracy.namedFrame("libcxxabi"); | 3311 | const named_frame = tracy.namedFrame("libcxxabi"); |
| 3495 | defer named_frame.end(); | 3312 | defer named_frame.end(); |
| 3496 | 3313 | ||
| 3497 | libcxx.buildLibCXXABI(comp) catch |err| { | 3314 | libcxx.buildLibCXXABI(comp, prog_node) catch |err| { |
| 3498 | // TODO Surface more error details. | 3315 | // TODO Surface more error details. |
| 3499 | comp.lockAndSetMiscFailure( | 3316 | comp.lockAndSetMiscFailure( |
| 3500 | .libcxxabi, | 3317 | .libcxxabi, |
| ... | @@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3507 | const named_frame = tracy.namedFrame("libtsan"); | 3324 | const named_frame = tracy.namedFrame("libtsan"); |
| 3508 | defer named_frame.end(); | 3325 | defer named_frame.end(); |
| 3509 | 3326 | ||
| 3510 | libtsan.buildTsan(comp) catch |err| { | 3327 | libtsan.buildTsan(comp, prog_node) catch |err| { |
| 3511 | // TODO Surface more error details. | 3328 | // TODO Surface more error details. |
| 3512 | comp.lockAndSetMiscFailure( | 3329 | comp.lockAndSetMiscFailure( |
| 3513 | .libtsan, | 3330 | .libtsan, |
| ... | @@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3520 | const named_frame = tracy.namedFrame("wasi_libc_crt_file"); | 3337 | const named_frame = tracy.namedFrame("wasi_libc_crt_file"); |
| 3521 | defer named_frame.end(); | 3338 | defer named_frame.end(); |
| 3522 | 3339 | ||
| 3523 | wasi_libc.buildCRTFile(comp, crt_file) catch |err| { | 3340 | wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3524 | // TODO Surface more error details. | 3341 | // TODO Surface more error details. |
| 3525 | comp.lockAndSetMiscFailure( | 3342 | comp.lockAndSetMiscFailure( |
| 3526 | .wasi_libc_crt_file, | 3343 | .wasi_libc_crt_file, |
| ... | @@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3538 | .Lib, | 3355 | .Lib, |
| 3539 | &comp.libssp_static_lib, | 3356 | &comp.libssp_static_lib, |
| 3540 | .libssp, | 3357 | .libssp, |
| 3358 | prog_node, | ||
| 3541 | ) catch |err| switch (err) { | 3359 | ) catch |err| switch (err) { |
| 3542 | error.OutOfMemory => return error.OutOfMemory, | 3360 | error.OutOfMemory => return error.OutOfMemory, |
| 3543 | error.SubCompilationFailed => return, // error reported already | 3361 | error.SubCompilationFailed => return, // error reported already |
| ... | @@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3557 | .Lib, | 3375 | .Lib, |
| 3558 | &comp.libc_static_lib, | 3376 | &comp.libc_static_lib, |
| 3559 | .zig_libc, | 3377 | .zig_libc, |
| 3378 | prog_node, | ||
| 3560 | ) catch |err| switch (err) { | 3379 | ) catch |err| switch (err) { |
| 3561 | error.OutOfMemory => return error.OutOfMemory, | 3380 | error.OutOfMemory => return error.OutOfMemory, |
| 3562 | error.SubCompilationFailed => return, // error reported already | 3381 | error.SubCompilationFailed => return, // error reported already |
| ... | @@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot( | ... | @@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot( |
| 3897 | comp: *Compilation, | 3716 | comp: *Compilation, |
| 3898 | output_mode: std.builtin.OutputMode, | 3717 | output_mode: std.builtin.OutputMode, |
| 3899 | out: *?CRTFile, | 3718 | out: *?CRTFile, |
| 3719 | prog_node: *std.Progress.Node, | ||
| 3900 | ) void { | 3720 | ) void { |
| 3901 | comp.buildOutputFromZig("compiler_rt.zig", output_mode, out, .compiler_rt) catch |err| switch (err) { | 3721 | comp.buildOutputFromZig( |
| 3722 | "compiler_rt.zig", | ||
| 3723 | output_mode, | ||
| 3724 | out, | ||
| 3725 | .compiler_rt, | ||
| 3726 | prog_node, | ||
| 3727 | ) catch |err| switch (err) { | ||
| 3902 | error.SubCompilationFailed => return, // error reported already | 3728 | error.SubCompilationFailed => return, // error reported already |
| 3903 | else => comp.lockAndSetMiscFailure( | 3729 | else => comp.lockAndSetMiscFailure( |
| 3904 | .compiler_rt, | 3730 | .compiler_rt, |
| ... | @@ -5237,7 +5063,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca | ... | @@ -5237,7 +5063,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca |
| 5237 | \\const std = @import("std"); | 5063 | \\const std = @import("std"); |
| 5238 | \\/// Zig version. When writing code that supports multiple versions of Zig, prefer | 5064 | \\/// Zig version. When writing code that supports multiple versions of Zig, prefer |
| 5239 | \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. | 5065 | \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. |
| 5240 | \\pub const zig_version = std.SemanticVersion.parse("{s}") catch unreachable; | 5066 | \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; |
| 5067 | \\pub const zig_version_string = "{s}"; | ||
| 5241 | \\pub const zig_backend = std.builtin.CompilerBackend.{}; | 5068 | \\pub const zig_backend = std.builtin.CompilerBackend.{}; |
| 5242 | \\ | 5069 | \\ |
| 5243 | \\pub const output_mode = std.builtin.OutputMode.{}; | 5070 | \\pub const output_mode = std.builtin.OutputMode.{}; |
| ... | @@ -5424,34 +5251,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca | ... | @@ -5424,34 +5251,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca |
| 5424 | return buffer.toOwnedSliceSentinel(0); | 5251 | return buffer.toOwnedSliceSentinel(0); |
| 5425 | } | 5252 | } |
| 5426 | 5253 | ||
| 5427 | pub fn updateSubCompilation(sub_compilation: *Compilation) !void { | 5254 | pub fn updateSubCompilation( |
| 5428 | try sub_compilation.update(); | 5255 | parent_comp: *Compilation, |
| 5429 | 5256 | sub_comp: *Compilation, | |
| 5430 | // Look for compilation errors in this sub_compilation | 5257 | misc_task: MiscTask, |
| 5431 | // TODO instead of logging these errors, handle them in the callsites | 5258 | prog_node: *std.Progress.Node, |
| 5432 | // of updateSubCompilation and attach them as sub-errors, properly | 5259 | ) !void { |
| 5433 | // surfacing the errors. You can see an example of this already | 5260 | { |
| 5434 | // done inside buildOutputFromZig. | 5261 | var sub_node = prog_node.start(@tagName(misc_task), 0); |
| 5435 | var errors = try sub_compilation.getAllErrorsAlloc(); | 5262 | sub_node.activate(); |
| 5436 | defer errors.deinit(sub_compilation.gpa); | 5263 | defer sub_node.end(); |
| 5437 | 5264 | ||
| 5438 | if (errors.list.len != 0) { | 5265 | try sub_comp.update(prog_node); |
| 5439 | for (errors.list) |full_err_msg| { | 5266 | } |
| 5440 | switch (full_err_msg) { | 5267 | |
| 5441 | .src => |src| { | 5268 | // Look for compilation errors in this sub compilation |
| 5442 | log.err("{s}:{d}:{d}: {s}", .{ | 5269 | const gpa = parent_comp.gpa; |
| 5443 | src.src_path, | 5270 | var keep_errors = false; |
| 5444 | src.line + 1, | 5271 | var errors = try sub_comp.getAllErrorsAlloc(); |
| 5445 | src.column + 1, | 5272 | defer if (!keep_errors) errors.deinit(gpa); |
| 5446 | src.msg, | 5273 | |
| 5447 | }); | 5274 | if (errors.errorMessageCount() > 0) { |
| 5448 | }, | 5275 | try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1); |
| 5449 | .plain => |plain| { | 5276 | parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{ |
| 5450 | log.err("{s}", .{plain.msg}); | 5277 | .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ |
| 5451 | }, | 5278 | @tagName(misc_task), |
| 5452 | } | 5279 | }), |
| 5453 | } | 5280 | .children = errors, |
| 5454 | return error.BuildingLibCObjectFailed; | 5281 | }); |
| 5282 | keep_errors = true; | ||
| 5283 | return error.SubCompilationFailed; | ||
| 5455 | } | 5284 | } |
| 5456 | } | 5285 | } |
| 5457 | 5286 | ||
| ... | @@ -5461,6 +5290,7 @@ fn buildOutputFromZig( | ... | @@ -5461,6 +5290,7 @@ fn buildOutputFromZig( |
| 5461 | output_mode: std.builtin.OutputMode, | 5290 | output_mode: std.builtin.OutputMode, |
| 5462 | out: *?CRTFile, | 5291 | out: *?CRTFile, |
| 5463 | misc_task_tag: MiscTask, | 5292 | misc_task_tag: MiscTask, |
| 5293 | prog_node: *std.Progress.Node, | ||
| 5464 | ) !void { | 5294 | ) !void { |
| 5465 | const tracy_trace = trace(@src()); | 5295 | const tracy_trace = trace(@src()); |
| 5466 | defer tracy_trace.end(); | 5296 | defer tracy_trace.end(); |
| ... | @@ -5527,23 +5357,7 @@ fn buildOutputFromZig( | ... | @@ -5527,23 +5357,7 @@ fn buildOutputFromZig( |
| 5527 | }); | 5357 | }); |
| 5528 | defer sub_compilation.destroy(); | 5358 | defer sub_compilation.destroy(); |
| 5529 | 5359 | ||
| 5530 | try sub_compilation.update(); | 5360 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 5531 | // Look for compilation errors in this sub_compilation. | ||
| 5532 | var keep_errors = false; | ||
| 5533 | var errors = try sub_compilation.getAllErrorsAlloc(); | ||
| 5534 | defer if (!keep_errors) errors.deinit(sub_compilation.gpa); | ||
| 5535 | |||
| 5536 | if (errors.list.len != 0) { | ||
| 5537 | try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1); | ||
| 5538 | comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ | ||
| 5539 | .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{ | ||
| 5540 | @tagName(misc_task_tag), | ||
| 5541 | }), | ||
| 5542 | .children = errors, | ||
| 5543 | }); | ||
| 5544 | keep_errors = true; | ||
| 5545 | return error.SubCompilationFailed; | ||
| 5546 | } | ||
| 5547 | 5361 | ||
| 5548 | assert(out.* == null); | 5362 | assert(out.* == null); |
| 5549 | out.* = Compilation.CRTFile{ | 5363 | out.* = Compilation.CRTFile{ |
| ... | @@ -5558,6 +5372,8 @@ pub fn build_crt_file( | ... | @@ -5558,6 +5372,8 @@ pub fn build_crt_file( |
| 5558 | comp: *Compilation, | 5372 | comp: *Compilation, |
| 5559 | root_name: []const u8, | 5373 | root_name: []const u8, |
| 5560 | output_mode: std.builtin.OutputMode, | 5374 | output_mode: std.builtin.OutputMode, |
| 5375 | misc_task_tag: MiscTask, | ||
| 5376 | prog_node: *std.Progress.Node, | ||
| 5561 | c_source_files: []const Compilation.CSourceFile, | 5377 | c_source_files: []const Compilation.CSourceFile, |
| 5562 | ) !void { | 5378 | ) !void { |
| 5563 | const tracy_trace = trace(@src()); | 5379 | const tracy_trace = trace(@src()); |
| ... | @@ -5618,7 +5434,7 @@ pub fn build_crt_file( | ... | @@ -5618,7 +5434,7 @@ pub fn build_crt_file( |
| 5618 | }); | 5434 | }); |
| 5619 | defer sub_compilation.destroy(); | 5435 | defer sub_compilation.destroy(); |
| 5620 | 5436 | ||
| 5621 | try sub_compilation.updateSubCompilation(); | 5437 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 5622 | 5438 | ||
| 5623 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | 5439 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); |
| 5624 | 5440 |
src/Liveness.zig+2| ... | @@ -226,6 +226,7 @@ pub fn categorizeOperand( | ... | @@ -226,6 +226,7 @@ pub fn categorizeOperand( |
| 226 | .ret_ptr, | 226 | .ret_ptr, |
| 227 | .constant, | 227 | .constant, |
| 228 | .const_ty, | 228 | .const_ty, |
| 229 | .trap, | ||
| 229 | .breakpoint, | 230 | .breakpoint, |
| 230 | .dbg_stmt, | 231 | .dbg_stmt, |
| 231 | .dbg_inline_begin, | 232 | .dbg_inline_begin, |
| ... | @@ -848,6 +849,7 @@ fn analyzeInst( | ... | @@ -848,6 +849,7 @@ fn analyzeInst( |
| 848 | .ret_ptr, | 849 | .ret_ptr, |
| 849 | .constant, | 850 | .constant, |
| 850 | .const_ty, | 851 | .const_ty, |
| 852 | .trap, | ||
| 851 | .breakpoint, | 853 | .breakpoint, |
| 852 | .dbg_stmt, | 854 | .dbg_stmt, |
| 853 | .dbg_inline_begin, | 855 | .dbg_inline_begin, |
src/Module.zig+72-125| ... | @@ -3528,6 +3528,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3528,6 +3528,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3528 | const digest = hash: { | 3528 | const digest = hash: { |
| 3529 | var path_hash: Cache.HashHelper = .{}; | 3529 | var path_hash: Cache.HashHelper = .{}; |
| 3530 | path_hash.addBytes(build_options.version); | 3530 | path_hash.addBytes(build_options.version); |
| 3531 | path_hash.add(builtin.zig_backend); | ||
| 3531 | if (!want_local_cache) { | 3532 | if (!want_local_cache) { |
| 3532 | path_hash.addOptionalBytes(file.pkg.root_src_directory.path); | 3533 | path_hash.addOptionalBytes(file.pkg.root_src_directory.path); |
| 3533 | } | 3534 | } |
| ... | @@ -3537,44 +3538,66 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3537,44 +3538,66 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3537 | const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache; | 3538 | const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache; |
| 3538 | const zir_dir = cache_directory.handle; | 3539 | const zir_dir = cache_directory.handle; |
| 3539 | 3540 | ||
| 3540 | var cache_file: ?std.fs.File = null; | ||
| 3541 | defer if (cache_file) |f| f.close(); | ||
| 3542 | |||
| 3543 | // Determine whether we need to reload the file from disk and redo parsing and AstGen. | 3541 | // Determine whether we need to reload the file from disk and redo parsing and AstGen. |
| 3544 | switch (file.status) { | 3542 | var lock: std.fs.File.Lock = switch (file.status) { |
| 3545 | .never_loaded, .retryable_failure => cached: { | 3543 | .never_loaded, .retryable_failure => lock: { |
| 3546 | // First, load the cached ZIR code, if any. | 3544 | // First, load the cached ZIR code, if any. |
| 3547 | log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{ | 3545 | log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{ |
| 3548 | file.sub_file_path, want_local_cache, &digest, | 3546 | file.sub_file_path, want_local_cache, &digest, |
| 3549 | }); | 3547 | }); |
| 3550 | 3548 | ||
| 3551 | // We ask for a lock in order to coordinate with other zig processes. | 3549 | break :lock .Shared; |
| 3552 | // If another process is already working on this file, we will get the cached | 3550 | }, |
| 3553 | // version. Likewise if we're working on AstGen and another process asks for | 3551 | .parse_failure, .astgen_failure, .success_zir => lock: { |
| 3554 | // the cached file, they'll get it. | 3552 | const unchanged_metadata = |
| 3555 | cache_file = zir_dir.openFile(&digest, .{ .lock = .Shared }) catch |err| switch (err) { | 3553 | stat.size == file.stat.size and |
| 3556 | error.PathAlreadyExists => unreachable, // opening for reading | 3554 | stat.mtime == file.stat.mtime and |
| 3557 | error.NoSpaceLeft => unreachable, // opening for reading | 3555 | stat.inode == file.stat.inode; |
| 3558 | error.NotDir => unreachable, // no dir components | 3556 | |
| 3559 | error.InvalidUtf8 => unreachable, // it's a hex encoded name | 3557 | if (unchanged_metadata) { |
| 3560 | error.BadPathName => unreachable, // it's a hex encoded name | 3558 | log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); |
| 3561 | error.NameTooLong => unreachable, // it's a fixed size name | 3559 | return; |
| 3562 | error.PipeBusy => unreachable, // it's not a pipe | 3560 | } |
| 3563 | error.WouldBlock => unreachable, // not asking for non-blocking I/O | 3561 | |
| 3564 | 3562 | log.debug("metadata changed: {s}", .{file.sub_file_path}); | |
| 3565 | error.SymLinkLoop, | 3563 | |
| 3566 | error.FileNotFound, | 3564 | break :lock .Exclusive; |
| 3567 | error.Unexpected, | 3565 | }, |
| 3568 | => break :cached, | 3566 | }; |
| 3569 | |||
| 3570 | else => |e| return e, // Retryable errors are handled at callsite. | ||
| 3571 | }; | ||
| 3572 | 3567 | ||
| 3568 | // We ask for a lock in order to coordinate with other zig processes. | ||
| 3569 | // If another process is already working on this file, we will get the cached | ||
| 3570 | // version. Likewise if we're working on AstGen and another process asks for | ||
| 3571 | // the cached file, they'll get it. | ||
| 3572 | const cache_file = while (true) { | ||
| 3573 | break zir_dir.createFile(&digest, .{ | ||
| 3574 | .read = true, | ||
| 3575 | .truncate = false, | ||
| 3576 | .lock = lock, | ||
| 3577 | }) catch |err| switch (err) { | ||
| 3578 | error.NotDir => unreachable, // no dir components | ||
| 3579 | error.InvalidUtf8 => unreachable, // it's a hex encoded name | ||
| 3580 | error.BadPathName => unreachable, // it's a hex encoded name | ||
| 3581 | error.NameTooLong => unreachable, // it's a fixed size name | ||
| 3582 | error.PipeBusy => unreachable, // it's not a pipe | ||
| 3583 | error.WouldBlock => unreachable, // not asking for non-blocking I/O | ||
| 3584 | // There are no dir components, so you would think that this was | ||
| 3585 | // unreachable, however we have observed on macOS two processes racing | ||
| 3586 | // to do openat() with O_CREAT manifest in ENOENT. | ||
| 3587 | error.FileNotFound => continue, | ||
| 3588 | |||
| 3589 | else => |e| return e, // Retryable errors are handled at callsite. | ||
| 3590 | }; | ||
| 3591 | }; | ||
| 3592 | defer cache_file.close(); | ||
| 3593 | |||
| 3594 | while (true) { | ||
| 3595 | update: { | ||
| 3573 | // First we read the header to determine the lengths of arrays. | 3596 | // First we read the header to determine the lengths of arrays. |
| 3574 | const header = cache_file.?.reader().readStruct(Zir.Header) catch |err| switch (err) { | 3597 | const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) { |
| 3575 | // This can happen if Zig bails out of this function between creating | 3598 | // This can happen if Zig bails out of this function between creating |
| 3576 | // the cached file and writing it. | 3599 | // the cached file and writing it. |
| 3577 | error.EndOfStream => break :cached, | 3600 | error.EndOfStream => break :update, |
| 3578 | else => |e| return e, | 3601 | else => |e| return e, |
| 3579 | }; | 3602 | }; |
| 3580 | const unchanged_metadata = | 3603 | const unchanged_metadata = |
| ... | @@ -3584,7 +3607,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3584,7 +3607,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3584 | 3607 | ||
| 3585 | if (!unchanged_metadata) { | 3608 | if (!unchanged_metadata) { |
| 3586 | log.debug("AstGen cache stale: {s}", .{file.sub_file_path}); | 3609 | log.debug("AstGen cache stale: {s}", .{file.sub_file_path}); |
| 3587 | break :cached; | 3610 | break :update; |
| 3588 | } | 3611 | } |
| 3589 | log.debug("AstGen cache hit: {s} instructions_len={d}", .{ | 3612 | log.debug("AstGen cache hit: {s} instructions_len={d}", .{ |
| 3590 | file.sub_file_path, header.instructions_len, | 3613 | file.sub_file_path, header.instructions_len, |
| ... | @@ -3636,13 +3659,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3636,13 +3659,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3636 | .iov_len = header.extra_len * 4, | 3659 | .iov_len = header.extra_len * 4, |
| 3637 | }, | 3660 | }, |
| 3638 | }; | 3661 | }; |
| 3639 | const amt_read = try cache_file.?.readvAll(&iovecs); | 3662 | const amt_read = try cache_file.readvAll(&iovecs); |
| 3640 | const amt_expected = zir.instructions.len * 9 + | 3663 | const amt_expected = zir.instructions.len * 9 + |
| 3641 | zir.string_bytes.len + | 3664 | zir.string_bytes.len + |
| 3642 | zir.extra.len * 4; | 3665 | zir.extra.len * 4; |
| 3643 | if (amt_read != amt_expected) { | 3666 | if (amt_read != amt_expected) { |
| 3644 | log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}); | 3667 | log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}); |
| 3645 | break :cached; | 3668 | break :update; |
| 3646 | } | 3669 | } |
| 3647 | if (data_has_safety_tag) { | 3670 | if (data_has_safety_tag) { |
| 3648 | const tags = zir.instructions.items(.tag); | 3671 | const tags = zir.instructions.items(.tag); |
| ... | @@ -3678,42 +3701,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3678,42 +3701,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3678 | return error.AnalysisFail; | 3701 | return error.AnalysisFail; |
| 3679 | } | 3702 | } |
| 3680 | return; | 3703 | return; |
| 3681 | }, | 3704 | } |
| 3682 | .parse_failure, .astgen_failure, .success_zir => { | ||
| 3683 | const unchanged_metadata = | ||
| 3684 | stat.size == file.stat.size and | ||
| 3685 | stat.mtime == file.stat.mtime and | ||
| 3686 | stat.inode == file.stat.inode; | ||
| 3687 | |||
| 3688 | if (unchanged_metadata) { | ||
| 3689 | log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); | ||
| 3690 | return; | ||
| 3691 | } | ||
| 3692 | 3705 | ||
| 3693 | log.debug("metadata changed: {s}", .{file.sub_file_path}); | 3706 | // If we already have the exclusive lock then it is our job to update. |
| 3694 | }, | 3707 | if (builtin.os.tag == .wasi or lock == .Exclusive) break; |
| 3708 | // Otherwise, unlock to give someone a chance to get the exclusive lock | ||
| 3709 | // and then upgrade to an exclusive lock. | ||
| 3710 | cache_file.unlock(); | ||
| 3711 | lock = .Exclusive; | ||
| 3712 | try cache_file.lock(lock); | ||
| 3695 | } | 3713 | } |
| 3696 | if (cache_file) |f| { | ||
| 3697 | f.close(); | ||
| 3698 | cache_file = null; | ||
| 3699 | } | ||
| 3700 | cache_file = zir_dir.createFile(&digest, .{ .lock = .Exclusive }) catch |err| switch (err) { | ||
| 3701 | error.NotDir => unreachable, // no dir components | ||
| 3702 | error.InvalidUtf8 => unreachable, // it's a hex encoded name | ||
| 3703 | error.BadPathName => unreachable, // it's a hex encoded name | ||
| 3704 | error.NameTooLong => unreachable, // it's a fixed size name | ||
| 3705 | error.PipeBusy => unreachable, // it's not a pipe | ||
| 3706 | error.WouldBlock => unreachable, // not asking for non-blocking I/O | ||
| 3707 | error.FileNotFound => unreachable, // no dir components | ||
| 3708 | 3714 | ||
| 3709 | else => |e| { | 3715 | // The cache is definitely stale so delete the contents to avoid an underwrite later. |
| 3710 | const pkg_path = file.pkg.root_src_directory.path orelse "."; | 3716 | cache_file.setEndPos(0) catch |err| switch (err) { |
| 3711 | const cache_path = cache_directory.path orelse "."; | 3717 | error.FileTooBig => unreachable, // 0 is not too big |
| 3712 | log.warn("unable to save cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ | 3718 | |
| 3713 | pkg_path, file.sub_file_path, cache_path, &digest, @errorName(e), | 3719 | else => |e| return e, |
| 3714 | }); | ||
| 3715 | return; | ||
| 3716 | }, | ||
| 3717 | }; | 3720 | }; |
| 3718 | 3721 | ||
| 3719 | mod.lockAndClearFileCompileError(file); | 3722 | mod.lockAndClearFileCompileError(file); |
| ... | @@ -3753,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3753,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3753 | file.source_loaded = true; | 3756 | file.source_loaded = true; |
| 3754 | 3757 | ||
| 3755 | file.tree = try Ast.parse(gpa, source, .zig); | 3758 | file.tree = try Ast.parse(gpa, source, .zig); |
| 3756 | defer if (!file.tree_loaded) file.tree.deinit(gpa); | ||
| 3757 | |||
| 3758 | if (file.tree.errors.len != 0) { | ||
| 3759 | const parse_err = file.tree.errors[0]; | ||
| 3760 | |||
| 3761 | var msg = std.ArrayList(u8).init(gpa); | ||
| 3762 | defer msg.deinit(); | ||
| 3763 | |||
| 3764 | const token_starts = file.tree.tokens.items(.start); | ||
| 3765 | const token_tags = file.tree.tokens.items(.tag); | ||
| 3766 | |||
| 3767 | const extra_offset = file.tree.errorOffset(parse_err); | ||
| 3768 | try file.tree.renderError(parse_err, msg.writer()); | ||
| 3769 | const err_msg = try gpa.create(ErrorMsg); | ||
| 3770 | err_msg.* = .{ | ||
| 3771 | .src_loc = .{ | ||
| 3772 | .file_scope = file, | ||
| 3773 | .parent_decl_node = 0, | ||
| 3774 | .lazy = if (extra_offset == 0) .{ | ||
| 3775 | .token_abs = parse_err.token, | ||
| 3776 | } else .{ | ||
| 3777 | .byte_abs = token_starts[parse_err.token] + extra_offset, | ||
| 3778 | }, | ||
| 3779 | }, | ||
| 3780 | .msg = try msg.toOwnedSlice(), | ||
| 3781 | }; | ||
| 3782 | if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { | ||
| 3783 | const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); | ||
| 3784 | const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; | ||
| 3785 | try mod.errNoteNonLazy(.{ | ||
| 3786 | .file_scope = file, | ||
| 3787 | .parent_decl_node = 0, | ||
| 3788 | .lazy = .{ .byte_abs = byte_abs }, | ||
| 3789 | }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); | ||
| 3790 | } | ||
| 3791 | |||
| 3792 | for (file.tree.errors[1..]) |note| { | ||
| 3793 | if (!note.is_note) break; | ||
| 3794 | |||
| 3795 | try file.tree.renderError(note, msg.writer()); | ||
| 3796 | err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1); | ||
| 3797 | err_msg.notes[err_msg.notes.len - 1] = .{ | ||
| 3798 | .src_loc = .{ | ||
| 3799 | .file_scope = file, | ||
| 3800 | .parent_decl_node = 0, | ||
| 3801 | .lazy = .{ .token_abs = note.token }, | ||
| 3802 | }, | ||
| 3803 | .msg = try msg.toOwnedSlice(), | ||
| 3804 | }; | ||
| 3805 | } | ||
| 3806 | |||
| 3807 | { | ||
| 3808 | comp.mutex.lock(); | ||
| 3809 | defer comp.mutex.unlock(); | ||
| 3810 | try mod.failed_files.putNoClobber(gpa, file, err_msg); | ||
| 3811 | } | ||
| 3812 | file.status = .parse_failure; | ||
| 3813 | return error.AnalysisFail; | ||
| 3814 | } | ||
| 3815 | file.tree_loaded = true; | 3759 | file.tree_loaded = true; |
| 3816 | 3760 | ||
| 3761 | // Any potential AST errors are converted to ZIR errors here. | ||
| 3817 | file.zir = try AstGen.generate(gpa, file.tree); | 3762 | file.zir = try AstGen.generate(gpa, file.tree); |
| 3818 | file.zir_loaded = true; | 3763 | file.zir_loaded = true; |
| 3819 | file.status = .success_zir; | 3764 | file.status = .success_zir; |
| ... | @@ -3870,7 +3815,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3870,7 +3815,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3870 | .iov_len = file.zir.extra.len * 4, | 3815 | .iov_len = file.zir.extra.len * 4, |
| 3871 | }, | 3816 | }, |
| 3872 | }; | 3817 | }; |
| 3873 | cache_file.?.writevAll(&iovecs) catch |err| { | 3818 | cache_file.writevAll(&iovecs) catch |err| { |
| 3874 | const pkg_path = file.pkg.root_src_directory.path orelse "."; | 3819 | const pkg_path = file.pkg.root_src_directory.path orelse "."; |
| 3875 | const cache_path = cache_directory.path orelse "."; | 3820 | const cache_path = cache_directory.path orelse "."; |
| 3876 | log.warn("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ | 3821 | log.warn("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ |
| ... | @@ -3922,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -3922,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { |
| 3922 | const gpa = mod.gpa; | 3867 | const gpa = mod.gpa; |
| 3923 | const new_zir = file.zir; | 3868 | const new_zir = file.zir; |
| 3924 | 3869 | ||
| 3870 | // The root decl will be null if the previous ZIR had AST errors. | ||
| 3871 | const root_decl = file.root_decl.unwrap() orelse return; | ||
| 3872 | |||
| 3925 | // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which | 3873 | // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which |
| 3926 | // creates a namespace, gets mapped from old to new here. | 3874 | // creates a namespace, gets mapped from old to new here. |
| 3927 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; | 3875 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; |
| ... | @@ -3939,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -3939,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { |
| 3939 | var decl_stack: ArrayListUnmanaged(Decl.Index) = .{}; | 3887 | var decl_stack: ArrayListUnmanaged(Decl.Index) = .{}; |
| 3940 | defer decl_stack.deinit(gpa); | 3888 | defer decl_stack.deinit(gpa); |
| 3941 | 3889 | ||
| 3942 | const root_decl = file.root_decl.unwrap().?; | ||
| 3943 | try decl_stack.append(gpa, root_decl); | 3890 | try decl_stack.append(gpa, root_decl); |
| 3944 | 3891 | ||
| 3945 | file.deleted_decls.clearRetainingCapacity(); | 3892 | file.deleted_decls.clearRetainingCapacity(); |
src/Package.zig+64-65| ... | @@ -8,11 +8,11 @@ const Allocator = mem.Allocator; | ... | @@ -8,11 +8,11 @@ const Allocator = mem.Allocator; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const log = std.log.scoped(.package); | 9 | const log = std.log.scoped(.package); |
| 10 | const main = @import("main.zig"); | 10 | const main = @import("main.zig"); |
| 11 | const ThreadPool = std.Thread.Pool; | ||
| 12 | const WaitGroup = std.Thread.WaitGroup; | ||
| 11 | 13 | ||
| 12 | const Compilation = @import("Compilation.zig"); | 14 | const Compilation = @import("Compilation.zig"); |
| 13 | const Module = @import("Module.zig"); | 15 | const Module = @import("Module.zig"); |
| 14 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 15 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 16 | const Cache = std.Build.Cache; | 16 | const Cache = std.Build.Cache; |
| 17 | const build_options = @import("build_options"); | 17 | const build_options = @import("build_options"); |
| 18 | const Manifest = @import("Manifest.zig"); | 18 | const Manifest = @import("Manifest.zig"); |
| ... | @@ -215,6 +215,7 @@ pub const build_zig_basename = "build.zig"; | ... | @@ -215,6 +215,7 @@ pub const build_zig_basename = "build.zig"; |
| 215 | 215 | ||
| 216 | pub fn fetchAndAddDependencies( | 216 | pub fn fetchAndAddDependencies( |
| 217 | pkg: *Package, | 217 | pkg: *Package, |
| 218 | root_pkg: *Package, | ||
| 218 | arena: Allocator, | 219 | arena: Allocator, |
| 219 | thread_pool: *ThreadPool, | 220 | thread_pool: *ThreadPool, |
| 220 | http_client: *std.http.Client, | 221 | http_client: *std.http.Client, |
| ... | @@ -224,7 +225,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -224,7 +225,7 @@ pub fn fetchAndAddDependencies( |
| 224 | dependencies_source: *std.ArrayList(u8), | 225 | dependencies_source: *std.ArrayList(u8), |
| 225 | build_roots_source: *std.ArrayList(u8), | 226 | build_roots_source: *std.ArrayList(u8), |
| 226 | name_prefix: []const u8, | 227 | name_prefix: []const u8, |
| 227 | color: main.Color, | 228 | error_bundle: *std.zig.ErrorBundle.Wip, |
| 228 | all_modules: *AllModules, | 229 | all_modules: *AllModules, |
| 229 | ) !void { | 230 | ) !void { |
| 230 | const max_bytes = 10 * 1024 * 1024; | 231 | const max_bytes = 10 * 1024 * 1024; |
| ... | @@ -249,7 +250,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -249,7 +250,7 @@ pub fn fetchAndAddDependencies( |
| 249 | 250 | ||
| 250 | if (ast.errors.len > 0) { | 251 | if (ast.errors.len > 0) { |
| 251 | const file_path = try directory.join(arena, &.{Manifest.basename}); | 252 | const file_path = try directory.join(arena, &.{Manifest.basename}); |
| 252 | try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color); | 253 | try main.putAstErrorsIntoBundle(gpa, ast, file_path, error_bundle); |
| 253 | return error.PackageFetchFailed; | 254 | return error.PackageFetchFailed; |
| 254 | } | 255 | } |
| 255 | 256 | ||
| ... | @@ -257,14 +258,9 @@ pub fn fetchAndAddDependencies( | ... | @@ -257,14 +258,9 @@ pub fn fetchAndAddDependencies( |
| 257 | defer manifest.deinit(gpa); | 258 | defer manifest.deinit(gpa); |
| 258 | 259 | ||
| 259 | if (manifest.errors.len > 0) { | 260 | if (manifest.errors.len > 0) { |
| 260 | const ttyconf: std.debug.TTY.Config = switch (color) { | ||
| 261 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 262 | .on => .escape_codes, | ||
| 263 | .off => .no_color, | ||
| 264 | }; | ||
| 265 | const file_path = try directory.join(arena, &.{Manifest.basename}); | 261 | const file_path = try directory.join(arena, &.{Manifest.basename}); |
| 266 | for (manifest.errors) |msg| { | 262 | for (manifest.errors) |msg| { |
| 267 | Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{}); | 263 | try Report.addErrorMessage(ast, file_path, error_bundle, 0, msg); |
| 268 | } | 264 | } |
| 269 | return error.PackageFetchFailed; | 265 | return error.PackageFetchFailed; |
| 270 | } | 266 | } |
| ... | @@ -272,8 +268,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -272,8 +268,7 @@ pub fn fetchAndAddDependencies( |
| 272 | const report: Report = .{ | 268 | const report: Report = .{ |
| 273 | .ast = &ast, | 269 | .ast = &ast, |
| 274 | .directory = directory, | 270 | .directory = directory, |
| 275 | .color = color, | 271 | .error_bundle = error_bundle, |
| 276 | .arena = arena, | ||
| 277 | }; | 272 | }; |
| 278 | 273 | ||
| 279 | var any_error = false; | 274 | var any_error = false; |
| ... | @@ -295,7 +290,8 @@ pub fn fetchAndAddDependencies( | ... | @@ -295,7 +290,8 @@ pub fn fetchAndAddDependencies( |
| 295 | all_modules, | 290 | all_modules, |
| 296 | ); | 291 | ); |
| 297 | 292 | ||
| 298 | try pkg.fetchAndAddDependencies( | 293 | try sub_pkg.fetchAndAddDependencies( |
| 294 | root_pkg, | ||
| 299 | arena, | 295 | arena, |
| 300 | thread_pool, | 296 | thread_pool, |
| 301 | http_client, | 297 | http_client, |
| ... | @@ -305,11 +301,12 @@ pub fn fetchAndAddDependencies( | ... | @@ -305,11 +301,12 @@ pub fn fetchAndAddDependencies( |
| 305 | dependencies_source, | 301 | dependencies_source, |
| 306 | build_roots_source, | 302 | build_roots_source, |
| 307 | sub_prefix, | 303 | sub_prefix, |
| 308 | color, | 304 | error_bundle, |
| 309 | all_modules, | 305 | all_modules, |
| 310 | ); | 306 | ); |
| 311 | 307 | ||
| 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); | ||
| 313 | 310 | ||
| 314 | try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ | 311 | try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ |
| 315 | std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), | 312 | std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), |
| ... | @@ -347,8 +344,7 @@ pub fn createFilePkg( | ... | @@ -347,8 +344,7 @@ pub fn createFilePkg( |
| 347 | const Report = struct { | 344 | const Report = struct { |
| 348 | ast: *const std.zig.Ast, | 345 | ast: *const std.zig.Ast, |
| 349 | directory: Compilation.Directory, | 346 | directory: Compilation.Directory, |
| 350 | color: main.Color, | 347 | error_bundle: *std.zig.ErrorBundle.Wip, |
| 351 | arena: Allocator, | ||
| 352 | 348 | ||
| 353 | fn fail( | 349 | fn fail( |
| 354 | report: Report, | 350 | report: Report, |
| ... | @@ -356,52 +352,46 @@ const Report = struct { | ... | @@ -356,52 +352,46 @@ const Report = struct { |
| 356 | comptime fmt_string: []const u8, | 352 | comptime fmt_string: []const u8, |
| 357 | fmt_args: anytype, | 353 | fmt_args: anytype, |
| 358 | ) error{ PackageFetchFailed, OutOfMemory } { | 354 | ) error{ PackageFetchFailed, OutOfMemory } { |
| 359 | return failWithNotes(report, &.{}, tok, fmt_string, fmt_args); | 355 | const gpa = report.error_bundle.gpa; |
| 360 | } | ||
| 361 | 356 | ||
| 362 | fn failWithNotes( | 357 | const file_path = try report.directory.join(gpa, &.{Manifest.basename}); |
| 363 | report: Report, | 358 | defer gpa.free(file_path); |
| 364 | notes: []const Compilation.AllErrors.Message, | 359 | |
| 365 | tok: std.zig.Ast.TokenIndex, | 360 | const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args); |
| 366 | comptime fmt_string: []const u8, | 361 | defer gpa.free(msg); |
| 367 | fmt_args: anytype, | 362 | |
| 368 | ) error{ PackageFetchFailed, OutOfMemory } { | 363 | try addErrorMessage(report.ast.*, file_path, report.error_bundle, 0, .{ |
| 369 | const ttyconf: std.debug.TTY.Config = switch (report.color) { | ||
| 370 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 371 | .on => .escape_codes, | ||
| 372 | .off => .no_color, | ||
| 373 | }; | ||
| 374 | const file_path = try report.directory.join(report.arena, &.{Manifest.basename}); | ||
| 375 | renderErrorMessage(report.ast.*, file_path, ttyconf, .{ | ||
| 376 | .tok = tok, | 364 | .tok = tok, |
| 377 | .off = 0, | 365 | .off = 0, |
| 378 | .msg = try std.fmt.allocPrint(report.arena, fmt_string, fmt_args), | 366 | .msg = msg, |
| 379 | }, notes); | 367 | }); |
| 368 | |||
| 380 | return error.PackageFetchFailed; | 369 | return error.PackageFetchFailed; |
| 381 | } | 370 | } |
| 382 | 371 | ||
| 383 | fn renderErrorMessage( | 372 | fn addErrorMessage( |
| 384 | ast: std.zig.Ast, | 373 | ast: std.zig.Ast, |
| 385 | file_path: []const u8, | 374 | file_path: []const u8, |
| 386 | ttyconf: std.debug.TTY.Config, | 375 | eb: *std.zig.ErrorBundle.Wip, |
| 376 | notes_len: u32, | ||
| 387 | msg: Manifest.ErrorMessage, | 377 | msg: Manifest.ErrorMessage, |
| 388 | notes: []const Compilation.AllErrors.Message, | 378 | ) error{OutOfMemory}!void { |
| 389 | ) void { | ||
| 390 | const token_starts = ast.tokens.items(.start); | 379 | const token_starts = ast.tokens.items(.start); |
| 391 | const start_loc = ast.tokenLocation(0, msg.tok); | 380 | const start_loc = ast.tokenLocation(0, msg.tok); |
| 392 | Compilation.AllErrors.Message.renderToStdErr(.{ .src = .{ | 381 | |
| 393 | .msg = msg.msg, | 382 | try eb.addRootErrorMessage(.{ |
| 394 | .src_path = file_path, | 383 | .msg = try eb.addString(msg.msg), |
| 395 | .line = @intCast(u32, start_loc.line), | 384 | .src_loc = try eb.addSourceLocation(.{ |
| 396 | .column = @intCast(u32, start_loc.column), | 385 | .src_path = try eb.addString(file_path), |
| 397 | .span = .{ | 386 | .span_start = token_starts[msg.tok], |
| 398 | .start = token_starts[msg.tok], | 387 | .span_end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), |
| 399 | .end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), | 388 | .span_main = token_starts[msg.tok] + msg.off, |
| 400 | .main = token_starts[msg.tok] + msg.off, | 389 | .line = @intCast(u32, start_loc.line), |
| 401 | }, | 390 | .column = @intCast(u32, start_loc.column), |
| 402 | .source_line = ast.source[start_loc.line_start..start_loc.line_end], | 391 | .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]), |
| 403 | .notes = notes, | 392 | }), |
| 404 | } }, ttyconf); | 393 | .notes_len = notes_len, |
| 394 | }); | ||
| 405 | } | 395 | } |
| 406 | }; | 396 | }; |
| 407 | 397 | ||
| ... | @@ -432,6 +422,12 @@ fn fetchAndUnpack( | ... | @@ -432,6 +422,12 @@ fn fetchAndUnpack( |
| 432 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | 422 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); |
| 433 | errdefer gpa.free(build_root); | 423 | errdefer gpa.free(build_root); |
| 434 | 424 | ||
| 425 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { | ||
| 426 | error.FileNotFound => break :cached, | ||
| 427 | else => |e| return e, | ||
| 428 | }; | ||
| 429 | errdefer pkg_dir.close(); | ||
| 430 | |||
| 435 | try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{ | 431 | try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{ |
| 436 | std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), | 432 | std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), |
| 437 | }); | 433 | }); |
| ... | @@ -444,12 +440,6 @@ fn fetchAndUnpack( | ... | @@ -444,12 +440,6 @@ fn fetchAndUnpack( |
| 444 | return gop.value_ptr.*; | 440 | return gop.value_ptr.*; |
| 445 | } | 441 | } |
| 446 | 442 | ||
| 447 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { | ||
| 448 | error.FileNotFound => break :cached, | ||
| 449 | else => |e| return e, | ||
| 450 | }; | ||
| 451 | errdefer pkg_dir.close(); | ||
| 452 | |||
| 453 | const ptr = try gpa.create(Package); | 443 | const ptr = try gpa.create(Package); |
| 454 | errdefer gpa.destroy(ptr); | 444 | errdefer gpa.destroy(ptr); |
| 455 | 445 | ||
| ... | @@ -501,9 +491,7 @@ fn fetchAndUnpack( | ... | @@ -501,9 +491,7 @@ fn fetchAndUnpack( |
| 501 | // by default, so the same logic applies for buffering the reader as for gzip. | 491 | // by default, so the same logic applies for buffering the reader as for gzip. |
| 502 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); | 492 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); |
| 503 | } else { | 493 | } else { |
| 504 | return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{ | 494 | return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{uri.path}); |
| 505 | uri.path, | ||
| 506 | }); | ||
| 507 | } | 495 | } |
| 508 | 496 | ||
| 509 | // TODO: delete files not included in the package prior to computing the package hash. | 497 | // TODO: delete files not included in the package prior to computing the package hash. |
| ... | @@ -530,10 +518,21 @@ fn fetchAndUnpack( | ... | @@ -530,10 +518,21 @@ fn fetchAndUnpack( |
| 530 | }); | 518 | }); |
| 531 | } | 519 | } |
| 532 | } else { | 520 | } else { |
| 533 | const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{ | 521 | const file_path = try report.directory.join(gpa, &.{Manifest.basename}); |
| 534 | .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}), | 522 | defer gpa.free(file_path); |
| 535 | } }}; | 523 | |
| 536 | return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{}); | 524 | const eb = report.error_bundle; |
| 525 | const notes_len = 1; | ||
| 526 | try Report.addErrorMessage(report.ast.*, file_path, eb, notes_len, .{ | ||
| 527 | .tok = dep.url_tok, | ||
| 528 | .off = 0, | ||
| 529 | .msg = "url field is missing corresponding hash field", | ||
| 530 | }); | ||
| 531 | const notes_start = try eb.reserveNotes(notes_len); | ||
| 532 | eb.extra.items[notes_start] = @enumToInt(try eb.addErrorMessage(.{ | ||
| 533 | .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}), | ||
| 534 | })); | ||
| 535 | return error.PackageFetchFailed; | ||
| 537 | } | 536 | } |
| 538 | 537 | ||
| 539 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | 538 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); |
src/Sema.zig+120-82| ... | @@ -574,11 +574,13 @@ pub const Block = struct { | ... | @@ -574,11 +574,13 @@ pub const Block = struct { |
| 574 | }); | 574 | }); |
| 575 | } | 575 | } |
| 576 | 576 | ||
| 577 | fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator, vector_ty: Air.Inst.Ref) !Air.Inst.Ref { | 577 | fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { |
| 578 | return block.addInst(.{ | 578 | return block.addInst(.{ |
| 579 | .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector, | 579 | .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector, |
| 580 | .data = .{ .ty_pl = .{ | 580 | .data = .{ .ty_pl = .{ |
| 581 | .ty = vector_ty, | 581 | .ty = try block.sema.addType( |
| 582 | try Type.vector(block.sema.arena, block.sema.typeOf(lhs).vectorLen(), Type.bool), | ||
| 583 | ), | ||
| 582 | .payload = try block.sema.addExtra(Air.VectorCmp{ | 584 | .payload = try block.sema.addExtra(Air.VectorCmp{ |
| 583 | .lhs = lhs, | 585 | .lhs = lhs, |
| 584 | .rhs = rhs, | 586 | .rhs = rhs, |
| ... | @@ -1101,6 +1103,7 @@ fn analyzeBodyInner( | ... | @@ -1101,6 +1103,7 @@ fn analyzeBodyInner( |
| 1101 | .@"unreachable" => break sema.zirUnreachable(block, inst), | 1103 | .@"unreachable" => break sema.zirUnreachable(block, inst), |
| 1102 | .panic => break sema.zirPanic(block, inst, false), | 1104 | .panic => break sema.zirPanic(block, inst, false), |
| 1103 | .panic_comptime => break sema.zirPanic(block, inst, true), | 1105 | .panic_comptime => break sema.zirPanic(block, inst, true), |
| 1106 | .trap => break sema.zirTrap(block, inst), | ||
| 1104 | // zig fmt: on | 1107 | // zig fmt: on |
| 1105 | 1108 | ||
| 1106 | .extended => ext: { | 1109 | .extended => ext: { |
| ... | @@ -1167,6 +1170,11 @@ fn analyzeBodyInner( | ... | @@ -1167,6 +1170,11 @@ fn analyzeBodyInner( |
| 1167 | i += 1; | 1170 | i += 1; |
| 1168 | continue; | 1171 | continue; |
| 1169 | }, | 1172 | }, |
| 1173 | .set_cold => { | ||
| 1174 | try sema.zirSetCold(block, extended); | ||
| 1175 | i += 1; | ||
| 1176 | continue; | ||
| 1177 | }, | ||
| 1170 | .breakpoint => { | 1178 | .breakpoint => { |
| 1171 | if (!block.is_comptime) { | 1179 | if (!block.is_comptime) { |
| 1172 | _ = try block.addNoOp(.breakpoint); | 1180 | _ = try block.addNoOp(.breakpoint); |
| ... | @@ -1304,11 +1312,6 @@ fn analyzeBodyInner( | ... | @@ -1304,11 +1312,6 @@ fn analyzeBodyInner( |
| 1304 | i += 1; | 1312 | i += 1; |
| 1305 | continue; | 1313 | continue; |
| 1306 | }, | 1314 | }, |
| 1307 | .set_cold => { | ||
| 1308 | try sema.zirSetCold(block, inst); | ||
| 1309 | i += 1; | ||
| 1310 | continue; | ||
| 1311 | }, | ||
| 1312 | .set_runtime_safety => { | 1315 | .set_runtime_safety => { |
| 1313 | try sema.zirSetRuntimeSafety(block, inst); | 1316 | try sema.zirSetRuntimeSafety(block, inst); |
| 1314 | i += 1; | 1317 | i += 1; |
| ... | @@ -1609,6 +1612,12 @@ fn analyzeBodyInner( | ... | @@ -1609,6 +1612,12 @@ fn analyzeBodyInner( |
| 1609 | const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); | 1612 | const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); |
| 1610 | const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; | 1613 | const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; |
| 1611 | const err_union = try sema.resolveInst(extra.data.operand); | 1614 | const err_union = try sema.resolveInst(extra.data.operand); |
| 1615 | const err_union_ty = sema.typeOf(err_union); | ||
| 1616 | if (err_union_ty.zigTypeTag() != .ErrorUnion) { | ||
| 1617 | return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ | ||
| 1618 | err_union_ty.fmt(sema.mod), | ||
| 1619 | }); | ||
| 1620 | } | ||
| 1612 | const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); | 1621 | const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); |
| 1613 | assert(is_non_err != .none); | 1622 | assert(is_non_err != .none); |
| 1614 | const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { | 1623 | const is_non_err_tv = sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { |
| ... | @@ -1616,7 +1625,6 @@ fn analyzeBodyInner( | ... | @@ -1616,7 +1625,6 @@ fn analyzeBodyInner( |
| 1616 | return err; | 1625 | return err; |
| 1617 | }; | 1626 | }; |
| 1618 | if (is_non_err_tv.val.toBool()) { | 1627 | if (is_non_err_tv.val.toBool()) { |
| 1619 | const err_union_ty = sema.typeOf(err_union); | ||
| 1620 | break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); | 1628 | break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); |
| 1621 | } | 1629 | } |
| 1622 | const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse | 1630 | const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse |
| ... | @@ -2203,29 +2211,27 @@ pub fn fail( | ... | @@ -2203,29 +2211,27 @@ pub fn fail( |
| 2203 | 2211 | ||
| 2204 | fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { | 2212 | fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2205 | @setCold(true); | 2213 | @setCold(true); |
| 2214 | const gpa = sema.gpa; | ||
| 2206 | 2215 | ||
| 2207 | if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { | 2216 | if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { |
| 2208 | if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; | 2217 | if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; |
| 2209 | var arena = std.heap.ArenaAllocator.init(sema.gpa); | 2218 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 2210 | errdefer arena.deinit(); | 2219 | wip_errors.init(gpa) catch unreachable; |
| 2211 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(sema.gpa); | 2220 | Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable; |
| 2212 | defer errors.deinit(); | ||
| 2213 | |||
| 2214 | Compilation.AllErrors.add(sema.mod, &arena, &errors, err_msg.*) catch unreachable; | ||
| 2215 | |||
| 2216 | std.debug.print("compile error during Sema:\n", .{}); | 2221 | std.debug.print("compile error during Sema:\n", .{}); |
| 2217 | Compilation.AllErrors.Message.renderToStdErr(errors.items[0], .no_color); | 2222 | var error_bundle = wip_errors.toOwnedBundle("") catch unreachable; |
| 2223 | error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); | ||
| 2218 | crash_report.compilerPanic("unexpected compile error occurred", null, null); | 2224 | crash_report.compilerPanic("unexpected compile error occurred", null, null); |
| 2219 | } | 2225 | } |
| 2220 | 2226 | ||
| 2221 | const mod = sema.mod; | 2227 | const mod = sema.mod; |
| 2222 | ref: { | 2228 | ref: { |
| 2223 | errdefer err_msg.destroy(mod.gpa); | 2229 | errdefer err_msg.destroy(gpa); |
| 2224 | if (err_msg.src_loc.lazy == .unneeded) { | 2230 | if (err_msg.src_loc.lazy == .unneeded) { |
| 2225 | return error.NeededSourceLocation; | 2231 | return error.NeededSourceLocation; |
| 2226 | } | 2232 | } |
| 2227 | try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); | 2233 | try mod.failed_decls.ensureUnusedCapacity(gpa, 1); |
| 2228 | try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1); | 2234 | try mod.failed_files.ensureUnusedCapacity(gpa, 1); |
| 2229 | 2235 | ||
| 2230 | const max_references = blk: { | 2236 | const max_references = blk: { |
| 2231 | if (sema.mod.comp.reference_trace) |num| break :blk num; | 2237 | if (sema.mod.comp.reference_trace) |num| break :blk num; |
| ... | @@ -2235,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { | ... | @@ -2235,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2235 | }; | 2241 | }; |
| 2236 | 2242 | ||
| 2237 | var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; | 2243 | var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; |
| 2238 | var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(sema.gpa); | 2244 | var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa); |
| 2239 | defer reference_stack.deinit(); | 2245 | defer reference_stack.deinit(); |
| 2240 | 2246 | ||
| 2241 | // Avoid infinite loops. | 2247 | // Avoid infinite loops. |
| 2242 | var seen = std.AutoHashMap(Module.Decl.Index, void).init(sema.gpa); | 2248 | var seen = std.AutoHashMap(Module.Decl.Index, void).init(gpa); |
| 2243 | defer seen.deinit(); | 2249 | defer seen.deinit(); |
| 2244 | 2250 | ||
| 2245 | var cur_reference_trace: u32 = 0; | 2251 | var cur_reference_trace: u32 = 0; |
| ... | @@ -2280,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { | ... | @@ -2280,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2280 | if (gop.found_existing) { | 2286 | if (gop.found_existing) { |
| 2281 | // If there are multiple errors for the same Decl, prefer the first one added. | 2287 | // If there are multiple errors for the same Decl, prefer the first one added. |
| 2282 | sema.err = null; | 2288 | sema.err = null; |
| 2283 | err_msg.destroy(mod.gpa); | 2289 | err_msg.destroy(gpa); |
| 2284 | } else { | 2290 | } else { |
| 2285 | sema.err = err_msg; | 2291 | sema.err = err_msg; |
| 2286 | gop.value_ptr.* = err_msg; | 2292 | gop.value_ptr.* = err_msg; |
| ... | @@ -5144,6 +5150,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo | ... | @@ -5144,6 +5150,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo |
| 5144 | return always_noreturn; | 5150 | return always_noreturn; |
| 5145 | } | 5151 | } |
| 5146 | 5152 | ||
| 5153 | fn 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 | |||
| 5147 | fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 5161 | fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5148 | const tracy = trace(@src()); | 5162 | const tracy = trace(@src()); |
| 5149 | defer tracy.end(); | 5163 | defer tracy.end(); |
| ... | @@ -5721,10 +5735,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst | ... | @@ -5721,10 +5735,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst |
| 5721 | gop.value_ptr.* = .{ .alignment = alignment, .src = src }; | 5735 | gop.value_ptr.* = .{ .alignment = alignment, .src = src }; |
| 5722 | } | 5736 | } |
| 5723 | 5737 | ||
| 5724 | fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | 5738 | fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { |
| 5725 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 5739 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 5726 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 5740 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; |
| 5727 | const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime-known"); | 5741 | const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known"); |
| 5728 | const func = sema.func orelse return; // does nothing outside a function | 5742 | const func = sema.func orelse return; // does nothing outside a function |
| 5729 | func.is_cold = is_cold; | 5743 | func.is_cold = is_cold; |
| 5730 | } | 5744 | } |
| ... | @@ -8766,7 +8780,7 @@ fn funcCommon( | ... | @@ -8766,7 +8780,7 @@ fn funcCommon( |
| 8766 | }; | 8780 | }; |
| 8767 | return sema.failWithOwnedErrorMsg(msg); | 8781 | return sema.failWithOwnedErrorMsg(msg); |
| 8768 | } | 8782 | } |
| 8769 | if (!Type.fnCallingConventionAllowsZigTypes(cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) { | 8783 | if (!ret_poison and !Type.fnCallingConventionAllowsZigTypes(cc_resolved) and !try sema.validateExternType(return_type, .ret_ty)) { |
| 8770 | const msg = msg: { | 8784 | const msg = msg: { |
| 8771 | const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ | 8785 | const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ |
| 8772 | return_type.fmt(sema.mod), @tagName(cc_resolved), | 8786 | return_type.fmt(sema.mod), @tagName(cc_resolved), |
| ... | @@ -9403,7 +9417,7 @@ fn intCast( | ... | @@ -9403,7 +9417,7 @@ fn intCast( |
| 9403 | const ok = if (is_vector) ok: { | 9417 | const ok = if (is_vector) ok: { |
| 9404 | const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero); | 9418 | const zeros = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 9405 | const zero_inst = try sema.addConstant(sema.typeOf(operand), zeros); | 9419 | const zero_inst = try sema.addConstant(sema.typeOf(operand), zeros); |
| 9406 | const is_in_range = try block.addCmpVector(operand, zero_inst, .eq, try sema.addType(operand_ty)); | 9420 | const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); |
| 9407 | const all_in_range = try block.addInst(.{ | 9421 | const all_in_range = try block.addInst(.{ |
| 9408 | .tag = .reduce, | 9422 | .tag = .reduce, |
| 9409 | .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } }, | 9423 | .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } }, |
| ... | @@ -9457,7 +9471,7 @@ fn intCast( | ... | @@ -9457,7 +9471,7 @@ fn intCast( |
| 9457 | const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); | 9471 | const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); |
| 9458 | 9472 | ||
| 9459 | const ok = if (is_vector) ok: { | 9473 | const ok = if (is_vector) ok: { |
| 9460 | const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty)); | 9474 | const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte); |
| 9461 | const all_in_range = try block.addInst(.{ | 9475 | const all_in_range = try block.addInst(.{ |
| 9462 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, | 9476 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, |
| 9463 | .data = .{ .reduce = .{ | 9477 | .data = .{ .reduce = .{ |
| ... | @@ -9474,7 +9488,7 @@ fn intCast( | ... | @@ -9474,7 +9488,7 @@ fn intCast( |
| 9474 | try sema.addSafetyCheck(block, ok, .cast_truncated_data); | 9488 | try sema.addSafetyCheck(block, ok, .cast_truncated_data); |
| 9475 | } else { | 9489 | } else { |
| 9476 | const ok = if (is_vector) ok: { | 9490 | const ok = if (is_vector) ok: { |
| 9477 | const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty)); | 9491 | const is_in_range = try block.addCmpVector(diff, dest_max, .lte); |
| 9478 | const all_in_range = try block.addInst(.{ | 9492 | const all_in_range = try block.addInst(.{ |
| 9479 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, | 9493 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, |
| 9480 | .data = .{ .reduce = .{ | 9494 | .data = .{ .reduce = .{ |
| ... | @@ -9495,7 +9509,7 @@ fn intCast( | ... | @@ -9495,7 +9509,7 @@ fn intCast( |
| 9495 | const ok = if (is_vector) ok: { | 9509 | const ok = if (is_vector) ok: { |
| 9496 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); | 9510 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 9497 | const zero_inst = try sema.addConstant(operand_ty, zero_val); | 9511 | const zero_inst = try sema.addConstant(operand_ty, zero_val); |
| 9498 | const is_in_range = try block.addCmpVector(operand, zero_inst, .gte, try sema.addType(operand_ty)); | 9512 | const is_in_range = try block.addCmpVector(operand, zero_inst, .gte); |
| 9499 | const all_in_range = try block.addInst(.{ | 9513 | const all_in_range = try block.addInst(.{ |
| 9500 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, | 9514 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, |
| 9501 | .data = .{ .reduce = .{ | 9515 | .data = .{ .reduce = .{ |
| ... | @@ -12007,7 +12021,7 @@ fn zirShl( | ... | @@ -12007,7 +12021,7 @@ fn zirShl( |
| 12007 | 12021 | ||
| 12008 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { | 12022 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { |
| 12009 | const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); | 12023 | const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); |
| 12010 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty)); | 12024 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); |
| 12011 | break :ok try block.addInst(.{ | 12025 | break :ok try block.addInst(.{ |
| 12012 | .tag = .reduce, | 12026 | .tag = .reduce, |
| 12013 | .data = .{ .reduce = .{ | 12027 | .data = .{ .reduce = .{ |
| ... | @@ -12163,7 +12177,7 @@ fn zirShr( | ... | @@ -12163,7 +12177,7 @@ fn zirShr( |
| 12163 | 12177 | ||
| 12164 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { | 12178 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { |
| 12165 | const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); | 12179 | const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); |
| 12166 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty)); | 12180 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); |
| 12167 | break :ok try block.addInst(.{ | 12181 | break :ok try block.addInst(.{ |
| 12168 | .tag = .reduce, | 12182 | .tag = .reduce, |
| 12169 | .data = .{ .reduce = .{ | 12183 | .data = .{ .reduce = .{ |
| ... | @@ -12182,7 +12196,7 @@ fn zirShr( | ... | @@ -12182,7 +12196,7 @@ fn zirShr( |
| 12182 | const back = try block.addBinOp(.shl, result, rhs); | 12196 | const back = try block.addBinOp(.shl, result, rhs); |
| 12183 | 12197 | ||
| 12184 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { | 12198 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { |
| 12185 | const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty)); | 12199 | const eql = try block.addCmpVector(lhs, back, .eq); |
| 12186 | break :ok try block.addInst(.{ | 12200 | break :ok try block.addInst(.{ |
| 12187 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, | 12201 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, |
| 12188 | .data = .{ .reduce = .{ | 12202 | .data = .{ .reduce = .{ |
| ... | @@ -13183,7 +13197,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -13183,7 +13197,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 13183 | const floored = try block.addUnOp(.floor, result); | 13197 | const floored = try block.addUnOp(.floor, result); |
| 13184 | 13198 | ||
| 13185 | if (resolved_type.zigTypeTag() == .Vector) { | 13199 | if (resolved_type.zigTypeTag() == .Vector) { |
| 13186 | const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type)); | 13200 | const eql = try block.addCmpVector(result, floored, .eq); |
| 13187 | break :ok try block.addInst(.{ | 13201 | break :ok try block.addInst(.{ |
| 13188 | .tag = switch (block.float_mode) { | 13202 | .tag = switch (block.float_mode) { |
| 13189 | .Strict => .reduce, | 13203 | .Strict => .reduce, |
| ... | @@ -13207,7 +13221,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -13207,7 +13221,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 13207 | if (resolved_type.zigTypeTag() == .Vector) { | 13221 | if (resolved_type.zigTypeTag() == .Vector) { |
| 13208 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); | 13222 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 13209 | const zero = try sema.addConstant(resolved_type, zero_val); | 13223 | const zero = try sema.addConstant(resolved_type, zero_val); |
| 13210 | const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type)); | 13224 | const eql = try block.addCmpVector(remainder, zero, .eq); |
| 13211 | break :ok try block.addInst(.{ | 13225 | break :ok try block.addInst(.{ |
| 13212 | .tag = .reduce, | 13226 | .tag = .reduce, |
| 13213 | .data = .{ .reduce = .{ | 13227 | .data = .{ .reduce = .{ |
| ... | @@ -13505,14 +13519,13 @@ fn addDivIntOverflowSafety( | ... | @@ -13505,14 +13519,13 @@ fn addDivIntOverflowSafety( |
| 13505 | 13519 | ||
| 13506 | var ok: Air.Inst.Ref = .none; | 13520 | var ok: Air.Inst.Ref = .none; |
| 13507 | if (resolved_type.zigTypeTag() == .Vector) { | 13521 | if (resolved_type.zigTypeTag() == .Vector) { |
| 13508 | const vector_ty_ref = try sema.addType(resolved_type); | ||
| 13509 | if (maybe_lhs_val == null) { | 13522 | if (maybe_lhs_val == null) { |
| 13510 | const min_int_ref = try sema.addConstant(resolved_type, min_int); | 13523 | const min_int_ref = try sema.addConstant(resolved_type, min_int); |
| 13511 | ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref); | 13524 | ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq); |
| 13512 | } | 13525 | } |
| 13513 | if (maybe_rhs_val == null) { | 13526 | if (maybe_rhs_val == null) { |
| 13514 | const neg_one_ref = try sema.addConstant(resolved_type, neg_one); | 13527 | const neg_one_ref = try sema.addConstant(resolved_type, neg_one); |
| 13515 | const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref); | 13528 | const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq); |
| 13516 | if (ok == .none) { | 13529 | if (ok == .none) { |
| 13517 | ok = rhs_ok; | 13530 | ok = rhs_ok; |
| 13518 | } else { | 13531 | } else { |
| ... | @@ -13564,7 +13577,7 @@ fn addDivByZeroSafety( | ... | @@ -13564,7 +13577,7 @@ fn addDivByZeroSafety( |
| 13564 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { | 13577 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 13565 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); | 13578 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 13566 | const zero = try sema.addConstant(resolved_type, zero_val); | 13579 | const zero = try sema.addConstant(resolved_type, zero_val); |
| 13567 | const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); | 13580 | const ok = try block.addCmpVector(casted_rhs, zero, .neq); |
| 13568 | break :ok try block.addInst(.{ | 13581 | break :ok try block.addInst(.{ |
| 13569 | .tag = if (is_int) .reduce else .reduce_optimized, | 13582 | .tag = if (is_int) .reduce else .reduce_optimized, |
| 13570 | .data = .{ .reduce = .{ | 13583 | .data = .{ .reduce = .{ |
| ... | @@ -15193,9 +15206,7 @@ fn cmpSelf( | ... | @@ -15193,9 +15206,7 @@ fn cmpSelf( |
| 15193 | }; | 15206 | }; |
| 15194 | try sema.requireRuntimeBlock(block, src, runtime_src); | 15207 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 15195 | if (resolved_type.zigTypeTag() == .Vector) { | 15208 | if (resolved_type.zigTypeTag() == .Vector) { |
| 15196 | const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.bool); | 15209 | return block.addCmpVector(casted_lhs, casted_rhs, op); |
| 15197 | const result_ty_ref = try sema.addType(result_ty); | ||
| 15198 | return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref); | ||
| 15199 | } | 15210 | } |
| 15200 | const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized); | 15211 | const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized); |
| 15201 | return block.addBinOp(tag, casted_lhs, casted_rhs); | 15212 | return block.addBinOp(tag, casted_lhs, casted_rhs); |
| ... | @@ -18360,7 +18371,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in | ... | @@ -18360,7 +18371,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in |
| 18360 | const union_val = val.cast(Value.Payload.Union).?.data; | 18371 | const union_val = val.cast(Value.Payload.Union).?.data; |
| 18361 | const target = mod.getTarget(); | 18372 | const target = mod.getTarget(); |
| 18362 | const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?; | 18373 | const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?; |
| 18363 | if (union_val.val.anyUndef()) return sema.failWithUseOfUndef(block, src); | 18374 | if (union_val.val.anyUndef(mod)) return sema.failWithUseOfUndef(block, src); |
| 18364 | switch (@intToEnum(std.builtin.TypeId, tag_index)) { | 18375 | switch (@intToEnum(std.builtin.TypeId, tag_index)) { |
| 18365 | .Type => return Air.Inst.Ref.type_type, | 18376 | .Type => return Air.Inst.Ref.type_type, |
| 18366 | .Void => return Air.Inst.Ref.void_type, | 18377 | .Void => return Air.Inst.Ref.void_type, |
| ... | @@ -22274,7 +22285,6 @@ fn zirBuiltinExtern( | ... | @@ -22274,7 +22285,6 @@ fn zirBuiltinExtern( |
| 22274 | extended: Zir.Inst.Extended.InstData, | 22285 | extended: Zir.Inst.Extended.InstData, |
| 22275 | ) CompileError!Air.Inst.Ref { | 22286 | ) CompileError!Air.Inst.Ref { |
| 22276 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; | 22287 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 22277 | const src = LazySrcLoc.nodeOffset(extra.node); | ||
| 22278 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; | 22288 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; |
| 22279 | const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; | 22289 | const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; |
| 22280 | 22290 | ||
| ... | @@ -22302,39 +22312,41 @@ fn zirBuiltinExtern( | ... | @@ -22302,39 +22312,41 @@ fn zirBuiltinExtern( |
| 22302 | const new_decl = sema.mod.declPtr(new_decl_index); | 22312 | const new_decl = sema.mod.declPtr(new_decl_index); |
| 22303 | new_decl.name = try sema.gpa.dupeZ(u8, options.name); | 22313 | new_decl.name = try sema.gpa.dupeZ(u8, options.name); |
| 22304 | 22314 | ||
| 22305 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | 22315 | { |
| 22306 | errdefer new_decl_arena.deinit(); | 22316 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); |
| 22307 | const new_decl_arena_allocator = new_decl_arena.allocator(); | 22317 | errdefer new_decl_arena.deinit(); |
| 22318 | const new_decl_arena_allocator = new_decl_arena.allocator(); | ||
| 22308 | 22319 | ||
| 22309 | const new_var = try new_decl_arena_allocator.create(Module.Var); | 22320 | const new_var = try new_decl_arena_allocator.create(Module.Var); |
| 22310 | errdefer new_decl_arena_allocator.destroy(new_var); | 22321 | new_var.* = .{ |
| 22322 | .owner_decl = sema.owner_decl_index, | ||
| 22323 | .init = Value.initTag(.unreachable_value), | ||
| 22324 | .is_extern = true, | ||
| 22325 | .is_mutable = false, | ||
| 22326 | .is_threadlocal = options.is_thread_local, | ||
| 22327 | .is_weak_linkage = options.linkage == .Weak, | ||
| 22328 | .lib_name = null, | ||
| 22329 | }; | ||
| 22311 | 22330 | ||
| 22312 | new_var.* = .{ | 22331 | new_decl.src_line = sema.owner_decl.src_line; |
| 22313 | .owner_decl = sema.owner_decl_index, | 22332 | // We only access this decl through the decl_ref with the correct type created |
| 22314 | .init = Value.initTag(.unreachable_value), | 22333 | // below, so this type doesn't matter |
| 22315 | .is_extern = true, | 22334 | new_decl.ty = Type.Tag.init(.anyopaque); |
| 22316 | .is_mutable = false, | 22335 | new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); |
| 22317 | .is_threadlocal = options.is_thread_local, | 22336 | new_decl.@"align" = 0; |
| 22318 | .is_weak_linkage = options.linkage == .Weak, | 22337 | new_decl.@"linksection" = null; |
| 22319 | .lib_name = null, | 22338 | new_decl.has_tv = true; |
| 22320 | }; | 22339 | new_decl.analysis = .complete; |
| 22340 | new_decl.generation = sema.mod.generation; | ||
| 22321 | 22341 | ||
| 22322 | new_decl.src_line = sema.owner_decl.src_line; | 22342 | try new_decl.finalizeNewArena(&new_decl_arena); |
| 22323 | new_decl.ty = try ty.copy(new_decl_arena_allocator); | 22343 | } |
| 22324 | new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); | ||
| 22325 | new_decl.@"align" = 0; | ||
| 22326 | new_decl.@"linksection" = null; | ||
| 22327 | new_decl.has_tv = true; | ||
| 22328 | new_decl.analysis = .complete; | ||
| 22329 | new_decl.generation = sema.mod.generation; | ||
| 22330 | 22344 | ||
| 22331 | const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State); | 22345 | try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); |
| 22332 | arena_state.* = new_decl_arena.state; | 22346 | try sema.ensureDeclAnalyzed(new_decl_index); |
| 22333 | new_decl.value_arena = arena_state; | ||
| 22334 | 22347 | ||
| 22335 | const ref = try sema.analyzeDeclRef(new_decl_index); | 22348 | const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index); |
| 22336 | try sema.requireRuntimeBlock(block, src, null); | 22349 | return sema.addConstant(ty, ref); |
| 22337 | return block.addBitCast(ty, ref); | ||
| 22338 | } | 22350 | } |
| 22339 | 22351 | ||
| 22340 | fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { | 22352 | fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { |
| ... | @@ -23026,7 +23038,7 @@ fn panicSentinelMismatch( | ... | @@ -23026,7 +23038,7 @@ fn panicSentinelMismatch( |
| 23026 | 23038 | ||
| 23027 | const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: { | 23039 | const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: { |
| 23028 | const eql = | 23040 | const eql = |
| 23029 | try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq, try sema.addType(sentinel_ty)); | 23041 | try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); |
| 23030 | break :ok try parent_block.addInst(.{ | 23042 | break :ok try parent_block.addInst(.{ |
| 23031 | .tag = .reduce, | 23043 | .tag = .reduce, |
| 23032 | .data = .{ .reduce = .{ | 23044 | .data = .{ .reduce = .{ |
| ... | @@ -23592,10 +23604,13 @@ fn fieldCallBind( | ... | @@ -23592,10 +23604,13 @@ fn fieldCallBind( |
| 23592 | } | 23604 | } |
| 23593 | 23605 | ||
| 23594 | // If we get here, we need to look for a decl in the struct type instead. | 23606 | // If we get here, we need to look for a decl in the struct type instead. |
| 23595 | switch (concrete_ty.zigTypeTag()) { | 23607 | const found_decl = switch (concrete_ty.zigTypeTag()) { |
| 23596 | .Struct, .Opaque, .Union, .Enum => { | 23608 | .Struct, .Opaque, .Union, .Enum => found_decl: { |
| 23597 | if (concrete_ty.getNamespace()) |namespace| { | 23609 | if (concrete_ty.getNamespace()) |namespace| { |
| 23598 | if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { | 23610 | if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| { |
| 23611 | try sema.addReferencedBy(block, src, decl_idx); | ||
| 23612 | const inst = try sema.analyzeDeclRef(decl_idx); | ||
| 23613 | |||
| 23599 | const decl_val = try sema.analyzeLoad(block, src, inst, src); | 23614 | const decl_val = try sema.analyzeLoad(block, src, inst, src); |
| 23600 | const decl_type = sema.typeOf(decl_val); | 23615 | const decl_type = sema.typeOf(decl_val); |
| 23601 | if (decl_type.zigTypeTag() == .Fn and | 23616 | if (decl_type.zigTypeTag() == .Fn and |
| ... | @@ -23612,7 +23627,7 @@ fn fieldCallBind( | ... | @@ -23612,7 +23627,7 @@ fn fieldCallBind( |
| 23612 | first_param_type.ptrSize() == .C) and | 23627 | first_param_type.ptrSize() == .C) and |
| 23613 | first_param_type.childType().eql(concrete_ty, sema.mod))) | 23628 | first_param_type.childType().eql(concrete_ty, sema.mod))) |
| 23614 | { | 23629 | { |
| 23615 | // zig fmt: on | 23630 | // zig fmt: on |
| 23616 | // TODO: bound fn calls on rvalues should probably | 23631 | // TODO: bound fn calls on rvalues should probably |
| 23617 | // generate a by-value argument somehow. | 23632 | // generate a by-value argument somehow. |
| 23618 | const ty = Type.Tag.bound_fn.init(); | 23633 | const ty = Type.Tag.bound_fn.init(); |
| ... | @@ -23651,16 +23666,22 @@ fn fieldCallBind( | ... | @@ -23651,16 +23666,22 @@ fn fieldCallBind( |
| 23651 | return sema.addConstant(ty, value); | 23666 | return sema.addConstant(ty, value); |
| 23652 | } | 23667 | } |
| 23653 | } | 23668 | } |
| 23669 | break :found_decl decl_idx; | ||
| 23654 | } | 23670 | } |
| 23655 | } | 23671 | } |
| 23672 | break :found_decl null; | ||
| 23656 | }, | 23673 | }, |
| 23657 | else => {}, | 23674 | else => null, |
| 23658 | } | 23675 | }; |
| 23659 | 23676 | ||
| 23660 | const msg = msg: { | 23677 | const msg = msg: { |
| 23661 | const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) }); | 23678 | const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) }); |
| 23662 | errdefer msg.destroy(sema.gpa); | 23679 | errdefer msg.destroy(sema.gpa); |
| 23663 | try sema.addDeclaredHereNote(msg, concrete_ty); | 23680 | try sema.addDeclaredHereNote(msg, concrete_ty); |
| 23681 | if (found_decl) |decl_idx| { | ||
| 23682 | const decl = sema.mod.declPtr(decl_idx); | ||
| 23683 | try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "'{s}' is not a member function", .{field_name}); | ||
| 23684 | } | ||
| 23664 | break :msg msg; | 23685 | break :msg msg; |
| 23665 | }; | 23686 | }; |
| 23666 | return sema.failWithOwnedErrorMsg(msg); | 23687 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -24832,6 +24853,7 @@ fn coerceExtra( | ... | @@ -24832,6 +24853,7 @@ fn coerceExtra( |
| 24832 | const array_ty = dest_info.pointee_type; | 24853 | const array_ty = dest_info.pointee_type; |
| 24833 | if (array_ty.zigTypeTag() != .Array) break :single_item; | 24854 | if (array_ty.zigTypeTag() != .Array) break :single_item; |
| 24834 | const array_elem_ty = array_ty.childType(); | 24855 | const array_elem_ty = array_ty.childType(); |
| 24856 | if (array_ty.arrayLen() != 1) break :single_item; | ||
| 24835 | const dest_is_mut = dest_info.mutable; | 24857 | const dest_is_mut = dest_info.mutable; |
| 24836 | switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { | 24858 | switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { |
| 24837 | .ok => {}, | 24859 | .ok => {}, |
| ... | @@ -26625,6 +26647,23 @@ fn beginComptimePtrMutation( | ... | @@ -26625,6 +26647,23 @@ fn beginComptimePtrMutation( |
| 26625 | }); | 26647 | }); |
| 26626 | } | 26648 | } |
| 26627 | const elem_ty = parent.ty.childType(); | 26649 | const elem_ty = parent.ty.childType(); |
| 26650 | |||
| 26651 | // We might have a pointer to multiple elements of the array (e.g. a pointer | ||
| 26652 | // to a sub-array). In this case, we just have to reinterpret the relevant | ||
| 26653 | // bytes of the whole array rather than any single element. | ||
| 26654 | const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty); | ||
| 26655 | if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) { | ||
| 26656 | const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); | ||
| 26657 | return .{ | ||
| 26658 | .decl_ref_mut = parent.decl_ref_mut, | ||
| 26659 | .pointee = .{ .reinterpret = .{ | ||
| 26660 | .val_ptr = val_ptr, | ||
| 26661 | .byte_offset = elem_abi_size * elem_ptr.index, | ||
| 26662 | } }, | ||
| 26663 | .ty = parent.ty, | ||
| 26664 | }; | ||
| 26665 | } | ||
| 26666 | |||
| 26628 | switch (val_ptr.tag()) { | 26667 | switch (val_ptr.tag()) { |
| 26629 | .undef => { | 26668 | .undef => { |
| 26630 | // An array has been initialized to undefined at comptime and now we | 26669 | // An array has been initialized to undefined at comptime and now we |
| ... | @@ -29359,8 +29398,7 @@ fn cmpVector( | ... | @@ -29359,8 +29398,7 @@ fn cmpVector( |
| 29359 | }; | 29398 | }; |
| 29360 | 29399 | ||
| 29361 | try sema.requireRuntimeBlock(block, src, runtime_src); | 29400 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 29362 | const result_ty_inst = try sema.addType(result_ty); | 29401 | return block.addCmpVector(lhs, rhs, op); |
| 29363 | return block.addCmpVector(lhs, rhs, op, result_ty_inst); | ||
| 29364 | } | 29402 | } |
| 29365 | 29403 | ||
| 29366 | fn wrapOptional( | 29404 | fn wrapOptional( |
src/ThreadPool.zig deleted-152| ... | @@ -1,152 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const ThreadPool = @This(); | ||
| 4 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 5 | |||
| 6 | mutex: std.Thread.Mutex = .{}, | ||
| 7 | cond: std.Thread.Condition = .{}, | ||
| 8 | run_queue: RunQueue = .{}, | ||
| 9 | is_running: bool = true, | ||
| 10 | allocator: std.mem.Allocator, | ||
| 11 | threads: []std.Thread, | ||
| 12 | |||
| 13 | const RunQueue = std.SinglyLinkedList(Runnable); | ||
| 14 | const Runnable = struct { | ||
| 15 | runFn: RunProto, | ||
| 16 | }; | ||
| 17 | |||
| 18 | const RunProto = *const fn (*Runnable) void; | ||
| 19 | |||
| 20 | pub 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 | |||
| 44 | pub fn deinit(pool: *ThreadPool) void { | ||
| 45 | pool.join(pool.threads.len); // kill and join all threads. | ||
| 46 | pool.* = undefined; | ||
| 47 | } | ||
| 48 | |||
| 49 | fn 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 | |||
| 72 | pub 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 | |||
| 115 | fn 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 | |||
| 138 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Atomic = std.atomic.Atomic; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const WaitGroup = @This(); | ||
| 5 | |||
| 6 | const is_waiting: usize = 1 << 0; | ||
| 7 | const one_pending: usize = 1 << 1; | ||
| 8 | |||
| 9 | state: Atomic(usize) = Atomic(usize).init(0), | ||
| 10 | event: std.Thread.ResetEvent = .{}, | ||
| 11 | |||
| 12 | pub 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 | |||
| 17 | pub 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 | |||
| 27 | pub 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 | |||
| 36 | pub fn reset(self: *WaitGroup) void { | ||
| 37 | self.state.store(0, .Monotonic); | ||
| 38 | self.event.reset(); | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn isDone(wg: *WaitGroup) bool { | ||
| 42 | const state = wg.state.load(.Acquire); | ||
| 43 | assert(state & is_waiting == 0); | ||
| 44 | |||
| 45 | return (state / one_pending) == 0; | ||
| 46 | } | ||
src/Zir.zig+20-10| ... | @@ -617,7 +617,7 @@ pub const Inst = struct { | ... | @@ -617,7 +617,7 @@ pub const Inst = struct { |
| 617 | /// Uses the `un_node` field. | 617 | /// Uses the `un_node` field. |
| 618 | typeof_log2_int_type, | 618 | typeof_log2_int_type, |
| 619 | /// Asserts control-flow will not reach this instruction (`unreachable`). | 619 | /// Asserts control-flow will not reach this instruction (`unreachable`). |
| 620 | /// Uses the `unreachable` union field. | 620 | /// Uses the `@"unreachable"` union field. |
| 621 | @"unreachable", | 621 | @"unreachable", |
| 622 | /// Bitwise XOR. `^` | 622 | /// Bitwise XOR. `^` |
| 623 | /// Uses the `pl_node` union field. Payload is `Bin`. | 623 | /// Uses the `pl_node` union field. Payload is `Bin`. |
| ... | @@ -808,8 +808,9 @@ pub const Inst = struct { | ... | @@ -808,8 +808,9 @@ pub const Inst = struct { |
| 808 | panic, | 808 | panic, |
| 809 | /// Same as `panic` but forces comptime. | 809 | /// Same as `panic` but forces comptime. |
| 810 | panic_comptime, | 810 | panic_comptime, |
| 811 | /// Implement builtin `@setCold`. Uses `un_node`. | 811 | /// Implements `@trap`. |
| 812 | set_cold, | 812 | /// Uses the `node` field. |
| 813 | trap, | ||
| 813 | /// Implement builtin `@setRuntimeSafety`. Uses `un_node`. | 814 | /// Implement builtin `@setRuntimeSafety`. Uses `un_node`. |
| 814 | set_runtime_safety, | 815 | set_runtime_safety, |
| 815 | /// Implement builtin `@sqrt`. Uses `un_node`. | 816 | /// Implement builtin `@sqrt`. Uses `un_node`. |
| ... | @@ -1187,7 +1188,6 @@ pub const Inst = struct { | ... | @@ -1187,7 +1188,6 @@ pub const Inst = struct { |
| 1187 | .bool_to_int, | 1188 | .bool_to_int, |
| 1188 | .embed_file, | 1189 | .embed_file, |
| 1189 | .error_name, | 1190 | .error_name, |
| 1190 | .set_cold, | ||
| 1191 | .set_runtime_safety, | 1191 | .set_runtime_safety, |
| 1192 | .sqrt, | 1192 | .sqrt, |
| 1193 | .sin, | 1193 | .sin, |
| ... | @@ -1277,6 +1277,7 @@ pub const Inst = struct { | ... | @@ -1277,6 +1277,7 @@ pub const Inst = struct { |
| 1277 | .repeat_inline, | 1277 | .repeat_inline, |
| 1278 | .panic, | 1278 | .panic, |
| 1279 | .panic_comptime, | 1279 | .panic_comptime, |
| 1280 | .trap, | ||
| 1280 | .check_comptime_control_flow, | 1281 | .check_comptime_control_flow, |
| 1281 | => true, | 1282 | => true, |
| 1282 | }; | 1283 | }; |
| ... | @@ -1323,7 +1324,6 @@ pub const Inst = struct { | ... | @@ -1323,7 +1324,6 @@ pub const Inst = struct { |
| 1323 | .validate_deref, | 1324 | .validate_deref, |
| 1324 | .@"export", | 1325 | .@"export", |
| 1325 | .export_value, | 1326 | .export_value, |
| 1326 | .set_cold, | ||
| 1327 | .set_runtime_safety, | 1327 | .set_runtime_safety, |
| 1328 | .memcpy, | 1328 | .memcpy, |
| 1329 | .memset, | 1329 | .memset, |
| ... | @@ -1553,6 +1553,7 @@ pub const Inst = struct { | ... | @@ -1553,6 +1553,7 @@ pub const Inst = struct { |
| 1553 | .repeat_inline, | 1553 | .repeat_inline, |
| 1554 | .panic, | 1554 | .panic, |
| 1555 | .panic_comptime, | 1555 | .panic_comptime, |
| 1556 | .trap, | ||
| 1556 | .for_len, | 1557 | .for_len, |
| 1557 | .@"try", | 1558 | .@"try", |
| 1558 | .try_ptr, | 1559 | .try_ptr, |
| ... | @@ -1561,7 +1562,7 @@ pub const Inst = struct { | ... | @@ -1561,7 +1562,7 @@ pub const Inst = struct { |
| 1561 | => false, | 1562 | => false, |
| 1562 | 1563 | ||
| 1563 | .extended => switch (data.extended.opcode) { | 1564 | .extended => switch (data.extended.opcode) { |
| 1564 | .breakpoint, .fence => true, | 1565 | .fence, .set_cold, .breakpoint => true, |
| 1565 | else => false, | 1566 | else => false, |
| 1566 | }, | 1567 | }, |
| 1567 | }; | 1568 | }; |
| ... | @@ -1750,7 +1751,7 @@ pub const Inst = struct { | ... | @@ -1750,7 +1751,7 @@ pub const Inst = struct { |
| 1750 | .error_name = .un_node, | 1751 | .error_name = .un_node, |
| 1751 | .panic = .un_node, | 1752 | .panic = .un_node, |
| 1752 | .panic_comptime = .un_node, | 1753 | .panic_comptime = .un_node, |
| 1753 | .set_cold = .un_node, | 1754 | .trap = .node, |
| 1754 | .set_runtime_safety = .un_node, | 1755 | .set_runtime_safety = .un_node, |
| 1755 | .sqrt = .un_node, | 1756 | .sqrt = .un_node, |
| 1756 | .sin = .un_node, | 1757 | .sin = .un_node, |
| ... | @@ -1979,11 +1980,15 @@ pub const Inst = struct { | ... | @@ -1979,11 +1980,15 @@ pub const Inst = struct { |
| 1979 | /// Implement builtin `@setAlignStack`. | 1980 | /// Implement builtin `@setAlignStack`. |
| 1980 | /// `operand` is payload index to `UnNode`. | 1981 | /// `operand` is payload index to `UnNode`. |
| 1981 | set_align_stack, | 1982 | set_align_stack, |
| 1983 | /// Implements `@setCold`. | ||
| 1984 | /// `operand` is payload index to `UnNode`. | ||
| 1985 | set_cold, | ||
| 1982 | /// Implements the `@errSetCast` builtin. | 1986 | /// Implements the `@errSetCast` builtin. |
| 1983 | /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. | 1987 | /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. |
| 1984 | err_set_cast, | 1988 | err_set_cast, |
| 1985 | /// `operand` is payload index to `UnNode`. | 1989 | /// `operand` is payload index to `UnNode`. |
| 1986 | await_nosuspend, | 1990 | await_nosuspend, |
| 1991 | /// Implements `@breakpoint`. | ||
| 1987 | /// `operand` is `src_node: i32`. | 1992 | /// `operand` is `src_node: i32`. |
| 1988 | breakpoint, | 1993 | breakpoint, |
| 1989 | /// Implements the `@select` builtin. | 1994 | /// Implements the `@select` builtin. |
| ... | @@ -1997,7 +2002,7 @@ pub const Inst = struct { | ... | @@ -1997,7 +2002,7 @@ pub const Inst = struct { |
| 1997 | int_to_error, | 2002 | int_to_error, |
| 1998 | /// Implement builtin `@Type`. | 2003 | /// Implement builtin `@Type`. |
| 1999 | /// `operand` is payload index to `UnNode`. | 2004 | /// `operand` is payload index to `UnNode`. |
| 2000 | /// `small` contains `NameStrategy | 2005 | /// `small` contains `NameStrategy`. |
| 2001 | reify, | 2006 | reify, |
| 2002 | /// Implements the `@asyncCall` builtin. | 2007 | /// Implements the `@asyncCall` builtin. |
| 2003 | /// `operand` is payload index to `AsyncCall`. | 2008 | /// `operand` is payload index to `AsyncCall`. |
| ... | @@ -2040,8 +2045,7 @@ pub const Inst = struct { | ... | @@ -2040,8 +2045,7 @@ pub const Inst = struct { |
| 2040 | 2045 | ||
| 2041 | /// A reference to a TypedValue or ZIR instruction. | 2046 | /// A reference to a TypedValue or ZIR instruction. |
| 2042 | /// | 2047 | /// |
| 2043 | /// If the Ref has a tag in this enum, it refers to a TypedValue which may be | 2048 | /// If the Ref has a tag in this enum, it refers to a TypedValue. |
| 2044 | /// retrieved with Ref.toTypedValue(). | ||
| 2045 | /// | 2049 | /// |
| 2046 | /// If the value of a Ref does not have a tag, it refers to a ZIR instruction. | 2050 | /// If the value of a Ref does not have a tag, it refers to a ZIR instruction. |
| 2047 | /// | 2051 | /// |
| ... | @@ -3590,6 +3594,12 @@ pub const Inst = struct { | ... | @@ -3590,6 +3594,12 @@ pub const Inst = struct { |
| 3590 | /// 0 or a payload index of a `Block`, each is a payload | 3594 | /// 0 or a payload index of a `Block`, each is a payload |
| 3591 | /// index of another `Item`. | 3595 | /// index of another `Item`. |
| 3592 | notes: u32, | 3596 | notes: u32, |
| 3597 | |||
| 3598 | pub fn notesLen(item: Item, zir: Zir) u32 { | ||
| 3599 | if (item.notes == 0) return 0; | ||
| 3600 | const block = zir.extraData(Block, item.notes); | ||
| 3601 | return block.data.body_len; | ||
| 3602 | } | ||
| 3593 | }; | 3603 | }; |
| 3594 | }; | 3604 | }; |
| 3595 | 3605 |
src/arch/aarch64/CodeGen.zig+30-200| ... | @@ -23,7 +23,7 @@ const leb128 = std.leb; | ... | @@ -23,7 +23,7 @@ const leb128 = std.leb; |
| 23 | const log = std.log.scoped(.codegen); | 23 | const log = std.log.scoped(.codegen); |
| 24 | const build_options = @import("build_options"); | 24 | const build_options = @import("build_options"); |
| 25 | 25 | ||
| 26 | const GenerateSymbolError = codegen.GenerateSymbolError; | 26 | const CodeGenError = codegen.CodeGenError; |
| 27 | const Result = codegen.Result; | 27 | const Result = codegen.Result; |
| 28 | const DebugInfoOutput = codegen.DebugInfoOutput; | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 29 | 29 | ||
| ... | @@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ... | @@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; |
| 41 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | 41 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; |
| 42 | const gp = abi.RegisterClass.gp; | 42 | const gp = abi.RegisterClass.gp; |
| 43 | 43 | ||
| 44 | const InnerError = error{ | 44 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 45 | OutOfMemory, | ||
| 46 | CodegenFail, | ||
| 47 | OutOfRegisters, | ||
| 48 | }; | ||
| 49 | 45 | ||
| 50 | gpa: Allocator, | 46 | gpa: Allocator, |
| 51 | air: Air, | 47 | air: Air, |
| ... | @@ -337,7 +333,7 @@ pub fn generate( | ... | @@ -337,7 +333,7 @@ pub fn generate( |
| 337 | liveness: Liveness, | 333 | liveness: Liveness, |
| 338 | code: *std.ArrayList(u8), | 334 | code: *std.ArrayList(u8), |
| 339 | debug_output: DebugInfoOutput, | 335 | debug_output: DebugInfoOutput, |
| 340 | ) GenerateSymbolError!Result { | 336 | ) CodeGenError!Result { |
| 341 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 337 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 342 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 338 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 343 | } | 339 | } |
| ... | @@ -737,6 +733,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -737,6 +733,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 737 | .bitcast => try self.airBitCast(inst), | 733 | .bitcast => try self.airBitCast(inst), |
| 738 | .block => try self.airBlock(inst), | 734 | .block => try self.airBlock(inst), |
| 739 | .br => try self.airBr(inst), | 735 | .br => try self.airBr(inst), |
| 736 | .trap => try self.airTrap(), | ||
| 740 | .breakpoint => try self.airBreakpoint(), | 737 | .breakpoint => try self.airBreakpoint(), |
| 741 | .ret_addr => try self.airRetAddr(inst), | 738 | .ret_addr => try self.airRetAddr(inst), |
| 742 | .frame_addr => try self.airFrameAddress(inst), | 739 | .frame_addr => try self.airFrameAddress(inst), |
| ... | @@ -4198,10 +4195,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4198,10 +4195,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 4198 | return self.finishAir(inst, result, .{ .none, .none, .none }); | 4195 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 4199 | } | 4196 | } |
| 4200 | 4197 | ||
| 4198 | fn airTrap(self: *Self) !void { | ||
| 4199 | _ = try self.addInst(.{ | ||
| 4200 | .tag = .brk, | ||
| 4201 | .data = .{ .imm16 = 0x0001 }, | ||
| 4202 | }); | ||
| 4203 | return self.finishAirBookkeeping(); | ||
| 4204 | } | ||
| 4205 | |||
| 4201 | fn airBreakpoint(self: *Self) !void { | 4206 | fn airBreakpoint(self: *Self) !void { |
| 4202 | _ = try self.addInst(.{ | 4207 | _ = try self.addInst(.{ |
| 4203 | .tag = .brk, | 4208 | .tag = .brk, |
| 4204 | .data = .{ .imm16 = 1 }, | 4209 | .data = .{ .imm16 = 0xf000 }, |
| 4205 | }); | 4210 | }); |
| 4206 | return self.finishAirBookkeeping(); | 4211 | return self.finishAirBookkeeping(); |
| 4207 | } | 4212 | } |
| ... | @@ -6137,201 +6142,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | ... | @@ -6137,201 +6142,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 6137 | } | 6142 | } |
| 6138 | } | 6143 | } |
| 6139 | 6144 | ||
| 6140 | fn 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 | |||
| 6184 | fn 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 | |||
| 6208 | fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { | 6145 | fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { |
| 6209 | var typed_value = arg_tv; | 6146 | const mcv: MCValue = switch (try codegen.genTypedValue( |
| 6210 | if (typed_value.val.castTag(.runtime_value)) |rt| { | 6147 | self.bin_file, |
| 6211 | typed_value.val = rt.data; | 6148 | self.src_loc, |
| 6212 | } | 6149 | arg_tv, |
| 6213 | log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); | 6150 | self.mod_fn.owner_decl, |
| 6214 | if (typed_value.val.isUndef()) | 6151 | )) { |
| 6215 | return MCValue{ .undef = {} }; | 6152 | .mcv => |mcv| switch (mcv) { |
| 6216 | 6153 | .none => .none, | |
| 6217 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 6154 | .undef => .undef, |
| 6218 | return self.lowerDeclRef(typed_value, payload.data); | 6155 | .linker_load => |ll| .{ .linker_load = ll }, |
| 6219 | } | 6156 | .immediate => |imm| .{ .immediate = imm }, |
| 6220 | if (typed_value.val.castTag(.decl_ref_mut)) |payload| { | 6157 | .memory => |addr| .{ .memory = addr }, |
| 6221 | return self.lowerDeclRef(typed_value, payload.data.decl_index); | ||
| 6222 | } | ||
| 6223 | const target = self.target.*; | ||
| 6224 | |||
| 6225 | switch (typed_value.ty.zigTypeTag()) { | ||
| 6226 | .Pointer => switch (typed_value.ty.ptrSize()) { | ||
| 6227 | .Slice => {}, | ||
| 6228 | else => { | ||
| 6229 | switch (typed_value.val.tag()) { | ||
| 6230 | .int_u64 => { | ||
| 6231 | return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; | ||
| 6232 | }, | ||
| 6233 | else => {}, | ||
| 6234 | } | ||
| 6235 | }, | ||
| 6236 | }, | ||
| 6237 | .Int => { | ||
| 6238 | const info = typed_value.ty.intInfo(self.target.*); | ||
| 6239 | if (info.bits <= 64) { | ||
| 6240 | const unsigned = switch (info.signedness) { | ||
| 6241 | .signed => blk: { | ||
| 6242 | const signed = typed_value.val.toSignedInt(target); | ||
| 6243 | break :blk @bitCast(u64, signed); | ||
| 6244 | }, | ||
| 6245 | .unsigned => typed_value.val.toUnsignedInt(target), | ||
| 6246 | }; | ||
| 6247 | |||
| 6248 | return MCValue{ .immediate = unsigned }; | ||
| 6249 | } | ||
| 6250 | }, | ||
| 6251 | .Bool => { | ||
| 6252 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | ||
| 6253 | }, | ||
| 6254 | .Optional => { | ||
| 6255 | if (typed_value.ty.isPtrLikeOptional()) { | ||
| 6256 | if (typed_value.val.isNull()) | ||
| 6257 | return MCValue{ .immediate = 0 }; | ||
| 6258 | |||
| 6259 | var buf: Type.Payload.ElemType = undefined; | ||
| 6260 | return self.genTypedValue(.{ | ||
| 6261 | .ty = typed_value.ty.optionalChild(&buf), | ||
| 6262 | .val = typed_value.val, | ||
| 6263 | }); | ||
| 6264 | } else if (typed_value.ty.abiSize(self.target.*) == 1) { | ||
| 6265 | return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; | ||
| 6266 | } | ||
| 6267 | }, | ||
| 6268 | .Enum => { | ||
| 6269 | if (typed_value.val.castTag(.enum_field_index)) |field_index| { | ||
| 6270 | switch (typed_value.ty.tag()) { | ||
| 6271 | .enum_simple => { | ||
| 6272 | return MCValue{ .immediate = field_index.data }; | ||
| 6273 | }, | ||
| 6274 | .enum_full, .enum_nonexhaustive => { | ||
| 6275 | const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; | ||
| 6276 | if (enum_full.values.count() != 0) { | ||
| 6277 | const tag_val = enum_full.values.keys()[field_index.data]; | ||
| 6278 | return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); | ||
| 6279 | } else { | ||
| 6280 | return MCValue{ .immediate = field_index.data }; | ||
| 6281 | } | ||
| 6282 | }, | ||
| 6283 | else => unreachable, | ||
| 6284 | } | ||
| 6285 | } else { | ||
| 6286 | var int_tag_buffer: Type.Payload.Bits = undefined; | ||
| 6287 | const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); | ||
| 6288 | return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); | ||
| 6289 | } | ||
| 6290 | }, | ||
| 6291 | .ErrorSet => { | ||
| 6292 | switch (typed_value.val.tag()) { | ||
| 6293 | .@"error" => { | ||
| 6294 | const err_name = typed_value.val.castTag(.@"error").?.data.name; | ||
| 6295 | const module = self.bin_file.options.module.?; | ||
| 6296 | const global_error_set = module.global_error_set; | ||
| 6297 | const error_index = global_error_set.get(err_name).?; | ||
| 6298 | return MCValue{ .immediate = error_index }; | ||
| 6299 | }, | ||
| 6300 | else => { | ||
| 6301 | // In this case we are rendering an error union which has a 0 bits payload. | ||
| 6302 | return MCValue{ .immediate = 0 }; | ||
| 6303 | }, | ||
| 6304 | } | ||
| 6305 | }, | 6158 | }, |
| 6306 | .ErrorUnion => { | 6159 | .fail => |msg| { |
| 6307 | const error_type = typed_value.ty.errorUnionSet(); | 6160 | self.err_msg = msg; |
| 6308 | const payload_type = typed_value.ty.errorUnionPayload(); | 6161 | return error.CodegenFail; |
| 6309 | |||
| 6310 | const is_pl = typed_value.val.errorUnionIsPayload(); | ||
| 6311 | |||
| 6312 | if (!payload_type.hasRuntimeBitsIgnoreComptime()) { | ||
| 6313 | // We use the error type directly as the type. | ||
| 6314 | const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); | ||
| 6315 | return self.genTypedValue(.{ .ty = error_type, .val = err_val }); | ||
| 6316 | } | ||
| 6317 | |||
| 6318 | return self.lowerUnnamedConst(typed_value); | ||
| 6319 | }, | 6162 | }, |
| 6320 | 6163 | }; | |
| 6321 | .ComptimeInt => unreachable, // semantic analysis prevents this | 6164 | return mcv; |
| 6322 | .ComptimeFloat => unreachable, // semantic analysis prevents this | ||
| 6323 | .Type => unreachable, | ||
| 6324 | .EnumLiteral => unreachable, | ||
| 6325 | .Void => unreachable, | ||
| 6326 | .NoReturn => unreachable, | ||
| 6327 | .Undefined => unreachable, | ||
| 6328 | .Null => unreachable, | ||
| 6329 | .Opaque => unreachable, | ||
| 6330 | |||
| 6331 | else => {}, | ||
| 6332 | } | ||
| 6333 | |||
| 6334 | return self.lowerUnnamedConst(typed_value); | ||
| 6335 | } | 6165 | } |
| 6336 | 6166 | ||
| 6337 | const CallMCValues = struct { | 6167 | const CallMCValues = struct { |
src/arch/arm/CodeGen.zig+29-176| ... | @@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen); | ... | @@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen); |
| 24 | const build_options = @import("build_options"); | 24 | const build_options = @import("build_options"); |
| 25 | 25 | ||
| 26 | const Result = codegen.Result; | 26 | const Result = codegen.Result; |
| 27 | const GenerateSymbolError = codegen.GenerateSymbolError; | 27 | const CodeGenError = codegen.CodeGenError; |
| 28 | const DebugInfoOutput = codegen.DebugInfoOutput; | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 29 | 29 | ||
| 30 | const bits = @import("bits.zig"); | 30 | const bits = @import("bits.zig"); |
| ... | @@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ... | @@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs; |
| 42 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | 42 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; |
| 43 | const gp = abi.RegisterClass.gp; | 43 | const gp = abi.RegisterClass.gp; |
| 44 | 44 | ||
| 45 | const InnerError = error{ | 45 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 46 | OutOfMemory, | ||
| 47 | CodegenFail, | ||
| 48 | OutOfRegisters, | ||
| 49 | }; | ||
| 50 | 46 | ||
| 51 | gpa: Allocator, | 47 | gpa: Allocator, |
| 52 | air: Air, | 48 | air: Air, |
| ... | @@ -343,7 +339,7 @@ pub fn generate( | ... | @@ -343,7 +339,7 @@ pub fn generate( |
| 343 | liveness: Liveness, | 339 | liveness: Liveness, |
| 344 | code: *std.ArrayList(u8), | 340 | code: *std.ArrayList(u8), |
| 345 | debug_output: DebugInfoOutput, | 341 | debug_output: DebugInfoOutput, |
| 346 | ) GenerateSymbolError!Result { | 342 | ) CodeGenError!Result { |
| 347 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 343 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 348 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 344 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 349 | } | 345 | } |
| ... | @@ -721,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -721,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 721 | .bitcast => try self.airBitCast(inst), | 717 | .bitcast => try self.airBitCast(inst), |
| 722 | .block => try self.airBlock(inst), | 718 | .block => try self.airBlock(inst), |
| 723 | .br => try self.airBr(inst), | 719 | .br => try self.airBr(inst), |
| 720 | .trap => try self.airTrap(), | ||
| 724 | .breakpoint => try self.airBreakpoint(), | 721 | .breakpoint => try self.airBreakpoint(), |
| 725 | .ret_addr => try self.airRetAddr(inst), | 722 | .ret_addr => try self.airRetAddr(inst), |
| 726 | .frame_addr => try self.airFrameAddress(inst), | 723 | .frame_addr => try self.airFrameAddress(inst), |
| ... | @@ -4146,6 +4143,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4146,6 +4143,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 4146 | return self.finishAir(inst, result, .{ .none, .none, .none }); | 4143 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 4147 | } | 4144 | } |
| 4148 | 4145 | ||
| 4146 | fn airTrap(self: *Self) !void { | ||
| 4147 | _ = try self.addInst(.{ | ||
| 4148 | .tag = .undefined_instruction, | ||
| 4149 | .data = .{ .nop = {} }, | ||
| 4150 | }); | ||
| 4151 | return self.finishAirBookkeeping(); | ||
| 4152 | } | ||
| 4153 | |||
| 4149 | fn airBreakpoint(self: *Self) !void { | 4154 | fn airBreakpoint(self: *Self) !void { |
| 4150 | _ = try self.addInst(.{ | 4155 | _ = try self.addInst(.{ |
| 4151 | .tag = .bkpt, | 4156 | .tag = .bkpt, |
| ... | @@ -6087,178 +6092,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | ... | @@ -6087,178 +6092,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 6087 | } | 6092 | } |
| 6088 | } | 6093 | } |
| 6089 | 6094 | ||
| 6090 | fn 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 | |||
| 6118 | fn 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 | |||
| 6135 | fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { | 6095 | fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { |
| 6136 | var typed_value = arg_tv; | 6096 | const mcv: MCValue = switch (try codegen.genTypedValue( |
| 6137 | if (typed_value.val.castTag(.runtime_value)) |rt| { | 6097 | self.bin_file, |
| 6138 | typed_value.val = rt.data; | 6098 | self.src_loc, |
| 6139 | } | 6099 | arg_tv, |
| 6140 | log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); | 6100 | self.mod_fn.owner_decl, |
| 6141 | if (typed_value.val.isUndef()) | 6101 | )) { |
| 6142 | return MCValue{ .undef = {} }; | 6102 | .mcv => |mcv| switch (mcv) { |
| 6143 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 6103 | .none => .none, |
| 6144 | 6104 | .undef => .undef, | |
| 6145 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 6105 | .linker_load => unreachable, // TODO |
| 6146 | return self.lowerDeclRef(typed_value, payload.data); | 6106 | .immediate => |imm| .{ .immediate = @truncate(u32, imm) }, |
| 6147 | } | 6107 | .memory => |addr| .{ .memory = addr }, |
| 6148 | if (typed_value.val.castTag(.decl_ref_mut)) |payload| { | ||
| 6149 | return self.lowerDeclRef(typed_value, payload.data.decl_index); | ||
| 6150 | } | ||
| 6151 | const target = self.target.*; | ||
| 6152 | |||
| 6153 | switch (typed_value.ty.zigTypeTag()) { | ||
| 6154 | .Pointer => switch (typed_value.ty.ptrSize()) { | ||
| 6155 | .Slice => {}, | ||
| 6156 | else => { | ||
| 6157 | switch (typed_value.val.tag()) { | ||
| 6158 | .int_u64 => { | ||
| 6159 | return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt(target)) }; | ||
| 6160 | }, | ||
| 6161 | else => {}, | ||
| 6162 | } | ||
| 6163 | }, | ||
| 6164 | }, | ||
| 6165 | .Int => { | ||
| 6166 | const info = typed_value.ty.intInfo(self.target.*); | ||
| 6167 | if (info.bits <= ptr_bits) { | ||
| 6168 | const unsigned = switch (info.signedness) { | ||
| 6169 | .signed => blk: { | ||
| 6170 | const signed = @intCast(i32, typed_value.val.toSignedInt(target)); | ||
| 6171 | break :blk @bitCast(u32, signed); | ||
| 6172 | }, | ||
| 6173 | .unsigned => @intCast(u32, typed_value.val.toUnsignedInt(target)), | ||
| 6174 | }; | ||
| 6175 | |||
| 6176 | return MCValue{ .immediate = unsigned }; | ||
| 6177 | } else { | ||
| 6178 | return self.lowerUnnamedConst(typed_value); | ||
| 6179 | } | ||
| 6180 | }, | ||
| 6181 | .Bool => { | ||
| 6182 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | ||
| 6183 | }, | ||
| 6184 | .Optional => { | ||
| 6185 | if (typed_value.ty.isPtrLikeOptional()) { | ||
| 6186 | if (typed_value.val.isNull()) | ||
| 6187 | return MCValue{ .immediate = 0 }; | ||
| 6188 | |||
| 6189 | var buf: Type.Payload.ElemType = undefined; | ||
| 6190 | return self.genTypedValue(.{ | ||
| 6191 | .ty = typed_value.ty.optionalChild(&buf), | ||
| 6192 | .val = typed_value.val, | ||
| 6193 | }); | ||
| 6194 | } else if (typed_value.ty.abiSize(self.target.*) == 1) { | ||
| 6195 | return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; | ||
| 6196 | } | ||
| 6197 | }, | ||
| 6198 | .Enum => { | ||
| 6199 | if (typed_value.val.castTag(.enum_field_index)) |field_index| { | ||
| 6200 | switch (typed_value.ty.tag()) { | ||
| 6201 | .enum_simple => { | ||
| 6202 | return MCValue{ .immediate = field_index.data }; | ||
| 6203 | }, | ||
| 6204 | .enum_full, .enum_nonexhaustive => { | ||
| 6205 | const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; | ||
| 6206 | if (enum_full.values.count() != 0) { | ||
| 6207 | const tag_val = enum_full.values.keys()[field_index.data]; | ||
| 6208 | return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); | ||
| 6209 | } else { | ||
| 6210 | return MCValue{ .immediate = field_index.data }; | ||
| 6211 | } | ||
| 6212 | }, | ||
| 6213 | else => unreachable, | ||
| 6214 | } | ||
| 6215 | } else { | ||
| 6216 | var int_tag_buffer: Type.Payload.Bits = undefined; | ||
| 6217 | const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); | ||
| 6218 | return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); | ||
| 6219 | } | ||
| 6220 | }, | 6108 | }, |
| 6221 | .ErrorSet => { | 6109 | .fail => |msg| { |
| 6222 | switch (typed_value.val.tag()) { | 6110 | self.err_msg = msg; |
| 6223 | .@"error" => { | 6111 | return error.CodegenFail; |
| 6224 | const err_name = typed_value.val.castTag(.@"error").?.data.name; | ||
| 6225 | const module = self.bin_file.options.module.?; | ||
| 6226 | const global_error_set = module.global_error_set; | ||
| 6227 | const error_index = global_error_set.get(err_name).?; | ||
| 6228 | return MCValue{ .immediate = error_index }; | ||
| 6229 | }, | ||
| 6230 | else => { | ||
| 6231 | // In this case we are rendering an error union which has a 0 bits payload. | ||
| 6232 | return MCValue{ .immediate = 0 }; | ||
| 6233 | }, | ||
| 6234 | } | ||
| 6235 | }, | ||
| 6236 | .ErrorUnion => { | ||
| 6237 | const error_type = typed_value.ty.errorUnionSet(); | ||
| 6238 | const payload_type = typed_value.ty.errorUnionPayload(); | ||
| 6239 | const is_pl = typed_value.val.errorUnionIsPayload(); | ||
| 6240 | |||
| 6241 | if (!payload_type.hasRuntimeBitsIgnoreComptime()) { | ||
| 6242 | // We use the error type directly as the type. | ||
| 6243 | const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); | ||
| 6244 | return self.genTypedValue(.{ .ty = error_type, .val = err_val }); | ||
| 6245 | } | ||
| 6246 | }, | 6112 | }, |
| 6247 | 6113 | }; | |
| 6248 | .ComptimeInt => unreachable, // semantic analysis prevents this | 6114 | return mcv; |
| 6249 | .ComptimeFloat => unreachable, // semantic analysis prevents this | ||
| 6250 | .Type => unreachable, | ||
| 6251 | .EnumLiteral => unreachable, | ||
| 6252 | .Void => unreachable, | ||
| 6253 | .NoReturn => unreachable, | ||
| 6254 | .Undefined => unreachable, | ||
| 6255 | .Null => unreachable, | ||
| 6256 | .Opaque => unreachable, | ||
| 6257 | |||
| 6258 | else => {}, | ||
| 6259 | } | ||
| 6260 | |||
| 6261 | return self.lowerUnnamedConst(typed_value); | ||
| 6262 | } | 6115 | } |
| 6263 | 6116 | ||
| 6264 | const CallMCValues = struct { | 6117 | const CallMCValues = struct { |
src/arch/arm/Emit.zig+7-2| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | //! This file contains the functionality for lowering AArch64 MIR into | 1 | //! This file contains the functionality for lowering AArch32 MIR into |
| 2 | //! machine code | 2 | //! machine code |
| 3 | 3 | ||
| 4 | const Emit = @This(); | 4 | const Emit = @This(); |
| ... | @@ -15,7 +15,7 @@ const Target = std.Target; | ... | @@ -15,7 +15,7 @@ const Target = std.Target; |
| 15 | const assert = std.debug.assert; | 15 | const assert = std.debug.assert; |
| 16 | const Instruction = bits.Instruction; | 16 | const Instruction = bits.Instruction; |
| 17 | const Register = bits.Register; | 17 | const Register = bits.Register; |
| 18 | const log = std.log.scoped(.aarch64_emit); | 18 | const log = std.log.scoped(.aarch32_emit); |
| 19 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 19 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 20 | const CodeGen = @import("CodeGen.zig"); | 20 | const CodeGen = @import("CodeGen.zig"); |
| 21 | 21 | ||
| ... | @@ -100,6 +100,7 @@ pub fn emitMir( | ... | @@ -100,6 +100,7 @@ pub fn emitMir( |
| 100 | 100 | ||
| 101 | .b => try emit.mirBranch(inst), | 101 | .b => try emit.mirBranch(inst), |
| 102 | 102 | ||
| 103 | .undefined_instruction => try emit.mirUndefinedInstruction(), | ||
| 103 | .bkpt => try emit.mirExceptionGeneration(inst), | 104 | .bkpt => try emit.mirExceptionGeneration(inst), |
| 104 | 105 | ||
| 105 | .blx => try emit.mirBranchExchange(inst), | 106 | .blx => try emit.mirBranchExchange(inst), |
| ... | @@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 494 | } | 495 | } |
| 495 | } | 496 | } |
| 496 | 497 | ||
| 498 | fn mirUndefinedInstruction(emit: *Emit) !void { | ||
| 499 | try emit.writeInstruction(Instruction.undefinedInstruction()); | ||
| 500 | } | ||
| 501 | |||
| 497 | fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void { | 502 | fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 498 | const tag = emit.mir.instructions.items(.tag)[inst]; | 503 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 499 | const imm16 = emit.mir.instructions.items(.data)[inst].imm16; | 504 | const imm16 = emit.mir.instructions.items(.data)[inst].imm16; |
src/arch/arm/Mir.zig+2| ... | @@ -35,6 +35,8 @@ pub const Inst = struct { | ... | @@ -35,6 +35,8 @@ pub const Inst = struct { |
| 35 | asr, | 35 | asr, |
| 36 | /// Branch | 36 | /// Branch |
| 37 | b, | 37 | b, |
| 38 | /// Undefined instruction | ||
| 39 | undefined_instruction, | ||
| 38 | /// Breakpoint | 40 | /// Breakpoint |
| 39 | bkpt, | 41 | bkpt, |
| 40 | /// Branch with Link and Exchange | 42 | /// Branch with Link and Exchange |
src/arch/arm/bits.zig+11| ... | @@ -307,6 +307,9 @@ pub const Instruction = union(enum) { | ... | @@ -307,6 +307,9 @@ pub const Instruction = union(enum) { |
| 307 | fixed: u4 = 0b1111, | 307 | fixed: u4 = 0b1111, |
| 308 | cond: u4, | 308 | cond: u4, |
| 309 | }, | 309 | }, |
| 310 | undefined_instruction: packed struct { | ||
| 311 | imm32: u32 = 0xe7ffdefe, | ||
| 312 | }, | ||
| 310 | breakpoint: packed struct { | 313 | breakpoint: packed struct { |
| 311 | imm4: u4, | 314 | imm4: u4, |
| 312 | fixed_1: u4 = 0b0111, | 315 | fixed_1: u4 = 0b0111, |
| ... | @@ -613,6 +616,7 @@ pub const Instruction = union(enum) { | ... | @@ -613,6 +616,7 @@ pub const Instruction = union(enum) { |
| 613 | .branch => |v| @bitCast(u32, v), | 616 | .branch => |v| @bitCast(u32, v), |
| 614 | .branch_exchange => |v| @bitCast(u32, v), | 617 | .branch_exchange => |v| @bitCast(u32, v), |
| 615 | .supervisor_call => |v| @bitCast(u32, v), | 618 | .supervisor_call => |v| @bitCast(u32, v), |
| 619 | .undefined_instruction => |v| v.imm32, | ||
| 616 | .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), | 620 | .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), |
| 617 | }; | 621 | }; |
| 618 | } | 622 | } |
| ... | @@ -890,6 +894,13 @@ pub const Instruction = union(enum) { | ... | @@ -890,6 +894,13 @@ pub const Instruction = union(enum) { |
| 890 | }; | 894 | }; |
| 891 | } | 895 | } |
| 892 | 896 | ||
| 897 | // This instruction has no official mnemonic equivalent so it is public as-is. | ||
| 898 | pub fn undefinedInstruction() Instruction { | ||
| 899 | return Instruction{ | ||
| 900 | .undefined_instruction = .{}, | ||
| 901 | }; | ||
| 902 | } | ||
| 903 | |||
| 893 | fn breakpoint(imm: u16) Instruction { | 904 | fn breakpoint(imm: u16) Instruction { |
| 894 | return Instruction{ | 905 | return Instruction{ |
| 895 | .breakpoint = .{ | 906 | .breakpoint = .{ |
src/arch/riscv64/CodeGen.zig+32-145| ... | @@ -21,10 +21,11 @@ const DW = std.dwarf; | ... | @@ -21,10 +21,11 @@ const DW = std.dwarf; |
| 21 | const leb128 = std.leb; | 21 | const leb128 = std.leb; |
| 22 | const log = std.log.scoped(.codegen); | 22 | const log = std.log.scoped(.codegen); |
| 23 | const build_options = @import("build_options"); | 23 | const build_options = @import("build_options"); |
| 24 | const codegen = @import("../../codegen.zig"); | ||
| 24 | 25 | ||
| 25 | const Result = @import("../../codegen.zig").Result; | 26 | const CodeGenError = codegen.CodeGenError; |
| 26 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 27 | const Result = codegen.Result; |
| 27 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 28 | 29 | ||
| 29 | const bits = @import("bits.zig"); | 30 | const bits = @import("bits.zig"); |
| 30 | const abi = @import("abi.zig"); | 31 | const abi = @import("abi.zig"); |
| ... | @@ -35,11 +36,7 @@ const Instruction = abi.Instruction; | ... | @@ -35,11 +36,7 @@ const Instruction = abi.Instruction; |
| 35 | const callee_preserved_regs = abi.callee_preserved_regs; | 36 | const callee_preserved_regs = abi.callee_preserved_regs; |
| 36 | const gp = abi.RegisterClass.gp; | 37 | const gp = abi.RegisterClass.gp; |
| 37 | 38 | ||
| 38 | const InnerError = error{ | 39 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 39 | OutOfMemory, | ||
| 40 | CodegenFail, | ||
| 41 | OutOfRegisters, | ||
| 42 | }; | ||
| 43 | 40 | ||
| 44 | gpa: Allocator, | 41 | gpa: Allocator, |
| 45 | air: Air, | 42 | air: Air, |
| ... | @@ -225,7 +222,7 @@ pub fn generate( | ... | @@ -225,7 +222,7 @@ pub fn generate( |
| 225 | liveness: Liveness, | 222 | liveness: Liveness, |
| 226 | code: *std.ArrayList(u8), | 223 | code: *std.ArrayList(u8), |
| 227 | debug_output: DebugInfoOutput, | 224 | debug_output: DebugInfoOutput, |
| 228 | ) GenerateSymbolError!Result { | 225 | ) CodeGenError!Result { |
| 229 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 226 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 230 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 227 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 231 | } | 228 | } |
| ... | @@ -550,6 +547,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -550,6 +547,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 550 | .bitcast => try self.airBitCast(inst), | 547 | .bitcast => try self.airBitCast(inst), |
| 551 | .block => try self.airBlock(inst), | 548 | .block => try self.airBlock(inst), |
| 552 | .br => try self.airBr(inst), | 549 | .br => try self.airBr(inst), |
| 550 | .trap => try self.airTrap(), | ||
| 553 | .breakpoint => try self.airBreakpoint(), | 551 | .breakpoint => try self.airBreakpoint(), |
| 554 | .ret_addr => try self.airRetAddr(inst), | 552 | .ret_addr => try self.airRetAddr(inst), |
| 555 | .frame_addr => try self.airFrameAddress(inst), | 553 | .frame_addr => try self.airFrameAddress(inst), |
| ... | @@ -1652,6 +1650,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1652,6 +1650,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1652 | return self.finishAir(inst, mcv, .{ .none, .none, .none }); | 1650 | return self.finishAir(inst, mcv, .{ .none, .none, .none }); |
| 1653 | } | 1651 | } |
| 1654 | 1652 | ||
| 1653 | fn airTrap(self: *Self) !void { | ||
| 1654 | _ = try self.addInst(.{ | ||
| 1655 | .tag = .unimp, | ||
| 1656 | .data = .{ .nop = {} }, | ||
| 1657 | }); | ||
| 1658 | return self.finishAirBookkeeping(); | ||
| 1659 | } | ||
| 1660 | |||
| 1655 | fn airBreakpoint(self: *Self) !void { | 1661 | fn airBreakpoint(self: *Self) !void { |
| 1656 | _ = try self.addInst(.{ | 1662 | _ = try self.addInst(.{ |
| 1657 | .tag = .ebreak, | 1663 | .tag = .ebreak, |
| ... | @@ -2552,145 +2558,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | ... | @@ -2552,145 +2558,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 2552 | } | 2558 | } |
| 2553 | } | 2559 | } |
| 2554 | 2560 | ||
| 2555 | fn 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 | |||
| 2580 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | 2561 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 2581 | if (typed_value.val.isUndef()) | 2562 | const mcv: MCValue = switch (try codegen.genTypedValue( |
| 2582 | return MCValue{ .undef = {} }; | 2563 | self.bin_file, |
| 2583 | 2564 | self.src_loc, | |
| 2584 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 2565 | typed_value, |
| 2585 | return self.lowerDeclRef(typed_value, payload.data); | 2566 | self.mod_fn.owner_decl, |
| 2586 | } | 2567 | )) { |
| 2587 | if (typed_value.val.castTag(.decl_ref_mut)) |payload| { | 2568 | .mcv => |mcv| switch (mcv) { |
| 2588 | return self.lowerDeclRef(typed_value, payload.data.decl_index); | 2569 | .none => .none, |
| 2589 | } | 2570 | .undef => .undef, |
| 2590 | const target = self.target.*; | 2571 | .linker_load => unreachable, // TODO |
| 2591 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 2572 | .immediate => |imm| .{ .immediate = imm }, |
| 2592 | switch (typed_value.ty.zigTypeTag()) { | 2573 | .memory => |addr| .{ .memory = addr }, |
| 2593 | .Pointer => switch (typed_value.ty.ptrSize()) { | ||
| 2594 | .Slice => { | ||
| 2595 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | ||
| 2596 | const ptr_type = typed_value.ty.slicePtrFieldType(&buf); | ||
| 2597 | const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); | ||
| 2598 | const mod = self.bin_file.options.module.?; | ||
| 2599 | const slice_len = typed_value.val.sliceLen(mod); | ||
| 2600 | // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean | ||
| 2601 | // the Sema code needs to use anonymous Decls or alloca instructions to store data. | ||
| 2602 | const ptr_imm = ptr_mcv.memory; | ||
| 2603 | _ = slice_len; | ||
| 2604 | _ = ptr_imm; | ||
| 2605 | // We need more general support for const data being stored in memory to make this work. | ||
| 2606 | return self.fail("TODO codegen for const slices", .{}); | ||
| 2607 | }, | ||
| 2608 | else => { | ||
| 2609 | if (typed_value.val.tag() == .int_u64) { | ||
| 2610 | return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; | ||
| 2611 | } | ||
| 2612 | return self.fail("TODO codegen more kinds of const pointers", .{}); | ||
| 2613 | }, | ||
| 2614 | }, | ||
| 2615 | .Int => { | ||
| 2616 | const info = typed_value.ty.intInfo(self.target.*); | ||
| 2617 | if (info.bits > ptr_bits or info.signedness == .signed) { | ||
| 2618 | return self.fail("TODO const int bigger than ptr and signed int", .{}); | ||
| 2619 | } | ||
| 2620 | return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; | ||
| 2621 | }, | ||
| 2622 | .Bool => { | ||
| 2623 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | ||
| 2624 | }, | ||
| 2625 | .ComptimeInt => unreachable, // semantic analysis prevents this | ||
| 2626 | .ComptimeFloat => unreachable, // semantic analysis prevents this | ||
| 2627 | .Optional => { | ||
| 2628 | if (typed_value.ty.isPtrLikeOptional()) { | ||
| 2629 | if (typed_value.val.isNull()) | ||
| 2630 | return MCValue{ .immediate = 0 }; | ||
| 2631 | |||
| 2632 | var buf: Type.Payload.ElemType = undefined; | ||
| 2633 | return self.genTypedValue(.{ | ||
| 2634 | .ty = typed_value.ty.optionalChild(&buf), | ||
| 2635 | .val = typed_value.val, | ||
| 2636 | }); | ||
| 2637 | } else if (typed_value.ty.abiSize(self.target.*) == 1) { | ||
| 2638 | return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; | ||
| 2639 | } | ||
| 2640 | return self.fail("TODO non pointer optionals", .{}); | ||
| 2641 | }, | 2574 | }, |
| 2642 | .Enum => { | 2575 | .fail => |msg| { |
| 2643 | if (typed_value.val.castTag(.enum_field_index)) |field_index| { | 2576 | self.err_msg = msg; |
| 2644 | switch (typed_value.ty.tag()) { | 2577 | return error.CodegenFail; |
| 2645 | .enum_simple => { | ||
| 2646 | return MCValue{ .immediate = field_index.data }; | ||
| 2647 | }, | ||
| 2648 | .enum_full, .enum_nonexhaustive => { | ||
| 2649 | const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; | ||
| 2650 | if (enum_full.values.count() != 0) { | ||
| 2651 | const tag_val = enum_full.values.keys()[field_index.data]; | ||
| 2652 | return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); | ||
| 2653 | } else { | ||
| 2654 | return MCValue{ .immediate = field_index.data }; | ||
| 2655 | } | ||
| 2656 | }, | ||
| 2657 | else => unreachable, | ||
| 2658 | } | ||
| 2659 | } else { | ||
| 2660 | var int_tag_buffer: Type.Payload.Bits = undefined; | ||
| 2661 | const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); | ||
| 2662 | return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); | ||
| 2663 | } | ||
| 2664 | }, | 2578 | }, |
| 2665 | .ErrorSet => { | 2579 | }; |
| 2666 | switch (typed_value.val.tag()) { | 2580 | return mcv; |
| 2667 | .@"error" => { | ||
| 2668 | const err_name = typed_value.val.castTag(.@"error").?.data.name; | ||
| 2669 | const module = self.bin_file.options.module.?; | ||
| 2670 | const global_error_set = module.global_error_set; | ||
| 2671 | const error_index = global_error_set.get(err_name).?; | ||
| 2672 | return MCValue{ .immediate = error_index }; | ||
| 2673 | }, | ||
| 2674 | else => { | ||
| 2675 | // In this case we are rendering an error union which has a 0 bits payload. | ||
| 2676 | return MCValue{ .immediate = 0 }; | ||
| 2677 | }, | ||
| 2678 | } | ||
| 2679 | }, | ||
| 2680 | .ErrorUnion => { | ||
| 2681 | const error_type = typed_value.ty.errorUnionSet(); | ||
| 2682 | const payload_type = typed_value.ty.errorUnionPayload(); | ||
| 2683 | const sub_val = typed_value.val.castTag(.eu_payload).?.data; | ||
| 2684 | |||
| 2685 | if (!payload_type.hasRuntimeBits()) { | ||
| 2686 | // We use the error type directly as the type. | ||
| 2687 | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty.fmtDebug()}); | ||
| 2691 | }, | ||
| 2692 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), | ||
| 2693 | } | ||
| 2694 | } | 2581 | } |
| 2695 | 2582 | ||
| 2696 | const CallMCValues = struct { | 2583 | const CallMCValues = struct { |
src/arch/riscv64/Emit.zig+2| ... | @@ -51,6 +51,7 @@ pub fn emitMir( | ... | @@ -51,6 +51,7 @@ pub fn emitMir( |
| 51 | 51 | ||
| 52 | .ebreak => try emit.mirSystem(inst), | 52 | .ebreak => try emit.mirSystem(inst), |
| 53 | .ecall => try emit.mirSystem(inst), | 53 | .ecall => try emit.mirSystem(inst), |
| 54 | .unimp => try emit.mirSystem(inst), | ||
| 54 | 55 | ||
| 55 | .dbg_line => try emit.mirDbgLine(inst), | 56 | .dbg_line => try emit.mirDbgLine(inst), |
| 56 | 57 | ||
| ... | @@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 153 | switch (tag) { | 154 | switch (tag) { |
| 154 | .ebreak => try emit.writeInstruction(Instruction.ebreak), | 155 | .ebreak => try emit.writeInstruction(Instruction.ebreak), |
| 155 | .ecall => try emit.writeInstruction(Instruction.ecall), | 156 | .ecall => try emit.writeInstruction(Instruction.ecall), |
| 157 | .unimp => try emit.writeInstruction(Instruction.unimp), | ||
| 156 | else => unreachable, | 158 | else => unreachable, |
| 157 | } | 159 | } |
| 158 | } | 160 | } |
src/arch/riscv64/Mir.zig+1| ... | @@ -32,6 +32,7 @@ pub const Inst = struct { | ... | @@ -32,6 +32,7 @@ pub const Inst = struct { |
| 32 | dbg_epilogue_begin, | 32 | dbg_epilogue_begin, |
| 33 | /// Pseudo-instruction: Update debug line | 33 | /// Pseudo-instruction: Update debug line |
| 34 | dbg_line, | 34 | dbg_line, |
| 35 | unimp, | ||
| 35 | ebreak, | 36 | ebreak, |
| 36 | ecall, | 37 | ecall, |
| 37 | jalr, | 38 | jalr, |
src/arch/riscv64/bits.zig+1| ... | @@ -380,6 +380,7 @@ pub const Instruction = union(enum) { | ... | @@ -380,6 +380,7 @@ pub const Instruction = union(enum) { |
| 380 | 380 | ||
| 381 | pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); | 381 | pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); |
| 382 | pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); | 382 | pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); |
| 383 | pub const unimp = iType(0, 0, .zero, .zero, 0); | ||
| 383 | }; | 384 | }; |
| 384 | 385 | ||
| 385 | pub const Register = enum(u6) { | 386 | pub const Register = enum(u6) { |
src/arch/sparc64/CodeGen.zig+36-154| ... | @@ -19,7 +19,7 @@ const Mir = @import("Mir.zig"); | ... | @@ -19,7 +19,7 @@ const Mir = @import("Mir.zig"); |
| 19 | const Emit = @import("Emit.zig"); | 19 | const Emit = @import("Emit.zig"); |
| 20 | const Liveness = @import("../../Liveness.zig"); | 20 | const Liveness = @import("../../Liveness.zig"); |
| 21 | const Type = @import("../../type.zig").Type; | 21 | const Type = @import("../../type.zig").Type; |
| 22 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 22 | const CodeGenError = codegen.CodeGenError; |
| 23 | const Result = @import("../../codegen.zig").Result; | 23 | const Result = @import("../../codegen.zig").Result; |
| 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 25 | 25 | ||
| ... | @@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp; | ... | @@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp; |
| 38 | 38 | ||
| 39 | const Self = @This(); | 39 | const Self = @This(); |
| 40 | 40 | ||
| 41 | const InnerError = error{ | 41 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 42 | OutOfMemory, | ||
| 43 | CodegenFail, | ||
| 44 | OutOfRegisters, | ||
| 45 | }; | ||
| 46 | 42 | ||
| 47 | const RegisterView = enum(u1) { | 43 | const RegisterView = enum(u1) { |
| 48 | caller, | 44 | caller, |
| ... | @@ -265,7 +261,7 @@ pub fn generate( | ... | @@ -265,7 +261,7 @@ pub fn generate( |
| 265 | liveness: Liveness, | 261 | liveness: Liveness, |
| 266 | code: *std.ArrayList(u8), | 262 | code: *std.ArrayList(u8), |
| 267 | debug_output: DebugInfoOutput, | 263 | debug_output: DebugInfoOutput, |
| 268 | ) GenerateSymbolError!Result { | 264 | ) CodeGenError!Result { |
| 269 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 265 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 270 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 266 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 271 | } | 267 | } |
| ... | @@ -566,6 +562,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -566,6 +562,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 566 | .bitcast => try self.airBitCast(inst), | 562 | .bitcast => try self.airBitCast(inst), |
| 567 | .block => try self.airBlock(inst), | 563 | .block => try self.airBlock(inst), |
| 568 | .br => try self.airBr(inst), | 564 | .br => try self.airBr(inst), |
| 565 | .trap => try self.airTrap(), | ||
| 569 | .breakpoint => try self.airBreakpoint(), | 566 | .breakpoint => try self.airBreakpoint(), |
| 570 | .ret_addr => @panic("TODO try self.airRetAddr(inst)"), | 567 | .ret_addr => @panic("TODO try self.airRetAddr(inst)"), |
| 571 | .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), | 568 | .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), |
| ... | @@ -1160,6 +1157,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1160,6 +1157,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1160 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); | 1157 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| 1161 | } | 1158 | } |
| 1162 | 1159 | ||
| 1160 | fn 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 | |||
| 1163 | fn airBreakpoint(self: *Self) !void { | 1175 | fn airBreakpoint(self: *Self) !void { |
| 1164 | // ta 0x01 | 1176 | // ta 0x01 |
| 1165 | _ = try self.addInst(.{ | 1177 | _ = try self.addInst(.{ |
| ... | @@ -3898,133 +3910,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t | ... | @@ -3898,133 +3910,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t |
| 3898 | } | 3910 | } |
| 3899 | 3911 | ||
| 3900 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | 3912 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3901 | var tv = typed_value; | 3913 | const mcv: MCValue = switch (try codegen.genTypedValue( |
| 3902 | log.debug("genTypedValue: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); | 3914 | self.bin_file, |
| 3903 | 3915 | self.src_loc, | |
| 3904 | if (tv.val.castTag(.runtime_value)) |rt| { | 3916 | typed_value, |
| 3905 | tv.val = rt.data; | 3917 | self.mod_fn.owner_decl, |
| 3906 | } | 3918 | )) { |
| 3907 | 3919 | .mcv => |mcv| switch (mcv) { | |
| 3908 | if (tv.val.isUndef()) | 3920 | .none => .none, |
| 3909 | return MCValue{ .undef = {} }; | 3921 | .undef => .undef, |
| 3910 | 3922 | .linker_load => unreachable, // TODO | |
| 3911 | if (tv.val.castTag(.decl_ref)) |payload| { | 3923 | .immediate => |imm| .{ .immediate = imm }, |
| 3912 | return self.lowerDeclRef(tv, payload.data); | 3924 | .memory => |addr| .{ .memory = addr }, |
| 3913 | } | ||
| 3914 | if (tv.val.castTag(.decl_ref_mut)) |payload| { | ||
| 3915 | return self.lowerDeclRef(tv, payload.data.decl_index); | ||
| 3916 | } | ||
| 3917 | const target = self.target.*; | ||
| 3918 | |||
| 3919 | switch (tv.ty.zigTypeTag()) { | ||
| 3920 | .Pointer => switch (tv.ty.ptrSize()) { | ||
| 3921 | .Slice => {}, | ||
| 3922 | else => { | ||
| 3923 | switch (tv.val.tag()) { | ||
| 3924 | .int_u64 => { | ||
| 3925 | return MCValue{ .immediate = tv.val.toUnsignedInt(target) }; | ||
| 3926 | }, | ||
| 3927 | else => {}, | ||
| 3928 | } | ||
| 3929 | }, | ||
| 3930 | }, | ||
| 3931 | .Bool => { | ||
| 3932 | return MCValue{ .immediate = @boolToInt(tv.val.toBool()) }; | ||
| 3933 | }, | ||
| 3934 | .Int => { | ||
| 3935 | const info = tv.ty.intInfo(self.target.*); | ||
| 3936 | if (info.bits <= 64) { | ||
| 3937 | const unsigned = switch (info.signedness) { | ||
| 3938 | .signed => blk: { | ||
| 3939 | const signed = tv.val.toSignedInt(target); | ||
| 3940 | break :blk @bitCast(u64, signed); | ||
| 3941 | }, | ||
| 3942 | .unsigned => tv.val.toUnsignedInt(target), | ||
| 3943 | }; | ||
| 3944 | |||
| 3945 | return MCValue{ .immediate = unsigned }; | ||
| 3946 | } else { | ||
| 3947 | return self.fail("TODO implement int genTypedValue of > 64 bits", .{}); | ||
| 3948 | } | ||
| 3949 | }, | 3925 | }, |
| 3950 | .Optional => { | 3926 | .fail => |msg| { |
| 3951 | if (tv.ty.isPtrLikeOptional()) { | 3927 | self.err_msg = msg; |
| 3952 | if (tv.val.isNull()) | 3928 | return error.CodegenFail; |
| 3953 | return MCValue{ .immediate = 0 }; | ||
| 3954 | |||
| 3955 | var buf: Type.Payload.ElemType = undefined; | ||
| 3956 | return self.genTypedValue(.{ | ||
| 3957 | .ty = tv.ty.optionalChild(&buf), | ||
| 3958 | .val = tv.val, | ||
| 3959 | }); | ||
| 3960 | } else if (tv.ty.abiSize(self.target.*) == 1) { | ||
| 3961 | return MCValue{ .immediate = @boolToInt(tv.val.isNull()) }; | ||
| 3962 | } | ||
| 3963 | }, | ||
| 3964 | .Enum => { | ||
| 3965 | if (tv.val.castTag(.enum_field_index)) |field_index| { | ||
| 3966 | switch (tv.ty.tag()) { | ||
| 3967 | .enum_simple => { | ||
| 3968 | return MCValue{ .immediate = field_index.data }; | ||
| 3969 | }, | ||
| 3970 | .enum_full, .enum_nonexhaustive => { | ||
| 3971 | const enum_full = tv.ty.cast(Type.Payload.EnumFull).?.data; | ||
| 3972 | if (enum_full.values.count() != 0) { | ||
| 3973 | const tag_val = enum_full.values.keys()[field_index.data]; | ||
| 3974 | return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); | ||
| 3975 | } else { | ||
| 3976 | return MCValue{ .immediate = field_index.data }; | ||
| 3977 | } | ||
| 3978 | }, | ||
| 3979 | else => unreachable, | ||
| 3980 | } | ||
| 3981 | } else { | ||
| 3982 | var int_tag_buffer: Type.Payload.Bits = undefined; | ||
| 3983 | const int_tag_ty = tv.ty.intTagType(&int_tag_buffer); | ||
| 3984 | return self.genTypedValue(.{ .ty = int_tag_ty, .val = tv.val }); | ||
| 3985 | } | ||
| 3986 | }, | ||
| 3987 | .ErrorSet => { | ||
| 3988 | const err_name = tv.val.castTag(.@"error").?.data.name; | ||
| 3989 | const module = self.bin_file.options.module.?; | ||
| 3990 | const global_error_set = module.global_error_set; | ||
| 3991 | const error_index = global_error_set.get(err_name).?; | ||
| 3992 | return MCValue{ .immediate = error_index }; | ||
| 3993 | }, | ||
| 3994 | .ErrorUnion => { | ||
| 3995 | const error_type = tv.ty.errorUnionSet(); | ||
| 3996 | const payload_type = tv.ty.errorUnionPayload(); | ||
| 3997 | |||
| 3998 | if (tv.val.castTag(.eu_payload)) |pl| { | ||
| 3999 | if (!payload_type.hasRuntimeBits()) { | ||
| 4000 | // We use the error type directly as the type. | ||
| 4001 | return MCValue{ .immediate = 0 }; | ||
| 4002 | } | ||
| 4003 | |||
| 4004 | _ = pl; | ||
| 4005 | return self.fail("TODO implement error union const of type '{}' (non-error)", .{tv.ty.fmtDebug()}); | ||
| 4006 | } else { | ||
| 4007 | if (!payload_type.hasRuntimeBits()) { | ||
| 4008 | // We use the error type directly as the type. | ||
| 4009 | return self.genTypedValue(.{ .ty = error_type, .val = tv.val }); | ||
| 4010 | } | ||
| 4011 | |||
| 4012 | return self.fail("TODO implement error union const of type '{}' (error)", .{tv.ty.fmtDebug()}); | ||
| 4013 | } | ||
| 4014 | }, | 3929 | }, |
| 4015 | .ComptimeInt => unreachable, // semantic analysis prevents this | 3930 | }; |
| 4016 | .ComptimeFloat => unreachable, // semantic analysis prevents this | 3931 | return mcv; |
| 4017 | .Type => unreachable, | ||
| 4018 | .EnumLiteral => unreachable, | ||
| 4019 | .Void => unreachable, | ||
| 4020 | .NoReturn => unreachable, | ||
| 4021 | .Undefined => unreachable, | ||
| 4022 | .Null => unreachable, | ||
| 4023 | .Opaque => unreachable, | ||
| 4024 | else => {}, | ||
| 4025 | } | ||
| 4026 | |||
| 4027 | return self.fail("TODO implement const of type '{}'", .{tv.ty.fmtDebug()}); | ||
| 4028 | } | 3932 | } |
| 4029 | 3933 | ||
| 4030 | fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | 3934 | fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| ... | @@ -4200,28 +4104,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -4200,28 +4104,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 4200 | } | 4104 | } |
| 4201 | } | 4105 | } |
| 4202 | 4106 | ||
| 4203 | fn 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 | |||
| 4225 | fn minMax( | 4107 | fn minMax( |
| 4226 | self: *Self, | 4108 | self: *Self, |
| 4227 | tag: Air.Inst.Tag, | 4109 | tag: Air.Inst.Tag, |
src/arch/wasm/CodeGen.zig+8-3| ... | @@ -733,8 +733,6 @@ const InnerError = error{ | ... | @@ -733,8 +733,6 @@ const InnerError = error{ |
| 733 | OutOfMemory, | 733 | OutOfMemory, |
| 734 | /// An error occurred when trying to lower AIR to MIR. | 734 | /// An error occurred when trying to lower AIR to MIR. |
| 735 | CodegenFail, | 735 | CodegenFail, |
| 736 | /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed | ||
| 737 | AnalysisFail, | ||
| 738 | /// Compiler implementation could not handle a large integer. | 736 | /// Compiler implementation could not handle a large integer. |
| 739 | Overflow, | 737 | Overflow, |
| 740 | }; | 738 | }; |
| ... | @@ -1164,7 +1162,7 @@ pub fn generate( | ... | @@ -1164,7 +1162,7 @@ pub fn generate( |
| 1164 | liveness: Liveness, | 1162 | liveness: Liveness, |
| 1165 | code: *std.ArrayList(u8), | 1163 | code: *std.ArrayList(u8), |
| 1166 | debug_output: codegen.DebugInfoOutput, | 1164 | debug_output: codegen.DebugInfoOutput, |
| 1167 | ) codegen.GenerateSymbolError!codegen.Result { | 1165 | ) codegen.CodeGenError!codegen.Result { |
| 1168 | _ = src_loc; | 1166 | _ = src_loc; |
| 1169 | var code_gen: CodeGen = .{ | 1167 | var code_gen: CodeGen = .{ |
| 1170 | .gpa = bin_file.allocator, | 1168 | .gpa = bin_file.allocator, |
| ... | @@ -1829,6 +1827,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1829,6 +1827,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1829 | .arg => func.airArg(inst), | 1827 | .arg => func.airArg(inst), |
| 1830 | .bitcast => func.airBitcast(inst), | 1828 | .bitcast => func.airBitcast(inst), |
| 1831 | .block => func.airBlock(inst), | 1829 | .block => func.airBlock(inst), |
| 1830 | .trap => func.airTrap(inst), | ||
| 1832 | .breakpoint => func.airBreakpoint(inst), | 1831 | .breakpoint => func.airBreakpoint(inst), |
| 1833 | .br => func.airBr(inst), | 1832 | .br => func.airBr(inst), |
| 1834 | .bool_to_int => func.airBoolToInt(inst), | 1833 | .bool_to_int => func.airBoolToInt(inst), |
| ... | @@ -3289,9 +3288,15 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3289,9 +3288,15 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3289 | func.finishAir(inst, result, &.{ty_op.operand}); | 3288 | func.finishAir(inst, result, &.{ty_op.operand}); |
| 3290 | } | 3289 | } |
| 3291 | 3290 | ||
| 3291 | fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ||
| 3292 | try func.addTag(.@"unreachable"); | ||
| 3293 | func.finishAir(inst, .none, &.{}); | ||
| 3294 | } | ||
| 3295 | |||
| 3292 | fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3296 | fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3293 | // unsupported by wasm itfunc. Can be implemented once we support DWARF | 3297 | // unsupported by wasm itfunc. Can be implemented once we support DWARF |
| 3294 | // for wasm | 3298 | // for wasm |
| 3299 | try func.addTag(.@"unreachable"); | ||
| 3295 | func.finishAir(inst, .none, &.{}); | 3300 | func.finishAir(inst, .none, &.{}); |
| 3296 | } | 3301 | } |
| 3297 | 3302 |
src/arch/x86_64/CodeGen.zig+957-1494| ... | @@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace; |
| 12 | 12 | ||
| 13 | const Air = @import("../../Air.zig"); | 13 | const Air = @import("../../Air.zig"); |
| 14 | const Allocator = mem.Allocator; | 14 | const Allocator = mem.Allocator; |
| 15 | const CodeGenError = codegen.CodeGenError; | ||
| 15 | const Compilation = @import("../../Compilation.zig"); | 16 | const Compilation = @import("../../Compilation.zig"); |
| 16 | const DebugInfoOutput = codegen.DebugInfoOutput; | 17 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 17 | const DW = std.dwarf; | 18 | const DW = std.dwarf; |
| 18 | const ErrorMsg = Module.ErrorMsg; | 19 | const ErrorMsg = Module.ErrorMsg; |
| 19 | const Result = codegen.Result; | 20 | const Result = codegen.Result; |
| 20 | const GenerateSymbolError = codegen.GenerateSymbolError; | ||
| 21 | const Emit = @import("Emit.zig"); | 21 | const Emit = @import("Emit.zig"); |
| 22 | const Liveness = @import("../../Liveness.zig"); | 22 | const Liveness = @import("../../Liveness.zig"); |
| 23 | const Mir = @import("Mir.zig"); | 23 | const Mir = @import("Mir.zig"); |
| ... | @@ -33,18 +33,16 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | ... | @@ -33,18 +33,16 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 34 | 34 | ||
| 35 | const Condition = bits.Condition; | 35 | const Condition = bits.Condition; |
| 36 | const Immediate = bits.Immediate; | ||
| 37 | const Memory = bits.Memory; | ||
| 38 | const Register = bits.Register; | ||
| 36 | const RegisterManager = abi.RegisterManager; | 39 | const RegisterManager = abi.RegisterManager; |
| 37 | const RegisterLock = RegisterManager.RegisterLock; | 40 | const RegisterLock = RegisterManager.RegisterLock; |
| 38 | const Register = bits.Register; | ||
| 39 | 41 | ||
| 40 | const gp = abi.RegisterClass.gp; | 42 | const gp = abi.RegisterClass.gp; |
| 41 | const sse = abi.RegisterClass.sse; | 43 | const sse = abi.RegisterClass.sse; |
| 42 | 44 | ||
| 43 | const InnerError = error{ | 45 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 44 | OutOfMemory, | ||
| 45 | CodegenFail, | ||
| 46 | OutOfRegisters, | ||
| 47 | }; | ||
| 48 | 46 | ||
| 49 | gpa: Allocator, | 47 | gpa: Allocator, |
| 50 | air: Air, | 48 | air: Air, |
| ... | @@ -186,8 +184,7 @@ const Branch = struct { | ... | @@ -186,8 +184,7 @@ const Branch = struct { |
| 186 | _ = options; | 184 | _ = options; |
| 187 | comptime assert(unused_format_string.len == 0); | 185 | comptime assert(unused_format_string.len == 0); |
| 188 | try writer.writeAll("Branch {\n"); | 186 | try writer.writeAll("Branch {\n"); |
| 189 | for (ctx.insts, 0..) |inst, i| { | 187 | for (ctx.insts, ctx.mcvs) |inst, mcv| { |
| 190 | const mcv = ctx.mcvs[i]; | ||
| 191 | try writer.print(" %{d} => {}\n", .{ inst, mcv }); | 188 | try writer.print(" %{d} => {}\n", .{ inst, mcv }); |
| 192 | } | 189 | } |
| 193 | try writer.writeAll("}"); | 190 | try writer.writeAll("}"); |
| ... | @@ -210,7 +207,7 @@ const Branch = struct { | ... | @@ -210,7 +207,7 @@ const Branch = struct { |
| 210 | }; | 207 | }; |
| 211 | 208 | ||
| 212 | const StackAllocation = struct { | 209 | const StackAllocation = struct { |
| 213 | inst: Air.Inst.Index, | 210 | inst: ?Air.Inst.Index, |
| 214 | /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*) | 211 | /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*) |
| 215 | size: u32, | 212 | size: u32, |
| 216 | }; | 213 | }; |
| ... | @@ -257,7 +254,7 @@ pub fn generate( | ... | @@ -257,7 +254,7 @@ pub fn generate( |
| 257 | liveness: Liveness, | 254 | liveness: Liveness, |
| 258 | code: *std.ArrayList(u8), | 255 | code: *std.ArrayList(u8), |
| 259 | debug_output: DebugInfoOutput, | 256 | debug_output: DebugInfoOutput, |
| 260 | ) GenerateSymbolError!Result { | 257 | ) CodeGenError!Result { |
| 261 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 258 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 262 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 259 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 263 | } | 260 | } |
| ... | @@ -307,7 +304,12 @@ pub fn generate( | ... | @@ -307,7 +304,12 @@ pub fn generate( |
| 307 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 304 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 308 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 305 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 309 | error.OutOfRegisters => return Result{ | 306 | error.OutOfRegisters => return Result{ |
| 310 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 307 | .fail = try ErrorMsg.create( |
| 308 | bin_file.allocator, | ||
| 309 | src_loc, | ||
| 310 | "CodeGen ran out of registers. This is a bug in the Zig compiler.", | ||
| 311 | .{}, | ||
| 312 | ), | ||
| 311 | }, | 313 | }, |
| 312 | else => |e| return e, | 314 | else => |e| return e, |
| 313 | }; | 315 | }; |
| ... | @@ -346,6 +348,20 @@ pub fn generate( | ... | @@ -346,6 +348,20 @@ pub fn generate( |
| 346 | defer emit.deinit(); | 348 | defer emit.deinit(); |
| 347 | emit.lowerMir() catch |err| switch (err) { | 349 | emit.lowerMir() catch |err| switch (err) { |
| 348 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | 350 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, |
| 351 | error.InvalidInstruction, error.CannotEncode => |e| { | ||
| 352 | const msg = switch (e) { | ||
| 353 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | ||
| 354 | error.CannotEncode => "CodeGen failed to encode the instruction.", | ||
| 355 | }; | ||
| 356 | return Result{ | ||
| 357 | .fail = try ErrorMsg.create( | ||
| 358 | bin_file.allocator, | ||
| 359 | src_loc, | ||
| 360 | "{s} This is a bug in the Zig compiler.", | ||
| 361 | .{msg}, | ||
| 362 | ), | ||
| 363 | }; | ||
| 364 | }, | ||
| 349 | else => |e| return e, | 365 | else => |e| return e, |
| 350 | }; | 366 | }; |
| 351 | 367 | ||
| ... | @@ -359,52 +375,263 @@ pub fn generate( | ... | @@ -359,52 +375,263 @@ pub fn generate( |
| 359 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { | 375 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| 360 | const gpa = self.gpa; | 376 | const gpa = self.gpa; |
| 361 | try self.mir_instructions.ensureUnusedCapacity(gpa, 1); | 377 | try self.mir_instructions.ensureUnusedCapacity(gpa, 1); |
| 362 | const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); | 378 | const result_index = @intCast(Mir.Inst.Index, self.mir_instructions.len); |
| 363 | self.mir_instructions.appendAssumeCapacity(inst); | 379 | self.mir_instructions.appendAssumeCapacity(inst); |
| 364 | return result_index; | 380 | return result_index; |
| 365 | } | 381 | } |
| 366 | 382 | ||
| 367 | pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { | 383 | fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { |
| 368 | const fields = std.meta.fields(@TypeOf(extra)); | 384 | const fields = std.meta.fields(@TypeOf(extra)); |
| 369 | try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); | 385 | try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); |
| 370 | return self.addExtraAssumeCapacity(extra); | 386 | return self.addExtraAssumeCapacity(extra); |
| 371 | } | 387 | } |
| 372 | 388 | ||
| 373 | pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { | 389 | fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 374 | const fields = std.meta.fields(@TypeOf(extra)); | 390 | const fields = std.meta.fields(@TypeOf(extra)); |
| 375 | const result = @intCast(u32, self.mir_extra.items.len); | 391 | const result = @intCast(u32, self.mir_extra.items.len); |
| 376 | inline for (fields) |field| { | 392 | inline for (fields) |field| { |
| 377 | self.mir_extra.appendAssumeCapacity(switch (field.type) { | 393 | self.mir_extra.appendAssumeCapacity(switch (field.type) { |
| 378 | u32 => @field(extra, field.name), | 394 | u32 => @field(extra, field.name), |
| 379 | i32 => @bitCast(u32, @field(extra, field.name)), | 395 | i32 => @bitCast(u32, @field(extra, field.name)), |
| 380 | else => @compileError("bad field type"), | 396 | else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), |
| 381 | }); | 397 | }); |
| 382 | } | 398 | } |
| 383 | return result; | 399 | return result; |
| 384 | } | 400 | } |
| 385 | 401 | ||
| 402 | fn 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 | |||
| 413 | fn 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 | |||
| 425 | fn 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 | |||
| 433 | fn 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 | |||
| 444 | fn asmOpOnly(self: *Self, tag: Mir.Inst.Tag) !void { | ||
| 445 | _ = try self.addInst(.{ | ||
| 446 | .tag = tag, | ||
| 447 | .ops = .none, | ||
| 448 | .data = undefined, | ||
| 449 | }); | ||
| 450 | } | ||
| 451 | |||
| 452 | fn 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 | |||
| 460 | fn 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 | |||
| 472 | fn 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 | |||
| 483 | fn 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 | |||
| 510 | fn 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 | |||
| 541 | fn 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 | |||
| 559 | fn 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 | |||
| 583 | fn 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 | |||
| 603 | fn 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 | |||
| 386 | fn gen(self: *Self) InnerError!void { | 623 | fn gen(self: *Self) InnerError!void { |
| 387 | const cc = self.fn_type.fnCallingConvention(); | 624 | const cc = self.fn_type.fnCallingConvention(); |
| 388 | if (cc != .Naked) { | 625 | if (cc != .Naked) { |
| 389 | _ = try self.addInst(.{ | 626 | try self.asmRegister(.push, .rbp); |
| 390 | .tag = .push, | 627 | try self.asmRegisterRegister(.mov, .rbp, .rsp); |
| 391 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), | 628 | |
| 392 | .data = undefined, // unused for push reg, | ||
| 393 | }); | ||
| 394 | _ = try self.addInst(.{ | ||
| 395 | .tag = .mov, | ||
| 396 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 397 | .reg1 = .rbp, | ||
| 398 | .reg2 = .rsp, | ||
| 399 | }), | ||
| 400 | .data = undefined, | ||
| 401 | }); | ||
| 402 | // We want to subtract the aligned stack frame size from rsp here, but we don't | 629 | // We want to subtract the aligned stack frame size from rsp here, but we don't |
| 403 | // yet know how big it will be, so we leave room for a 4-byte stack size. | 630 | // yet know how big it will be, so we leave room for a 4-byte stack size. |
| 404 | // TODO During semantic analysis, check if there are no function calls. If there | 631 | // TODO During semantic analysis, check if there are no function calls. If there |
| 405 | // are none, here we can omit the part where we subtract and then add rsp. | 632 | // are none, here we can omit the part where we subtract and then add rsp. |
| 406 | const backpatch_stack_sub = try self.addInst(.{ | 633 | const backpatch_stack_sub = try self.addInst(.{ |
| 407 | .tag = .nop, | 634 | .tag = .dead, |
| 408 | .ops = undefined, | 635 | .ops = undefined, |
| 409 | .data = undefined, | 636 | .data = undefined, |
| 410 | }); | 637 | }); |
| ... | @@ -431,7 +658,7 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -431,7 +658,7 @@ fn gen(self: *Self) InnerError!void { |
| 431 | 658 | ||
| 432 | // Push callee-preserved regs that were used actually in use. | 659 | // Push callee-preserved regs that were used actually in use. |
| 433 | const backpatch_push_callee_preserved_regs = try self.addInst(.{ | 660 | const backpatch_push_callee_preserved_regs = try self.addInst(.{ |
| 434 | .tag = .nop, | 661 | .tag = .dead, |
| 435 | .ops = undefined, | 662 | .ops = undefined, |
| 436 | .data = undefined, | 663 | .data = undefined, |
| 437 | }); | 664 | }); |
| ... | @@ -462,7 +689,7 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -462,7 +689,7 @@ fn gen(self: *Self) InnerError!void { |
| 462 | 689 | ||
| 463 | // Pop saved callee-preserved regs. | 690 | // Pop saved callee-preserved regs. |
| 464 | const backpatch_pop_callee_preserved_regs = try self.addInst(.{ | 691 | const backpatch_pop_callee_preserved_regs = try self.addInst(.{ |
| 465 | .tag = .nop, | 692 | .tag = .dead, |
| 466 | .ops = undefined, | 693 | .ops = undefined, |
| 467 | .data = undefined, | 694 | .data = undefined, |
| 468 | }); | 695 | }); |
| ... | @@ -475,22 +702,13 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -475,22 +702,13 @@ fn gen(self: *Self) InnerError!void { |
| 475 | 702 | ||
| 476 | // Maybe add rsp, x if required. This is backpatched later. | 703 | // Maybe add rsp, x if required. This is backpatched later. |
| 477 | const backpatch_stack_add = try self.addInst(.{ | 704 | const backpatch_stack_add = try self.addInst(.{ |
| 478 | .tag = .nop, | 705 | .tag = .dead, |
| 479 | .ops = undefined, | 706 | .ops = undefined, |
| 480 | .data = undefined, | 707 | .data = undefined, |
| 481 | }); | 708 | }); |
| 482 | 709 | ||
| 483 | _ = try self.addInst(.{ | 710 | try self.asmRegister(.pop, .rbp); |
| 484 | .tag = .pop, | 711 | try self.asmOpOnly(.ret); |
| 485 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), | ||
| 486 | .data = undefined, | ||
| 487 | }); | ||
| 488 | |||
| 489 | _ = try self.addInst(.{ | ||
| 490 | .tag = .ret, | ||
| 491 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), | ||
| 492 | .data = undefined, | ||
| 493 | }); | ||
| 494 | 712 | ||
| 495 | // Adjust the stack | 713 | // Adjust the stack |
| 496 | if (self.max_end_stack > math.maxInt(i32)) { | 714 | if (self.max_end_stack > math.maxInt(i32)) { |
| ... | @@ -504,27 +722,34 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -504,27 +722,34 @@ fn gen(self: *Self) InnerError!void { |
| 504 | if (aligned_stack_end > 0) { | 722 | if (aligned_stack_end > 0) { |
| 505 | self.mir_instructions.set(backpatch_stack_sub, .{ | 723 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 506 | .tag = .sub, | 724 | .tag = .sub, |
| 507 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), | 725 | .ops = .ri_u, |
| 508 | .data = .{ .imm = aligned_stack_end }, | 726 | .data = .{ .ri = .{ |
| 727 | .r1 = .rsp, | ||
| 728 | .imm = aligned_stack_end, | ||
| 729 | } }, | ||
| 509 | }); | 730 | }); |
| 510 | self.mir_instructions.set(backpatch_stack_add, .{ | 731 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 511 | .tag = .add, | 732 | .tag = .add, |
| 512 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), | 733 | .ops = .ri_u, |
| 513 | .data = .{ .imm = aligned_stack_end }, | 734 | .data = .{ .ri = .{ |
| 735 | .r1 = .rsp, | ||
| 736 | .imm = aligned_stack_end, | ||
| 737 | } }, | ||
| 514 | }); | 738 | }); |
| 515 | 739 | ||
| 516 | const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ | 740 | const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ |
| 741 | .base_reg = @enumToInt(Register.rbp), | ||
| 517 | .register_list = reg_list.asInt(), | 742 | .register_list = reg_list.asInt(), |
| 518 | .stack_end = aligned_stack_end, | 743 | .stack_end = aligned_stack_end, |
| 519 | }); | 744 | }); |
| 520 | self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{ | 745 | self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{ |
| 521 | .tag = .push_regs, | 746 | .tag = .push_regs, |
| 522 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), | 747 | .ops = undefined, |
| 523 | .data = .{ .payload = save_reg_list }, | 748 | .data = .{ .payload = save_reg_list }, |
| 524 | }); | 749 | }); |
| 525 | self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{ | 750 | self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{ |
| 526 | .tag = .pop_regs, | 751 | .tag = .pop_regs, |
| 527 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), | 752 | .ops = undefined, |
| 528 | .data = .{ .payload = save_reg_list }, | 753 | .data = .{ .payload = save_reg_list }, |
| 529 | }); | 754 | }); |
| 530 | } | 755 | } |
| ... | @@ -638,6 +863,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -638,6 +863,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 638 | .bitcast => try self.airBitCast(inst), | 863 | .bitcast => try self.airBitCast(inst), |
| 639 | .block => try self.airBlock(inst), | 864 | .block => try self.airBlock(inst), |
| 640 | .br => try self.airBr(inst), | 865 | .br => try self.airBr(inst), |
| 866 | .trap => try self.airTrap(), | ||
| 641 | .breakpoint => try self.airBreakpoint(), | 867 | .breakpoint => try self.airBreakpoint(), |
| 642 | .ret_addr => try self.airRetAddr(inst), | 868 | .ret_addr => try self.airRetAddr(inst), |
| 643 | .frame_addr => try self.airFrameAddress(inst), | 869 | .frame_addr => try self.airFrameAddress(inst), |
| ... | @@ -811,17 +1037,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -811,17 +1037,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 811 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 1037 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 812 | const air_tags = self.air.instructions.items(.tag); | 1038 | const air_tags = self.air.instructions.items(.tag); |
| 813 | if (air_tags[inst] == .constant) return; // Constants are immortal. | 1039 | if (air_tags[inst] == .constant) return; // Constants are immortal. |
| 814 | log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } }); | 1040 | const prev_value = self.getResolvedInstValue(inst) orelse return; |
| 1041 | log.debug("%{d} => {}", .{ inst, MCValue.dead }); | ||
| 815 | // When editing this function, note that the logic must synchronize with `reuseOperand`. | 1042 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| 816 | const prev_value = self.getResolvedInstValue(inst); | ||
| 817 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 1043 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 818 | branch.inst_table.putAssumeCapacity(inst, .dead); | 1044 | branch.inst_table.putAssumeCapacity(inst, .dead); |
| 819 | switch (prev_value) { | 1045 | switch (prev_value) { |
| 820 | .register => |reg| { | 1046 | .register => |reg| { |
| 821 | self.register_manager.freeReg(reg.to64()); | 1047 | self.register_manager.freeReg(reg); |
| 822 | }, | 1048 | }, |
| 823 | .register_overflow => |ro| { | 1049 | .register_overflow => |ro| { |
| 824 | self.register_manager.freeReg(ro.reg.to64()); | 1050 | self.register_manager.freeReg(ro.reg); |
| 825 | self.eflags_inst = null; | 1051 | self.eflags_inst = null; |
| 826 | }, | 1052 | }, |
| 827 | .eflags => { | 1053 | .eflags => { |
| ... | @@ -882,7 +1108,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { | ... | @@ -882,7 +1108,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 882 | try table.ensureUnusedCapacity(self.gpa, additional_count); | 1108 | try table.ensureUnusedCapacity(self.gpa, additional_count); |
| 883 | } | 1109 | } |
| 884 | 1110 | ||
| 885 | fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { | 1111 | fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { |
| 886 | if (abi_align > self.stack_align) | 1112 | if (abi_align > self.stack_align) |
| 887 | self.stack_align = abi_align; | 1113 | self.stack_align = abi_align; |
| 888 | // TODO find a free slot instead of always appending | 1114 | // TODO find a free slot instead of always appending |
| ... | @@ -915,7 +1141,14 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { | ... | @@ -915,7 +1141,14 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 915 | } | 1141 | } |
| 916 | 1142 | ||
| 917 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | 1143 | fn 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 | |||
| 1147 | fn allocTempRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool) !MCValue { | ||
| 1148 | return self.allocRegOrMemAdvanced(elem_ty, null, reg_ok); | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue { | ||
| 919 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { | 1152 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { |
| 920 | const mod = self.bin_file.options.module.?; | 1153 | const mod = self.bin_file.options.module.?; |
| 921 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); | 1154 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| ... | @@ -992,7 +1225,7 @@ fn revertState(self: *Self, state: State) void { | ... | @@ -992,7 +1225,7 @@ fn revertState(self: *Self, state: State) void { |
| 992 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 1225 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 993 | const stack_mcv = try self.allocRegOrMem(inst, false); | 1226 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 994 | log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv }); | 1227 | log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 995 | const reg_mcv = self.getResolvedInstValue(inst); | 1228 | const reg_mcv = self.getResolvedInstValue(inst).?; |
| 996 | switch (reg_mcv) { | 1229 | switch (reg_mcv) { |
| 997 | .register => |other| { | 1230 | .register => |other| { |
| 998 | assert(reg.to64() == other.to64()); | 1231 | assert(reg.to64() == other.to64()); |
| ... | @@ -1009,7 +1242,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -1009,7 +1242,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 1009 | 1242 | ||
| 1010 | pub fn spillEflagsIfOccupied(self: *Self) !void { | 1243 | pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 1011 | if (self.eflags_inst) |inst_to_save| { | 1244 | if (self.eflags_inst) |inst_to_save| { |
| 1012 | const mcv = self.getResolvedInstValue(inst_to_save); | 1245 | const mcv = self.getResolvedInstValue(inst_to_save).?; |
| 1013 | const new_mcv = switch (mcv) { | 1246 | const new_mcv = switch (mcv) { |
| 1014 | .register_overflow => try self.allocRegOrMem(inst_to_save, false), | 1247 | .register_overflow => try self.allocRegOrMem(inst_to_save, false), |
| 1015 | .eflags => try self.allocRegOrMem(inst_to_save, true), | 1248 | .eflags => try self.allocRegOrMem(inst_to_save, true), |
| ... | @@ -1219,7 +1452,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1219,7 +1452,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1219 | }; | 1452 | }; |
| 1220 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); | 1453 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 1221 | 1454 | ||
| 1222 | const mask = ~@as(u64, 0); | 1455 | const mask = switch (operand_ty.abiSize(self.target.*)) { |
| 1456 | 1 => ~@as(u8, 0), | ||
| 1457 | 2 => ~@as(u16, 0), | ||
| 1458 | 4 => ~@as(u32, 0), | ||
| 1459 | 8 => ~@as(u64, 0), | ||
| 1460 | else => unreachable, | ||
| 1461 | }; | ||
| 1223 | try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask }); | 1462 | try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask }); |
| 1224 | 1463 | ||
| 1225 | break :result dst_mcv; | 1464 | break :result dst_mcv; |
| ... | @@ -1266,14 +1505,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1266,14 +1505,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { |
| 1266 | .unsigned => .b, | 1505 | .unsigned => .b, |
| 1267 | .signed => .l, | 1506 | .signed => .l, |
| 1268 | }; | 1507 | }; |
| 1269 | _ = try self.addInst(.{ | 1508 | try self.asmCmovccRegisterRegister(dst_mcv.register, lhs_reg, cc); |
| 1270 | .tag = .cond_mov, | ||
| 1271 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1272 | .reg1 = dst_mcv.register, | ||
| 1273 | .reg2 = lhs_reg, | ||
| 1274 | }), | ||
| 1275 | .data = .{ .cc = cc }, | ||
| 1276 | }); | ||
| 1277 | 1509 | ||
| 1278 | break :result dst_mcv; | 1510 | break :result dst_mcv; |
| 1279 | }; | 1511 | }; |
| ... | @@ -1473,13 +1705,7 @@ fn genSetStackTruncatedOverflowCompare( | ... | @@ -1473,13 +1705,7 @@ fn genSetStackTruncatedOverflowCompare( |
| 1473 | .signed => .o, | 1705 | .signed => .o, |
| 1474 | .unsigned => .c, | 1706 | .unsigned => .c, |
| 1475 | }; | 1707 | }; |
| 1476 | _ = try self.addInst(.{ | 1708 | try self.asmSetccRegister(overflow_reg.to8(), cc); |
| 1477 | .tag = .cond_set_byte, | ||
| 1478 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1479 | .reg1 = overflow_reg.to8(), | ||
| 1480 | }), | ||
| 1481 | .data = .{ .cc = cc }, | ||
| 1482 | }); | ||
| 1483 | 1709 | ||
| 1484 | const scratch_reg = temp_regs[1]; | 1710 | const scratch_reg = temp_regs[1]; |
| 1485 | try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); | 1711 | try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); |
| ... | @@ -1492,12 +1718,7 @@ fn genSetStackTruncatedOverflowCompare( | ... | @@ -1492,12 +1718,7 @@ fn genSetStackTruncatedOverflowCompare( |
| 1492 | ); | 1718 | ); |
| 1493 | 1719 | ||
| 1494 | const eq_reg = temp_regs[2]; | 1720 | const eq_reg = temp_regs[2]; |
| 1495 | _ = try self.addInst(.{ | 1721 | try self.asmSetccRegister(eq_reg.to8(), .ne); |
| 1496 | .tag = .cond_set_byte, | ||
| 1497 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), | ||
| 1498 | .data = .{ .cc = .ne }, | ||
| 1499 | }); | ||
| 1500 | |||
| 1501 | try self.genBinOpMir( | 1722 | try self.genBinOpMir( |
| 1502 | .@"or", | 1723 | .@"or", |
| 1503 | Type.u8, | 1724 | Type.u8, |
| ... | @@ -1641,23 +1862,8 @@ fn genIntMulDivOpMir( | ... | @@ -1641,23 +1862,8 @@ fn genIntMulDivOpMir( |
| 1641 | } | 1862 | } |
| 1642 | 1863 | ||
| 1643 | switch (signedness) { | 1864 | switch (signedness) { |
| 1644 | .signed => { | 1865 | .signed => try self.asmOpOnly(.cqo), |
| 1645 | _ = try self.addInst(.{ | 1866 | .unsigned => try self.asmRegisterRegister(.xor, .rdx, .rdx), |
| 1646 | .tag = .cwd, | ||
| 1647 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), | ||
| 1648 | .data = undefined, | ||
| 1649 | }); | ||
| 1650 | }, | ||
| 1651 | .unsigned => { | ||
| 1652 | _ = try self.addInst(.{ | ||
| 1653 | .tag = .xor, | ||
| 1654 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1655 | .reg1 = .rdx, | ||
| 1656 | .reg2 = .rdx, | ||
| 1657 | }), | ||
| 1658 | .data = undefined, | ||
| 1659 | }); | ||
| 1660 | }, | ||
| 1661 | } | 1867 | } |
| 1662 | 1868 | ||
| 1663 | const factor = switch (rhs) { | 1869 | const factor = switch (rhs) { |
| ... | @@ -1670,29 +1876,11 @@ fn genIntMulDivOpMir( | ... | @@ -1670,29 +1876,11 @@ fn genIntMulDivOpMir( |
| 1670 | }; | 1876 | }; |
| 1671 | 1877 | ||
| 1672 | switch (factor) { | 1878 | switch (factor) { |
| 1673 | .register => |reg| { | 1879 | .register => |reg| try self.asmRegister(tag, reg), |
| 1674 | _ = try self.addInst(.{ | 1880 | .stack_offset => |off| try self.asmMemory(tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 1675 | .tag = tag, | 1881 | .base = .rbp, |
| 1676 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), | 1882 | .disp = -off, |
| 1677 | .data = undefined, | 1883 | })), |
| 1678 | }); | ||
| 1679 | }, | ||
| 1680 | .stack_offset => |off| { | ||
| 1681 | _ = try self.addInst(.{ | ||
| 1682 | .tag = tag, | ||
| 1683 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1684 | .reg2 = .rbp, | ||
| 1685 | .flags = switch (abi_size) { | ||
| 1686 | 1 => 0b00, | ||
| 1687 | 2 => 0b01, | ||
| 1688 | 4 => 0b10, | ||
| 1689 | 8 => 0b11, | ||
| 1690 | else => unreachable, | ||
| 1691 | }, | ||
| 1692 | }), | ||
| 1693 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 1694 | }); | ||
| 1695 | }, | ||
| 1696 | else => unreachable, | 1884 | else => unreachable, |
| 1697 | } | 1885 | } |
| 1698 | } | 1886 | } |
| ... | @@ -1720,38 +1908,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa | ... | @@ -1720,38 +1908,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 1720 | .unsigned => .div, | 1908 | .unsigned => .div, |
| 1721 | }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); | 1909 | }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); |
| 1722 | 1910 | ||
| 1723 | _ = try self.addInst(.{ | 1911 | try self.asmRegisterRegister(.xor, divisor.to64(), dividend.to64()); |
| 1724 | .tag = .xor, | 1912 | try self.asmRegisterImmediate(.sar, divisor.to64(), Immediate.u(63)); |
| 1725 | .ops = Mir.Inst.Ops.encode(.{ | 1913 | try self.asmRegisterRegister(.@"test", .rdx, .rdx); |
| 1726 | .reg1 = divisor.to64(), | 1914 | try self.asmCmovccRegisterRegister(divisor.to64(), .rdx, .e); |
| 1727 | .reg2 = dividend.to64(), | ||
| 1728 | }), | ||
| 1729 | .data = undefined, | ||
| 1730 | }); | ||
| 1731 | _ = try self.addInst(.{ | ||
| 1732 | .tag = .sar, | ||
| 1733 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1734 | .reg1 = divisor.to64(), | ||
| 1735 | .flags = 0b10, | ||
| 1736 | }), | ||
| 1737 | .data = .{ .imm = 63 }, | ||
| 1738 | }); | ||
| 1739 | _ = try self.addInst(.{ | ||
| 1740 | .tag = .@"test", | ||
| 1741 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1742 | .reg1 = .rdx, | ||
| 1743 | .reg2 = .rdx, | ||
| 1744 | }), | ||
| 1745 | .data = undefined, | ||
| 1746 | }); | ||
| 1747 | _ = try self.addInst(.{ | ||
| 1748 | .tag = .cond_mov, | ||
| 1749 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 1750 | .reg1 = divisor.to64(), | ||
| 1751 | .reg2 = .rdx, | ||
| 1752 | }), | ||
| 1753 | .data = .{ .cc = .e }, | ||
| 1754 | }); | ||
| 1755 | try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); | 1915 | try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); |
| 1756 | return MCValue{ .register = divisor }; | 1916 | return MCValue{ .register = divisor }; |
| 1757 | } | 1917 | } |
| ... | @@ -2185,18 +2345,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { | ... | @@ -2185,18 +2345,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { |
| 2185 | 2345 | ||
| 2186 | const addr_reg = try self.register_manager.allocReg(null, gp); | 2346 | const addr_reg = try self.register_manager.allocReg(null, gp); |
| 2187 | switch (slice_mcv) { | 2347 | switch (slice_mcv) { |
| 2188 | .stack_offset => |off| { | 2348 | .stack_offset => |off| try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ |
| 2189 | // mov reg, [rbp - 8] | 2349 | .base = .rbp, |
| 2190 | _ = try self.addInst(.{ | 2350 | .disp = -off, |
| 2191 | .tag = .mov, | 2351 | })), |
| 2192 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 2193 | .reg1 = addr_reg.to64(), | ||
| 2194 | .reg2 = .rbp, | ||
| 2195 | .flags = 0b01, | ||
| 2196 | }), | ||
| 2197 | .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, | ||
| 2198 | }); | ||
| 2199 | }, | ||
| 2200 | else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}), | 2352 | else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}), |
| 2201 | } | 2353 | } |
| 2202 | // TODO we could allocate register here, but need to expect addr register and potentially | 2354 | // TODO we could allocate register here, but need to expect addr register and potentially |
| ... | @@ -2271,26 +2423,16 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2271,26 +2423,16 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2271 | array_ty.abiAlignment(self.target.*), | 2423 | array_ty.abiAlignment(self.target.*), |
| 2272 | )); | 2424 | )); |
| 2273 | try self.genSetStack(array_ty, off, array, .{}); | 2425 | try self.genSetStack(array_ty, off, array, .{}); |
| 2274 | // lea reg, [rbp] | 2426 | try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ |
| 2275 | _ = try self.addInst(.{ | 2427 | .base = .rbp, |
| 2276 | .tag = .lea, | 2428 | .disp = -off, |
| 2277 | .ops = Mir.Inst.Ops.encode(.{ | 2429 | })); |
| 2278 | .reg1 = addr_reg.to64(), | ||
| 2279 | .reg2 = .rbp, | ||
| 2280 | }), | ||
| 2281 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 2282 | }); | ||
| 2283 | }, | 2430 | }, |
| 2284 | .stack_offset => |off| { | 2431 | .stack_offset => |off| { |
| 2285 | // lea reg, [rbp] | 2432 | try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ |
| 2286 | _ = try self.addInst(.{ | 2433 | .base = .rbp, |
| 2287 | .tag = .lea, | 2434 | .disp = -off, |
| 2288 | .ops = Mir.Inst.Ops.encode(.{ | 2435 | })); |
| 2289 | .reg1 = addr_reg.to64(), | ||
| 2290 | .reg2 = .rbp, | ||
| 2291 | }), | ||
| 2292 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 2293 | }); | ||
| 2294 | }, | 2436 | }, |
| 2295 | .memory, .linker_load => { | 2437 | .memory, .linker_load => { |
| 2296 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); | 2438 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); |
| ... | @@ -2327,7 +2469,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2327,7 +2469,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2327 | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); | 2469 | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 2328 | 2470 | ||
| 2329 | const elem_ty = ptr_ty.elemType2(); | 2471 | const elem_ty = ptr_ty.elemType2(); |
| 2330 | const elem_abi_size = elem_ty.abiSize(self.target.*); | 2472 | const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 2331 | const index_ty = self.air.typeOf(bin_op.rhs); | 2473 | const index_ty = self.air.typeOf(bin_op.rhs); |
| 2332 | const index = try self.resolveInst(bin_op.rhs); | 2474 | const index = try self.resolveInst(bin_op.rhs); |
| 2333 | const index_lock: ?RegisterLock = switch (index) { | 2475 | const index_lock: ?RegisterLock = switch (index) { |
| ... | @@ -2347,16 +2489,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2347,16 +2489,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2347 | if (elem_abi_size > 8) { | 2489 | if (elem_abi_size > 8) { |
| 2348 | return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); | 2490 | return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); |
| 2349 | } else { | 2491 | } else { |
| 2350 | // mov dst_mcv, [dst_mcv] | 2492 | try self.asmRegisterMemory( |
| 2351 | _ = try self.addInst(.{ | 2493 | .mov, |
| 2352 | .tag = .mov, | 2494 | registerAlias(dst_mcv.register, elem_abi_size), |
| 2353 | .ops = Mir.Inst.Ops.encode(.{ | 2495 | Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{ |
| 2354 | .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), | 2496 | .base = dst_mcv.register, |
| 2355 | .reg2 = dst_mcv.register, | 2497 | .disp = 0, |
| 2356 | .flags = 0b01, | ||
| 2357 | }), | 2498 | }), |
| 2358 | .data = .{ .imm = 0 }, | 2499 | ); |
| 2359 | }); | ||
| 2360 | break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; | 2500 | break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; |
| 2361 | } | 2501 | } |
| 2362 | }; | 2502 | }; |
| ... | @@ -2583,7 +2723,7 @@ fn reuseOperand( | ... | @@ -2583,7 +2723,7 @@ fn reuseOperand( |
| 2583 | 2723 | ||
| 2584 | fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { | 2724 | fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { |
| 2585 | const elem_ty = ptr_ty.elemType(); | 2725 | const elem_ty = ptr_ty.elemType(); |
| 2586 | const abi_size = elem_ty.abiSize(self.target.*); | 2726 | const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 2587 | switch (ptr) { | 2727 | switch (ptr) { |
| 2588 | .none => unreachable, | 2728 | .none => unreachable, |
| 2589 | .undef => unreachable, | 2729 | .undef => unreachable, |
| ... | @@ -2610,16 +2750,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -2610,16 +2750,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2610 | .undef => unreachable, | 2750 | .undef => unreachable, |
| 2611 | .eflags => unreachable, | 2751 | .eflags => unreachable, |
| 2612 | .register => |dst_reg| { | 2752 | .register => |dst_reg| { |
| 2613 | // mov dst_reg, [reg] | 2753 | try self.asmRegisterMemory( |
| 2614 | _ = try self.addInst(.{ | 2754 | .mov, |
| 2615 | .tag = .mov, | 2755 | registerAlias(dst_reg, abi_size), |
| 2616 | .ops = Mir.Inst.Ops.encode(.{ | 2756 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg, .disp = 0 }), |
| 2617 | .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), | 2757 | ); |
| 2618 | .reg2 = reg, | ||
| 2619 | .flags = 0b01, | ||
| 2620 | }), | ||
| 2621 | .data = .{ .imm = 0 }, | ||
| 2622 | }); | ||
| 2623 | }, | 2758 | }, |
| 2624 | .stack_offset => |off| { | 2759 | .stack_offset => |off| { |
| 2625 | if (abi_size <= 8) { | 2760 | if (abi_size <= 8) { |
| ... | @@ -2679,23 +2814,19 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue | ... | @@ -2679,23 +2814,19 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2679 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | 2814 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); |
| 2680 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; | 2815 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; |
| 2681 | } else unreachable; | 2816 | } else unreachable; |
| 2682 | const flags: u2 = switch (load_struct.type) { | 2817 | const ops: Mir.Inst.Ops = switch (load_struct.type) { |
| 2683 | .got => 0b00, | 2818 | .got => .got_reloc, |
| 2684 | .direct => 0b01, | 2819 | .direct => .direct_reloc, |
| 2685 | .import => 0b10, | 2820 | .import => .import_reloc, |
| 2686 | }; | 2821 | }; |
| 2687 | _ = try self.addInst(.{ | 2822 | _ = try self.addInst(.{ |
| 2688 | .tag = .lea_pic, | 2823 | .tag = .lea_linker, |
| 2689 | .ops = Mir.Inst.Ops.encode(.{ | 2824 | .ops = ops, |
| 2690 | .reg1 = registerAlias(reg, abi_size), | 2825 | .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{ |
| 2691 | .flags = flags, | 2826 | .reg = @enumToInt(registerAlias(reg, abi_size)), |
| 2692 | }), | 2827 | .atom_index = atom_index, |
| 2693 | .data = .{ | 2828 | .sym_index = load_struct.sym_index, |
| 2694 | .relocation = .{ | 2829 | }) }, |
| 2695 | .atom_index = atom_index, | ||
| 2696 | .sym_index = load_struct.sym_index, | ||
| 2697 | }, | ||
| 2698 | }, | ||
| 2699 | }); | 2830 | }); |
| 2700 | }, | 2831 | }, |
| 2701 | .memory => |addr| { | 2832 | .memory => |addr| { |
| ... | @@ -2708,7 +2839,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue | ... | @@ -2708,7 +2839,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2708 | } | 2839 | } |
| 2709 | 2840 | ||
| 2710 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { | 2841 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { |
| 2711 | const abi_size = value_ty.abiSize(self.target.*); | 2842 | const abi_size = @intCast(u32, value_ty.abiSize(self.target.*)); |
| 2712 | switch (ptr) { | 2843 | switch (ptr) { |
| 2713 | .none => unreachable, | 2844 | .none => unreachable, |
| 2714 | .undef => unreachable, | 2845 | .undef => unreachable, |
| ... | @@ -2736,30 +2867,25 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2736,30 +2867,25 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2736 | .unreach => unreachable, | 2867 | .unreach => unreachable, |
| 2737 | .eflags => unreachable, | 2868 | .eflags => unreachable, |
| 2738 | .undef => { | 2869 | .undef => { |
| 2739 | try self.genSetReg(value_ty, reg, value); | 2870 | switch (abi_size) { |
| 2871 | 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty), | ||
| 2872 | 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty), | ||
| 2873 | 4 => try self.store(ptr, .{ .immediate = 0xaaaaaaaa }, ptr_ty, value_ty), | ||
| 2874 | 8 => try self.store(ptr, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, ptr_ty, value_ty), | ||
| 2875 | else => try self.genInlineMemset(ptr, .{ .immediate = 0xaa }, .{ .immediate = abi_size }, .{}), | ||
| 2876 | } | ||
| 2740 | }, | 2877 | }, |
| 2741 | .immediate => |imm| { | 2878 | .immediate => |imm| { |
| 2742 | switch (abi_size) { | 2879 | switch (abi_size) { |
| 2743 | 1, 2, 4 => { | 2880 | 1, 2, 4 => { |
| 2744 | // TODO this is wasteful! | 2881 | const immediate = if (value_ty.isSignedInt()) |
| 2745 | // introduce new MIR tag specifically for mov [reg + 0], imm | 2882 | Immediate.s(@intCast(i32, @bitCast(i64, imm))) |
| 2746 | const payload = try self.addExtra(Mir.ImmPair{ | 2883 | else |
| 2747 | .dest_off = 0, | 2884 | Immediate.u(@truncate(u32, imm)); |
| 2748 | .operand = @truncate(u32, imm), | 2885 | try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 2749 | }); | 2886 | .base = reg.to64(), |
| 2750 | _ = try self.addInst(.{ | 2887 | .disp = 0, |
| 2751 | .tag = .mov_mem_imm, | 2888 | }), immediate); |
| 2752 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 2753 | .reg1 = reg.to64(), | ||
| 2754 | .flags = switch (abi_size) { | ||
| 2755 | 1 => 0b00, | ||
| 2756 | 2 => 0b01, | ||
| 2757 | 4 => 0b10, | ||
| 2758 | else => unreachable, | ||
| 2759 | }, | ||
| 2760 | }), | ||
| 2761 | .data = .{ .payload = payload }, | ||
| 2762 | }); | ||
| 2763 | }, | 2889 | }, |
| 2764 | 8 => { | 2890 | 8 => { |
| 2765 | // TODO: optimization: if the imm is only using the lower | 2891 | // TODO: optimization: if the imm is only using the lower |
| ... | @@ -2789,13 +2915,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2789,13 +2915,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2789 | const overflow_bit_ty = value_ty.structFieldType(1); | 2915 | const overflow_bit_ty = value_ty.structFieldType(1); |
| 2790 | const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); | 2916 | const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); |
| 2791 | const tmp_reg = try self.register_manager.allocReg(null, gp); | 2917 | const tmp_reg = try self.register_manager.allocReg(null, gp); |
| 2792 | _ = try self.addInst(.{ | 2918 | try self.asmSetccRegister(tmp_reg.to8(), ro.eflags); |
| 2793 | .tag = .cond_set_byte, | ||
| 2794 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 2795 | .reg1 = tmp_reg.to8(), | ||
| 2796 | }), | ||
| 2797 | .data = .{ .cc = ro.eflags }, | ||
| 2798 | }); | ||
| 2799 | try self.genInlineMemcpyRegisterRegister( | 2919 | try self.genInlineMemcpyRegisterRegister( |
| 2800 | overflow_bit_ty, | 2920 | overflow_bit_ty, |
| 2801 | reg, | 2921 | reg, |
| ... | @@ -2836,17 +2956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2836,17 +2956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2836 | 2956 | ||
| 2837 | try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr); | 2957 | try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr); |
| 2838 | 2958 | ||
| 2839 | // to get the actual address of the value we want to modify we have to go through the GOT | 2959 | // To get the actual address of the value we want to modify we have to go through the GOT |
| 2840 | // mov reg, [reg] | 2960 | try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ |
| 2841 | _ = try self.addInst(.{ | 2961 | .base = addr_reg.to64(), |
| 2842 | .tag = .mov, | 2962 | .disp = 0, |
| 2843 | .ops = Mir.Inst.Ops.encode(.{ | 2963 | })); |
| 2844 | .reg1 = addr_reg.to64(), | ||
| 2845 | .reg2 = addr_reg.to64(), | ||
| 2846 | .flags = 0b01, | ||
| 2847 | }), | ||
| 2848 | .data = .{ .imm = 0 }, | ||
| 2849 | }); | ||
| 2850 | 2964 | ||
| 2851 | const new_ptr = MCValue{ .register = addr_reg.to64() }; | 2965 | const new_ptr = MCValue{ .register = addr_reg.to64() }; |
| 2852 | 2966 | ||
| ... | @@ -2856,19 +2970,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2856,19 +2970,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2856 | return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size}); | 2970 | return self.fail("TODO saving imm to memory for abi_size {}", .{abi_size}); |
| 2857 | } | 2971 | } |
| 2858 | 2972 | ||
| 2859 | const payload = try self.addExtra(Mir.ImmPair{ | 2973 | if (abi_size == 8) { |
| 2860 | .dest_off = 0, | 2974 | // TODO |
| 2861 | // TODO check if this logic is correct | ||
| 2862 | .operand = @truncate(u32, imm), | ||
| 2863 | }); | ||
| 2864 | const flags: u2 = switch (abi_size) { | ||
| 2865 | 1 => 0b00, | ||
| 2866 | 2 => 0b01, | ||
| 2867 | 4 => 0b10, | ||
| 2868 | 8 => 0b11, | ||
| 2869 | else => unreachable, | ||
| 2870 | }; | ||
| 2871 | if (flags == 0b11) { | ||
| 2872 | const top_bits: u32 = @intCast(u32, imm >> 32); | 2975 | const top_bits: u32 = @intCast(u32, imm >> 32); |
| 2873 | const can_extend = if (value_ty.isUnsignedInt()) | 2976 | const can_extend = if (value_ty.isUnsignedInt()) |
| 2874 | (top_bits == 0) and (imm & 0x8000_0000) == 0 | 2977 | (top_bits == 0) and (imm & 0x8000_0000) == 0 |
| ... | @@ -2879,14 +2982,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2879,14 +2982,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2879 | return self.fail("TODO imm64 would get incorrectly sign extended", .{}); | 2982 | return self.fail("TODO imm64 would get incorrectly sign extended", .{}); |
| 2880 | } | 2983 | } |
| 2881 | } | 2984 | } |
| 2882 | _ = try self.addInst(.{ | 2985 | try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 2883 | .tag = .mov_mem_imm, | 2986 | .base = addr_reg.to64(), |
| 2884 | .ops = Mir.Inst.Ops.encode(.{ | 2987 | .disp = 0, |
| 2885 | .reg1 = addr_reg.to64(), | 2988 | }), Immediate.u(@intCast(u32, imm))); |
| 2886 | .flags = flags, | ||
| 2887 | }), | ||
| 2888 | .data = .{ .payload = payload }, | ||
| 2889 | }); | ||
| 2890 | }, | 2989 | }, |
| 2891 | .register => { | 2990 | .register => { |
| 2892 | return self.store(new_ptr, value, ptr_ty, value_ty); | 2991 | return self.store(new_ptr, value, ptr_ty, value_ty); |
| ... | @@ -2898,16 +2997,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2898,16 +2997,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2898 | defer self.register_manager.unlockReg(tmp_reg_lock); | 2997 | defer self.register_manager.unlockReg(tmp_reg_lock); |
| 2899 | 2998 | ||
| 2900 | try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); | 2999 | try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); |
| 3000 | try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib(.qword, .{ | ||
| 3001 | .base = tmp_reg, | ||
| 3002 | .disp = 0, | ||
| 3003 | })); | ||
| 2901 | 3004 | ||
| 2902 | _ = try self.addInst(.{ | ||
| 2903 | .tag = .mov, | ||
| 2904 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 2905 | .reg1 = tmp_reg, | ||
| 2906 | .reg2 = tmp_reg, | ||
| 2907 | .flags = 0b01, | ||
| 2908 | }), | ||
| 2909 | .data = .{ .imm = 0 }, | ||
| 2910 | }); | ||
| 2911 | return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); | 3005 | return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); |
| 2912 | } | 3006 | } |
| 2913 | 3007 | ||
| ... | @@ -2959,6 +3053,9 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde | ... | @@ -2959,6 +3053,9 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 2959 | const mcv = try self.resolveInst(operand); | 3053 | const mcv = try self.resolveInst(operand); |
| 2960 | const ptr_ty = self.air.typeOf(operand); | 3054 | const ptr_ty = self.air.typeOf(operand); |
| 2961 | const struct_ty = ptr_ty.childType(); | 3055 | const struct_ty = ptr_ty.childType(); |
| 3056 | if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { | ||
| 3057 | return self.fail("TODO structFieldPtr implement packed structs", .{}); | ||
| 3058 | } | ||
| 2962 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); | 3059 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); |
| 2963 | 3060 | ||
| 2964 | const dst_mcv: MCValue = result: { | 3061 | const dst_mcv: MCValue = result: { |
| ... | @@ -3022,6 +3119,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3022,6 +3119,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3022 | 3119 | ||
| 3023 | const mcv = try self.resolveInst(operand); | 3120 | const mcv = try self.resolveInst(operand); |
| 3024 | const struct_ty = self.air.typeOf(operand); | 3121 | const struct_ty = self.air.typeOf(operand); |
| 3122 | if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { | ||
| 3123 | return self.fail("TODO airStructFieldVal implement packed structs", .{}); | ||
| 3124 | } | ||
| 3025 | const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); | 3125 | const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); |
| 3026 | const struct_field_ty = struct_ty.structFieldType(index); | 3126 | const struct_field_ty = struct_ty.structFieldType(index); |
| 3027 | 3127 | ||
| ... | @@ -3055,8 +3155,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3055,8 +3155,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3055 | const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); | 3155 | const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); |
| 3056 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); | 3156 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); |
| 3057 | 3157 | ||
| 3058 | // Mask with reg.size() - struct_field_size | 3158 | // Mask with reg.bitSize() - struct_field_size |
| 3059 | const max_reg_bit_width = Register.rax.size(); | 3159 | const max_reg_bit_width = Register.rax.bitSize(); |
| 3060 | const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*))); | 3160 | const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*))); |
| 3061 | const mask = (~@as(u64, 0)) >> mask_shift; | 3161 | const mask = (~@as(u64, 0)) >> mask_shift; |
| 3062 | 3162 | ||
| ... | @@ -3069,14 +3169,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3069,14 +3169,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3069 | }; | 3169 | }; |
| 3070 | const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); | 3170 | const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); |
| 3071 | if (signedness == .signed and field_size < 8) { | 3171 | if (signedness == .signed and field_size < 8) { |
| 3072 | _ = try self.addInst(.{ | 3172 | try self.asmRegisterRegister( |
| 3073 | .tag = .mov_sign_extend, | 3173 | .movsx, |
| 3074 | .ops = Mir.Inst.Ops.encode(.{ | 3174 | dst_mcv.register, |
| 3075 | .reg1 = dst_mcv.register, | 3175 | registerAlias(dst_mcv.register, field_size), |
| 3076 | .reg2 = registerAlias(dst_mcv.register, field_size), | 3176 | ); |
| 3077 | }), | ||
| 3078 | .data = undefined, | ||
| 3079 | }); | ||
| 3080 | } | 3177 | } |
| 3081 | 3178 | ||
| 3082 | break :result dst_mcv; | 3179 | break :result dst_mcv; |
| ... | @@ -3093,13 +3190,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3093,13 +3190,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3093 | defer self.register_manager.unlockReg(reg_lock); | 3190 | defer self.register_manager.unlockReg(reg_lock); |
| 3094 | 3191 | ||
| 3095 | const dst_reg = try self.register_manager.allocReg(inst, gp); | 3192 | const dst_reg = try self.register_manager.allocReg(inst, gp); |
| 3096 | _ = try self.addInst(.{ | 3193 | try self.asmSetccRegister(dst_reg.to8(), ro.eflags); |
| 3097 | .tag = .cond_set_byte, | ||
| 3098 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 3099 | .reg1 = dst_reg.to8(), | ||
| 3100 | }), | ||
| 3101 | .data = .{ .cc = ro.eflags }, | ||
| 3102 | }); | ||
| 3103 | break :result MCValue{ .register = dst_reg.to8() }; | 3194 | break :result MCValue{ .register = dst_reg.to8() }; |
| 3104 | }, | 3195 | }, |
| 3105 | else => unreachable, | 3196 | else => unreachable, |
| ... | @@ -3135,25 +3226,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi | ... | @@ -3135,25 +3226,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi |
| 3135 | switch (shift) { | 3226 | switch (shift) { |
| 3136 | .immediate => |imm| switch (imm) { | 3227 | .immediate => |imm| switch (imm) { |
| 3137 | 0 => return, | 3228 | 0 => return, |
| 3138 | 1 => { | 3229 | else => return self.asmRegisterImmediate(tag, registerAlias(reg, abi_size), Immediate.u(imm)), |
| 3139 | _ = try self.addInst(.{ | ||
| 3140 | .tag = tag, | ||
| 3141 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), | ||
| 3142 | .data = undefined, | ||
| 3143 | }); | ||
| 3144 | return; | ||
| 3145 | }, | ||
| 3146 | else => { | ||
| 3147 | _ = try self.addInst(.{ | ||
| 3148 | .tag = tag, | ||
| 3149 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 3150 | .reg1 = registerAlias(reg, abi_size), | ||
| 3151 | .flags = 0b10, | ||
| 3152 | }), | ||
| 3153 | .data = .{ .imm = @intCast(u8, imm) }, | ||
| 3154 | }); | ||
| 3155 | return; | ||
| 3156 | }, | ||
| 3157 | }, | 3230 | }, |
| 3158 | .register => |shift_reg| { | 3231 | .register => |shift_reg| { |
| 3159 | if (shift_reg == .rcx) break :blk; | 3232 | if (shift_reg == .rcx) break :blk; |
| ... | @@ -3165,14 +3238,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi | ... | @@ -3165,14 +3238,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi |
| 3165 | try self.genSetReg(Type.u8, .rcx, shift); | 3238 | try self.genSetReg(Type.u8, .rcx, shift); |
| 3166 | } | 3239 | } |
| 3167 | 3240 | ||
| 3168 | _ = try self.addInst(.{ | 3241 | try self.asmRegisterRegister(tag, registerAlias(reg, abi_size), .cl); |
| 3169 | .tag = tag, | ||
| 3170 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 3171 | .reg1 = registerAlias(reg, abi_size), | ||
| 3172 | .flags = 0b01, | ||
| 3173 | }), | ||
| 3174 | .data = undefined, | ||
| 3175 | }); | ||
| 3176 | } | 3242 | } |
| 3177 | 3243 | ||
| 3178 | /// Result is always a register. | 3244 | /// Result is always a register. |
| ... | @@ -3545,59 +3611,67 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -3545,59 +3611,67 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3545 | if (intrinsicsAllowed(self.target.*, dst_ty)) { | 3611 | if (intrinsicsAllowed(self.target.*, dst_ty)) { |
| 3546 | const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { | 3612 | const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { |
| 3547 | .f32 => switch (mir_tag) { | 3613 | .f32 => switch (mir_tag) { |
| 3548 | .add => if (hasAvxSupport(self.target.*)) | 3614 | .add => .addss, |
| 3549 | Mir.Inst.Tag.add_f32_avx | 3615 | .cmp => .ucomiss, |
| 3550 | else | 3616 | else => return self.fail( |
| 3551 | Mir.Inst.Tag.add_f32_sse, | 3617 | "TODO genBinOpMir for f32 register-register with MIR tag {}", |
| 3552 | .cmp => if (hasAvxSupport(self.target.*)) | 3618 | .{mir_tag}, |
| 3553 | Mir.Inst.Tag.cmp_f32_avx | 3619 | ), |
| 3554 | else | ||
| 3555 | Mir.Inst.Tag.cmp_f32_sse, | ||
| 3556 | else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), | ||
| 3557 | }, | 3620 | }, |
| 3558 | .f64 => switch (mir_tag) { | 3621 | .f64 => switch (mir_tag) { |
| 3559 | .add => if (hasAvxSupport(self.target.*)) | 3622 | .add => .addsd, |
| 3560 | Mir.Inst.Tag.add_f64_avx | 3623 | .cmp => .ucomisd, |
| 3561 | else | 3624 | else => return self.fail( |
| 3562 | Mir.Inst.Tag.add_f64_sse, | 3625 | "TODO genBinOpMir for f64 register-register with MIR tag {}", |
| 3563 | .cmp => if (hasAvxSupport(self.target.*)) | 3626 | .{mir_tag}, |
| 3564 | Mir.Inst.Tag.cmp_f64_avx | 3627 | ), |
| 3565 | else | ||
| 3566 | Mir.Inst.Tag.cmp_f64_sse, | ||
| 3567 | else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), | ||
| 3568 | }, | 3628 | }, |
| 3569 | else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), | 3629 | else => return self.fail( |
| 3630 | "TODO genBinOpMir for float register-register and type {}", | ||
| 3631 | .{dst_ty.fmtDebug()}, | ||
| 3632 | ), | ||
| 3570 | }; | 3633 | }; |
| 3571 | _ = try self.addInst(.{ | 3634 | return self.asmRegisterRegister(actual_tag, dst_reg.to128(), src_reg.to128()); |
| 3572 | .tag = actual_tag, | ||
| 3573 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 3574 | .reg1 = dst_reg.to128(), | ||
| 3575 | .reg2 = src_reg.to128(), | ||
| 3576 | }), | ||
| 3577 | .data = undefined, | ||
| 3578 | }); | ||
| 3579 | return; | ||
| 3580 | } | 3635 | } |
| 3581 | 3636 | ||
| 3582 | return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); | 3637 | return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); |
| 3583 | }, | 3638 | }, |
| 3584 | else => { | 3639 | else => try self.asmRegisterRegister( |
| 3585 | _ = try self.addInst(.{ | 3640 | mir_tag, |
| 3586 | .tag = mir_tag, | 3641 | registerAlias(dst_reg, abi_size), |
| 3587 | .ops = Mir.Inst.Ops.encode(.{ | 3642 | registerAlias(src_reg, abi_size), |
| 3588 | .reg1 = registerAlias(dst_reg, abi_size), | 3643 | ), |
| 3589 | .reg2 = registerAlias(src_reg, abi_size), | ||
| 3590 | }), | ||
| 3591 | .data = undefined, | ||
| 3592 | }); | ||
| 3593 | }, | ||
| 3594 | }, | 3644 | }, |
| 3595 | .immediate => |imm| { | 3645 | .immediate => |imm| { |
| 3596 | _ = try self.addInst(.{ | 3646 | switch (abi_size) { |
| 3597 | .tag = mir_tag, | 3647 | 0 => unreachable, |
| 3598 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), | 3648 | 1...4 => { |
| 3599 | .data = .{ .imm = @truncate(u32, imm) }, | 3649 | try self.asmRegisterImmediate( |
| 3600 | }); | 3650 | mir_tag, |
| 3651 | registerAlias(dst_reg, abi_size), | ||
| 3652 | Immediate.u(@intCast(u32, imm)), | ||
| 3653 | ); | ||
| 3654 | }, | ||
| 3655 | 5...8 => { | ||
| 3656 | if (math.cast(i32, @bitCast(i64, imm))) |small| { | ||
| 3657 | try self.asmRegisterImmediate( | ||
| 3658 | mir_tag, | ||
| 3659 | registerAlias(dst_reg, abi_size), | ||
| 3660 | Immediate.s(small), | ||
| 3661 | ); | ||
| 3662 | } else { | ||
| 3663 | const tmp_reg = try self.register_manager.allocReg(null, gp); | ||
| 3664 | const tmp_alias = registerAlias(tmp_reg, abi_size); | ||
| 3665 | try self.asmRegisterImmediate(.mov, tmp_alias, Immediate.u(imm)); | ||
| 3666 | try self.asmRegisterRegister( | ||
| 3667 | mir_tag, | ||
| 3668 | registerAlias(dst_reg, abi_size), | ||
| 3669 | tmp_alias, | ||
| 3670 | ); | ||
| 3671 | } | ||
| 3672 | }, | ||
| 3673 | else => return self.fail("TODO getBinOpMir implement large immediate ABI", .{}), | ||
| 3674 | } | ||
| 3601 | }, | 3675 | }, |
| 3602 | .memory, | 3676 | .memory, |
| 3603 | .linker_load, | 3677 | .linker_load, |
| ... | @@ -3614,15 +3688,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -3614,15 +3688,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3614 | if (off > math.maxInt(i32)) { | 3688 | if (off > math.maxInt(i32)) { |
| 3615 | return self.fail("stack offset too large", .{}); | 3689 | return self.fail("stack offset too large", .{}); |
| 3616 | } | 3690 | } |
| 3617 | _ = try self.addInst(.{ | 3691 | try self.asmRegisterMemory( |
| 3618 | .tag = mir_tag, | 3692 | mir_tag, |
| 3619 | .ops = Mir.Inst.Ops.encode(.{ | 3693 | registerAlias(dst_reg, abi_size), |
| 3620 | .reg1 = registerAlias(dst_reg, abi_size), | 3694 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), |
| 3621 | .reg2 = .rbp, | 3695 | ); |
| 3622 | .flags = 0b01, | ||
| 3623 | }), | ||
| 3624 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 3625 | }); | ||
| 3626 | }, | 3696 | }, |
| 3627 | } | 3697 | } |
| 3628 | }, | 3698 | }, |
| ... | @@ -3640,45 +3710,17 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -3640,45 +3710,17 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3640 | .dead, .unreach => unreachable, | 3710 | .dead, .unreach => unreachable, |
| 3641 | .register_overflow => unreachable, | 3711 | .register_overflow => unreachable, |
| 3642 | .register => |src_reg| { | 3712 | .register => |src_reg| { |
| 3643 | _ = try self.addInst(.{ | 3713 | try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 3644 | .tag = mir_tag, | 3714 | .base = .rbp, |
| 3645 | .ops = Mir.Inst.Ops.encode(.{ | 3715 | .disp = -off, |
| 3646 | .reg1 = .rbp, | 3716 | }), registerAlias(src_reg, abi_size)); |
| 3647 | .reg2 = registerAlias(src_reg, abi_size), | ||
| 3648 | .flags = 0b10, | ||
| 3649 | }), | ||
| 3650 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 3651 | }); | ||
| 3652 | }, | 3717 | }, |
| 3653 | .immediate => |imm| { | 3718 | .immediate => |imm| { |
| 3654 | const tag: Mir.Inst.Tag = switch (mir_tag) { | 3719 | // TODO |
| 3655 | .add => .add_mem_imm, | 3720 | try self.asmMemoryImmediate(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 3656 | .@"or" => .or_mem_imm, | 3721 | .base = .rbp, |
| 3657 | .@"and" => .and_mem_imm, | 3722 | .disp = -off, |
| 3658 | .sub => .sub_mem_imm, | 3723 | }), Immediate.u(@intCast(u32, imm))); |
| 3659 | .xor => .xor_mem_imm, | ||
| 3660 | .cmp => .cmp_mem_imm, | ||
| 3661 | else => unreachable, | ||
| 3662 | }; | ||
| 3663 | const flags: u2 = switch (abi_size) { | ||
| 3664 | 1 => 0b00, | ||
| 3665 | 2 => 0b01, | ||
| 3666 | 4 => 0b10, | ||
| 3667 | 8 => 0b11, | ||
| 3668 | else => unreachable, | ||
| 3669 | }; | ||
| 3670 | const payload = try self.addExtra(Mir.ImmPair{ | ||
| 3671 | .dest_off = @bitCast(u32, -off), | ||
| 3672 | .operand = @truncate(u32, imm), | ||
| 3673 | }); | ||
| 3674 | _ = try self.addInst(.{ | ||
| 3675 | .tag = tag, | ||
| 3676 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 3677 | .reg1 = .rbp, | ||
| 3678 | .flags = flags, | ||
| 3679 | }), | ||
| 3680 | .data = .{ .payload = payload }, | ||
| 3681 | }); | ||
| 3682 | }, | 3724 | }, |
| 3683 | .memory, | 3725 | .memory, |
| 3684 | .stack_offset, | 3726 | .stack_offset, |
| ... | @@ -3721,30 +3763,21 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3721,30 +3763,21 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3721 | .dead, .unreach => unreachable, | 3763 | .dead, .unreach => unreachable, |
| 3722 | .ptr_stack_offset => unreachable, | 3764 | .ptr_stack_offset => unreachable, |
| 3723 | .register_overflow => unreachable, | 3765 | .register_overflow => unreachable, |
| 3724 | .register => |src_reg| { | 3766 | .register => |src_reg| try self.asmRegisterRegister( |
| 3725 | // register, register | 3767 | .imul, |
| 3726 | _ = try self.addInst(.{ | 3768 | registerAlias(dst_reg, abi_size), |
| 3727 | .tag = .imul_complex, | 3769 | registerAlias(src_reg, abi_size), |
| 3728 | .ops = Mir.Inst.Ops.encode(.{ | 3770 | ), |
| 3729 | .reg1 = registerAlias(dst_reg, abi_size), | ||
| 3730 | .reg2 = registerAlias(src_reg, abi_size), | ||
| 3731 | }), | ||
| 3732 | .data = undefined, | ||
| 3733 | }); | ||
| 3734 | }, | ||
| 3735 | .immediate => |imm| { | 3771 | .immediate => |imm| { |
| 3736 | // TODO take into account the type's ABI size when selecting the register alias | ||
| 3737 | // register, immediate | ||
| 3738 | if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { | 3772 | if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { |
| 3739 | _ = try self.addInst(.{ | 3773 | // TODO take into account the type's ABI size when selecting the register alias |
| 3740 | .tag = .imul_complex, | 3774 | // register, immediate |
| 3741 | .ops = Mir.Inst.Ops.encode(.{ | 3775 | try self.asmRegisterRegisterImmediate( |
| 3742 | .reg1 = dst_reg.to32(), | 3776 | .imul, |
| 3743 | .reg2 = dst_reg.to32(), | 3777 | dst_reg.to32(), |
| 3744 | .flags = 0b10, | 3778 | dst_reg.to32(), |
| 3745 | }), | 3779 | Immediate.u(@intCast(u32, imm)), |
| 3746 | .data = .{ .imm = @truncate(u32, imm) }, | 3780 | ); |
| 3747 | }); | ||
| 3748 | } else { | 3781 | } else { |
| 3749 | // TODO verify we don't spill and assign to the same register as dst_mcv | 3782 | // TODO verify we don't spill and assign to the same register as dst_mcv |
| 3750 | const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); | 3783 | const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); |
| ... | @@ -3752,15 +3785,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3752,15 +3785,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3752 | } | 3785 | } |
| 3753 | }, | 3786 | }, |
| 3754 | .stack_offset => |off| { | 3787 | .stack_offset => |off| { |
| 3755 | _ = try self.addInst(.{ | 3788 | try self.asmRegisterMemory( |
| 3756 | .tag = .imul_complex, | 3789 | .imul, |
| 3757 | .ops = Mir.Inst.Ops.encode(.{ | 3790 | registerAlias(dst_reg, abi_size), |
| 3758 | .reg1 = registerAlias(dst_reg, abi_size), | 3791 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), |
| 3759 | .reg2 = .rbp, | 3792 | ); |
| 3760 | .flags = 0b01, | ||
| 3761 | }), | ||
| 3762 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 3763 | }); | ||
| 3764 | }, | 3793 | }, |
| 3765 | .memory => { | 3794 | .memory => { |
| 3766 | return self.fail("TODO implement x86 multiply source memory", .{}); | 3795 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| ... | @@ -3783,16 +3812,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3783,16 +3812,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3783 | .register => |src_reg| { | 3812 | .register => |src_reg| { |
| 3784 | // copy dst to a register | 3813 | // copy dst to a register |
| 3785 | const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); | 3814 | const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); |
| 3786 | // multiply into dst_reg | 3815 | try self.asmRegisterRegister( |
| 3787 | // register, register | 3816 | .imul, |
| 3788 | _ = try self.addInst(.{ | 3817 | registerAlias(dst_reg, abi_size), |
| 3789 | .tag = .imul_complex, | 3818 | registerAlias(src_reg, abi_size), |
| 3790 | .ops = Mir.Inst.Ops.encode(.{ | 3819 | ); |
| 3791 | .reg1 = registerAlias(dst_reg, abi_size), | ||
| 3792 | .reg2 = registerAlias(src_reg, abi_size), | ||
| 3793 | }), | ||
| 3794 | .data = undefined, | ||
| 3795 | }); | ||
| 3796 | // copy dst_reg back out | 3820 | // copy dst_reg back out |
| 3797 | return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); | 3821 | return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); |
| 3798 | }, | 3822 | }, |
| ... | @@ -3917,12 +3941,13 @@ fn genVarDbgInfo( | ... | @@ -3917,12 +3941,13 @@ fn genVarDbgInfo( |
| 3917 | } | 3941 | } |
| 3918 | } | 3942 | } |
| 3919 | 3943 | ||
| 3944 | fn airTrap(self: *Self) !void { | ||
| 3945 | try self.asmOpOnly(.ud2); | ||
| 3946 | return self.finishAirBookkeeping(); | ||
| 3947 | } | ||
| 3948 | |||
| 3920 | fn airBreakpoint(self: *Self) !void { | 3949 | fn airBreakpoint(self: *Self) !void { |
| 3921 | _ = try self.addInst(.{ | 3950 | try self.asmOpOnly(.int3); |
| 3922 | .tag = .interrupt, | ||
| 3923 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 3924 | .data = undefined, | ||
| 3925 | }); | ||
| 3926 | return self.finishAirBookkeeping(); | 3951 | return self.finishAirBookkeeping(); |
| 3927 | } | 3952 | } |
| 3928 | 3953 | ||
| ... | @@ -3985,10 +4010,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -3985,10 +4010,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 3985 | }; | 4010 | }; |
| 3986 | defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); | 4011 | defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3987 | 4012 | ||
| 3988 | for (args, 0..) |arg, arg_i| { | 4013 | for (args, info.args) |arg, info_arg| { |
| 3989 | const mc_arg = info.args[arg_i]; | 4014 | const mc_arg = info_arg; |
| 3990 | const arg_ty = self.air.typeOf(arg); | 4015 | const arg_ty = self.air.typeOf(arg); |
| 3991 | const arg_mcv = try self.resolveInst(args[arg_i]); | 4016 | const arg_mcv = try self.resolveInst(arg); |
| 3992 | // Here we do not use setRegOrMem even though the logic is similar, because | 4017 | // Here we do not use setRegOrMem even though the logic is similar, because |
| 3993 | // the function call will move the stack pointer, so the offsets are different. | 4018 | // the function call will move the stack pointer, so the offsets are different. |
| 3994 | switch (mc_arg) { | 4019 | switch (mc_arg) { |
| ... | @@ -4017,11 +4042,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4017,11 +4042,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4017 | 4042 | ||
| 4018 | if (info.stack_byte_count > 0) { | 4043 | if (info.stack_byte_count > 0) { |
| 4019 | // Adjust the stack | 4044 | // Adjust the stack |
| 4020 | _ = try self.addInst(.{ | 4045 | try self.asmRegisterImmediate(.sub, .rsp, Immediate.u(info.stack_byte_count)); |
| 4021 | .tag = .sub, | ||
| 4022 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), | ||
| 4023 | .data = .{ .imm = info.stack_byte_count }, | ||
| 4024 | }); | ||
| 4025 | } | 4046 | } |
| 4026 | 4047 | ||
| 4027 | // Due to incremental compilation, how function calls are generated depends | 4048 | // Due to incremental compilation, how function calls are generated depends |
| ... | @@ -4034,12 +4055,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4034,12 +4055,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4034 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 4055 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4035 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | 4056 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4036 | const atom = elf_file.getAtom(atom_index); | 4057 | const atom = elf_file.getAtom(atom_index); |
| 4037 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); | 4058 | const got_addr = atom.getOffsetTableAddress(elf_file); |
| 4038 | _ = try self.addInst(.{ | 4059 | try self.asmMemory(.call, Memory.sib(.qword, .{ |
| 4039 | .tag = .call, | 4060 | .base = .ds, |
| 4040 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | 4061 | .disp = @intCast(i32, got_addr), |
| 4041 | .data = .{ .imm = got_addr }, | 4062 | })); |
| 4042 | }); | ||
| 4043 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 4063 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 4044 | const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); | 4064 | const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4045 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; | 4065 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; |
| ... | @@ -4049,14 +4069,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4049,14 +4069,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4049 | .sym_index = sym_index, | 4069 | .sym_index = sym_index, |
| 4050 | }, | 4070 | }, |
| 4051 | }); | 4071 | }); |
| 4052 | _ = try self.addInst(.{ | 4072 | try self.asmRegister(.call, .rax); |
| 4053 | .tag = .call, | ||
| 4054 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4055 | .reg1 = .rax, | ||
| 4056 | .flags = 0b01, | ||
| 4057 | }), | ||
| 4058 | .data = undefined, | ||
| 4059 | }); | ||
| 4060 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 4073 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 4061 | const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); | 4074 | const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4062 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; | 4075 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; |
| ... | @@ -4066,14 +4079,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4066,14 +4079,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4066 | .sym_index = sym_index, | 4079 | .sym_index = sym_index, |
| 4067 | }, | 4080 | }, |
| 4068 | }); | 4081 | }); |
| 4069 | _ = try self.addInst(.{ | 4082 | try self.asmRegister(.call, .rax); |
| 4070 | .tag = .call, | ||
| 4071 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4072 | .reg1 = .rax, | ||
| 4073 | .flags = 0b01, | ||
| 4074 | }), | ||
| 4075 | .data = undefined, | ||
| 4076 | }); | ||
| 4077 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 4083 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 4078 | const decl_block_index = try p9.seeDecl(func.owner_decl); | 4084 | const decl_block_index = try p9.seeDecl(func.owner_decl); |
| 4079 | const decl_block = p9.getDeclBlock(decl_block_index); | 4085 | const decl_block = p9.getDeclBlock(decl_block_index); |
| ... | @@ -4082,11 +4088,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4082,11 +4088,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4082 | const got_addr = p9.bases.data; | 4088 | const got_addr = p9.bases.data; |
| 4083 | const got_index = decl_block.got_index.?; | 4089 | const got_index = decl_block.got_index.?; |
| 4084 | const fn_got_addr = got_addr + got_index * ptr_bytes; | 4090 | const fn_got_addr = got_addr + got_index * ptr_bytes; |
| 4085 | _ = try self.addInst(.{ | 4091 | try self.asmMemory(.call, Memory.sib(.qword, .{ |
| 4086 | .tag = .call, | 4092 | .base = .ds, |
| 4087 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | 4093 | .disp = @intCast(i32, fn_got_addr), |
| 4088 | .data = .{ .imm = @intCast(u32, fn_got_addr) }, | 4094 | })); |
| 4089 | }); | ||
| 4090 | } else unreachable; | 4095 | } else unreachable; |
| 4091 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | 4096 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 4092 | const extern_fn = func_payload.data; | 4097 | const extern_fn = func_payload.data; |
| ... | @@ -4106,14 +4111,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4106,14 +4111,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4106 | .sym_index = sym_index, | 4111 | .sym_index = sym_index, |
| 4107 | }, | 4112 | }, |
| 4108 | }); | 4113 | }); |
| 4109 | _ = try self.addInst(.{ | 4114 | try self.asmRegister(.call, .rax); |
| 4110 | .tag = .call, | ||
| 4111 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4112 | .reg1 = .rax, | ||
| 4113 | .flags = 0b01, | ||
| 4114 | }), | ||
| 4115 | .data = undefined, | ||
| 4116 | }); | ||
| 4117 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 4115 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 4118 | const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | 4116 | const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); |
| 4119 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | 4117 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); |
| ... | @@ -4136,23 +4134,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4136,23 +4134,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4136 | assert(ty.zigTypeTag() == .Pointer); | 4134 | assert(ty.zigTypeTag() == .Pointer); |
| 4137 | const mcv = try self.resolveInst(callee); | 4135 | const mcv = try self.resolveInst(callee); |
| 4138 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | 4136 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); |
| 4139 | _ = try self.addInst(.{ | 4137 | try self.asmRegister(.call, .rax); |
| 4140 | .tag = .call, | ||
| 4141 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4142 | .reg1 = .rax, | ||
| 4143 | .flags = 0b01, | ||
| 4144 | }), | ||
| 4145 | .data = undefined, | ||
| 4146 | }); | ||
| 4147 | } | 4138 | } |
| 4148 | 4139 | ||
| 4149 | if (info.stack_byte_count > 0) { | 4140 | if (info.stack_byte_count > 0) { |
| 4150 | // Readjust the stack | 4141 | // Readjust the stack |
| 4151 | _ = try self.addInst(.{ | 4142 | try self.asmRegisterImmediate(.add, .rsp, Immediate.u(info.stack_byte_count)); |
| 4152 | .tag = .add, | ||
| 4153 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), | ||
| 4154 | .data = .{ .imm = info.stack_byte_count }, | ||
| 4155 | }); | ||
| 4156 | } | 4143 | } |
| 4157 | 4144 | ||
| 4158 | const result: MCValue = result: { | 4145 | const result: MCValue = result: { |
| ... | @@ -4209,11 +4196,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4209,11 +4196,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 4209 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. | 4196 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. |
| 4210 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction | 4197 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 4211 | // which is available if the jump is 127 bytes or less forward. | 4198 | // which is available if the jump is 127 bytes or less forward. |
| 4212 | const jmp_reloc = try self.addInst(.{ | 4199 | const jmp_reloc = try self.asmJmpReloc(undefined); |
| 4213 | .tag = .jmp, | ||
| 4214 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 4215 | .data = .{ .inst = undefined }, | ||
| 4216 | }); | ||
| 4217 | try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); | 4200 | try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); |
| 4218 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); | 4201 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 4219 | } | 4202 | } |
| ... | @@ -4245,11 +4228,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4245,11 +4228,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 4245 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. | 4228 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. |
| 4246 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction | 4229 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 4247 | // which is available if the jump is 127 bytes or less forward. | 4230 | // which is available if the jump is 127 bytes or less forward. |
| 4248 | const jmp_reloc = try self.addInst(.{ | 4231 | const jmp_reloc = try self.asmJmpReloc(undefined); |
| 4249 | .tag = .jmp, | ||
| 4250 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 4251 | .data = .{ .inst = undefined }, | ||
| 4252 | }); | ||
| 4253 | try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); | 4232 | try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); |
| 4254 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); | 4233 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 4255 | } | 4234 | } |
| ... | @@ -4424,33 +4403,13 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { | ... | @@ -4424,33 +4403,13 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { |
| 4424 | const abi_size = ty.abiSize(self.target.*); | 4403 | const abi_size = ty.abiSize(self.target.*); |
| 4425 | switch (mcv) { | 4404 | switch (mcv) { |
| 4426 | .eflags => |cc| { | 4405 | .eflags => |cc| { |
| 4427 | return self.addInst(.{ | 4406 | // Here we map the opposites since the jump is to the false branch. |
| 4428 | .tag = .cond_jmp, | 4407 | return self.asmJccReloc(undefined, cc.negate()); |
| 4429 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 4430 | .data = .{ | ||
| 4431 | .inst_cc = .{ | ||
| 4432 | .inst = undefined, | ||
| 4433 | // Here we map the opposites since the jump is to the false branch. | ||
| 4434 | .cc = cc.negate(), | ||
| 4435 | }, | ||
| 4436 | }, | ||
| 4437 | }); | ||
| 4438 | }, | 4408 | }, |
| 4439 | .register => |reg| { | 4409 | .register => |reg| { |
| 4440 | try self.spillEflagsIfOccupied(); | 4410 | try self.spillEflagsIfOccupied(); |
| 4441 | _ = try self.addInst(.{ | 4411 | try self.asmRegisterImmediate(.@"test", reg, Immediate.u(1)); |
| 4442 | .tag = .@"test", | 4412 | return self.asmJccReloc(undefined, .e); |
| 4443 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), | ||
| 4444 | .data = .{ .imm = 1 }, | ||
| 4445 | }); | ||
| 4446 | return self.addInst(.{ | ||
| 4447 | .tag = .cond_jmp, | ||
| 4448 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 4449 | .data = .{ .inst_cc = .{ | ||
| 4450 | .inst = undefined, | ||
| 4451 | .cc = .e, | ||
| 4452 | } }, | ||
| 4453 | }); | ||
| 4454 | }, | 4413 | }, |
| 4455 | .immediate, | 4414 | .immediate, |
| 4456 | .stack_offset, | 4415 | .stack_offset, |
| ... | @@ -4464,6 +4423,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { | ... | @@ -4464,6 +4423,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { |
| 4464 | }, | 4423 | }, |
| 4465 | else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}), | 4424 | else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}), |
| 4466 | } | 4425 | } |
| 4426 | return 0; // TODO | ||
| 4467 | } | 4427 | } |
| 4468 | 4428 | ||
| 4469 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | 4429 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -4491,16 +4451,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4491,16 +4451,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4491 | // Capture the state of register and stack allocation state so that we can revert to it. | 4451 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4492 | const saved_state = try self.captureState(); | 4452 | const saved_state = try self.captureState(); |
| 4493 | 4453 | ||
| 4494 | try self.branch_stack.append(.{}); | 4454 | { |
| 4495 | errdefer { | 4455 | try self.branch_stack.append(.{}); |
| 4496 | _ = self.branch_stack.pop(); | 4456 | errdefer _ = self.branch_stack.pop(); |
| 4497 | } | ||
| 4498 | 4457 | ||
| 4499 | try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); | 4458 | try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); |
| 4500 | for (liveness_condbr.then_deaths) |operand| { | 4459 | for (liveness_condbr.then_deaths) |operand| { |
| 4501 | self.processDeath(operand); | 4460 | self.processDeath(operand); |
| 4461 | } | ||
| 4462 | try self.genBody(then_body); | ||
| 4502 | } | 4463 | } |
| 4503 | try self.genBody(then_body); | ||
| 4504 | 4464 | ||
| 4505 | // Revert to the previous register and stack allocation state. | 4465 | // Revert to the previous register and stack allocation state. |
| 4506 | 4466 | ||
| ... | @@ -4511,16 +4471,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4511,16 +4471,16 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4511 | 4471 | ||
| 4512 | try self.performReloc(reloc); | 4472 | try self.performReloc(reloc); |
| 4513 | 4473 | ||
| 4514 | try self.branch_stack.append(.{}); | 4474 | { |
| 4515 | errdefer { | 4475 | try self.branch_stack.append(.{}); |
| 4516 | _ = self.branch_stack.pop(); | 4476 | errdefer _ = self.branch_stack.pop(); |
| 4517 | } | ||
| 4518 | 4477 | ||
| 4519 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); | 4478 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); |
| 4520 | for (liveness_condbr.else_deaths) |operand| { | 4479 | for (liveness_condbr.else_deaths) |operand| { |
| 4521 | self.processDeath(operand); | 4480 | self.processDeath(operand); |
| 4481 | } | ||
| 4482 | try self.genBody(else_body); | ||
| 4522 | } | 4483 | } |
| 4523 | try self.genBody(else_body); | ||
| 4524 | 4484 | ||
| 4525 | var else_branch = self.branch_stack.pop(); | 4485 | var else_branch = self.branch_stack.pop(); |
| 4526 | defer else_branch.deinit(self.gpa); | 4486 | defer else_branch.deinit(self.gpa); |
| ... | @@ -4646,18 +4606,16 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4646,18 +4606,16 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4646 | }; | 4606 | }; |
| 4647 | defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); | 4607 | defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 4648 | 4608 | ||
| 4649 | const operand: MCValue = blk: { | ||
| 4650 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | ||
| 4651 | // The MCValue that holds the pointer can be re-used as the value. | ||
| 4652 | break :blk operand_ptr; | ||
| 4653 | } else { | ||
| 4654 | break :blk try self.allocRegOrMem(inst, true); | ||
| 4655 | } | ||
| 4656 | }; | ||
| 4657 | const ptr_ty = self.air.typeOf(un_op); | 4609 | const ptr_ty = self.air.typeOf(un_op); |
| 4610 | const elem_ty = ptr_ty.childType(); | ||
| 4611 | const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr)) | ||
| 4612 | // The MCValue that holds the pointer can be re-used as the value. | ||
| 4613 | operand_ptr | ||
| 4614 | else | ||
| 4615 | try self.allocTempRegOrMem(elem_ty, true); | ||
| 4658 | try self.load(operand, operand_ptr, ptr_ty); | 4616 | try self.load(operand, operand_ptr, ptr_ty); |
| 4659 | 4617 | ||
| 4660 | const result = try self.isNull(inst, ptr_ty.elemType(), operand); | 4618 | const result = try self.isNull(inst, elem_ty, operand); |
| 4661 | 4619 | ||
| 4662 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4620 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4663 | } | 4621 | } |
| ... | @@ -4686,15 +4644,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4686,15 +4644,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4686 | }; | 4644 | }; |
| 4687 | defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); | 4645 | defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 4688 | 4646 | ||
| 4689 | const operand: MCValue = blk: { | ||
| 4690 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | ||
| 4691 | // The MCValue that holds the pointer can be re-used as the value. | ||
| 4692 | break :blk operand_ptr; | ||
| 4693 | } else { | ||
| 4694 | break :blk try self.allocRegOrMem(inst, true); | ||
| 4695 | } | ||
| 4696 | }; | ||
| 4697 | const ptr_ty = self.air.typeOf(un_op); | 4647 | const ptr_ty = self.air.typeOf(un_op); |
| 4648 | const elem_ty = ptr_ty.childType(); | ||
| 4649 | const operand = if (elem_ty.isPtrLikeOptional() and self.reuseOperand(inst, un_op, 0, operand_ptr)) | ||
| 4650 | // The MCValue that holds the pointer can be re-used as the value. | ||
| 4651 | operand_ptr | ||
| 4652 | else | ||
| 4653 | try self.allocTempRegOrMem(elem_ty, true); | ||
| 4698 | try self.load(operand, operand_ptr, ptr_ty); | 4654 | try self.load(operand, operand_ptr, ptr_ty); |
| 4699 | 4655 | ||
| 4700 | const result = try self.isNonNull(inst, ptr_ty.elemType(), operand); | 4656 | const result = try self.isNonNull(inst, ptr_ty.elemType(), operand); |
| ... | @@ -4789,11 +4745,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4789,11 +4745,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 4789 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; | 4745 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 4790 | const jmp_target = @intCast(u32, self.mir_instructions.len); | 4746 | const jmp_target = @intCast(u32, self.mir_instructions.len); |
| 4791 | try self.genBody(body); | 4747 | try self.genBody(body); |
| 4792 | _ = try self.addInst(.{ | 4748 | _ = try self.asmJmpReloc(jmp_target); |
| 4793 | .tag = .jmp, | ||
| 4794 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 4795 | .data = .{ .inst = jmp_target }, | ||
| 4796 | }); | ||
| 4797 | return self.finishAirBookkeeping(); | 4749 | return self.finishAirBookkeeping(); |
| 4798 | } | 4750 | } |
| 4799 | 4751 | ||
| ... | @@ -4806,7 +4758,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4806,7 +4758,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 4806 | // break instruction will choose a MCValue for the block result and overwrite | 4758 | // break instruction will choose a MCValue for the block result and overwrite |
| 4807 | // this field. Following break instructions will use that MCValue to put their | 4759 | // this field. Following break instructions will use that MCValue to put their |
| 4808 | // block results. | 4760 | // block results. |
| 4809 | .mcv = MCValue{ .none = {} }, | 4761 | .mcv = .none, |
| 4810 | }); | 4762 | }); |
| 4811 | defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); | 4763 | defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); |
| 4812 | 4764 | ||
| ... | @@ -4838,23 +4790,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u | ... | @@ -4838,23 +4790,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 4838 | .none => unreachable, | 4790 | .none => unreachable, |
| 4839 | .undef => unreachable, | 4791 | .undef => unreachable, |
| 4840 | .dead, .unreach => unreachable, | 4792 | .dead, .unreach => unreachable, |
| 4841 | .immediate => |imm| { | 4793 | .immediate => |imm| try self.asmRegisterImmediate( |
| 4842 | _ = try self.addInst(.{ | 4794 | .xor, |
| 4843 | .tag = .xor, | 4795 | registerAlias(cond_reg, abi_size), |
| 4844 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), | 4796 | Immediate.u(imm), |
| 4845 | .data = .{ .imm = @intCast(u32, imm) }, | 4797 | ), |
| 4846 | }); | 4798 | .register => |reg| try self.asmRegisterRegister( |
| 4847 | }, | 4799 | .xor, |
| 4848 | .register => |reg| { | 4800 | registerAlias(cond_reg, abi_size), |
| 4849 | _ = try self.addInst(.{ | 4801 | registerAlias(reg, abi_size), |
| 4850 | .tag = .xor, | 4802 | ), |
| 4851 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4852 | .reg1 = registerAlias(cond_reg, abi_size), | ||
| 4853 | .reg2 = registerAlias(reg, abi_size), | ||
| 4854 | }), | ||
| 4855 | .data = undefined, | ||
| 4856 | }); | ||
| 4857 | }, | ||
| 4858 | .stack_offset => { | 4803 | .stack_offset => { |
| 4859 | if (abi_size <= 8) { | 4804 | if (abi_size <= 8) { |
| 4860 | const reg = try self.copyToTmpRegister(ty, case); | 4805 | const reg = try self.copyToTmpRegister(ty, case); |
| ... | @@ -4868,22 +4813,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u | ... | @@ -4868,22 +4813,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 4868 | }, | 4813 | }, |
| 4869 | } | 4814 | } |
| 4870 | 4815 | ||
| 4871 | _ = try self.addInst(.{ | 4816 | const aliased_reg = registerAlias(cond_reg, abi_size); |
| 4872 | .tag = .@"test", | 4817 | try self.asmRegisterRegister(.@"test", aliased_reg, aliased_reg); |
| 4873 | .ops = Mir.Inst.Ops.encode(.{ | 4818 | return self.asmJccReloc(undefined, .ne); |
| 4874 | .reg1 = registerAlias(cond_reg, abi_size), | ||
| 4875 | .reg2 = registerAlias(cond_reg, abi_size), | ||
| 4876 | }), | ||
| 4877 | .data = undefined, | ||
| 4878 | }); | ||
| 4879 | return self.addInst(.{ | ||
| 4880 | .tag = .cond_jmp, | ||
| 4881 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 4882 | .data = .{ .inst_cc = .{ | ||
| 4883 | .inst = undefined, | ||
| 4884 | .cc = .ne, | ||
| 4885 | } }, | ||
| 4886 | }); | ||
| 4887 | }, | 4819 | }, |
| 4888 | .stack_offset => { | 4820 | .stack_offset => { |
| 4889 | try self.spillEflagsIfOccupied(); | 4821 | try self.spillEflagsIfOccupied(); |
| ... | @@ -4901,6 +4833,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u | ... | @@ -4901,6 +4833,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 4901 | return self.fail("TODO implemenent switch mir when condition is {}", .{condition}); | 4833 | return self.fail("TODO implemenent switch mir when condition is {}", .{condition}); |
| 4902 | }, | 4834 | }, |
| 4903 | } | 4835 | } |
| 4836 | return 0; // TODO | ||
| 4904 | } | 4837 | } |
| 4905 | 4838 | ||
| 4906 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | 4839 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -4946,25 +4879,25 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4946,25 +4879,25 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 4946 | var relocs = try self.gpa.alloc(u32, items.len); | 4879 | var relocs = try self.gpa.alloc(u32, items.len); |
| 4947 | defer self.gpa.free(relocs); | 4880 | defer self.gpa.free(relocs); |
| 4948 | 4881 | ||
| 4949 | for (items, 0..) |item, item_i| { | 4882 | for (items, relocs) |item, *reloc| { |
| 4950 | const item_mcv = try self.resolveInst(item); | 4883 | const item_mcv = try self.resolveInst(item); |
| 4951 | relocs[item_i] = try self.genCondSwitchMir(condition_ty, condition, item_mcv); | 4884 | reloc.* = try self.genCondSwitchMir(condition_ty, condition, item_mcv); |
| 4952 | } | 4885 | } |
| 4953 | 4886 | ||
| 4954 | // Capture the state of register and stack allocation state so that we can revert to it. | 4887 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4955 | const saved_state = try self.captureState(); | 4888 | const saved_state = try self.captureState(); |
| 4956 | 4889 | ||
| 4957 | try self.branch_stack.append(.{}); | 4890 | { |
| 4958 | errdefer { | 4891 | try self.branch_stack.append(.{}); |
| 4959 | _ = self.branch_stack.pop(); | 4892 | errdefer _ = self.branch_stack.pop(); |
| 4960 | } | ||
| 4961 | 4893 | ||
| 4962 | try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); | 4894 | try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); |
| 4963 | for (liveness.deaths[case_i]) |operand| { | 4895 | for (liveness.deaths[case_i]) |operand| { |
| 4964 | self.processDeath(operand); | 4896 | self.processDeath(operand); |
| 4965 | } | 4897 | } |
| 4966 | 4898 | ||
| 4967 | try self.genBody(case_body); | 4899 | try self.genBody(case_body); |
| 4900 | } | ||
| 4968 | 4901 | ||
| 4969 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); | 4902 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); |
| 4970 | 4903 | ||
| ... | @@ -4982,18 +4915,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4982,18 +4915,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 4982 | // Capture the state of register and stack allocation state so that we can revert to it. | 4915 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4983 | const saved_state = try self.captureState(); | 4916 | const saved_state = try self.captureState(); |
| 4984 | 4917 | ||
| 4985 | try self.branch_stack.append(.{}); | 4918 | { |
| 4986 | errdefer { | 4919 | try self.branch_stack.append(.{}); |
| 4987 | _ = self.branch_stack.pop(); | 4920 | errdefer _ = self.branch_stack.pop(); |
| 4988 | } | ||
| 4989 | 4921 | ||
| 4990 | const else_deaths = liveness.deaths.len - 1; | 4922 | const else_deaths = liveness.deaths.len - 1; |
| 4991 | try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); | 4923 | try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); |
| 4992 | for (liveness.deaths[else_deaths]) |operand| { | 4924 | for (liveness.deaths[else_deaths]) |operand| { |
| 4993 | self.processDeath(operand); | 4925 | self.processDeath(operand); |
| 4994 | } | 4926 | } |
| 4995 | 4927 | ||
| 4996 | try self.genBody(else_body); | 4928 | try self.genBody(else_body); |
| 4929 | } | ||
| 4997 | 4930 | ||
| 4998 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); | 4931 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); |
| 4999 | 4932 | ||
| ... | @@ -5030,11 +4963,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran | ... | @@ -5030,11 +4963,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran |
| 5030 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count()); | 4963 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count()); |
| 5031 | 4964 | ||
| 5032 | const target_slice = target_branch.inst_table.entries.slice(); | 4965 | const target_slice = target_branch.inst_table.entries.slice(); |
| 5033 | const target_keys = target_slice.items(.key); | 4966 | for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| { |
| 5034 | const target_values = target_slice.items(.value); | ||
| 5035 | |||
| 5036 | for (target_keys, 0..) |target_key, target_idx| { | ||
| 5037 | const target_value = target_values[target_idx]; | ||
| 5038 | const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { | 4967 | const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { |
| 5039 | // The instruction's MCValue is overridden in both branches. | 4968 | // The instruction's MCValue is overridden in both branches. |
| 5040 | parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); | 4969 | parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); |
| ... | @@ -5064,10 +4993,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran | ... | @@ -5064,10 +4993,7 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran |
| 5064 | } | 4993 | } |
| 5065 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count()); | 4994 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count()); |
| 5066 | const canon_slice = canon_branch.inst_table.entries.slice(); | 4995 | const canon_slice = canon_branch.inst_table.entries.slice(); |
| 5067 | const canon_keys = canon_slice.items(.key); | 4996 | for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| { |
| 5068 | const canon_values = canon_slice.items(.value); | ||
| 5069 | for (canon_keys, 0..) |canon_key, canon_idx| { | ||
| 5070 | const canon_value = canon_values[canon_idx]; | ||
| 5071 | // We already deleted the items from this table that matched the target_branch. | 4997 | // We already deleted the items from this table that matched the target_branch. |
| 5072 | // So these are all instructions that are only overridden in the canon branch. | 4998 | // So these are all instructions that are only overridden in the canon branch. |
| 5073 | parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); | 4999 | parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); |
| ... | @@ -5095,10 +5021,10 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran | ... | @@ -5095,10 +5021,10 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran |
| 5095 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { | 5021 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5096 | const next_inst = @intCast(u32, self.mir_instructions.len); | 5022 | const next_inst = @intCast(u32, self.mir_instructions.len); |
| 5097 | switch (self.mir_instructions.items(.tag)[reloc]) { | 5023 | switch (self.mir_instructions.items(.tag)[reloc]) { |
| 5098 | .cond_jmp => { | 5024 | .jcc => { |
| 5099 | self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; | 5025 | self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; |
| 5100 | }, | 5026 | }, |
| 5101 | .jmp => { | 5027 | .jmp_reloc => { |
| 5102 | self.mir_instructions.items(.data)[reloc].inst = next_inst; | 5028 | self.mir_instructions.items(.data)[reloc].inst = next_inst; |
| 5103 | }, | 5029 | }, |
| 5104 | else => unreachable, | 5030 | else => unreachable, |
| ... | @@ -5140,11 +5066,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -5140,11 +5066,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 5140 | // Emit a jump with a relocation. It will be patched up after the block ends. | 5066 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 5141 | try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); | 5067 | try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); |
| 5142 | // Leave the jump offset undefined | 5068 | // Leave the jump offset undefined |
| 5143 | const jmp_reloc = try self.addInst(.{ | 5069 | const jmp_reloc = try self.asmJmpReloc(undefined); |
| 5144 | .tag = .jmp, | ||
| 5145 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 5146 | .data = .{ .inst = undefined }, | ||
| 5147 | }); | ||
| 5148 | block_data.relocs.appendAssumeCapacity(jmp_reloc); | 5070 | block_data.relocs.appendAssumeCapacity(jmp_reloc); |
| 5149 | } | 5071 | } |
| 5150 | 5072 | ||
| ... | @@ -5217,31 +5139,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5217,31 +5139,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5217 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); | 5139 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); |
| 5218 | while (iter.next()) |ins| { | 5140 | while (iter.next()) |ins| { |
| 5219 | if (mem.eql(u8, ins, "syscall")) { | 5141 | if (mem.eql(u8, ins, "syscall")) { |
| 5220 | _ = try self.addInst(.{ | 5142 | try self.asmOpOnly(.syscall); |
| 5221 | .tag = .syscall, | ||
| 5222 | .ops = undefined, | ||
| 5223 | .data = undefined, | ||
| 5224 | }); | ||
| 5225 | } else if (mem.indexOf(u8, ins, "push")) |_| { | 5143 | } else if (mem.indexOf(u8, ins, "push")) |_| { |
| 5226 | const arg = ins[4..]; | 5144 | const arg = ins[4..]; |
| 5227 | if (mem.indexOf(u8, arg, "$")) |l| { | 5145 | if (mem.indexOf(u8, arg, "$")) |l| { |
| 5228 | const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { | 5146 | const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { |
| 5229 | return self.fail("TODO implement more inline asm int parsing", .{}); | 5147 | return self.fail("TODO implement more inline asm int parsing", .{}); |
| 5230 | }; | 5148 | }; |
| 5231 | _ = try self.addInst(.{ | 5149 | try self.asmImmediate(.push, Immediate.u(n)); |
| 5232 | .tag = .push, | ||
| 5233 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b10 }), | ||
| 5234 | .data = .{ .imm = n }, | ||
| 5235 | }); | ||
| 5236 | } else if (mem.indexOf(u8, arg, "%%")) |l| { | 5150 | } else if (mem.indexOf(u8, arg, "%%")) |l| { |
| 5237 | const reg_name = ins[4 + l + 2 ..]; | 5151 | const reg_name = ins[4 + l + 2 ..]; |
| 5238 | const reg = parseRegName(reg_name) orelse | 5152 | const reg = parseRegName(reg_name) orelse |
| 5239 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 5153 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 5240 | _ = try self.addInst(.{ | 5154 | try self.asmRegister(.push, reg); |
| 5241 | .tag = .push, | ||
| 5242 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), | ||
| 5243 | .data = undefined, | ||
| 5244 | }); | ||
| 5245 | } else return self.fail("TODO more push operands", .{}); | 5155 | } else return self.fail("TODO more push operands", .{}); |
| 5246 | } else if (mem.indexOf(u8, ins, "pop")) |_| { | 5156 | } else if (mem.indexOf(u8, ins, "pop")) |_| { |
| 5247 | const arg = ins[3..]; | 5157 | const arg = ins[3..]; |
| ... | @@ -5249,11 +5159,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5249,11 +5159,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5249 | const reg_name = ins[3 + l + 2 ..]; | 5159 | const reg_name = ins[3 + l + 2 ..]; |
| 5250 | const reg = parseRegName(reg_name) orelse | 5160 | const reg = parseRegName(reg_name) orelse |
| 5251 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 5161 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 5252 | _ = try self.addInst(.{ | 5162 | try self.asmRegister(.pop, reg); |
| 5253 | .tag = .pop, | ||
| 5254 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), | ||
| 5255 | .data = undefined, | ||
| 5256 | }); | ||
| 5257 | } else return self.fail("TODO more pop operands", .{}); | 5163 | } else return self.fail("TODO more pop operands", .{}); |
| 5258 | } else { | 5164 | } else { |
| 5259 | return self.fail("TODO implement support for more x86 assembly instructions", .{}); | 5165 | return self.fail("TODO implement support for more x86 assembly instructions", .{}); |
| ... | @@ -5268,9 +5174,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5268,9 +5174,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5268 | const reg_name = output[2 .. output.len - 1]; | 5174 | const reg_name = output[2 .. output.len - 1]; |
| 5269 | const reg = parseRegName(reg_name) orelse | 5175 | const reg = parseRegName(reg_name) orelse |
| 5270 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 5176 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 5271 | break :result MCValue{ .register = reg }; | 5177 | break :result .{ .register = reg }; |
| 5272 | } else { | 5178 | } else { |
| 5273 | break :result MCValue{ .none = {} }; | 5179 | break :result .none; |
| 5274 | } | 5180 | } |
| 5275 | }; | 5181 | }; |
| 5276 | 5182 | ||
| ... | @@ -5326,7 +5232,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { | ... | @@ -5326,7 +5232,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 5326 | } | 5232 | } |
| 5327 | 5233 | ||
| 5328 | fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { | 5234 | fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { |
| 5329 | const abi_size = ty.abiSize(self.target.*); | 5235 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 5330 | switch (mcv) { | 5236 | switch (mcv) { |
| 5331 | .dead => unreachable, | 5237 | .dead => unreachable, |
| 5332 | .unreach, .none => return, | 5238 | .unreach, .none => return, |
| ... | @@ -5350,26 +5256,17 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -5350,26 +5256,17 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5350 | .immediate => |imm| { | 5256 | .immediate => |imm| { |
| 5351 | switch (abi_size) { | 5257 | switch (abi_size) { |
| 5352 | 1, 2, 4 => { | 5258 | 1, 2, 4 => { |
| 5259 | // TODO | ||
| 5353 | // We have a positive stack offset value but we want a twos complement negative | 5260 | // We have a positive stack offset value but we want a twos complement negative |
| 5354 | // offset from rbp, which is at the top of the stack frame. | 5261 | // offset from rbp, which is at the top of the stack frame. |
| 5355 | // mov [rbp+offset], immediate | 5262 | const immediate = if (ty.isSignedInt()) |
| 5356 | const payload = try self.addExtra(Mir.ImmPair{ | 5263 | Immediate.s(@intCast(i32, @bitCast(i64, imm))) |
| 5357 | .dest_off = @bitCast(u32, -stack_offset), | 5264 | else |
| 5358 | .operand = @truncate(u32, imm), | 5265 | Immediate.u(@intCast(u32, imm)); |
| 5359 | }); | 5266 | try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 5360 | _ = try self.addInst(.{ | 5267 | .base = .rsp, |
| 5361 | .tag = .mov_mem_imm, | 5268 | .disp = -stack_offset, |
| 5362 | .ops = Mir.Inst.Ops.encode(.{ | 5269 | }), immediate); |
| 5363 | .reg1 = .rsp, | ||
| 5364 | .flags = switch (abi_size) { | ||
| 5365 | 1 => 0b00, | ||
| 5366 | 2 => 0b01, | ||
| 5367 | 4 => 0b10, | ||
| 5368 | else => unreachable, | ||
| 5369 | }, | ||
| 5370 | }), | ||
| 5371 | .data = .{ .payload = payload }, | ||
| 5372 | }); | ||
| 5373 | }, | 5270 | }, |
| 5374 | 8 => { | 5271 | 8 => { |
| 5375 | const reg = try self.copyToTmpRegister(ty, mcv); | 5272 | const reg = try self.copyToTmpRegister(ty, mcv); |
| ... | @@ -5394,44 +5291,32 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -5394,44 +5291,32 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5394 | .Float => { | 5291 | .Float => { |
| 5395 | if (intrinsicsAllowed(self.target.*, ty)) { | 5292 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 5396 | const tag: Mir.Inst.Tag = switch (ty.tag()) { | 5293 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 5397 | .f32 => if (hasAvxSupport(self.target.*)) | 5294 | .f32 => .movss, |
| 5398 | Mir.Inst.Tag.mov_f32_avx | 5295 | .f64 => .movsd, |
| 5399 | else | 5296 | else => return self.fail( |
| 5400 | Mir.Inst.Tag.mov_f32_sse, | 5297 | "TODO genSetStackArg for register for type {}", |
| 5401 | .f64 => if (hasAvxSupport(self.target.*)) | 5298 | .{ty.fmtDebug()}, |
| 5402 | Mir.Inst.Tag.mov_f64_avx | 5299 | ), |
| 5403 | else | ||
| 5404 | Mir.Inst.Tag.mov_f64_sse, | ||
| 5405 | else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), | ||
| 5406 | }; | 5300 | }; |
| 5407 | _ = try self.addInst(.{ | 5301 | // TODO verify this |
| 5408 | .tag = tag, | 5302 | const ptr_size: Memory.PtrSize = switch (ty.tag()) { |
| 5409 | .ops = Mir.Inst.Ops.encode(.{ | 5303 | .f32 => .dword, |
| 5410 | .reg1 = switch (ty.tag()) { | 5304 | .f64 => .qword, |
| 5411 | .f32 => .esp, | 5305 | else => unreachable, |
| 5412 | .f64 => .rsp, | 5306 | }; |
| 5413 | else => unreachable, | 5307 | return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{ |
| 5414 | }, | 5308 | .base = .rsp, |
| 5415 | .reg2 = reg.to128(), | 5309 | .disp = -stack_offset, |
| 5416 | .flags = 0b01, | 5310 | }), reg.to128()); |
| 5417 | }), | ||
| 5418 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, | ||
| 5419 | }); | ||
| 5420 | return; | ||
| 5421 | } | 5311 | } |
| 5422 | 5312 | ||
| 5423 | return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); | 5313 | return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); |
| 5424 | }, | 5314 | }, |
| 5425 | else => { | 5315 | else => { |
| 5426 | _ = try self.addInst(.{ | 5316 | try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 5427 | .tag = .mov, | 5317 | .base = .rsp, |
| 5428 | .ops = Mir.Inst.Ops.encode(.{ | 5318 | .disp = -stack_offset, |
| 5429 | .reg1 = .rsp, | 5319 | }), registerAlias(reg, abi_size)); |
| 5430 | .reg2 = registerAlias(reg, @intCast(u32, abi_size)), | ||
| 5431 | .flags = 0b10, | ||
| 5432 | }), | ||
| 5433 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, | ||
| 5434 | }); | ||
| 5435 | }, | 5320 | }, |
| 5436 | } | 5321 | } |
| 5437 | }, | 5322 | }, |
| ... | @@ -5454,7 +5339,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -5454,7 +5339,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5454 | } | 5339 | } |
| 5455 | 5340 | ||
| 5456 | fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: InlineMemcpyOpts) InnerError!void { | 5341 | fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: InlineMemcpyOpts) InnerError!void { |
| 5457 | const abi_size = ty.abiSize(self.target.*); | 5342 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 5458 | switch (mcv) { | 5343 | switch (mcv) { |
| 5459 | .dead => unreachable, | 5344 | .dead => unreachable, |
| 5460 | .unreach, .none => return, // Nothing to do. | 5345 | .unreach, .none => return, // Nothing to do. |
| ... | @@ -5462,10 +5347,19 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5462,10 +5347,19 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5462 | if (!self.wantSafety()) | 5347 | if (!self.wantSafety()) |
| 5463 | return; // The already existing value will do just fine. | 5348 | return; // The already existing value will do just fine. |
| 5464 | // TODO Upgrade this to a memset call when we have that available. | 5349 | // TODO Upgrade this to a memset call when we have that available. |
| 5465 | switch (ty.abiSize(self.target.*)) { | 5350 | switch (abi_size) { |
| 5466 | 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }, opts), | 5351 | 1, 2, 4 => { |
| 5467 | 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }, opts), | 5352 | const value: u64 = switch (abi_size) { |
| 5468 | 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }, opts), | 5353 | 1 => 0xaa, |
| 5354 | 2 => 0xaaaa, | ||
| 5355 | 4 => 0xaaaaaaaa, | ||
| 5356 | else => unreachable, | ||
| 5357 | }; | ||
| 5358 | return self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ | ||
| 5359 | .base = opts.dest_stack_base orelse .rbp, | ||
| 5360 | .disp = -stack_offset, | ||
| 5361 | }), Immediate.u(value)); | ||
| 5362 | }, | ||
| 5469 | 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts), | 5363 | 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts), |
| 5470 | else => |x| return self.genInlineMemset( | 5364 | else => |x| return self.genInlineMemset( |
| 5471 | .{ .stack_offset = stack_offset }, | 5365 | .{ .stack_offset = stack_offset }, |
| ... | @@ -5485,13 +5379,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5485,13 +5379,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5485 | const overflow_bit_ty = ty.structFieldType(1); | 5379 | const overflow_bit_ty = ty.structFieldType(1); |
| 5486 | const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); | 5380 | const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); |
| 5487 | const tmp_reg = try self.register_manager.allocReg(null, gp); | 5381 | const tmp_reg = try self.register_manager.allocReg(null, gp); |
| 5488 | _ = try self.addInst(.{ | 5382 | try self.asmSetccRegister(tmp_reg.to8(), ro.eflags); |
| 5489 | .tag = .cond_set_byte, | ||
| 5490 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 5491 | .reg1 = tmp_reg.to8(), | ||
| 5492 | }), | ||
| 5493 | .data = .{ .cc = ro.eflags }, | ||
| 5494 | }); | ||
| 5495 | 5383 | ||
| 5496 | return self.genSetStack( | 5384 | return self.genSetStack( |
| 5497 | overflow_bit_ty, | 5385 | overflow_bit_ty, |
| ... | @@ -5506,72 +5394,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5506,72 +5394,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5506 | }, | 5394 | }, |
| 5507 | .immediate => |x_big| { | 5395 | .immediate => |x_big| { |
| 5508 | const base_reg = opts.dest_stack_base orelse .rbp; | 5396 | const base_reg = opts.dest_stack_base orelse .rbp; |
| 5397 | // TODO | ||
| 5509 | switch (abi_size) { | 5398 | switch (abi_size) { |
| 5510 | 0 => { | 5399 | 0 => { |
| 5511 | assert(ty.isError()); | 5400 | assert(ty.isError()); |
| 5512 | const payload = try self.addExtra(Mir.ImmPair{ | 5401 | try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ |
| 5513 | .dest_off = @bitCast(u32, -stack_offset), | 5402 | .base = base_reg, |
| 5514 | .operand = @truncate(u32, x_big), | 5403 | .disp = -stack_offset, |
| 5515 | }); | 5404 | }), Immediate.u(@truncate(u8, x_big))); |
| 5516 | _ = try self.addInst(.{ | ||
| 5517 | .tag = .mov_mem_imm, | ||
| 5518 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 5519 | .reg1 = base_reg, | ||
| 5520 | .flags = 0b00, | ||
| 5521 | }), | ||
| 5522 | .data = .{ .payload = payload }, | ||
| 5523 | }); | ||
| 5524 | }, | 5405 | }, |
| 5525 | 1, 2, 4 => { | 5406 | 1, 2, 4 => { |
| 5526 | const payload = try self.addExtra(Mir.ImmPair{ | 5407 | const immediate = if (ty.isSignedInt()) |
| 5527 | .dest_off = @bitCast(u32, -stack_offset), | 5408 | Immediate.s(@truncate(i32, @bitCast(i64, x_big))) |
| 5528 | .operand = @truncate(u32, x_big), | 5409 | else |
| 5529 | }); | 5410 | Immediate.u(@intCast(u32, x_big)); |
| 5530 | _ = try self.addInst(.{ | 5411 | try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 5531 | .tag = .mov_mem_imm, | 5412 | .base = base_reg, |
| 5532 | .ops = Mir.Inst.Ops.encode(.{ | 5413 | .disp = -stack_offset, |
| 5533 | .reg1 = base_reg, | 5414 | }), immediate); |
| 5534 | .flags = switch (abi_size) { | ||
| 5535 | 1 => 0b00, | ||
| 5536 | 2 => 0b01, | ||
| 5537 | 4 => 0b10, | ||
| 5538 | else => unreachable, | ||
| 5539 | }, | ||
| 5540 | }), | ||
| 5541 | .data = .{ .payload = payload }, | ||
| 5542 | }); | ||
| 5543 | }, | 5415 | }, |
| 5544 | 8 => { | 5416 | 8 => { |
| 5545 | // 64 bit write to memory would take two mov's anyways so we | 5417 | // 64 bit write to memory would take two mov's anyways so we |
| 5546 | // insted just use two 32 bit writes to avoid register allocation | 5418 | // insted just use two 32 bit writes to avoid register allocation |
| 5547 | { | 5419 | try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ |
| 5548 | const payload = try self.addExtra(Mir.ImmPair{ | 5420 | .base = base_reg, |
| 5549 | .dest_off = @bitCast(u32, -stack_offset + 4), | 5421 | .disp = -stack_offset + 4, |
| 5550 | .operand = @truncate(u32, x_big >> 32), | 5422 | }), Immediate.u(@truncate(u32, x_big >> 32))); |
| 5551 | }); | 5423 | try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ |
| 5552 | _ = try self.addInst(.{ | 5424 | .base = base_reg, |
| 5553 | .tag = .mov_mem_imm, | 5425 | .disp = -stack_offset, |
| 5554 | .ops = Mir.Inst.Ops.encode(.{ | 5426 | }), Immediate.u(@truncate(u32, x_big))); |
| 5555 | .reg1 = base_reg, | ||
| 5556 | .flags = 0b10, | ||
| 5557 | }), | ||
| 5558 | .data = .{ .payload = payload }, | ||
| 5559 | }); | ||
| 5560 | } | ||
| 5561 | { | ||
| 5562 | const payload = try self.addExtra(Mir.ImmPair{ | ||
| 5563 | .dest_off = @bitCast(u32, -stack_offset), | ||
| 5564 | .operand = @truncate(u32, x_big), | ||
| 5565 | }); | ||
| 5566 | _ = try self.addInst(.{ | ||
| 5567 | .tag = .mov_mem_imm, | ||
| 5568 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 5569 | .reg1 = base_reg, | ||
| 5570 | .flags = 0b10, | ||
| 5571 | }), | ||
| 5572 | .data = .{ .payload = payload }, | ||
| 5573 | }); | ||
| 5574 | } | ||
| 5575 | }, | 5427 | }, |
| 5576 | else => { | 5428 | else => { |
| 5577 | return self.fail("TODO implement set abi_size=large stack variable with immediate", .{}); | 5429 | return self.fail("TODO implement set abi_size=large stack variable with immediate", .{}); |
| ... | @@ -5589,30 +5441,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5589,30 +5441,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5589 | .Float => { | 5441 | .Float => { |
| 5590 | if (intrinsicsAllowed(self.target.*, ty)) { | 5442 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 5591 | const tag: Mir.Inst.Tag = switch (ty.tag()) { | 5443 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 5592 | .f32 => if (hasAvxSupport(self.target.*)) | 5444 | .f32 => .movss, |
| 5593 | Mir.Inst.Tag.mov_f32_avx | 5445 | .f64 => .movsd, |
| 5594 | else | 5446 | else => return self.fail( |
| 5595 | Mir.Inst.Tag.mov_f32_sse, | 5447 | "TODO genSetStack for register for type {}", |
| 5596 | .f64 => if (hasAvxSupport(self.target.*)) | 5448 | .{ty.fmtDebug()}, |
| 5597 | Mir.Inst.Tag.mov_f64_avx | 5449 | ), |
| 5598 | else | ||
| 5599 | Mir.Inst.Tag.mov_f64_sse, | ||
| 5600 | else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), | ||
| 5601 | }; | 5450 | }; |
| 5602 | _ = try self.addInst(.{ | 5451 | const ptr_size: Memory.PtrSize = switch (ty.tag()) { |
| 5603 | .tag = tag, | 5452 | .f32 => .dword, |
| 5604 | .ops = Mir.Inst.Ops.encode(.{ | 5453 | .f64 => .qword, |
| 5605 | .reg1 = switch (ty.tag()) { | 5454 | else => unreachable, |
| 5606 | .f32 => base_reg.to32(), | 5455 | }; |
| 5607 | .f64 => base_reg.to64(), | 5456 | return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{ |
| 5608 | else => unreachable, | 5457 | .base = base_reg.to64(), |
| 5609 | }, | 5458 | .disp = -stack_offset, |
| 5610 | .reg2 = reg.to128(), | 5459 | }), reg.to128()); |
| 5611 | .flags = 0b01, | ||
| 5612 | }), | ||
| 5613 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, | ||
| 5614 | }); | ||
| 5615 | return; | ||
| 5616 | } | 5460 | } |
| 5617 | 5461 | ||
| 5618 | return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); | 5462 | return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); |
| ... | @@ -5660,7 +5504,7 @@ fn genInlineMemcpyRegisterRegister( | ... | @@ -5660,7 +5504,7 @@ fn genInlineMemcpyRegisterRegister( |
| 5660 | src_reg: Register, | 5504 | src_reg: Register, |
| 5661 | offset: i32, | 5505 | offset: i32, |
| 5662 | ) InnerError!void { | 5506 | ) InnerError!void { |
| 5663 | assert(dst_reg.size() == 64); | 5507 | assert(dst_reg.bitSize() == 64); |
| 5664 | 5508 | ||
| 5665 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); | 5509 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 5666 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); | 5510 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | @@ -5677,16 +5521,10 @@ fn genInlineMemcpyRegisterRegister( | ... | @@ -5677,16 +5521,10 @@ fn genInlineMemcpyRegisterRegister( |
| 5677 | var remainder = abi_size; | 5521 | var remainder = abi_size; |
| 5678 | while (remainder > 0) { | 5522 | while (remainder > 0) { |
| 5679 | const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); | 5523 | const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); |
| 5680 | 5524 | try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(nearest_power_of_two), .{ | |
| 5681 | _ = try self.addInst(.{ | 5525 | .base = dst_reg, |
| 5682 | .tag = .mov, | 5526 | .disp = -next_offset, |
| 5683 | .ops = Mir.Inst.Ops.encode(.{ | 5527 | }), registerAlias(tmp_reg, nearest_power_of_two)); |
| 5684 | .reg1 = dst_reg, | ||
| 5685 | .reg2 = registerAlias(tmp_reg, nearest_power_of_two), | ||
| 5686 | .flags = 0b10, | ||
| 5687 | }), | ||
| 5688 | .data = .{ .imm = @bitCast(u32, -next_offset) }, | ||
| 5689 | }); | ||
| 5690 | 5528 | ||
| 5691 | if (nearest_power_of_two > 1) { | 5529 | if (nearest_power_of_two > 1) { |
| 5692 | try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ | 5530 | try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ |
| ... | @@ -5698,15 +5536,10 @@ fn genInlineMemcpyRegisterRegister( | ... | @@ -5698,15 +5536,10 @@ fn genInlineMemcpyRegisterRegister( |
| 5698 | next_offset -= nearest_power_of_two; | 5536 | next_offset -= nearest_power_of_two; |
| 5699 | } | 5537 | } |
| 5700 | } else { | 5538 | } else { |
| 5701 | _ = try self.addInst(.{ | 5539 | try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 5702 | .tag = .mov, | 5540 | .base = dst_reg, |
| 5703 | .ops = Mir.Inst.Ops.encode(.{ | 5541 | .disp = -offset, |
| 5704 | .reg1 = dst_reg, | 5542 | }), registerAlias(src_reg, abi_size)); |
| 5705 | .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), | ||
| 5706 | .flags = 0b10, | ||
| 5707 | }), | ||
| 5708 | .data = .{ .imm = @bitCast(u32, -offset) }, | ||
| 5709 | }); | ||
| 5710 | } | 5543 | } |
| 5711 | } | 5544 | } |
| 5712 | 5545 | ||
| ... | @@ -5746,24 +5579,17 @@ fn genInlineMemcpy( | ... | @@ -5746,24 +5579,17 @@ fn genInlineMemcpy( |
| 5746 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); | 5579 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); |
| 5747 | }, | 5580 | }, |
| 5748 | .ptr_stack_offset, .stack_offset => |off| { | 5581 | .ptr_stack_offset, .stack_offset => |off| { |
| 5749 | _ = try self.addInst(.{ | 5582 | try self.asmRegisterMemory(.lea, dst_addr_reg.to64(), Memory.sib(.qword, .{ |
| 5750 | .tag = .lea, | 5583 | .base = opts.dest_stack_base orelse .rbp, |
| 5751 | .ops = Mir.Inst.Ops.encode(.{ | 5584 | .disp = -off, |
| 5752 | .reg1 = dst_addr_reg.to64(), | 5585 | })); |
| 5753 | .reg2 = opts.dest_stack_base orelse .rbp, | ||
| 5754 | }), | ||
| 5755 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 5756 | }); | ||
| 5757 | }, | 5586 | }, |
| 5758 | .register => |reg| { | 5587 | .register => |reg| { |
| 5759 | _ = try self.addInst(.{ | 5588 | try self.asmRegisterRegister( |
| 5760 | .tag = .mov, | 5589 | .mov, |
| 5761 | .ops = Mir.Inst.Ops.encode(.{ | 5590 | registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), |
| 5762 | .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)), | 5591 | reg, |
| 5763 | .reg2 = reg, | 5592 | ); |
| 5764 | }), | ||
| 5765 | .data = undefined, | ||
| 5766 | }); | ||
| 5767 | }, | 5593 | }, |
| 5768 | else => { | 5594 | else => { |
| 5769 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); | 5595 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| ... | @@ -5775,24 +5601,17 @@ fn genInlineMemcpy( | ... | @@ -5775,24 +5601,17 @@ fn genInlineMemcpy( |
| 5775 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); | 5601 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); |
| 5776 | }, | 5602 | }, |
| 5777 | .ptr_stack_offset, .stack_offset => |off| { | 5603 | .ptr_stack_offset, .stack_offset => |off| { |
| 5778 | _ = try self.addInst(.{ | 5604 | try self.asmRegisterMemory(.lea, src_addr_reg.to64(), Memory.sib(.qword, .{ |
| 5779 | .tag = .lea, | 5605 | .base = opts.source_stack_base orelse .rbp, |
| 5780 | .ops = Mir.Inst.Ops.encode(.{ | 5606 | .disp = -off, |
| 5781 | .reg1 = src_addr_reg.to64(), | 5607 | })); |
| 5782 | .reg2 = opts.source_stack_base orelse .rbp, | ||
| 5783 | }), | ||
| 5784 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 5785 | }); | ||
| 5786 | }, | 5608 | }, |
| 5787 | .register => |reg| { | 5609 | .register => |reg| { |
| 5788 | _ = try self.addInst(.{ | 5610 | try self.asmRegisterRegister( |
| 5789 | .tag = .mov, | 5611 | .mov, |
| 5790 | .ops = Mir.Inst.Ops.encode(.{ | 5612 | registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), |
| 5791 | .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)), | 5613 | reg, |
| 5792 | .reg2 = reg, | 5614 | ); |
| 5793 | }), | ||
| 5794 | .data = undefined, | ||
| 5795 | }); | ||
| 5796 | }, | 5615 | }, |
| 5797 | else => { | 5616 | else => { |
| 5798 | return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); | 5617 | return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); |
| ... | @@ -5800,74 +5619,35 @@ fn genInlineMemcpy( | ... | @@ -5800,74 +5619,35 @@ fn genInlineMemcpy( |
| 5800 | } | 5619 | } |
| 5801 | 5620 | ||
| 5802 | try self.genSetReg(Type.usize, count_reg, len); | 5621 | try self.genSetReg(Type.usize, count_reg, len); |
| 5803 | 5622 | try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0)); | |
| 5804 | // mov index_reg, 0 | ||
| 5805 | _ = try self.addInst(.{ | ||
| 5806 | .tag = .mov, | ||
| 5807 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | ||
| 5808 | .data = .{ .imm = 0 }, | ||
| 5809 | }); | ||
| 5810 | |||
| 5811 | // loop: | ||
| 5812 | // cmp count, 0 | ||
| 5813 | const loop_start = try self.addInst(.{ | 5623 | const loop_start = try self.addInst(.{ |
| 5814 | .tag = .cmp, | 5624 | .tag = .cmp, |
| 5815 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), | 5625 | .ops = .ri_u, |
| 5816 | .data = .{ .imm = 0 }, | 5626 | .data = .{ .ri = .{ |
| 5817 | }); | 5627 | .r1 = count_reg, |
| 5818 | 5628 | .imm = 0, | |
| 5819 | // je end | ||
| 5820 | const loop_reloc = try self.addInst(.{ | ||
| 5821 | .tag = .cond_jmp, | ||
| 5822 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 5823 | .data = .{ .inst_cc = .{ | ||
| 5824 | .inst = undefined, | ||
| 5825 | .cc = .e, | ||
| 5826 | } }, | 5629 | } }, |
| 5827 | }); | 5630 | }); |
| 5828 | 5631 | const loop_reloc = try self.asmJccReloc(undefined, .e); | |
| 5829 | // mov tmp, [addr + index_reg] | 5632 | try self.asmRegisterMemory(.mov, tmp_reg.to8(), Memory.sib(.byte, .{ |
| 5830 | _ = try self.addInst(.{ | 5633 | .base = src_addr_reg, |
| 5831 | .tag = .mov_scale_src, | 5634 | .scale_index = .{ |
| 5832 | .ops = Mir.Inst.Ops.encode(.{ | 5635 | .scale = 1, |
| 5833 | .reg1 = tmp_reg.to8(), | 5636 | .index = index_reg, |
| 5834 | .reg2 = src_addr_reg, | 5637 | }, |
| 5835 | }), | 5638 | .disp = 0, |
| 5836 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | 5639 | })); |
| 5837 | }); | 5640 | try self.asmMemoryRegister(.mov, Memory.sib(.byte, .{ |
| 5838 | 5641 | .base = dst_addr_reg, | |
| 5839 | // mov [stack_offset + index_reg], tmp | 5642 | .scale_index = .{ |
| 5840 | _ = try self.addInst(.{ | 5643 | .scale = 1, |
| 5841 | .tag = .mov_scale_dst, | 5644 | .index = index_reg, |
| 5842 | .ops = Mir.Inst.Ops.encode(.{ | 5645 | }, |
| 5843 | .reg1 = dst_addr_reg, | 5646 | .disp = 0, |
| 5844 | .reg2 = tmp_reg.to8(), | 5647 | }), tmp_reg.to8()); |
| 5845 | }), | 5648 | try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1)); |
| 5846 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | 5649 | try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); |
| 5847 | }); | 5650 | _ = try self.asmJmpReloc(loop_start); |
| 5848 | |||
| 5849 | // add index_reg, 1 | ||
| 5850 | _ = try self.addInst(.{ | ||
| 5851 | .tag = .add, | ||
| 5852 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | ||
| 5853 | .data = .{ .imm = 1 }, | ||
| 5854 | }); | ||
| 5855 | |||
| 5856 | // sub count, 1 | ||
| 5857 | _ = try self.addInst(.{ | ||
| 5858 | .tag = .sub, | ||
| 5859 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), | ||
| 5860 | .data = .{ .imm = 1 }, | ||
| 5861 | }); | ||
| 5862 | |||
| 5863 | // jmp loop | ||
| 5864 | _ = try self.addInst(.{ | ||
| 5865 | .tag = .jmp, | ||
| 5866 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 5867 | .data = .{ .inst = loop_start }, | ||
| 5868 | }); | ||
| 5869 | |||
| 5870 | // end: | ||
| 5871 | try self.performReloc(loop_reloc); | 5651 | try self.performReloc(loop_reloc); |
| 5872 | } | 5652 | } |
| 5873 | 5653 | ||
| ... | @@ -5899,24 +5679,17 @@ fn genInlineMemset( | ... | @@ -5899,24 +5679,17 @@ fn genInlineMemset( |
| 5899 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); | 5679 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); |
| 5900 | }, | 5680 | }, |
| 5901 | .ptr_stack_offset, .stack_offset => |off| { | 5681 | .ptr_stack_offset, .stack_offset => |off| { |
| 5902 | _ = try self.addInst(.{ | 5682 | try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ |
| 5903 | .tag = .lea, | 5683 | .base = opts.dest_stack_base orelse .rbp, |
| 5904 | .ops = Mir.Inst.Ops.encode(.{ | 5684 | .disp = -off, |
| 5905 | .reg1 = addr_reg.to64(), | 5685 | })); |
| 5906 | .reg2 = opts.dest_stack_base orelse .rbp, | ||
| 5907 | }), | ||
| 5908 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 5909 | }); | ||
| 5910 | }, | 5686 | }, |
| 5911 | .register => |reg| { | 5687 | .register => |reg| { |
| 5912 | _ = try self.addInst(.{ | 5688 | try self.asmRegisterRegister( |
| 5913 | .tag = .mov, | 5689 | .mov, |
| 5914 | .ops = Mir.Inst.Ops.encode(.{ | 5690 | registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), |
| 5915 | .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)), | 5691 | reg, |
| 5916 | .reg2 = reg, | 5692 | ); |
| 5917 | }), | ||
| 5918 | .data = undefined, | ||
| 5919 | }); | ||
| 5920 | }, | 5693 | }, |
| 5921 | else => { | 5694 | else => { |
| 5922 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); | 5695 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| ... | @@ -5926,54 +5699,35 @@ fn genInlineMemset( | ... | @@ -5926,54 +5699,35 @@ fn genInlineMemset( |
| 5926 | try self.genSetReg(Type.usize, index_reg, len); | 5699 | try self.genSetReg(Type.usize, index_reg, len); |
| 5927 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); | 5700 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); |
| 5928 | 5701 | ||
| 5929 | // loop: | ||
| 5930 | // cmp index_reg, -1 | ||
| 5931 | const loop_start = try self.addInst(.{ | 5702 | const loop_start = try self.addInst(.{ |
| 5932 | .tag = .cmp, | 5703 | .tag = .cmp, |
| 5933 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | 5704 | .ops = .ri_s, |
| 5934 | .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, | 5705 | .data = .{ .ri = .{ |
| 5935 | }); | 5706 | .r1 = index_reg, |
| 5936 | 5707 | .imm = @bitCast(u32, @as(i32, -1)), | |
| 5937 | // je end | ||
| 5938 | const loop_reloc = try self.addInst(.{ | ||
| 5939 | .tag = .cond_jmp, | ||
| 5940 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 5941 | .data = .{ .inst_cc = .{ | ||
| 5942 | .inst = undefined, | ||
| 5943 | .cc = .e, | ||
| 5944 | } }, | 5708 | } }, |
| 5945 | }); | 5709 | }); |
| 5710 | const loop_reloc = try self.asmJccReloc(undefined, .e); | ||
| 5946 | 5711 | ||
| 5947 | switch (value) { | 5712 | switch (value) { |
| 5948 | .immediate => |x| { | 5713 | .immediate => |x| { |
| 5949 | if (x > math.maxInt(i32)) { | 5714 | if (x > math.maxInt(i32)) { |
| 5950 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); | 5715 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); |
| 5951 | } | 5716 | } |
| 5952 | // mov byte ptr [rbp + index_reg + stack_offset], imm | 5717 | try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ |
| 5953 | _ = try self.addInst(.{ | 5718 | .base = addr_reg, |
| 5954 | .tag = .mov_mem_index_imm, | 5719 | .scale_index = .{ |
| 5955 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), | 5720 | .scale = 1, |
| 5956 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) }, | 5721 | .index = index_reg, |
| 5957 | }); | 5722 | }, |
| 5723 | .disp = 0, | ||
| 5724 | }), Immediate.u(@intCast(u8, x))); | ||
| 5958 | }, | 5725 | }, |
| 5959 | else => return self.fail("TODO inline memset for value of type {}", .{value}), | 5726 | else => return self.fail("TODO inline memset for value of type {}", .{value}), |
| 5960 | } | 5727 | } |
| 5961 | 5728 | ||
| 5962 | // sub index_reg, 1 | 5729 | try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1)); |
| 5963 | _ = try self.addInst(.{ | 5730 | _ = try self.asmJmpReloc(loop_start); |
| 5964 | .tag = .sub, | ||
| 5965 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | ||
| 5966 | .data = .{ .imm = 1 }, | ||
| 5967 | }); | ||
| 5968 | |||
| 5969 | // jmp loop | ||
| 5970 | _ = try self.addInst(.{ | ||
| 5971 | .tag = .jmp, | ||
| 5972 | .ops = Mir.Inst.Ops.encode(.{}), | ||
| 5973 | .data = .{ .inst = loop_start }, | ||
| 5974 | }); | ||
| 5975 | |||
| 5976 | // end: | ||
| 5977 | try self.performReloc(loop_reloc); | 5731 | try self.performReloc(loop_reloc); |
| 5978 | } | 5732 | } |
| 5979 | 5733 | ||
| ... | @@ -5986,21 +5740,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5986,21 +5740,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5986 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { | 5740 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| 5987 | return self.fail("stack offset too large", .{}); | 5741 | return self.fail("stack offset too large", .{}); |
| 5988 | } | 5742 | } |
| 5989 | _ = try self.addInst(.{ | 5743 | try self.asmRegisterMemory( |
| 5990 | .tag = .lea, | 5744 | .lea, |
| 5991 | .ops = Mir.Inst.Ops.encode(.{ | 5745 | registerAlias(reg, abi_size), |
| 5992 | .reg1 = registerAlias(reg, abi_size), | 5746 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), |
| 5993 | .reg2 = .rbp, | 5747 | ); |
| 5994 | }), | ||
| 5995 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 5996 | }); | ||
| 5997 | }, | 5748 | }, |
| 5998 | .unreach, .none => return, // Nothing to do. | 5749 | .unreach, .none => return, // Nothing to do. |
| 5999 | .undef => { | 5750 | .undef => { |
| 6000 | if (!self.wantSafety()) | 5751 | if (!self.wantSafety()) |
| 6001 | return; // The already existing value will do just fine. | 5752 | return; // The already existing value will do just fine. |
| 6002 | // Write the debug undefined value. | 5753 | // Write the debug undefined value. |
| 6003 | switch (registerAlias(reg, abi_size).size()) { | 5754 | switch (registerAlias(reg, abi_size).bitSize()) { |
| 6004 | 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }), | 5755 | 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }), |
| 6005 | 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }), | 5756 | 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }), |
| 6006 | 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), | 5757 | 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), |
| ... | @@ -6009,50 +5760,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -6009,50 +5760,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6009 | } | 5760 | } |
| 6010 | }, | 5761 | }, |
| 6011 | .eflags => |cc| { | 5762 | .eflags => |cc| { |
| 6012 | _ = try self.addInst(.{ | 5763 | return self.asmSetccRegister(reg.to8(), cc); |
| 6013 | .tag = .cond_set_byte, | ||
| 6014 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 6015 | .reg1 = reg.to8(), | ||
| 6016 | }), | ||
| 6017 | .data = .{ .cc = cc }, | ||
| 6018 | }); | ||
| 6019 | }, | 5764 | }, |
| 6020 | .immediate => |x| { | 5765 | .immediate => |x| { |
| 6021 | // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit | ||
| 6022 | // register is the fastest way to zero a register. | ||
| 6023 | if (x == 0) { | 5766 | if (x == 0) { |
| 6024 | _ = try self.addInst(.{ | 5767 | // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit |
| 6025 | .tag = .xor, | 5768 | // register is the fastest way to zero a register. |
| 6026 | .ops = Mir.Inst.Ops.encode(.{ | 5769 | return self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); |
| 6027 | .reg1 = reg.to32(), | ||
| 6028 | .reg2 = reg.to32(), | ||
| 6029 | }), | ||
| 6030 | .data = undefined, | ||
| 6031 | }); | ||
| 6032 | return; | ||
| 6033 | } | 5770 | } |
| 6034 | if (x <= math.maxInt(i32)) { | 5771 | if (ty.isSignedInt()) { |
| 6035 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. | 5772 | const signed_x = @bitCast(i64, x); |
| 6036 | _ = try self.addInst(.{ | 5773 | if (math.minInt(i32) <= signed_x and signed_x <= math.maxInt(i32)) { |
| 6037 | .tag = .mov, | 5774 | return self.asmRegisterImmediate( |
| 6038 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), | 5775 | .mov, |
| 6039 | .data = .{ .imm = @truncate(u32, x) }, | 5776 | registerAlias(reg, abi_size), |
| 6040 | }); | 5777 | Immediate.s(@intCast(i32, signed_x)), |
| 6041 | return; | 5778 | ); |
| 5779 | } | ||
| 6042 | } | 5780 | } |
| 6043 | // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls | 5781 | return self.asmRegisterImmediate( |
| 6044 | // this `movabs`, though this is officially just a different variant of the plain `mov` | 5782 | .mov, |
| 6045 | // instruction. | 5783 | registerAlias(reg, abi_size), |
| 6046 | // | 5784 | Immediate.u(x), |
| 6047 | // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only | 5785 | ); |
| 6048 | // difference is that we set REX.W before the instruction, which extends the load to | ||
| 6049 | // 64-bit and uses the full bit-width of the register. | ||
| 6050 | const payload = try self.addExtra(Mir.Imm64.encode(x)); | ||
| 6051 | _ = try self.addInst(.{ | ||
| 6052 | .tag = .movabs, | ||
| 6053 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }), | ||
| 6054 | .data = .{ .payload = payload }, | ||
| 6055 | }); | ||
| 6056 | }, | 5786 | }, |
| 6057 | .register => |src_reg| { | 5787 | .register => |src_reg| { |
| 6058 | // If the registers are the same, nothing to do. | 5788 | // If the registers are the same, nothing to do. |
| ... | @@ -6063,69 +5793,38 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -6063,69 +5793,38 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6063 | .Int => switch (ty.intInfo(self.target.*).signedness) { | 5793 | .Int => switch (ty.intInfo(self.target.*).signedness) { |
| 6064 | .signed => { | 5794 | .signed => { |
| 6065 | if (abi_size <= 4) { | 5795 | if (abi_size <= 4) { |
| 6066 | _ = try self.addInst(.{ | 5796 | return self.asmRegisterRegister( |
| 6067 | .tag = .mov_sign_extend, | 5797 | .movsx, |
| 6068 | .ops = Mir.Inst.Ops.encode(.{ | 5798 | reg.to64(), |
| 6069 | .reg1 = reg.to64(), | 5799 | registerAlias(src_reg, abi_size), |
| 6070 | .reg2 = registerAlias(src_reg, abi_size), | 5800 | ); |
| 6071 | }), | ||
| 6072 | .data = undefined, | ||
| 6073 | }); | ||
| 6074 | return; | ||
| 6075 | } | 5801 | } |
| 6076 | }, | 5802 | }, |
| 6077 | .unsigned => { | 5803 | .unsigned => { |
| 6078 | if (abi_size <= 2) { | 5804 | if (abi_size <= 2) { |
| 6079 | _ = try self.addInst(.{ | 5805 | return self.asmRegisterRegister( |
| 6080 | .tag = .mov_zero_extend, | 5806 | .movzx, |
| 6081 | .ops = Mir.Inst.Ops.encode(.{ | 5807 | reg.to64(), |
| 6082 | .reg1 = reg.to64(), | 5808 | registerAlias(src_reg, abi_size), |
| 6083 | .reg2 = registerAlias(src_reg, abi_size), | 5809 | ); |
| 6084 | }), | ||
| 6085 | .data = undefined, | ||
| 6086 | }); | ||
| 6087 | return; | ||
| 6088 | } | 5810 | } |
| 6089 | }, | 5811 | }, |
| 6090 | }, | 5812 | }, |
| 6091 | .Float => { | 5813 | .Float => { |
| 6092 | if (intrinsicsAllowed(self.target.*, ty)) { | 5814 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 6093 | const tag: Mir.Inst.Tag = switch (ty.tag()) { | 5815 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6094 | .f32 => if (hasAvxSupport(self.target.*)) | 5816 | .f32 => .movss, |
| 6095 | Mir.Inst.Tag.mov_f32_avx | 5817 | .f64 => .movsd, |
| 6096 | else | ||
| 6097 | Mir.Inst.Tag.mov_f32_sse, | ||
| 6098 | .f64 => if (hasAvxSupport(self.target.*)) | ||
| 6099 | Mir.Inst.Tag.mov_f64_avx | ||
| 6100 | else | ||
| 6101 | Mir.Inst.Tag.mov_f64_sse, | ||
| 6102 | else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), | 5818 | else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), |
| 6103 | }; | 5819 | }; |
| 6104 | _ = try self.addInst(.{ | 5820 | return self.asmRegisterRegister(tag, reg.to128(), src_reg.to128()); |
| 6105 | .tag = tag, | ||
| 6106 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 6107 | .reg1 = reg.to128(), | ||
| 6108 | .reg2 = src_reg.to128(), | ||
| 6109 | .flags = 0b10, | ||
| 6110 | }), | ||
| 6111 | .data = undefined, | ||
| 6112 | }); | ||
| 6113 | return; | ||
| 6114 | } | 5821 | } |
| 6115 | |||
| 6116 | return self.fail("TODO genSetReg from register for float with no intrinsics", .{}); | 5822 | return self.fail("TODO genSetReg from register for float with no intrinsics", .{}); |
| 6117 | }, | 5823 | }, |
| 6118 | else => {}, | 5824 | else => {}, |
| 6119 | } | 5825 | } |
| 6120 | 5826 | ||
| 6121 | _ = try self.addInst(.{ | 5827 | try self.asmRegisterRegister(.mov, registerAlias(reg, abi_size), registerAlias(src_reg, abi_size)); |
| 6122 | .tag = .mov, | ||
| 6123 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 6124 | .reg1 = registerAlias(reg, abi_size), | ||
| 6125 | .reg2 = registerAlias(src_reg, abi_size), | ||
| 6126 | }), | ||
| 6127 | .data = undefined, | ||
| 6128 | }); | ||
| 6129 | }, | 5828 | }, |
| 6130 | .linker_load => { | 5829 | .linker_load => { |
| 6131 | switch (ty.zigTypeTag()) { | 5830 | switch (ty.zigTypeTag()) { |
| ... | @@ -6135,45 +5834,30 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -6135,45 +5834,30 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6135 | 5834 | ||
| 6136 | if (intrinsicsAllowed(self.target.*, ty)) { | 5835 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 6137 | const tag: Mir.Inst.Tag = switch (ty.tag()) { | 5836 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6138 | .f32 => if (hasAvxSupport(self.target.*)) | 5837 | .f32 => .movss, |
| 6139 | Mir.Inst.Tag.mov_f32_avx | 5838 | .f64 => .movsd, |
| 6140 | else | ||
| 6141 | Mir.Inst.Tag.mov_f32_sse, | ||
| 6142 | .f64 => if (hasAvxSupport(self.target.*)) | ||
| 6143 | Mir.Inst.Tag.mov_f64_avx | ||
| 6144 | else | ||
| 6145 | Mir.Inst.Tag.mov_f64_sse, | ||
| 6146 | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), | 5839 | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), |
| 6147 | }; | 5840 | }; |
| 6148 | 5841 | const ptr_size: Memory.PtrSize = switch (ty.tag()) { | |
| 6149 | _ = try self.addInst(.{ | 5842 | .f32 => .dword, |
| 6150 | .tag = tag, | 5843 | .f64 => .qword, |
| 6151 | .ops = Mir.Inst.Ops.encode(.{ | 5844 | else => unreachable, |
| 6152 | .reg1 = reg.to128(), | 5845 | }; |
| 6153 | .reg2 = switch (ty.tag()) { | 5846 | return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ |
| 6154 | .f32 => base_reg.to32(), | 5847 | .base = base_reg.to64(), |
| 6155 | .f64 => base_reg.to64(), | 5848 | .disp = 0, |
| 6156 | else => unreachable, | 5849 | })); |
| 6157 | }, | ||
| 6158 | }), | ||
| 6159 | .data = .{ .imm = 0 }, | ||
| 6160 | }); | ||
| 6161 | return; | ||
| 6162 | } | 5850 | } |
| 6163 | 5851 | ||
| 6164 | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); | 5852 | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); |
| 6165 | }, | 5853 | }, |
| 6166 | else => { | 5854 | else => { |
| 6167 | try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); | 5855 | try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); |
| 6168 | _ = try self.addInst(.{ | 5856 | try self.asmRegisterMemory( |
| 6169 | .tag = .mov, | 5857 | .mov, |
| 6170 | .ops = Mir.Inst.Ops.encode(.{ | 5858 | registerAlias(reg, abi_size), |
| 6171 | .reg1 = registerAlias(reg, abi_size), | 5859 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }), |
| 6172 | .reg2 = reg.to64(), | 5860 | ); |
| 6173 | .flags = 0b01, | ||
| 6174 | }), | ||
| 6175 | .data = .{ .imm = 0 }, | ||
| 6176 | }); | ||
| 6177 | }, | 5861 | }, |
| 6178 | } | 5862 | } |
| 6179 | }, | 5863 | }, |
| ... | @@ -6184,73 +5868,56 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -6184,73 +5868,56 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6184 | 5868 | ||
| 6185 | if (intrinsicsAllowed(self.target.*, ty)) { | 5869 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 6186 | const tag: Mir.Inst.Tag = switch (ty.tag()) { | 5870 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6187 | .f32 => if (hasAvxSupport(self.target.*)) | 5871 | .f32 => .movss, |
| 6188 | Mir.Inst.Tag.mov_f32_avx | 5872 | .f64 => .movsd, |
| 6189 | else | ||
| 6190 | Mir.Inst.Tag.mov_f32_sse, | ||
| 6191 | .f64 => if (hasAvxSupport(self.target.*)) | ||
| 6192 | Mir.Inst.Tag.mov_f64_avx | ||
| 6193 | else | ||
| 6194 | Mir.Inst.Tag.mov_f64_sse, | ||
| 6195 | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), | 5873 | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), |
| 6196 | }; | 5874 | }; |
| 6197 | 5875 | const ptr_size: Memory.PtrSize = switch (ty.tag()) { | |
| 6198 | _ = try self.addInst(.{ | 5876 | .f32 => .dword, |
| 6199 | .tag = tag, | 5877 | .f64 => .qword, |
| 6200 | .ops = Mir.Inst.Ops.encode(.{ | 5878 | else => unreachable, |
| 6201 | .reg1 = reg.to128(), | 5879 | }; |
| 6202 | .reg2 = switch (ty.tag()) { | 5880 | return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ |
| 6203 | .f32 => base_reg.to32(), | 5881 | .base = base_reg.to64(), |
| 6204 | .f64 => base_reg.to64(), | 5882 | .disp = 0, |
| 6205 | else => unreachable, | 5883 | })); |
| 6206 | }, | ||
| 6207 | }), | ||
| 6208 | .data = .{ .imm = 0 }, | ||
| 6209 | }); | ||
| 6210 | return; | ||
| 6211 | } | 5884 | } |
| 6212 | 5885 | ||
| 6213 | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); | 5886 | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); |
| 6214 | }, | 5887 | }, |
| 6215 | else => { | 5888 | else => { |
| 6216 | if (x <= math.maxInt(i32)) { | 5889 | if (x <= math.maxInt(i32)) { |
| 6217 | // mov reg, [ds:imm32] | 5890 | try self.asmRegisterMemory( |
| 6218 | _ = try self.addInst(.{ | 5891 | .mov, |
| 6219 | .tag = .mov, | 5892 | registerAlias(reg, abi_size), |
| 6220 | .ops = Mir.Inst.Ops.encode(.{ | 5893 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 6221 | .reg1 = registerAlias(reg, abi_size), | 5894 | .base = .ds, |
| 6222 | .flags = 0b01, | 5895 | .disp = @intCast(i32, x), |
| 6223 | }), | 5896 | }), |
| 6224 | .data = .{ .imm = @truncate(u32, x) }, | 5897 | ); |
| 6225 | }); | ||
| 6226 | } else { | 5898 | } else { |
| 6227 | // If this is RAX, we can use a direct load. | 5899 | if (reg.to64() == .rax) { |
| 6228 | // Otherwise, we need to load the address, then indirectly load the value. | 5900 | // If this is RAX, we can use a direct load. |
| 6229 | if (reg.id() == 0) { | 5901 | // Otherwise, we need to load the address, then indirectly load the value. |
| 6230 | // movabs rax, ds:moffs64 | 5902 | var moffs: Mir.MemoryMoffs = .{ |
| 6231 | const payload = try self.addExtra(Mir.Imm64.encode(x)); | 5903 | .seg = @enumToInt(Register.ds), |
| 5904 | .msb = undefined, | ||
| 5905 | .lsb = undefined, | ||
| 5906 | }; | ||
| 5907 | moffs.encodeOffset(x); | ||
| 6232 | _ = try self.addInst(.{ | 5908 | _ = try self.addInst(.{ |
| 6233 | .tag = .movabs, | 5909 | .tag = .mov_moffs, |
| 6234 | .ops = Mir.Inst.Ops.encode(.{ | 5910 | .ops = .rax_moffs, |
| 6235 | .reg1 = .rax, | 5911 | .data = .{ .payload = try self.addExtra(moffs) }, |
| 6236 | .flags = 0b01, // imm64 will become moffs64 | ||
| 6237 | }), | ||
| 6238 | .data = .{ .payload = payload }, | ||
| 6239 | }); | 5912 | }); |
| 6240 | } else { | 5913 | } else { |
| 6241 | // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. | 5914 | // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. |
| 6242 | try self.genSetReg(ty, reg, MCValue{ .immediate = x }); | 5915 | try self.genSetReg(ty, reg, MCValue{ .immediate = x }); |
| 6243 | 5916 | try self.asmRegisterMemory( | |
| 6244 | // mov reg, [reg + 0x0] | 5917 | .mov, |
| 6245 | _ = try self.addInst(.{ | 5918 | registerAlias(reg, abi_size), |
| 6246 | .tag = .mov, | 5919 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64(), .disp = 0 }), |
| 6247 | .ops = Mir.Inst.Ops.encode(.{ | 5920 | ); |
| 6248 | .reg1 = registerAlias(reg, abi_size), | ||
| 6249 | .reg2 = reg.to64(), | ||
| 6250 | .flags = 0b01, | ||
| 6251 | }), | ||
| 6252 | .data = .{ .imm = 0 }, | ||
| 6253 | }); | ||
| 6254 | } | 5921 | } |
| 6255 | } | 5922 | } |
| 6256 | }, | 5923 | }, |
| ... | @@ -6264,85 +5931,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -6264,85 +5931,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6264 | .Int => switch (ty.intInfo(self.target.*).signedness) { | 5931 | .Int => switch (ty.intInfo(self.target.*).signedness) { |
| 6265 | .signed => { | 5932 | .signed => { |
| 6266 | if (abi_size <= 4) { | 5933 | if (abi_size <= 4) { |
| 6267 | const flags: u2 = switch (abi_size) { | 5934 | return self.asmRegisterMemory( |
| 6268 | 1 => 0b01, | 5935 | .movsx, |
| 6269 | 2 => 0b10, | 5936 | reg.to64(), |
| 6270 | 4 => 0b11, | 5937 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 6271 | else => unreachable, | 5938 | .base = .rbp, |
| 6272 | }; | 5939 | .disp = -off, |
| 6273 | _ = try self.addInst(.{ | ||
| 6274 | .tag = .mov_sign_extend, | ||
| 6275 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 6276 | .reg1 = reg.to64(), | ||
| 6277 | .reg2 = .rbp, | ||
| 6278 | .flags = flags, | ||
| 6279 | }), | 5940 | }), |
| 6280 | .data = .{ .imm = @bitCast(u32, -off) }, | 5941 | ); |
| 6281 | }); | ||
| 6282 | return; | ||
| 6283 | } | 5942 | } |
| 6284 | }, | 5943 | }, |
| 6285 | .unsigned => { | 5944 | .unsigned => { |
| 6286 | if (abi_size <= 2) { | 5945 | if (abi_size <= 2) { |
| 6287 | const flags: u2 = switch (abi_size) { | 5946 | return self.asmRegisterMemory( |
| 6288 | 1 => 0b01, | 5947 | .movzx, |
| 6289 | 2 => 0b10, | 5948 | reg.to64(), |
| 6290 | else => unreachable, | 5949 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 6291 | }; | 5950 | .base = .rbp, |
| 6292 | _ = try self.addInst(.{ | 5951 | .disp = -off, |
| 6293 | .tag = .mov_zero_extend, | ||
| 6294 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 6295 | .reg1 = reg.to64(), | ||
| 6296 | .reg2 = .rbp, | ||
| 6297 | .flags = flags, | ||
| 6298 | }), | 5952 | }), |
| 6299 | .data = .{ .imm = @bitCast(u32, -off) }, | 5953 | ); |
| 6300 | }); | ||
| 6301 | return; | ||
| 6302 | } | 5954 | } |
| 6303 | }, | 5955 | }, |
| 6304 | }, | 5956 | }, |
| 6305 | .Float => { | 5957 | .Float => { |
| 6306 | if (intrinsicsAllowed(self.target.*, ty)) { | 5958 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 6307 | const tag: Mir.Inst.Tag = switch (ty.tag()) { | 5959 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6308 | .f32 => if (hasAvxSupport(self.target.*)) | 5960 | .f32 => .movss, |
| 6309 | Mir.Inst.Tag.mov_f32_avx | 5961 | .f64 => .movsd, |
| 6310 | else | 5962 | else => return self.fail( |
| 6311 | Mir.Inst.Tag.mov_f32_sse, | 5963 | "TODO genSetReg from stack offset for {}", |
| 6312 | .f64 => if (hasAvxSupport(self.target.*)) | 5964 | .{ty.fmtDebug()}, |
| 6313 | Mir.Inst.Tag.mov_f64_avx | 5965 | ), |
| 6314 | else | ||
| 6315 | Mir.Inst.Tag.mov_f64_sse, | ||
| 6316 | else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), | ||
| 6317 | }; | 5966 | }; |
| 6318 | _ = try self.addInst(.{ | 5967 | const ptr_size: Memory.PtrSize = switch (ty.tag()) { |
| 6319 | .tag = tag, | 5968 | .f32 => .dword, |
| 6320 | .ops = Mir.Inst.Ops.encode(.{ | 5969 | .f64 => .qword, |
| 6321 | .reg1 = reg.to128(), | 5970 | else => unreachable, |
| 6322 | .reg2 = switch (ty.tag()) { | 5971 | }; |
| 6323 | .f32 => .ebp, | 5972 | return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ |
| 6324 | .f64 => .rbp, | 5973 | .base = .rbp, |
| 6325 | else => unreachable, | 5974 | .disp = -off, |
| 6326 | }, | 5975 | })); |
| 6327 | }), | ||
| 6328 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 6329 | }); | ||
| 6330 | return; | ||
| 6331 | } | 5976 | } |
| 6332 | return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); | 5977 | return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); |
| 6333 | }, | 5978 | }, |
| 6334 | else => {}, | 5979 | else => {}, |
| 6335 | } | 5980 | } |
| 6336 | 5981 | ||
| 6337 | _ = try self.addInst(.{ | 5982 | try self.asmRegisterMemory( |
| 6338 | .tag = .mov, | 5983 | .mov, |
| 6339 | .ops = Mir.Inst.Ops.encode(.{ | 5984 | registerAlias(reg, abi_size), |
| 6340 | .reg1 = registerAlias(reg, abi_size), | 5985 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), |
| 6341 | .reg2 = .rbp, | 5986 | ); |
| 6342 | .flags = 0b01, | ||
| 6343 | }), | ||
| 6344 | .data = .{ .imm = @bitCast(u32, -off) }, | ||
| 6345 | }); | ||
| 6346 | }, | 5987 | }, |
| 6347 | } | 5988 | } |
| 6348 | } | 5989 | } |
| ... | @@ -6406,6 +6047,16 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6406,6 +6047,16 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 6406 | const src_ty = self.air.typeOf(ty_op.operand); | 6047 | const src_ty = self.air.typeOf(ty_op.operand); |
| 6407 | const dst_ty = self.air.typeOfIndex(inst); | 6048 | const dst_ty = self.air.typeOfIndex(inst); |
| 6408 | const operand = try self.resolveInst(ty_op.operand); | 6049 | const operand = try self.resolveInst(ty_op.operand); |
| 6050 | const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); | ||
| 6051 | const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); | ||
| 6052 | |||
| 6053 | switch (src_abi_size) { | ||
| 6054 | 4, 8 => {}, | ||
| 6055 | else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), | ||
| 6056 | } | ||
| 6057 | if (dst_abi_size > 8) { | ||
| 6058 | return self.fail("TODO convert float with abiSize={}", .{dst_abi_size}); | ||
| 6059 | } | ||
| 6409 | 6060 | ||
| 6410 | // move float src to ST(0) | 6061 | // move float src to ST(0) |
| 6411 | const stack_offset = switch (operand) { | 6062 | const stack_offset = switch (operand) { |
| ... | @@ -6413,41 +6064,24 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6413,41 +6064,24 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 6413 | else => blk: { | 6064 | else => blk: { |
| 6414 | const offset = @intCast(i32, try self.allocMem( | 6065 | const offset = @intCast(i32, try self.allocMem( |
| 6415 | inst, | 6066 | inst, |
| 6416 | @intCast(u32, src_ty.abiSize(self.target.*)), | 6067 | src_abi_size, |
| 6417 | src_ty.abiAlignment(self.target.*), | 6068 | src_ty.abiAlignment(self.target.*), |
| 6418 | )); | 6069 | )); |
| 6419 | try self.genSetStack(src_ty, offset, operand, .{}); | 6070 | try self.genSetStack(src_ty, offset, operand, .{}); |
| 6420 | break :blk offset; | 6071 | break :blk offset; |
| 6421 | }, | 6072 | }, |
| 6422 | }; | 6073 | }; |
| 6423 | _ = try self.addInst(.{ | 6074 | try self.asmMemory(.fld, Memory.sib(Memory.PtrSize.fromSize(src_abi_size), .{ |
| 6424 | .tag = .fld, | 6075 | .base = .rbp, |
| 6425 | .ops = Mir.Inst.Ops.encode(.{ | 6076 | .disp = -stack_offset, |
| 6426 | .reg1 = .rbp, | 6077 | })); |
| 6427 | .flags = switch (src_ty.abiSize(self.target.*)) { | ||
| 6428 | 4 => 0b01, | ||
| 6429 | 8 => 0b10, | ||
| 6430 | else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), | ||
| 6431 | }, | ||
| 6432 | }), | ||
| 6433 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, | ||
| 6434 | }); | ||
| 6435 | 6078 | ||
| 6436 | // convert | 6079 | // convert |
| 6437 | const stack_dst = try self.allocRegOrMem(inst, false); | 6080 | const stack_dst = try self.allocRegOrMem(inst, false); |
| 6438 | _ = try self.addInst(.{ | 6081 | try self.asmMemory(.fisttp, Memory.sib(Memory.PtrSize.fromSize(dst_abi_size), .{ |
| 6439 | .tag = .fisttp, | 6082 | .base = .rbp, |
| 6440 | .ops = Mir.Inst.Ops.encode(.{ | 6083 | .disp = -stack_dst.stack_offset, |
| 6441 | .reg1 = .rbp, | 6084 | })); |
| 6442 | .flags = switch (dst_ty.abiSize(self.target.*)) { | ||
| 6443 | 1...2 => 0b00, | ||
| 6444 | 3...4 => 0b01, | ||
| 6445 | 5...8 => 0b10, | ||
| 6446 | else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), | ||
| 6447 | }, | ||
| 6448 | }), | ||
| 6449 | .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) }, | ||
| 6450 | }); | ||
| 6451 | 6085 | ||
| 6452 | return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); | 6086 | return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); |
| 6453 | } | 6087 | } |
| ... | @@ -6538,15 +6172,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6538,15 +6172,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 6538 | .linker_load, .memory => { | 6172 | .linker_load, .memory => { |
| 6539 | const reg = try self.register_manager.allocReg(null, gp); | 6173 | const reg = try self.register_manager.allocReg(null, gp); |
| 6540 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); | 6174 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); |
| 6541 | _ = try self.addInst(.{ | 6175 | try self.asmRegisterMemory(.mov, reg, Memory.sib(.qword, .{ |
| 6542 | .tag = .mov, | 6176 | .base = reg, |
| 6543 | .ops = Mir.Inst.Ops.encode(.{ | 6177 | .disp = 0, |
| 6544 | .reg1 = reg, | 6178 | })); |
| 6545 | .reg2 = reg, | ||
| 6546 | .flags = 0b01, | ||
| 6547 | }), | ||
| 6548 | .data = .{ .imm = 0 }, | ||
| 6549 | }); | ||
| 6550 | break :blk MCValue{ .register = reg }; | 6179 | break :blk MCValue{ .register = reg }; |
| 6551 | }, | 6180 | }, |
| 6552 | else => break :blk src_ptr, | 6181 | else => break :blk src_ptr, |
| ... | @@ -6619,6 +6248,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6619,6 +6248,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 6619 | if (self.liveness.isUnused(inst)) break :res MCValue.dead; | 6248 | if (self.liveness.isUnused(inst)) break :res MCValue.dead; |
| 6620 | switch (result_ty.zigTypeTag()) { | 6249 | switch (result_ty.zigTypeTag()) { |
| 6621 | .Struct => { | 6250 | .Struct => { |
| 6251 | if (result_ty.containerLayout() == .Packed) { | ||
| 6252 | return self.fail("TODO airAggregateInit implement packed structs", .{}); | ||
| 6253 | } | ||
| 6622 | const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); | 6254 | const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); |
| 6623 | for (elements, 0..) |elem, elem_i| { | 6255 | for (elements, 0..) |elem, elem_i| { |
| 6624 | if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem | 6256 | if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem |
| ... | @@ -6683,13 +6315,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6683,13 +6315,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 6683 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); | 6315 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); |
| 6684 | } | 6316 | } |
| 6685 | 6317 | ||
| 6686 | pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 6318 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 6687 | // First section of indexes correspond to a set number of constant values. | 6319 | // First section of indexes correspond to a set number of constant values. |
| 6688 | const ref_int = @enumToInt(inst); | 6320 | const ref_int = @enumToInt(inst); |
| 6689 | if (ref_int < Air.Inst.Ref.typed_value_map.len) { | 6321 | if (ref_int < Air.Inst.Ref.typed_value_map.len) { |
| 6690 | const tv = Air.Inst.Ref.typed_value_map[ref_int]; | 6322 | const tv = Air.Inst.Ref.typed_value_map[ref_int]; |
| 6691 | if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { | 6323 | if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { |
| 6692 | return MCValue{ .none = {} }; | 6324 | return .none; |
| 6693 | } | 6325 | } |
| 6694 | return self.genTypedValue(tv); | 6326 | return self.genTypedValue(tv); |
| 6695 | } | 6327 | } |
| ... | @@ -6697,7 +6329,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | ... | @@ -6697,7 +6329,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 6697 | // If the type has no codegen bits, no need to store it. | 6329 | // If the type has no codegen bits, no need to store it. |
| 6698 | const inst_ty = self.air.typeOf(inst); | 6330 | const inst_ty = self.air.typeOf(inst); |
| 6699 | if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) | 6331 | if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) |
| 6700 | return MCValue{ .none = {} }; | 6332 | return .none; |
| 6701 | 6333 | ||
| 6702 | const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); | 6334 | const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); |
| 6703 | switch (self.air.instructions.items(.tag)[inst_index]) { | 6335 | switch (self.air.instructions.items(.tag)[inst_index]) { |
| ... | @@ -6715,18 +6347,17 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | ... | @@ -6715,18 +6347,17 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 6715 | return gop.value_ptr.*; | 6347 | return gop.value_ptr.*; |
| 6716 | }, | 6348 | }, |
| 6717 | .const_ty => unreachable, | 6349 | .const_ty => unreachable, |
| 6718 | else => return self.getResolvedInstValue(inst_index), | 6350 | else => return self.getResolvedInstValue(inst_index).?, |
| 6719 | } | 6351 | } |
| 6720 | } | 6352 | } |
| 6721 | 6353 | ||
| 6722 | fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | 6354 | fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue { |
| 6723 | // Treat each stack item as a "layer" on top of the previous one. | 6355 | // Treat each stack item as a "layer" on top of the previous one. |
| 6724 | var i: usize = self.branch_stack.items.len; | 6356 | var i: usize = self.branch_stack.items.len; |
| 6725 | while (true) { | 6357 | while (true) { |
| 6726 | i -= 1; | 6358 | i -= 1; |
| 6727 | if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { | 6359 | if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { |
| 6728 | assert(mcv != .dead); | 6360 | return if (mcv != .dead) mcv else null; |
| 6729 | return mcv; | ||
| 6730 | } | 6361 | } |
| 6731 | } | 6362 | } |
| 6732 | } | 6363 | } |
| ... | @@ -6752,200 +6383,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV | ... | @@ -6752,200 +6383,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV |
| 6752 | return mcv; | 6383 | return mcv; |
| 6753 | } | 6384 | } |
| 6754 | 6385 | ||
| 6755 | fn 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 | |||
| 6800 | fn 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 | |||
| 6828 | fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { | 6386 | fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { |
| 6829 | var typed_value = arg_tv; | 6387 | const mcv: MCValue = switch (try codegen.genTypedValue( |
| 6830 | if (typed_value.val.castTag(.runtime_value)) |rt| { | 6388 | self.bin_file, |
| 6831 | typed_value.val = rt.data; | 6389 | self.src_loc, |
| 6832 | } | 6390 | arg_tv, |
| 6833 | log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); | 6391 | self.mod_fn.owner_decl, |
| 6834 | if (typed_value.val.isUndef()) | 6392 | )) { |
| 6835 | return MCValue{ .undef = {} }; | 6393 | .mcv => |mcv| switch (mcv) { |
| 6836 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 6394 | .none => .none, |
| 6837 | 6395 | .undef => .undef, | |
| 6838 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 6396 | .linker_load => |ll| .{ .linker_load = ll }, |
| 6839 | return self.lowerDeclRef(typed_value, payload.data); | 6397 | .immediate => |imm| .{ .immediate = imm }, |
| 6840 | } | 6398 | .memory => |addr| .{ .memory = addr }, |
| 6841 | if (typed_value.val.castTag(.decl_ref_mut)) |payload| { | ||
| 6842 | return self.lowerDeclRef(typed_value, payload.data.decl_index); | ||
| 6843 | } | ||
| 6844 | |||
| 6845 | const target = self.target.*; | ||
| 6846 | |||
| 6847 | switch (typed_value.ty.zigTypeTag()) { | ||
| 6848 | .Void => return MCValue{ .none = {} }, | ||
| 6849 | .Pointer => switch (typed_value.ty.ptrSize()) { | ||
| 6850 | .Slice => {}, | ||
| 6851 | else => { | ||
| 6852 | switch (typed_value.val.tag()) { | ||
| 6853 | .int_u64 => { | ||
| 6854 | return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; | ||
| 6855 | }, | ||
| 6856 | else => {}, | ||
| 6857 | } | ||
| 6858 | }, | ||
| 6859 | }, | ||
| 6860 | .Int => { | ||
| 6861 | const info = typed_value.ty.intInfo(self.target.*); | ||
| 6862 | if (info.bits <= ptr_bits and info.signedness == .signed) { | ||
| 6863 | return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) }; | ||
| 6864 | } | ||
| 6865 | if (!(info.bits > ptr_bits or info.signedness == .signed)) { | ||
| 6866 | return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; | ||
| 6867 | } | ||
| 6868 | }, | 6399 | }, |
| 6869 | .Bool => { | 6400 | .fail => |msg| { |
| 6870 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | 6401 | self.err_msg = msg; |
| 6402 | return error.CodegenFail; | ||
| 6871 | }, | 6403 | }, |
| 6872 | .Optional => { | 6404 | }; |
| 6873 | if (typed_value.ty.isPtrLikeOptional()) { | 6405 | return mcv; |
| 6874 | if (typed_value.val.isNull()) | ||
| 6875 | return MCValue{ .immediate = 0 }; | ||
| 6876 | |||
| 6877 | var buf: Type.Payload.ElemType = undefined; | ||
| 6878 | return self.genTypedValue(.{ | ||
| 6879 | .ty = typed_value.ty.optionalChild(&buf), | ||
| 6880 | .val = typed_value.val, | ||
| 6881 | }); | ||
| 6882 | } else if (typed_value.ty.abiSize(self.target.*) == 1) { | ||
| 6883 | return MCValue{ .immediate = @boolToInt(!typed_value.val.isNull()) }; | ||
| 6884 | } | ||
| 6885 | }, | ||
| 6886 | .Enum => { | ||
| 6887 | if (typed_value.val.castTag(.enum_field_index)) |field_index| { | ||
| 6888 | switch (typed_value.ty.tag()) { | ||
| 6889 | .enum_simple => { | ||
| 6890 | return MCValue{ .immediate = field_index.data }; | ||
| 6891 | }, | ||
| 6892 | .enum_full, .enum_nonexhaustive => { | ||
| 6893 | const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; | ||
| 6894 | if (enum_full.values.count() != 0) { | ||
| 6895 | const tag_val = enum_full.values.keys()[field_index.data]; | ||
| 6896 | return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); | ||
| 6897 | } else { | ||
| 6898 | return MCValue{ .immediate = field_index.data }; | ||
| 6899 | } | ||
| 6900 | }, | ||
| 6901 | else => unreachable, | ||
| 6902 | } | ||
| 6903 | } else { | ||
| 6904 | var int_tag_buffer: Type.Payload.Bits = undefined; | ||
| 6905 | const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); | ||
| 6906 | return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); | ||
| 6907 | } | ||
| 6908 | }, | ||
| 6909 | .ErrorSet => { | ||
| 6910 | switch (typed_value.val.tag()) { | ||
| 6911 | .@"error" => { | ||
| 6912 | const err_name = typed_value.val.castTag(.@"error").?.data.name; | ||
| 6913 | const module = self.bin_file.options.module.?; | ||
| 6914 | const global_error_set = module.global_error_set; | ||
| 6915 | const error_index = global_error_set.get(err_name).?; | ||
| 6916 | return MCValue{ .immediate = error_index }; | ||
| 6917 | }, | ||
| 6918 | else => { | ||
| 6919 | // In this case we are rendering an error union which has a 0 bits payload. | ||
| 6920 | return MCValue{ .immediate = 0 }; | ||
| 6921 | }, | ||
| 6922 | } | ||
| 6923 | }, | ||
| 6924 | .ErrorUnion => { | ||
| 6925 | const error_type = typed_value.ty.errorUnionSet(); | ||
| 6926 | const payload_type = typed_value.ty.errorUnionPayload(); | ||
| 6927 | const is_pl = typed_value.val.errorUnionIsPayload(); | ||
| 6928 | |||
| 6929 | if (!payload_type.hasRuntimeBitsIgnoreComptime()) { | ||
| 6930 | // We use the error type directly as the type. | ||
| 6931 | const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); | ||
| 6932 | return self.genTypedValue(.{ .ty = error_type, .val = err_val }); | ||
| 6933 | } | ||
| 6934 | }, | ||
| 6935 | |||
| 6936 | .ComptimeInt => unreachable, | ||
| 6937 | .ComptimeFloat => unreachable, | ||
| 6938 | .Type => unreachable, | ||
| 6939 | .EnumLiteral => unreachable, | ||
| 6940 | .NoReturn => unreachable, | ||
| 6941 | .Undefined => unreachable, | ||
| 6942 | .Null => unreachable, | ||
| 6943 | .Opaque => unreachable, | ||
| 6944 | |||
| 6945 | else => {}, | ||
| 6946 | } | ||
| 6947 | |||
| 6948 | return self.lowerUnnamedConst(typed_value); | ||
| 6949 | } | 6406 | } |
| 6950 | 6407 | ||
| 6951 | const CallMCValues = struct { | 6408 | const CallMCValues = struct { |
| ... | @@ -6980,7 +6437,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -6980,7 +6437,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 6980 | switch (cc) { | 6437 | switch (cc) { |
| 6981 | .Naked => { | 6438 | .Naked => { |
| 6982 | assert(result.args.len == 0); | 6439 | assert(result.args.len == 0); |
| 6983 | result.return_value = .{ .unreach = {} }; | 6440 | result.return_value = .unreach; |
| 6984 | result.stack_byte_count = 0; | 6441 | result.stack_byte_count = 0; |
| 6985 | result.stack_align = 1; | 6442 | result.stack_align = 1; |
| 6986 | return result; | 6443 | return result; |
| ... | @@ -6988,10 +6445,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -6988,10 +6445,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 6988 | .C => { | 6445 | .C => { |
| 6989 | // Return values | 6446 | // Return values |
| 6990 | if (ret_ty.zigTypeTag() == .NoReturn) { | 6447 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 6991 | result.return_value = .{ .unreach = {} }; | 6448 | result.return_value = .unreach; |
| 6992 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { | 6449 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 6993 | // TODO: is this even possible for C calling convention? | 6450 | // TODO: is this even possible for C calling convention? |
| 6994 | result.return_value = .{ .none = {} }; | 6451 | result.return_value = .none; |
| 6995 | } else { | 6452 | } else { |
| 6996 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 6453 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 6997 | if (ret_ty_size == 0) { | 6454 | if (ret_ty_size == 0) { |
| ... | @@ -7012,7 +6469,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7012,7 +6469,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7012 | else => 0, | 6469 | else => 0, |
| 7013 | }; | 6470 | }; |
| 7014 | 6471 | ||
| 7015 | for (param_types, 0..) |ty, i| { | 6472 | for (param_types, result.args, 0..) |ty, *arg, i| { |
| 7016 | assert(ty.hasRuntimeBits()); | 6473 | assert(ty.hasRuntimeBits()); |
| 7017 | 6474 | ||
| 7018 | const classes: []const abi.Class = switch (self.target.os.tag) { | 6475 | const classes: []const abi.Class = switch (self.target.os.tag) { |
| ... | @@ -7025,7 +6482,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7025,7 +6482,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7025 | switch (classes[0]) { | 6482 | switch (classes[0]) { |
| 7026 | .integer => blk: { | 6483 | .integer => blk: { |
| 7027 | if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough | 6484 | if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough |
| 7028 | result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] }; | 6485 | arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] }; |
| 7029 | continue; | 6486 | continue; |
| 7030 | }, | 6487 | }, |
| 7031 | .memory => {}, // fallthrough | 6488 | .memory => {}, // fallthrough |
| ... | @@ -7037,7 +6494,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7037,7 +6494,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7037 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | 6494 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 7038 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); | 6495 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); |
| 7039 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | 6496 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); |
| 7040 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | 6497 | arg.* = .{ .stack_offset = @intCast(i32, offset) }; |
| 7041 | next_stack_offset = offset; | 6498 | next_stack_offset = offset; |
| 7042 | } | 6499 | } |
| 7043 | 6500 | ||
| ... | @@ -7063,15 +6520,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7063,15 +6520,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7063 | .Unspecified => { | 6520 | .Unspecified => { |
| 7064 | // Return values | 6521 | // Return values |
| 7065 | if (ret_ty.zigTypeTag() == .NoReturn) { | 6522 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 7066 | result.return_value = .{ .unreach = {} }; | 6523 | result.return_value = .unreach; |
| 7067 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { | 6524 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 7068 | result.return_value = .{ .none = {} }; | 6525 | result.return_value = .none; |
| 7069 | } else { | 6526 | } else { |
| 7070 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 6527 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 7071 | if (ret_ty_size == 0) { | 6528 | if (ret_ty_size == 0) { |
| 7072 | assert(ret_ty.isError()); | 6529 | assert(ret_ty.isError()); |
| 7073 | result.return_value = .{ .immediate = 0 }; | 6530 | result.return_value = .{ .immediate = 0 }; |
| 7074 | } else if (ret_ty_size <= 8) { | 6531 | } else if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) { |
| 7075 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); | 6532 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); |
| 7076 | result.return_value = .{ .register = aliased_reg }; | 6533 | result.return_value = .{ .register = aliased_reg }; |
| 7077 | } else { | 6534 | } else { |
| ... | @@ -7088,15 +6545,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7088,15 +6545,15 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7088 | else => 0, | 6545 | else => 0, |
| 7089 | }; | 6546 | }; |
| 7090 | 6547 | ||
| 7091 | for (param_types, 0..) |ty, i| { | 6548 | for (param_types, result.args) |ty, *arg| { |
| 7092 | if (!ty.hasRuntimeBits()) { | 6549 | if (!ty.hasRuntimeBits()) { |
| 7093 | result.args[i] = .{ .none = {} }; | 6550 | arg.* = .none; |
| 7094 | continue; | 6551 | continue; |
| 7095 | } | 6552 | } |
| 7096 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | 6553 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 7097 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); | 6554 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); |
| 7098 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | 6555 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); |
| 7099 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | 6556 | arg.* = .{ .stack_offset = @intCast(i32, offset) }; |
| 7100 | next_stack_offset = offset; | 6557 | next_stack_offset = offset; |
| 7101 | } | 6558 | } |
| 7102 | 6559 | ||
| ... | @@ -7146,28 +6603,34 @@ fn parseRegName(name: []const u8) ?Register { | ... | @@ -7146,28 +6603,34 @@ fn parseRegName(name: []const u8) ?Register { |
| 7146 | 6603 | ||
| 7147 | /// Returns register wide enough to hold at least `size_bytes`. | 6604 | /// Returns register wide enough to hold at least `size_bytes`. |
| 7148 | fn registerAlias(reg: Register, size_bytes: u32) Register { | 6605 | fn registerAlias(reg: Register, size_bytes: u32) Register { |
| 7149 | if (size_bytes == 0) { | 6606 | return switch (reg.class()) { |
| 7150 | unreachable; // should be comptime-known | 6607 | .general_purpose => if (size_bytes == 0) |
| 7151 | } else if (size_bytes <= 1) { | 6608 | unreachable // should be comptime-known |
| 7152 | return reg.to8(); | 6609 | else if (size_bytes <= 1) |
| 7153 | } else if (size_bytes <= 2) { | 6610 | reg.to8() |
| 7154 | return reg.to16(); | 6611 | else if (size_bytes <= 2) |
| 7155 | } else if (size_bytes <= 4) { | 6612 | reg.to16() |
| 7156 | return reg.to32(); | 6613 | else if (size_bytes <= 4) |
| 7157 | } else if (size_bytes <= 8) { | 6614 | reg.to32() |
| 7158 | return reg.to64(); | 6615 | else if (size_bytes <= 8) |
| 7159 | } else if (size_bytes <= 16) { | 6616 | reg.to64() |
| 7160 | return reg.to128(); | 6617 | else |
| 7161 | } else if (size_bytes <= 32) { | 6618 | unreachable, |
| 7162 | return reg.to256(); | 6619 | .floating_point => if (size_bytes <= 16) |
| 7163 | } else unreachable; | 6620 | reg.to128() |
| 6621 | else if (size_bytes <= 32) | ||
| 6622 | reg.to256() | ||
| 6623 | else | ||
| 6624 | unreachable, | ||
| 6625 | .segment => unreachable, | ||
| 6626 | }; | ||
| 7164 | } | 6627 | } |
| 7165 | 6628 | ||
| 7166 | /// Truncates the value in the register in place. | 6629 | /// Truncates the value in the register in place. |
| 7167 | /// Clobbers any remaining bits. | 6630 | /// Clobbers any remaining bits. |
| 7168 | fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { | 6631 | fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 7169 | const int_info = ty.intInfo(self.target.*); | 6632 | const int_info = ty.intInfo(self.target.*); |
| 7170 | const max_reg_bit_width = Register.rax.size(); | 6633 | const max_reg_bit_width = Register.rax.bitSize(); |
| 7171 | switch (int_info.signedness) { | 6634 | switch (int_info.signedness) { |
| 7172 | .signed => { | 6635 | .signed => { |
| 7173 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); | 6636 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| ... | @@ -7177,7 +6640,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { | ... | @@ -7177,7 +6640,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 7177 | .unsigned => { | 6640 | .unsigned => { |
| 7178 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); | 6641 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| 7179 | const mask = (~@as(u64, 0)) >> shift; | 6642 | const mask = (~@as(u64, 0)) >> shift; |
| 7180 | if (int_info.bits <= 32) { | 6643 | if (int_info.bits < 32) { |
| 7181 | try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); | 6644 | try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); |
| 7182 | } else { | 6645 | } else { |
| 7183 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); | 6646 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
src/arch/x86_64/Emit.zig+385-2830| ... | @@ -7,6 +7,7 @@ const std = @import("std"); | ... | @@ -7,6 +7,7 @@ const std = @import("std"); |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const bits = @import("bits.zig"); | 8 | const bits = @import("bits.zig"); |
| 9 | const abi = @import("abi.zig"); | 9 | const abi = @import("abi.zig"); |
| 10 | const encoder = @import("encoder.zig"); | ||
| 10 | const link = @import("../../link.zig"); | 11 | const link = @import("../../link.zig"); |
| 11 | const log = std.log.scoped(.codegen); | 12 | const log = std.log.scoped(.codegen); |
| 12 | const math = std.math; | 13 | const math = std.math; |
| ... | @@ -19,12 +20,14 @@ const CodeGen = @import("CodeGen.zig"); | ... | @@ -19,12 +20,14 @@ const CodeGen = @import("CodeGen.zig"); |
| 19 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 20 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 20 | const Encoder = bits.Encoder; | 21 | const Encoder = bits.Encoder; |
| 21 | const ErrorMsg = Module.ErrorMsg; | 22 | const ErrorMsg = Module.ErrorMsg; |
| 23 | const Immediate = bits.Immediate; | ||
| 24 | const Instruction = encoder.Instruction; | ||
| 22 | const MCValue = @import("CodeGen.zig").MCValue; | 25 | const MCValue = @import("CodeGen.zig").MCValue; |
| 26 | const Memory = bits.Memory; | ||
| 23 | const Mir = @import("Mir.zig"); | 27 | const Mir = @import("Mir.zig"); |
| 24 | const Module = @import("../../Module.zig"); | 28 | const Module = @import("../../Module.zig"); |
| 25 | const Instruction = bits.Instruction; | ||
| 26 | const Type = @import("../../type.zig").Type; | ||
| 27 | const Register = bits.Register; | 29 | const Register = bits.Register; |
| 30 | const Type = @import("../../type.zig").Type; | ||
| 28 | 31 | ||
| 29 | mir: Mir, | 32 | mir: Mir, |
| 30 | bin_file: *link.File, | 33 | bin_file: *link.File, |
| ... | @@ -45,6 +48,8 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{}, | ... | @@ -45,6 +48,8 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{}, |
| 45 | const InnerError = error{ | 48 | const InnerError = error{ |
| 46 | OutOfMemory, | 49 | OutOfMemory, |
| 47 | EmitFail, | 50 | EmitFail, |
| 51 | InvalidInstruction, | ||
| 52 | CannotEncode, | ||
| 48 | }; | 53 | }; |
| 49 | 54 | ||
| 50 | const Reloc = struct { | 55 | const Reloc = struct { |
| ... | @@ -65,132 +70,66 @@ pub fn lowerMir(emit: *Emit) InnerError!void { | ... | @@ -65,132 +70,66 @@ pub fn lowerMir(emit: *Emit) InnerError!void { |
| 65 | const inst = @intCast(u32, index); | 70 | const inst = @intCast(u32, index); |
| 66 | try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); | 71 | try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); |
| 67 | switch (tag) { | 72 | switch (tag) { |
| 68 | // GPR instructions | 73 | .adc, |
| 69 | .adc => try emit.mirArith(.adc, inst), | 74 | .add, |
| 70 | .add => try emit.mirArith(.add, inst), | 75 | .@"and", |
| 71 | .sub => try emit.mirArith(.sub, inst), | 76 | .call, |
| 72 | .xor => try emit.mirArith(.xor, inst), | 77 | .cbw, |
| 73 | .@"and" => try emit.mirArith(.@"and", inst), | 78 | .cwde, |
| 74 | .@"or" => try emit.mirArith(.@"or", inst), | 79 | .cdqe, |
| 75 | .sbb => try emit.mirArith(.sbb, inst), | 80 | .cwd, |
| 76 | .cmp => try emit.mirArith(.cmp, inst), | 81 | .cdq, |
| 77 | .mov => try emit.mirArith(.mov, inst), | 82 | .cqo, |
| 78 | 83 | .cmp, | |
| 79 | .adc_mem_imm => try emit.mirArithMemImm(.adc, inst), | 84 | .div, |
| 80 | .add_mem_imm => try emit.mirArithMemImm(.add, inst), | 85 | .fisttp, |
| 81 | .sub_mem_imm => try emit.mirArithMemImm(.sub, inst), | 86 | .fld, |
| 82 | .xor_mem_imm => try emit.mirArithMemImm(.xor, inst), | 87 | .idiv, |
| 83 | .and_mem_imm => try emit.mirArithMemImm(.@"and", inst), | 88 | .imul, |
| 84 | .or_mem_imm => try emit.mirArithMemImm(.@"or", inst), | 89 | .int3, |
| 85 | .sbb_mem_imm => try emit.mirArithMemImm(.sbb, inst), | 90 | .jmp, |
| 86 | .cmp_mem_imm => try emit.mirArithMemImm(.cmp, inst), | 91 | .lea, |
| 87 | .mov_mem_imm => try emit.mirArithMemImm(.mov, inst), | 92 | .mov, |
| 88 | 93 | .movzx, | |
| 89 | .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst), | 94 | .mul, |
| 90 | .add_scale_src => try emit.mirArithScaleSrc(.add, inst), | 95 | .nop, |
| 91 | .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst), | 96 | .@"or", |
| 92 | .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst), | 97 | .pop, |
| 93 | .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst), | 98 | .push, |
| 94 | .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst), | 99 | .ret, |
| 95 | .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst), | 100 | .sal, |
| 96 | .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst), | 101 | .sar, |
| 97 | .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst), | 102 | .sbb, |
| 98 | 103 | .shl, | |
| 99 | .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst), | 104 | .shr, |
| 100 | .add_scale_dst => try emit.mirArithScaleDst(.add, inst), | 105 | .sub, |
| 101 | .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst), | 106 | .syscall, |
| 102 | .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst), | 107 | .@"test", |
| 103 | .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst), | 108 | .ud2, |
| 104 | .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst), | 109 | .xor, |
| 105 | .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst), | ||
| 106 | .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst), | ||
| 107 | .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst), | ||
| 108 | |||
| 109 | .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst), | ||
| 110 | .add_scale_imm => try emit.mirArithScaleImm(.add, inst), | ||
| 111 | .sub_scale_imm => try emit.mirArithScaleImm(.sub, inst), | ||
| 112 | .xor_scale_imm => try emit.mirArithScaleImm(.xor, inst), | ||
| 113 | .and_scale_imm => try emit.mirArithScaleImm(.@"and", inst), | ||
| 114 | .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst), | ||
| 115 | .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst), | ||
| 116 | .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst), | ||
| 117 | .mov_scale_imm => try emit.mirArithScaleImm(.mov, inst), | ||
| 118 | |||
| 119 | .adc_mem_index_imm => try emit.mirArithMemIndexImm(.adc, inst), | ||
| 120 | .add_mem_index_imm => try emit.mirArithMemIndexImm(.add, inst), | ||
| 121 | .sub_mem_index_imm => try emit.mirArithMemIndexImm(.sub, inst), | ||
| 122 | .xor_mem_index_imm => try emit.mirArithMemIndexImm(.xor, inst), | ||
| 123 | .and_mem_index_imm => try emit.mirArithMemIndexImm(.@"and", inst), | ||
| 124 | .or_mem_index_imm => try emit.mirArithMemIndexImm(.@"or", inst), | ||
| 125 | .sbb_mem_index_imm => try emit.mirArithMemIndexImm(.sbb, inst), | ||
| 126 | .cmp_mem_index_imm => try emit.mirArithMemIndexImm(.cmp, inst), | ||
| 127 | .mov_mem_index_imm => try emit.mirArithMemIndexImm(.mov, inst), | ||
| 128 | |||
| 129 | .mov_sign_extend => try emit.mirMovSignExtend(inst), | ||
| 130 | .mov_zero_extend => try emit.mirMovZeroExtend(inst), | ||
| 131 | |||
| 132 | .movabs => try emit.mirMovabs(inst), | ||
| 133 | |||
| 134 | .fisttp => try emit.mirFisttp(inst), | ||
| 135 | .fld => try emit.mirFld(inst), | ||
| 136 | |||
| 137 | .lea => try emit.mirLea(inst), | ||
| 138 | .lea_pic => try emit.mirLeaPic(inst), | ||
| 139 | |||
| 140 | .shl => try emit.mirShift(.shl, inst), | ||
| 141 | .sal => try emit.mirShift(.sal, inst), | ||
| 142 | .shr => try emit.mirShift(.shr, inst), | ||
| 143 | .sar => try emit.mirShift(.sar, inst), | ||
| 144 | |||
| 145 | .imul => try emit.mirMulDiv(.imul, inst), | ||
| 146 | .mul => try emit.mirMulDiv(.mul, inst), | ||
| 147 | .idiv => try emit.mirMulDiv(.idiv, inst), | ||
| 148 | .div => try emit.mirMulDiv(.div, inst), | ||
| 149 | .imul_complex => try emit.mirIMulComplex(inst), | ||
| 150 | |||
| 151 | .cwd => try emit.mirCwd(inst), | ||
| 152 | |||
| 153 | .push => try emit.mirPushPop(.push, inst), | ||
| 154 | .pop => try emit.mirPushPop(.pop, inst), | ||
| 155 | |||
| 156 | .jmp => try emit.mirJmpCall(.jmp_near, inst), | ||
| 157 | .call => try emit.mirJmpCall(.call_near, inst), | ||
| 158 | |||
| 159 | .cond_jmp => try emit.mirCondJmp(inst), | ||
| 160 | .cond_set_byte => try emit.mirCondSetByte(inst), | ||
| 161 | .cond_mov => try emit.mirCondMov(inst), | ||
| 162 | |||
| 163 | .ret => try emit.mirRet(inst), | ||
| 164 | |||
| 165 | .syscall => try emit.mirSyscall(), | ||
| 166 | |||
| 167 | .@"test" => try emit.mirTest(inst), | ||
| 168 | |||
| 169 | .interrupt => try emit.mirInterrupt(inst), | ||
| 170 | .nop => {}, // just skip it | ||
| 171 | |||
| 172 | // SSE instructions | ||
| 173 | .mov_f64_sse => try emit.mirMovFloatSse(.movsd, inst), | ||
| 174 | .mov_f32_sse => try emit.mirMovFloatSse(.movss, inst), | ||
| 175 | 110 | ||
| 176 | .add_f64_sse => try emit.mirAddFloatSse(.addsd, inst), | 111 | .addss, |
| 177 | .add_f32_sse => try emit.mirAddFloatSse(.addss, inst), | 112 | .cmpss, |
| 113 | .movss, | ||
| 114 | .ucomiss, | ||
| 115 | .addsd, | ||
| 116 | .cmpsd, | ||
| 117 | .movsd, | ||
| 118 | .ucomisd, | ||
| 119 | => try emit.mirEncodeGeneric(tag, inst), | ||
| 178 | 120 | ||
| 179 | .cmp_f64_sse => try emit.mirCmpFloatSse(.ucomisd, inst), | 121 | .jmp_reloc => try emit.mirJmpReloc(inst), |
| 180 | .cmp_f32_sse => try emit.mirCmpFloatSse(.ucomiss, inst), | ||
| 181 | 122 | ||
| 182 | // AVX instructions | 123 | .call_extern => try emit.mirCallExtern(inst), |
| 183 | .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst), | ||
| 184 | .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst), | ||
| 185 | 124 | ||
| 186 | .add_f64_avx => try emit.mirAddFloatAvx(.vaddsd, inst), | 125 | .lea_linker => try emit.mirLeaLinker(inst), |
| 187 | .add_f32_avx => try emit.mirAddFloatAvx(.vaddss, inst), | ||
| 188 | 126 | ||
| 189 | .cmp_f64_avx => try emit.mirCmpFloatAvx(.vucomisd, inst), | 127 | .mov_moffs => try emit.mirMovMoffs(inst), |
| 190 | .cmp_f32_avx => try emit.mirCmpFloatAvx(.vucomiss, inst), | ||
| 191 | 128 | ||
| 192 | // Pseudo-instructions | 129 | .movsx => try emit.mirMovsx(inst), |
| 193 | .call_extern => try emit.mirCallExtern(inst), | 130 | .cmovcc => try emit.mirCmovcc(inst), |
| 131 | .setcc => try emit.mirSetcc(inst), | ||
| 132 | .jcc => try emit.mirJcc(inst), | ||
| 194 | 133 | ||
| 195 | .dbg_line => try emit.mirDbgLine(inst), | 134 | .dbg_line => try emit.mirDbgLine(inst), |
| 196 | .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), | 135 | .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), |
| ... | @@ -199,9 +138,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { | ... | @@ -199,9 +138,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { |
| 199 | .push_regs => try emit.mirPushPopRegisterList(.push, inst), | 138 | .push_regs => try emit.mirPushPopRegisterList(.push, inst), |
| 200 | .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), | 139 | .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), |
| 201 | 140 | ||
| 202 | else => { | 141 | .dead => {}, |
| 203 | return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag}); | ||
| 204 | }, | ||
| 205 | } | 142 | } |
| 206 | } | 143 | } |
| 207 | 144 | ||
| ... | @@ -234,794 +171,385 @@ fn fixupRelocs(emit: *Emit) InnerError!void { | ... | @@ -234,794 +171,385 @@ fn fixupRelocs(emit: *Emit) InnerError!void { |
| 234 | } | 171 | } |
| 235 | } | 172 | } |
| 236 | 173 | ||
| 237 | fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 174 | fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct { |
| 238 | const tag = emit.mir.instructions.items(.tag)[inst]; | 175 | op1: Instruction.Operand = .none, |
| 239 | assert(tag == .interrupt); | 176 | op2: Instruction.Operand = .none, |
| 240 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 177 | op3: Instruction.Operand = .none, |
| 241 | switch (ops.flags) { | 178 | op4: Instruction.Operand = .none, |
| 242 | 0b00 => return lowerToZoEnc(.int3, emit.code), | 179 | }) InnerError!void { |
| 243 | else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}), | 180 | const inst = try Instruction.new(mnemonic, .{ |
| 244 | } | 181 | .op1 = ops.op1, |
| 182 | .op2 = ops.op2, | ||
| 183 | .op3 = ops.op3, | ||
| 184 | .op4 = ops.op4, | ||
| 185 | }); | ||
| 186 | return inst.encode(emit.code.writer()); | ||
| 245 | } | 187 | } |
| 246 | 188 | ||
| 247 | fn mirSyscall(emit: *Emit) InnerError!void { | 189 | fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { |
| 248 | return lowerToZoEnc(.syscall, emit.code); | 190 | const mnemonic = inline for (@typeInfo(Instruction.Mnemonic).Enum.fields) |field| { |
| 249 | } | 191 | if (mem.eql(u8, field.name, @tagName(tag))) break @field(Instruction.Mnemonic, field.name); |
| 192 | } else unreachable; | ||
| 250 | 193 | ||
| 251 | fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 194 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 252 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 195 | const data = emit.mir.instructions.items(.data)[inst]; |
| 253 | switch (ops.flags) { | ||
| 254 | 0b00 => { | ||
| 255 | // PUSH/POP reg | ||
| 256 | return lowerToOEnc(tag, ops.reg1, emit.code); | ||
| 257 | }, | ||
| 258 | 0b01 => { | ||
| 259 | // PUSH/POP r/m64 | ||
| 260 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 261 | const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) { | ||
| 262 | 16 => .word_ptr, | ||
| 263 | else => .qword_ptr, | ||
| 264 | }; | ||
| 265 | return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ | ||
| 266 | .disp = imm, | ||
| 267 | .base = ops.reg1, | ||
| 268 | }), emit.code); | ||
| 269 | }, | ||
| 270 | 0b10 => { | ||
| 271 | // PUSH imm32 | ||
| 272 | assert(tag == .push); | ||
| 273 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 274 | return lowerToIEnc(.push, imm, emit.code); | ||
| 275 | }, | ||
| 276 | 0b11 => unreachable, | ||
| 277 | } | ||
| 278 | } | ||
| 279 | 196 | ||
| 280 | fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 197 | var op1: Instruction.Operand = .none; |
| 281 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 198 | var op2: Instruction.Operand = .none; |
| 282 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 199 | var op3: Instruction.Operand = .none; |
| 283 | const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; | 200 | var op4: Instruction.Operand = .none; |
| 284 | var disp: i32 = -@intCast(i32, save_reg_list.stack_end); | 201 | |
| 285 | const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); | 202 | switch (ops) { |
| 286 | const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); | 203 | .none => {}, |
| 287 | for (callee_preserved_regs) |reg| { | 204 | .imm_s => op1 = .{ .imm = Immediate.s(@bitCast(i32, data.imm)) }, |
| 288 | if (reg_list.isSet(callee_preserved_regs, reg)) { | 205 | .imm_u => op1 = .{ .imm = Immediate.u(data.imm) }, |
| 289 | switch (tag) { | 206 | .r => op1 = .{ .reg = data.r }, |
| 290 | .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ | 207 | .rr => { |
| 291 | .disp = @bitCast(u32, disp), | 208 | op1 = .{ .reg = data.rr.r1 }; |
| 292 | .base = ops.reg1, | 209 | op2 = .{ .reg = data.rr.r2 }; |
| 293 | }), reg, emit.code), | 210 | }, |
| 294 | .pop => try lowerToRmEnc(.mov, reg, RegisterOrMemory.mem(.qword_ptr, .{ | 211 | .ri_s, .ri_u => { |
| 295 | .disp = @bitCast(u32, disp), | 212 | const imm = switch (ops) { |
| 296 | .base = ops.reg1, | 213 | .ri_s => Immediate.s(@bitCast(i32, data.ri.imm)), |
| 297 | }), emit.code), | 214 | .ri_u => Immediate.u(data.ri.imm), |
| 215 | else => unreachable, | ||
| 216 | }; | ||
| 217 | op1 = .{ .reg = data.ri.r1 }; | ||
| 218 | op2 = .{ .imm = imm }; | ||
| 219 | }, | ||
| 220 | .ri64 => { | ||
| 221 | const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data; | ||
| 222 | op1 = .{ .reg = data.rx.r1 }; | ||
| 223 | op2 = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }; | ||
| 224 | }, | ||
| 225 | .rri_s, .rri_u => { | ||
| 226 | const imm = switch (ops) { | ||
| 227 | .rri_s => Immediate.s(@bitCast(i32, data.rri.imm)), | ||
| 228 | .rri_u => Immediate.u(data.rri.imm), | ||
| 229 | else => unreachable, | ||
| 230 | }; | ||
| 231 | op1 = .{ .reg = data.rri.r1 }; | ||
| 232 | op2 = .{ .reg = data.rri.r2 }; | ||
| 233 | op3 = .{ .imm = imm }; | ||
| 234 | }, | ||
| 235 | .m_sib => { | ||
| 236 | const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data; | ||
| 237 | op1 = .{ .mem = Mir.MemorySib.decode(msib) }; | ||
| 238 | }, | ||
| 239 | .m_rip => { | ||
| 240 | const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; | ||
| 241 | op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; | ||
| 242 | }, | ||
| 243 | .mi_s_sib, .mi_u_sib => { | ||
| 244 | const msib = emit.mir.extraData(Mir.MemorySib, data.xi.payload).data; | ||
| 245 | const imm = switch (ops) { | ||
| 246 | .mi_s_sib => Immediate.s(@bitCast(i32, data.xi.imm)), | ||
| 247 | .mi_u_sib => Immediate.u(data.xi.imm), | ||
| 248 | else => unreachable, | ||
| 249 | }; | ||
| 250 | op1 = .{ .mem = Mir.MemorySib.decode(msib) }; | ||
| 251 | op2 = .{ .imm = imm }; | ||
| 252 | }, | ||
| 253 | .mi_u_rip, .mi_s_rip => { | ||
| 254 | const mrip = emit.mir.extraData(Mir.MemoryRip, data.xi.payload).data; | ||
| 255 | const imm = switch (ops) { | ||
| 256 | .mi_s_rip => Immediate.s(@bitCast(i32, data.xi.imm)), | ||
| 257 | .mi_u_rip => Immediate.u(data.xi.imm), | ||
| 258 | else => unreachable, | ||
| 259 | }; | ||
| 260 | op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; | ||
| 261 | op2 = .{ .imm = imm }; | ||
| 262 | }, | ||
| 263 | .rm_sib, .mr_sib => { | ||
| 264 | const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; | ||
| 265 | const op_r = .{ .reg = data.rx.r1 }; | ||
| 266 | const op_m = .{ .mem = Mir.MemorySib.decode(msib) }; | ||
| 267 | switch (ops) { | ||
| 268 | .rm_sib => { | ||
| 269 | op1 = op_r; | ||
| 270 | op2 = op_m; | ||
| 271 | }, | ||
| 272 | .mr_sib => { | ||
| 273 | op1 = op_m; | ||
| 274 | op2 = op_r; | ||
| 275 | }, | ||
| 298 | else => unreachable, | 276 | else => unreachable, |
| 299 | } | 277 | } |
| 300 | disp += 8; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | ||
| 306 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | ||
| 307 | switch (ops.flags) { | ||
| 308 | 0b00 => { | ||
| 309 | const target = emit.mir.instructions.items(.data)[inst].inst; | ||
| 310 | const source = emit.code.items.len; | ||
| 311 | try lowerToDEnc(tag, 0, emit.code); | ||
| 312 | try emit.relocs.append(emit.bin_file.allocator, .{ | ||
| 313 | .source = source, | ||
| 314 | .target = target, | ||
| 315 | .offset = emit.code.items.len - 4, | ||
| 316 | .length = 5, | ||
| 317 | }); | ||
| 318 | }, | 278 | }, |
| 319 | 0b01 => { | 279 | .rm_rip, .mr_rip => { |
| 320 | if (ops.reg1 == .none) { | 280 | const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; |
| 321 | // JMP/CALL [imm] | 281 | const op_r = .{ .reg = data.rx.r1 }; |
| 322 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 282 | const op_m = .{ .mem = Mir.MemoryRip.decode(mrip) }; |
| 323 | const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) { | 283 | switch (ops) { |
| 324 | 16 => .word_ptr, | 284 | .rm_sib => { |
| 325 | else => .qword_ptr, | 285 | op1 = op_r; |
| 326 | }; | 286 | op2 = op_m; |
| 327 | return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code); | 287 | }, |
| 288 | .mr_sib => { | ||
| 289 | op1 = op_m; | ||
| 290 | op2 = op_r; | ||
| 291 | }, | ||
| 292 | else => unreachable, | ||
| 328 | } | 293 | } |
| 329 | // JMP/CALL reg | ||
| 330 | return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); | ||
| 331 | }, | 294 | }, |
| 332 | 0b10 => { | 295 | else => return emit.fail("TODO handle generic encoding: {s}, {s}", .{ |
| 333 | // JMP/CALL r/m64 | 296 | @tagName(mnemonic), |
| 334 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 297 | @tagName(ops), |
| 335 | return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | 298 | }), |
| 336 | .disp = imm, | ||
| 337 | .base = ops.reg1, | ||
| 338 | }), emit.code); | ||
| 339 | }, | ||
| 340 | 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}), | ||
| 341 | } | 299 | } |
| 342 | } | ||
| 343 | 300 | ||
| 344 | fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 301 | return emit.encode(mnemonic, .{ |
| 345 | const mir_tag = emit.mir.instructions.items(.tag)[inst]; | 302 | .op1 = op1, |
| 346 | assert(mir_tag == .cond_jmp); | 303 | .op2 = op2, |
| 347 | const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; | 304 | .op3 = op3, |
| 348 | const tag: Tag = switch (inst_cc.cc) { | 305 | .op4 = op4, |
| 349 | .a => .ja, | ||
| 350 | .ae => .jae, | ||
| 351 | .b => .jb, | ||
| 352 | .be => .jbe, | ||
| 353 | .c => .jc, | ||
| 354 | .e => .je, | ||
| 355 | .g => .jg, | ||
| 356 | .ge => .jge, | ||
| 357 | .l => .jl, | ||
| 358 | .le => .jle, | ||
| 359 | .na => .jna, | ||
| 360 | .nae => .jnae, | ||
| 361 | .nb => .jnb, | ||
| 362 | .nbe => .jnbe, | ||
| 363 | .nc => .jnc, | ||
| 364 | .ne => .jne, | ||
| 365 | .ng => .jng, | ||
| 366 | .nge => .jnge, | ||
| 367 | .nl => .jnl, | ||
| 368 | .nle => .jnle, | ||
| 369 | .no => .jno, | ||
| 370 | .np => .jnp, | ||
| 371 | .ns => .jns, | ||
| 372 | .nz => .jnz, | ||
| 373 | .o => .jo, | ||
| 374 | .p => .jp, | ||
| 375 | .pe => .jpe, | ||
| 376 | .po => .jpo, | ||
| 377 | .s => .js, | ||
| 378 | .z => .jz, | ||
| 379 | }; | ||
| 380 | const source = emit.code.items.len; | ||
| 381 | try lowerToDEnc(tag, 0, emit.code); | ||
| 382 | try emit.relocs.append(emit.bin_file.allocator, .{ | ||
| 383 | .source = source, | ||
| 384 | .target = inst_cc.inst, | ||
| 385 | .offset = emit.code.items.len - 4, | ||
| 386 | .length = 6, | ||
| 387 | }); | 306 | }); |
| 388 | } | 307 | } |
| 389 | 308 | ||
| 390 | fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 309 | fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 391 | const mir_tag = emit.mir.instructions.items(.tag)[inst]; | 310 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 392 | assert(mir_tag == .cond_set_byte); | 311 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 393 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 312 | const moffs = emit.mir.extraData(Mir.MemoryMoffs, payload).data; |
| 394 | const cc = emit.mir.instructions.items(.data)[inst].cc; | 313 | const seg = @intToEnum(Register, moffs.seg); |
| 395 | const tag: Tag = switch (cc) { | 314 | const offset = moffs.decodeOffset(); |
| 396 | .a => .seta, | 315 | switch (ops) { |
| 397 | .ae => .setae, | 316 | .rax_moffs => { |
| 398 | .b => .setb, | 317 | try emit.encode(.mov, .{ |
| 399 | .be => .setbe, | 318 | .op1 = .{ .reg = .rax }, |
| 400 | .c => .setc, | 319 | .op2 = .{ .mem = Memory.moffs(seg, offset) }, |
| 401 | .e => .sete, | 320 | }); |
| 402 | .g => .setg, | ||
| 403 | .ge => .setge, | ||
| 404 | .l => .setl, | ||
| 405 | .le => .setle, | ||
| 406 | .na => .setna, | ||
| 407 | .nae => .setnae, | ||
| 408 | .nb => .setnb, | ||
| 409 | .nbe => .setnbe, | ||
| 410 | .nc => .setnc, | ||
| 411 | .ne => .setne, | ||
| 412 | .ng => .setng, | ||
| 413 | .nge => .setnge, | ||
| 414 | .nl => .setnl, | ||
| 415 | .nle => .setnle, | ||
| 416 | .no => .setno, | ||
| 417 | .np => .setnp, | ||
| 418 | .ns => .setns, | ||
| 419 | .nz => .setnz, | ||
| 420 | .o => .seto, | ||
| 421 | .p => .setp, | ||
| 422 | .pe => .setpe, | ||
| 423 | .po => .setpo, | ||
| 424 | .s => .sets, | ||
| 425 | .z => .setz, | ||
| 426 | }; | ||
| 427 | return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); | ||
| 428 | } | ||
| 429 | |||
| 430 | fn 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 | |||
| 484 | fn 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 | |||
| 508 | fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ||
| 509 | const tag = emit.mir.instructions.items(.tag)[inst]; | ||
| 510 | assert(tag == .ret); | ||
| 511 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | ||
| 512 | switch (ops.flags) { | ||
| 513 | 0b00 => { | ||
| 514 | // RETF imm16 | ||
| 515 | // I | ||
| 516 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 517 | return lowerToIEnc(.ret_far, imm, emit.code); | ||
| 518 | }, | ||
| 519 | 0b01 => { | ||
| 520 | return lowerToZoEnc(.ret_far, emit.code); | ||
| 521 | }, | ||
| 522 | 0b10 => { | ||
| 523 | // RET imm16 | ||
| 524 | // I | ||
| 525 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 526 | return lowerToIEnc(.ret_near, imm, emit.code); | ||
| 527 | }, | 321 | }, |
| 528 | 0b11 => { | 322 | .moffs_rax => { |
| 529 | return lowerToZoEnc(.ret_near, emit.code); | 323 | try emit.encode(.mov, .{ |
| 324 | .op1 = .{ .mem = Memory.moffs(seg, offset) }, | ||
| 325 | .op2 = .{ .reg = .rax }, | ||
| 326 | }); | ||
| 530 | }, | 327 | }, |
| 328 | else => unreachable, | ||
| 531 | } | 329 | } |
| 532 | } | 330 | } |
| 533 | 331 | ||
| 534 | fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 332 | fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 535 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 333 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 536 | switch (ops.flags) { | 334 | const data = emit.mir.instructions.items(.data)[inst]; |
| 537 | 0b00 => { | 335 | |
| 538 | if (ops.reg2 == .none) { | 336 | var op1: Instruction.Operand = .none; |
| 539 | // mov reg1, imm32 | 337 | var op2: Instruction.Operand = .none; |
| 540 | // MI | 338 | switch (ops) { |
| 541 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 339 | .rr => { |
| 542 | return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); | 340 | op1 = .{ .reg = data.rr.r1 }; |
| 543 | } | 341 | op2 = .{ .reg = data.rr.r2 }; |
| 544 | // mov reg1, reg2 | ||
| 545 | // RM | ||
| 546 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); | ||
| 547 | }, | ||
| 548 | 0b01 => { | ||
| 549 | // mov reg1, [reg2 + imm32] | ||
| 550 | // RM | ||
| 551 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 552 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; | ||
| 553 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | ||
| 554 | .disp = imm, | ||
| 555 | .base = src_reg, | ||
| 556 | }), emit.code); | ||
| 557 | }, | 342 | }, |
| 558 | 0b10 => { | 343 | .rm_sib => { |
| 559 | if (ops.reg2 == .none) { | 344 | const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; |
| 560 | return emit.fail("TODO unused variant: mov reg1, none, 0b10", .{}); | 345 | op1 = .{ .reg = data.rx.r1 }; |
| 561 | } | 346 | op2 = .{ .mem = Mir.MemorySib.decode(msib) }; |
| 562 | // mov [reg1 + imm32], reg2 | ||
| 563 | // MR | ||
| 564 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 565 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ | ||
| 566 | .disp = imm, | ||
| 567 | .base = ops.reg1, | ||
| 568 | }), ops.reg2, emit.code); | ||
| 569 | }, | 347 | }, |
| 570 | 0b11 => { | 348 | .rm_rip => { |
| 571 | return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); | 349 | const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; |
| 350 | op1 = .{ .reg = data.rx.r1 }; | ||
| 351 | op2 = .{ .mem = Mir.MemoryRip.decode(mrip) }; | ||
| 572 | }, | 352 | }, |
| 353 | else => unreachable, // TODO | ||
| 573 | } | 354 | } |
| 574 | } | ||
| 575 | |||
| 576 | fn 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 | |||
| 593 | inline 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 | |||
| 602 | inline 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 | } | ||
| 612 | 355 | ||
| 613 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 356 | const mnemonic: Instruction.Mnemonic = switch (op1.bitSize()) { |
| 614 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 357 | 32, 64 => if (op2.bitSize() == 32) .movsxd else .movsx, |
| 615 | const scale = ops.flags; | 358 | else => .movsx, |
| 616 | const payload = emit.mir.instructions.items(.data)[inst].payload; | ||
| 617 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | ||
| 618 | // OP reg1, [reg2 + scale*index + imm32] | ||
| 619 | const scale_index = ScaleIndex{ | ||
| 620 | .scale = scale, | ||
| 621 | .index = index_reg_disp.index, | ||
| 622 | }; | ||
| 623 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | ||
| 624 | .disp = index_reg_disp.disp, | ||
| 625 | .base = ops.reg2, | ||
| 626 | .scale_index = scale_index, | ||
| 627 | }), emit.code); | ||
| 628 | } | ||
| 629 | |||
| 630 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | ||
| 631 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | ||
| 632 | const scale = ops.flags; | ||
| 633 | const payload = emit.mir.instructions.items(.data)[inst].payload; | ||
| 634 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | ||
| 635 | const scale_index = ScaleIndex{ | ||
| 636 | .scale = scale, | ||
| 637 | .index = index_reg_disp.index, | ||
| 638 | }; | 359 | }; |
| 639 | assert(ops.reg2 != .none); | ||
| 640 | // OP [reg1 + scale*index + imm32], reg2 | ||
| 641 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ | ||
| 642 | .disp = index_reg_disp.disp, | ||
| 643 | .base = ops.reg1, | ||
| 644 | .scale_index = scale_index, | ||
| 645 | }), ops.reg2, emit.code); | ||
| 646 | } | ||
| 647 | 360 | ||
| 648 | fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 361 | return emit.encode(mnemonic, .{ |
| 649 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 362 | .op1 = op1, |
| 650 | const scale = ops.flags; | 363 | .op2 = op2, |
| 651 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 364 | }); |
| 652 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); | ||
| 653 | const scale_index = ScaleIndex{ | ||
| 654 | .scale = scale, | ||
| 655 | .index = index_reg_disp_imm.index, | ||
| 656 | }; | ||
| 657 | // OP qword ptr [reg1 + scale*index + imm32], imm32 | ||
| 658 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ | ||
| 659 | .disp = index_reg_disp_imm.disp, | ||
| 660 | .base = ops.reg1, | ||
| 661 | .scale_index = scale_index, | ||
| 662 | }), index_reg_disp_imm.imm, emit.code); | ||
| 663 | } | 365 | } |
| 664 | 366 | ||
| 665 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 367 | fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { |
| 666 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 368 | inline for (@typeInfo(bits.Condition).Enum.fields) |field| { |
| 667 | assert(ops.reg2 == .none); | 369 | if (mem.eql(u8, field.name, @tagName(cc))) |
| 668 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 370 | return @field(Instruction.Mnemonic, basename ++ field.name); |
| 669 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); | 371 | } else unreachable; |
| 670 | const ptr_size: Memory.PtrSize = switch (ops.flags) { | ||
| 671 | 0b00 => .byte_ptr, | ||
| 672 | 0b01 => .word_ptr, | ||
| 673 | 0b10 => .dword_ptr, | ||
| 674 | 0b11 => .qword_ptr, | ||
| 675 | }; | ||
| 676 | const scale_index = ScaleIndex{ | ||
| 677 | .scale = 0, | ||
| 678 | .index = index_reg_disp_imm.index, | ||
| 679 | }; | ||
| 680 | // OP ptr [reg1 + index + imm32], imm32 | ||
| 681 | return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ | ||
| 682 | .disp = index_reg_disp_imm.disp, | ||
| 683 | .base = ops.reg1, | ||
| 684 | .scale_index = scale_index, | ||
| 685 | }), index_reg_disp_imm.imm, emit.code); | ||
| 686 | } | 372 | } |
| 687 | 373 | ||
| 688 | fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 374 | fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 689 | const mir_tag = emit.mir.instructions.items(.tag)[inst]; | 375 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 690 | assert(mir_tag == .mov_sign_extend); | 376 | switch (ops) { |
| 691 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 377 | .rr_c => { |
| 692 | const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; | 378 | const data = emit.mir.instructions.items(.data)[inst].rr_c; |
| 693 | switch (ops.flags) { | 379 | const mnemonic = mnemonicFromConditionCode("cmov", data.cc); |
| 694 | 0b00 => { | 380 | return emit.encode(mnemonic, .{ |
| 695 | const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx; | 381 | .op1 = .{ .reg = data.r1 }, |
| 696 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); | 382 | .op2 = .{ .reg = data.r2 }, |
| 697 | }, | 383 | }); |
| 698 | 0b01 => { | ||
| 699 | return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ | ||
| 700 | .disp = imm, | ||
| 701 | .base = ops.reg2, | ||
| 702 | }), emit.code); | ||
| 703 | }, | ||
| 704 | 0b10 => { | ||
| 705 | return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ | ||
| 706 | .disp = imm, | ||
| 707 | .base = ops.reg2, | ||
| 708 | }), emit.code); | ||
| 709 | }, | ||
| 710 | 0b11 => { | ||
| 711 | return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{ | ||
| 712 | .disp = imm, | ||
| 713 | .base = ops.reg2, | ||
| 714 | }), emit.code); | ||
| 715 | }, | 384 | }, |
| 385 | else => unreachable, // TODO | ||
| 716 | } | 386 | } |
| 717 | } | 387 | } |
| 718 | 388 | ||
| 719 | fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 389 | fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 720 | const mir_tag = emit.mir.instructions.items(.tag)[inst]; | 390 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 721 | assert(mir_tag == .mov_zero_extend); | 391 | switch (ops) { |
| 722 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 392 | .r_c => { |
| 723 | const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; | 393 | const data = emit.mir.instructions.items(.data)[inst].r_c; |
| 724 | switch (ops.flags) { | 394 | const mnemonic = mnemonicFromConditionCode("set", data.cc); |
| 725 | 0b00 => { | 395 | return emit.encode(mnemonic, .{ |
| 726 | return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); | 396 | .op1 = .{ .reg = data.r1 }, |
| 727 | }, | 397 | }); |
| 728 | 0b01 => { | ||
| 729 | return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ | ||
| 730 | .disp = imm, | ||
| 731 | .base = ops.reg2, | ||
| 732 | }), emit.code); | ||
| 733 | }, | ||
| 734 | 0b10 => { | ||
| 735 | return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ | ||
| 736 | .disp = imm, | ||
| 737 | .base = ops.reg2, | ||
| 738 | }), emit.code); | ||
| 739 | }, | ||
| 740 | 0b11 => { | ||
| 741 | return emit.fail("TODO unused variant: movzx 0b11", .{}); | ||
| 742 | }, | 398 | }, |
| 399 | else => unreachable, // TODO | ||
| 743 | } | 400 | } |
| 744 | } | 401 | } |
| 745 | 402 | ||
| 746 | fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 403 | fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 747 | const tag = emit.mir.instructions.items(.tag)[inst]; | 404 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 748 | assert(tag == .movabs); | 405 | switch (ops) { |
| 749 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 406 | .inst_cc => { |
| 750 | switch (ops.flags) { | 407 | const data = emit.mir.instructions.items(.data)[inst].inst_cc; |
| 751 | 0b00 => { | 408 | const mnemonic = mnemonicFromConditionCode("j", data.cc); |
| 752 | const imm: u64 = if (ops.reg1.size() == 64) blk: { | 409 | const source = emit.code.items.len; |
| 753 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 410 | try emit.encode(mnemonic, .{ |
| 754 | const imm = emit.mir.extraData(Mir.Imm64, payload).data; | 411 | .op1 = .{ .imm = Immediate.s(0) }, |
| 755 | break :blk imm.decode(); | 412 | }); |
| 756 | } else emit.mir.instructions.items(.data)[inst].imm; | 413 | try emit.relocs.append(emit.bin_file.allocator, .{ |
| 757 | // movabs reg, imm64 | 414 | .source = source, |
| 758 | // OI | 415 | .target = data.inst, |
| 759 | return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); | 416 | .offset = emit.code.items.len - 4, |
| 760 | }, | 417 | .length = 6, |
| 761 | 0b01 => { | 418 | }); |
| 762 | if (ops.reg1 == .none) { | ||
| 763 | const imm: u64 = if (ops.reg2.size() == 64) blk: { | ||
| 764 | const payload = emit.mir.instructions.items(.data)[inst].payload; | ||
| 765 | const imm = emit.mir.extraData(Mir.Imm64, payload).data; | ||
| 766 | break :blk imm.decode(); | ||
| 767 | } else emit.mir.instructions.items(.data)[inst].imm; | ||
| 768 | // movabs moffs64, rax | ||
| 769 | // TD | ||
| 770 | return lowerToTdEnc(.mov, imm, ops.reg2, emit.code); | ||
| 771 | } | ||
| 772 | const imm: u64 = if (ops.reg1.size() == 64) blk: { | ||
| 773 | const payload = emit.mir.instructions.items(.data)[inst].payload; | ||
| 774 | const imm = emit.mir.extraData(Mir.Imm64, payload).data; | ||
| 775 | break :blk imm.decode(); | ||
| 776 | } else emit.mir.instructions.items(.data)[inst].imm; | ||
| 777 | // movabs rax, moffs64 | ||
| 778 | // FD | ||
| 779 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); | ||
| 780 | }, | 419 | }, |
| 781 | else => return emit.fail("TODO unused movabs variant", .{}), | 420 | else => unreachable, // TODO |
| 782 | } | 421 | } |
| 783 | } | 422 | } |
| 784 | 423 | ||
| 785 | fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 424 | fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 786 | const tag = emit.mir.instructions.items(.tag)[inst]; | 425 | const target = emit.mir.instructions.items(.data)[inst].inst; |
| 787 | assert(tag == .fisttp); | 426 | const source = emit.code.items.len; |
| 788 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 427 | try emit.encode(.jmp, .{ |
| 789 | 428 | .op1 = .{ .imm = Immediate.s(0) }, | |
| 790 | // the selecting between operand sizes for this particular `fisttp` instruction | 429 | }); |
| 791 | // is done via opcode instead of the usual prefixes. | 430 | try emit.relocs.append(emit.bin_file.allocator, .{ |
| 792 | 431 | .source = source, | |
| 793 | const opcode: Tag = switch (ops.flags) { | 432 | .target = target, |
| 794 | 0b00 => .fisttp16, | 433 | .offset = emit.code.items.len - 4, |
| 795 | 0b01 => .fisttp32, | 434 | .length = 5, |
| 796 | 0b10 => .fisttp64, | 435 | }); |
| 797 | else => unreachable, | ||
| 798 | }; | ||
| 799 | const mem_or_reg = Memory{ | ||
| 800 | .base = ops.reg1, | ||
| 801 | .disp = emit.mir.instructions.items(.data)[inst].imm, | ||
| 802 | .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used | ||
| 803 | }; | ||
| 804 | return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); | ||
| 805 | } | ||
| 806 | |||
| 807 | fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ||
| 808 | const tag = emit.mir.instructions.items(.tag)[inst]; | ||
| 809 | assert(tag == .fld); | ||
| 810 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | ||
| 811 | |||
| 812 | // the selecting between operand sizes for this particular `fisttp` instruction | ||
| 813 | // is done via opcode instead of the usual prefixes. | ||
| 814 | |||
| 815 | const opcode: Tag = switch (ops.flags) { | ||
| 816 | 0b01 => .fld32, | ||
| 817 | 0b10 => .fld64, | ||
| 818 | else => unreachable, | ||
| 819 | }; | ||
| 820 | const mem_or_reg = Memory{ | ||
| 821 | .base = ops.reg1, | ||
| 822 | .disp = emit.mir.instructions.items(.data)[inst].imm, | ||
| 823 | .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used | ||
| 824 | }; | ||
| 825 | return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); | ||
| 826 | } | 436 | } |
| 827 | 437 | ||
| 828 | fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 438 | fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 829 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 439 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; |
| 830 | switch (ops.flags) { | ||
| 831 | 0b00 => { | ||
| 832 | // sal reg1, 1 | ||
| 833 | // M1 | ||
| 834 | return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); | ||
| 835 | }, | ||
| 836 | 0b01 => { | ||
| 837 | // sal reg1, .cl | ||
| 838 | // MC | ||
| 839 | return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); | ||
| 840 | }, | ||
| 841 | 0b10 => { | ||
| 842 | // sal reg1, imm8 | ||
| 843 | // MI | ||
| 844 | const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); | ||
| 845 | return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); | ||
| 846 | }, | ||
| 847 | 0b11 => { | ||
| 848 | return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); | ||
| 849 | }, | ||
| 850 | } | ||
| 851 | } | ||
| 852 | 440 | ||
| 853 | fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 441 | const offset = blk: { |
| 854 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 442 | try emit.encode(.call, .{ |
| 855 | if (ops.reg1 != .none) { | 443 | .op1 = .{ .imm = Immediate.s(0) }, |
| 856 | assert(ops.reg2 == .none); | 444 | }); |
| 857 | return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); | 445 | break :blk @intCast(u32, emit.code.items.len) - 4; |
| 858 | } | ||
| 859 | assert(ops.reg2 != .none); | ||
| 860 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 861 | const ptr_size: Memory.PtrSize = switch (ops.flags) { | ||
| 862 | 0b00 => .byte_ptr, | ||
| 863 | 0b01 => .word_ptr, | ||
| 864 | 0b10 => .dword_ptr, | ||
| 865 | 0b11 => .qword_ptr, | ||
| 866 | }; | 446 | }; |
| 867 | return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ | ||
| 868 | .disp = imm, | ||
| 869 | .base = ops.reg2, | ||
| 870 | }), emit.code); | ||
| 871 | } | ||
| 872 | 447 | ||
| 873 | fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 448 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 874 | const tag = emit.mir.instructions.items(.tag)[inst]; | 449 | // Add relocation to the decl. |
| 875 | assert(tag == .imul_complex); | 450 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; |
| 876 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 451 | const target = macho_file.getGlobalByIndex(relocation.sym_index); |
| 877 | switch (ops.flags) { | 452 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ |
| 878 | 0b00 => { | 453 | .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 879 | return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); | 454 | .target = target, |
| 880 | }, | 455 | .offset = offset, |
| 881 | 0b01 => { | 456 | .addend = 0, |
| 882 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 457 | .pcrel = true, |
| 883 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; | 458 | .length = 2, |
| 884 | return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ | 459 | }); |
| 885 | .disp = imm, | 460 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 886 | .base = src_reg, | 461 | // Add relocation to the decl. |
| 887 | }), emit.code); | 462 | const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; |
| 888 | }, | 463 | const target = coff_file.getGlobalByIndex(relocation.sym_index); |
| 889 | 0b10 => { | 464 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ |
| 890 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 465 | .type = .direct, |
| 891 | return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code); | 466 | .target = target, |
| 892 | }, | 467 | .offset = offset, |
| 893 | 0b11 => { | 468 | .addend = 0, |
| 894 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 469 | .pcrel = true, |
| 895 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | 470 | .length = 2, |
| 896 | return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ | 471 | }); |
| 897 | .disp = imm_pair.dest_off, | 472 | } else { |
| 898 | .base = ops.reg2, | 473 | return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); |
| 899 | }), imm_pair.operand, emit.code); | ||
| 900 | }, | ||
| 901 | } | 474 | } |
| 902 | } | 475 | } |
| 903 | 476 | ||
| 904 | fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 477 | fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { |
| 905 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 478 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 906 | const tag: Tag = switch (ops.flags) { | 479 | const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; |
| 907 | 0b00 => .cbw, | 480 | const base = @intToEnum(Register, save_reg_list.base_reg); |
| 908 | 0b01 => .cwd, | 481 | var disp: i32 = -@intCast(i32, save_reg_list.stack_end); |
| 909 | 0b10 => .cdq, | 482 | const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); |
| 910 | 0b11 => .cqo, | 483 | const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); |
| 911 | }; | 484 | for (callee_preserved_regs) |reg| { |
| 912 | return lowerToZoEnc(tag, emit.code); | 485 | if (reg_list.isSet(callee_preserved_regs, reg)) { |
| 913 | } | 486 | const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{ |
| 914 | 487 | .base = base, | |
| 915 | fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 488 | .disp = disp, |
| 916 | const tag = emit.mir.instructions.items(.tag)[inst]; | 489 | }) }; |
| 917 | assert(tag == .lea); | 490 | const op2: Instruction.Operand = .{ .reg = reg }; |
| 918 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 491 | switch (tag) { |
| 919 | switch (ops.flags) { | 492 | .push => try emit.encode(.mov, .{ |
| 920 | 0b00 => { | 493 | .op1 = op1, |
| 921 | // lea reg1, [reg2 + imm32] | 494 | .op2 = op2, |
| 922 | // RM | ||
| 923 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 924 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; | ||
| 925 | return lowerToRmEnc( | ||
| 926 | .lea, | ||
| 927 | ops.reg1, | ||
| 928 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | ||
| 929 | .disp = imm, | ||
| 930 | .base = src_reg, | ||
| 931 | }), | 495 | }), |
| 932 | emit.code, | 496 | .pop => try emit.encode(.mov, .{ |
| 933 | ); | 497 | .op1 = op2, |
| 934 | }, | 498 | .op2 = op1, |
| 935 | 0b01 => { | ||
| 936 | // lea reg1, [rip + imm32] | ||
| 937 | // RM | ||
| 938 | const start_offset = emit.code.items.len; | ||
| 939 | try lowerToRmEnc( | ||
| 940 | .lea, | ||
| 941 | ops.reg1, | ||
| 942 | RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0), | ||
| 943 | emit.code, | ||
| 944 | ); | ||
| 945 | const end_offset = emit.code.items.len; | ||
| 946 | // Backpatch the displacement | ||
| 947 | const payload = emit.mir.instructions.items(.data)[inst].payload; | ||
| 948 | const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode(); | ||
| 949 | const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset)); | ||
| 950 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); | ||
| 951 | }, | ||
| 952 | 0b10 => { | ||
| 953 | // lea reg, [rbp + index + imm32] | ||
| 954 | const payload = emit.mir.instructions.items(.data)[inst].payload; | ||
| 955 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | ||
| 956 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; | ||
| 957 | const scale_index = ScaleIndex{ | ||
| 958 | .scale = 0, | ||
| 959 | .index = index_reg_disp.index, | ||
| 960 | }; | ||
| 961 | return lowerToRmEnc( | ||
| 962 | .lea, | ||
| 963 | ops.reg1, | ||
| 964 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | ||
| 965 | .disp = index_reg_disp.disp, | ||
| 966 | .base = src_reg, | ||
| 967 | .scale_index = scale_index, | ||
| 968 | }), | 499 | }), |
| 969 | emit.code, | 500 | else => unreachable, |
| 970 | ); | 501 | } |
| 971 | }, | 502 | disp += 8; |
| 972 | 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}), | 503 | } |
| 973 | } | 504 | } |
| 974 | } | 505 | } |
| 975 | 506 | ||
| 976 | fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 507 | fn mirLeaLinker(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 977 | const tag = emit.mir.instructions.items(.tag)[inst]; | 508 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 978 | assert(tag == .lea_pic); | 509 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 979 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 510 | const metadata = emit.mir.extraData(Mir.LeaRegisterReloc, payload).data; |
| 980 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; | 511 | const reg = @intToEnum(Register, metadata.reg); |
| 981 | |||
| 982 | switch (ops.flags) { | ||
| 983 | 0b00, 0b01, 0b10 => {}, | ||
| 984 | else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), | ||
| 985 | } | ||
| 986 | 512 | ||
| 987 | // lea reg1, [rip + reloc] | 513 | try emit.encode(.lea, .{ |
| 988 | // RM | 514 | .op1 = .{ .reg = reg }, |
| 989 | try lowerToRmEnc( | 515 | .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, |
| 990 | .lea, | 516 | }); |
| 991 | ops.reg1, | ||
| 992 | RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0), | ||
| 993 | emit.code, | ||
| 994 | ); | ||
| 995 | 517 | ||
| 996 | const end_offset = emit.code.items.len; | 518 | const end_offset = emit.code.items.len; |
| 997 | 519 | ||
| 998 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { | 520 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 999 | const reloc_type = switch (ops.flags) { | 521 | const reloc_type = switch (ops) { |
| 1000 | 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), | 522 | .got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), |
| 1001 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), | 523 | .direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 1002 | else => unreachable, | 524 | else => unreachable, |
| 1003 | }; | 525 | }; |
| 1004 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | 526 | const atom_index = macho_file.getAtomIndexForSymbol(.{ |
| 527 | .sym_index = metadata.atom_index, | ||
| 528 | .file = null, | ||
| 529 | }).?; | ||
| 1005 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ | 530 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ |
| 1006 | .type = reloc_type, | 531 | .type = reloc_type, |
| 1007 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | 532 | .target = .{ .sym_index = metadata.sym_index, .file = null }, |
| 1008 | .offset = @intCast(u32, end_offset - 4), | 533 | .offset = @intCast(u32, end_offset - 4), |
| 1009 | .addend = 0, | 534 | .addend = 0, |
| 1010 | .pcrel = true, | 535 | .pcrel = true, |
| 1011 | .length = 2, | 536 | .length = 2, |
| 1012 | }); | 537 | }); |
| 1013 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | 538 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 1014 | const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | 539 | const atom_index = coff_file.getAtomIndexForSymbol(.{ |
| 540 | .sym_index = metadata.atom_index, | ||
| 541 | .file = null, | ||
| 542 | }).?; | ||
| 1015 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ | 543 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ |
| 1016 | .type = switch (ops.flags) { | 544 | .type = switch (ops) { |
| 1017 | 0b00 => .got, | 545 | .got_reloc => .got, |
| 1018 | 0b01 => .direct, | 546 | .direct_reloc => .direct, |
| 1019 | 0b10 => .import, | 547 | .import_reloc => .import, |
| 1020 | else => unreachable, | 548 | else => unreachable, |
| 1021 | }, | 549 | }, |
| 1022 | .target = switch (ops.flags) { | 550 | .target = switch (ops) { |
| 1023 | 0b00, 0b01 => .{ .sym_index = relocation.sym_index, .file = null }, | 551 | .got_reloc, .direct_reloc => .{ .sym_index = metadata.sym_index, .file = null }, |
| 1024 | 0b10 => coff_file.getGlobalByIndex(relocation.sym_index), | 552 | .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index), |
| 1025 | else => unreachable, | 553 | else => unreachable, |
| 1026 | }, | 554 | }, |
| 1027 | .offset = @intCast(u32, end_offset - 4), | 555 | .offset = @intCast(u32, end_offset - 4), |
| ... | @@ -1034,158 +562,23 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1034,158 +562,23 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1034 | } | 562 | } |
| 1035 | } | 563 | } |
| 1036 | 564 | ||
| 1037 | // SSE instructions | 565 | fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1038 | 566 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 1039 | fn mirMovFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 567 | const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data; |
| 1040 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 568 | log.debug("mirDbgLine", .{}); |
| 1041 | switch (ops.flags) { | 569 | try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column); |
| 1042 | 0b00 => { | ||
| 1043 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 1044 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ | ||
| 1045 | .disp = imm, | ||
| 1046 | .base = ops.reg2, | ||
| 1047 | }), emit.code); | ||
| 1048 | }, | ||
| 1049 | 0b01 => { | ||
| 1050 | const imm = emit.mir.instructions.items(.data)[inst].imm; | ||
| 1051 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | ||
| 1052 | .disp = imm, | ||
| 1053 | .base = ops.reg1, | ||
| 1054 | }), ops.reg2, emit.code); | ||
| 1055 | }, | ||
| 1056 | 0b10 => { | ||
| 1057 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); | ||
| 1058 | }, | ||
| 1059 | else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), | ||
| 1060 | } | ||
| 1061 | } | 570 | } |
| 1062 | 571 | ||
| 1063 | fn mirAddFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 572 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { |
| 1064 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 573 | const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); |
| 1065 | switch (ops.flags) { | 574 | const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; |
| 1066 | 0b00 => { | 575 | log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc }); |
| 1067 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); | 576 | switch (emit.debug_output) { |
| 1068 | }, | 577 | .dwarf => |dw| { |
| 1069 | else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), | 578 | try dw.advancePCAndLine(delta_line, delta_pc); |
| 1070 | } | 579 | emit.prev_di_line = line; |
| 1071 | } | 580 | emit.prev_di_column = column; |
| 1072 | 581 | emit.prev_di_pc = emit.code.items.len; | |
| 1073 | fn 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 | |||
| 1084 | fn 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 | |||
| 1108 | fn 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 | |||
| 1118 | fn 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 | |||
| 1130 | fn 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 | |||
| 1170 | fn 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 | |||
| 1179 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { | ||
| 1180 | const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); | ||
| 1181 | const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; | ||
| 1182 | log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc }); | ||
| 1183 | switch (emit.debug_output) { | ||
| 1184 | .dwarf => |dw| { | ||
| 1185 | try dw.advancePCAndLine(delta_line, delta_pc); | ||
| 1186 | emit.prev_di_line = line; | ||
| 1187 | emit.prev_di_column = column; | ||
| 1188 | emit.prev_di_pc = emit.code.items.len; | ||
| 1189 | }, | 582 | }, |
| 1190 | .plan9 => |dbg_out| { | 583 | .plan9 => |dbg_out| { |
| 1191 | if (delta_pc <= 0) return; // only do this when the pc changes | 584 | if (delta_pc <= 0) return; // only do this when the pc changes |
| ... | @@ -1227,8 +620,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { | ... | @@ -1227,8 +620,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { |
| 1227 | } | 620 | } |
| 1228 | 621 | ||
| 1229 | fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 622 | fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1230 | const tag = emit.mir.instructions.items(.tag)[inst]; | 623 | _ = inst; |
| 1231 | assert(tag == .dbg_prologue_end); | ||
| 1232 | switch (emit.debug_output) { | 624 | switch (emit.debug_output) { |
| 1233 | .dwarf => |dw| { | 625 | .dwarf => |dw| { |
| 1234 | try dw.setPrologueEnd(); | 626 | try dw.setPrologueEnd(); |
| ... | @@ -1244,8 +636,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1244,8 +636,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1244 | } | 636 | } |
| 1245 | 637 | ||
| 1246 | fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 638 | fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1247 | const tag = emit.mir.instructions.items(.tag)[inst]; | 639 | _ = inst; |
| 1248 | assert(tag == .dbg_epilogue_begin); | ||
| 1249 | switch (emit.debug_output) { | 640 | switch (emit.debug_output) { |
| 1250 | .dwarf => |dw| { | 641 | .dwarf => |dw| { |
| 1251 | try dw.setEpilogueBegin(); | 642 | try dw.setEpilogueBegin(); |
| ... | @@ -1259,1839 +650,3 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1259,1839 +650,3 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1259 | .none => {}, | 650 | .none => {}, |
| 1260 | } | 651 | } |
| 1261 | } | 652 | } |
| 1262 | |||
| 1263 | const 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 | |||
| 1481 | const 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 | |||
| 1540 | const 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 | |||
| 1568 | inline 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 | |||
| 1865 | inline 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 | |||
| 1930 | const VexEncoding = struct { | ||
| 1931 | prefix: Encoder.Vex, | ||
| 1932 | reg: ?enum { | ||
| 1933 | ndd, | ||
| 1934 | nds, | ||
| 1935 | dds, | ||
| 1936 | }, | ||
| 1937 | }; | ||
| 1938 | |||
| 1939 | inline 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 | |||
| 2020 | const ScaleIndex = packed struct { | ||
| 2021 | scale: u2, | ||
| 2022 | index: Register, | ||
| 2023 | }; | ||
| 2024 | |||
| 2025 | const 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 | |||
| 2109 | fn 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 | |||
| 2118 | const 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 | |||
| 2161 | fn 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 | |||
| 2176 | fn 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 | |||
| 2194 | fn 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 | |||
| 2208 | fn 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 | |||
| 2216 | fn 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 | |||
| 2253 | fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { | ||
| 2254 | return lowerToMxEnc(tag, reg_or_mem, .m, code); | ||
| 2255 | } | ||
| 2256 | |||
| 2257 | fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { | ||
| 2258 | return lowerToMxEnc(tag, reg_or_mem, .m1, code); | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { | ||
| 2262 | return lowerToMxEnc(tag, reg_or_mem, .mc, code); | ||
| 2263 | } | ||
| 2264 | |||
| 2265 | fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { | ||
| 2266 | return lowerToTdFdEnc(tag, reg, moffs, code, true); | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) InnerError!void { | ||
| 2270 | return lowerToTdFdEnc(tag, reg, moffs, code, false); | ||
| 2271 | } | ||
| 2272 | |||
| 2273 | fn 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 | |||
| 2293 | fn 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 | |||
| 2314 | fn 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 | |||
| 2365 | fn 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 | |||
| 2369 | fn 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 | |||
| 2373 | fn 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 | |||
| 2422 | fn 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 | |||
| 2469 | fn 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. | ||
| 2517 | fn 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. | ||
| 2559 | fn 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 | |||
| 2600 | fn 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 | |||
| 2634 | fn 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 | |||
| 2673 | fn 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 | |||
| 2693 | const 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 | |||
| 2718 | test "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 | |||
| 2796 | test "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 | |||
| 2887 | test "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 | |||
| 2922 | test "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 | |||
| 2945 | test "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 | |||
| 2962 | test "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 | |||
| 2996 | test "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 | |||
| 3031 | test "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 | |||
| 3040 | test "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 | |||
| 3068 | test "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 | |||
| 3079 | test "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 | |||
| 3090 | test "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 @@ | ||
| 1 | const Encoding = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const math = std.math; | ||
| 6 | |||
| 7 | const bits = @import("bits.zig"); | ||
| 8 | const encoder = @import("encoder.zig"); | ||
| 9 | const Instruction = encoder.Instruction; | ||
| 10 | const Register = bits.Register; | ||
| 11 | const Rex = encoder.Rex; | ||
| 12 | const LegacyPrefixes = encoder.LegacyPrefixes; | ||
| 13 | |||
| 14 | const table = @import("encodings.zig").table; | ||
| 15 | |||
| 16 | mnemonic: Mnemonic, | ||
| 17 | op_en: OpEn, | ||
| 18 | op1: Op, | ||
| 19 | op2: Op, | ||
| 20 | op3: Op, | ||
| 21 | op4: Op, | ||
| 22 | opc_len: u2, | ||
| 23 | opc: [3]u8, | ||
| 24 | modrm_ext: u3, | ||
| 25 | mode: Mode, | ||
| 26 | |||
| 27 | pub 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. | ||
| 161 | pub 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 | |||
| 210 | pub fn opcode(encoding: *const Encoding) []const u8 { | ||
| 211 | return encoding.opc[0..encoding.opc_len]; | ||
| 212 | } | ||
| 213 | |||
| 214 | pub 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 | |||
| 222 | pub 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 | |||
| 229 | pub 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 | |||
| 244 | pub 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 | |||
| 315 | pub 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 | |||
| 355 | pub 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 | |||
| 366 | pub 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 | |||
| 580 | pub const Mode = enum { | ||
| 581 | none, | ||
| 582 | fpu, | ||
| 583 | rex, | ||
| 584 | long, | ||
| 585 | sse, | ||
| 586 | sse2, | ||
| 587 | }; | ||
src/arch/x86_64/Mir.zig+358-451| ... | @@ -12,9 +12,12 @@ const builtin = @import("builtin"); | ... | @@ -12,9 +12,12 @@ const builtin = @import("builtin"); |
| 12 | const assert = std.debug.assert; | 12 | const assert = std.debug.assert; |
| 13 | 13 | ||
| 14 | const bits = @import("bits.zig"); | 14 | const bits = @import("bits.zig"); |
| 15 | const encoder = @import("encoder.zig"); | ||
| 16 | |||
| 15 | const Air = @import("../../Air.zig"); | 17 | const Air = @import("../../Air.zig"); |
| 16 | const CodeGen = @import("CodeGen.zig"); | 18 | const CodeGen = @import("CodeGen.zig"); |
| 17 | const IntegerBitSet = std.bit_set.IntegerBitSet; | 19 | const IntegerBitSet = std.bit_set.IntegerBitSet; |
| 20 | const Memory = bits.Memory; | ||
| 18 | const Register = bits.Register; | 21 | const Register = bits.Register; |
| 19 | 22 | ||
| 20 | instructions: std.MultiArrayList(Inst).Slice, | 23 | instructions: std.MultiArrayList(Inst).Slice, |
| ... | @@ -24,428 +27,300 @@ extra: []const u32, | ... | @@ -24,428 +27,300 @@ extra: []const u32, |
| 24 | pub const Inst = struct { | 27 | pub const Inst = struct { |
| 25 | tag: Tag, | 28 | tag: Tag, |
| 26 | ops: Ops, | 29 | ops: Ops, |
| 27 | /// The meaning of this depends on `tag` and `ops`. | ||
| 28 | data: Data, | 30 | data: Data, |
| 29 | 31 | ||
| 30 | pub const Tag = enum(u16) { | 32 | pub const Index = u32; |
| 31 | /// ops flags: form: | ||
| 32 | /// 0b00 reg1, reg2 | ||
| 33 | /// 0b00 reg1, imm32 | ||
| 34 | /// 0b01 reg1, [reg2 + imm32] | ||
| 35 | /// 0b01 reg1, [ds:imm32] | ||
| 36 | /// 0b10 [reg1 + imm32], reg2 | ||
| 37 | /// Notes: | ||
| 38 | /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. | ||
| 39 | /// * When two imm32 values are required, Data field `payload` points at `ImmPair`. | ||
| 40 | adc, | ||
| 41 | |||
| 42 | /// ops flags: form: | ||
| 43 | /// 0b00 byte ptr [reg1 + imm32], imm8 | ||
| 44 | /// 0b01 word ptr [reg1 + imm32], imm16 | ||
| 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 | ||
| 46 | /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) | ||
| 47 | /// Notes: | ||
| 48 | /// * Uses `ImmPair` as payload | ||
| 49 | adc_mem_imm, | ||
| 50 | |||
| 51 | /// form: reg1, [reg2 + scale*index + imm32] | ||
| 52 | /// ops flags scale | ||
| 53 | /// 0b00 1 | ||
| 54 | /// 0b01 2 | ||
| 55 | /// 0b10 4 | ||
| 56 | /// 0b11 8 | ||
| 57 | /// Notes: | ||
| 58 | /// * Uses `IndexRegisterDisp` as payload | ||
| 59 | adc_scale_src, | ||
| 60 | |||
| 61 | /// form: [reg1 + scale*index + imm32], reg2 | ||
| 62 | /// ops flags scale | ||
| 63 | /// 0b00 1 | ||
| 64 | /// 0b01 2 | ||
| 65 | /// 0b10 4 | ||
| 66 | /// 0b11 8 | ||
| 67 | /// Notes: | ||
| 68 | /// * Uses `IndexRegisterDisp` payload. | ||
| 69 | adc_scale_dst, | ||
| 70 | |||
| 71 | /// form: [reg1 + scale*rax + imm32], imm32 | ||
| 72 | /// ops flags scale | ||
| 73 | /// 0b00 1 | ||
| 74 | /// 0b01 2 | ||
| 75 | /// 0b10 4 | ||
| 76 | /// 0b11 8 | ||
| 77 | /// Notes: | ||
| 78 | /// * Uses `IndexRegisterDispImm` payload. | ||
| 79 | adc_scale_imm, | ||
| 80 | |||
| 81 | /// ops flags: form: | ||
| 82 | /// 0b00 byte ptr [reg1 + index + imm32], imm8 | ||
| 83 | /// 0b01 word ptr [reg1 + index + imm32], imm16 | ||
| 84 | /// 0b10 dword ptr [reg1 + index + imm32], imm32 | ||
| 85 | /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64) | ||
| 86 | /// Notes: | ||
| 87 | /// * Uses `IndexRegisterDispImm` payload. | ||
| 88 | adc_mem_index_imm, | ||
| 89 | |||
| 90 | // The following instructions all have the same encoding as `adc`. | ||
| 91 | 33 | ||
| 34 | pub const Tag = enum(u8) { | ||
| 35 | /// Add with carry | ||
| 36 | adc, | ||
| 37 | /// Add | ||
| 92 | add, | 38 | add, |
| 93 | add_mem_imm, | 39 | /// Logical and |
| 94 | add_scale_src, | ||
| 95 | add_scale_dst, | ||
| 96 | add_scale_imm, | ||
| 97 | add_mem_index_imm, | ||
| 98 | sub, | ||
| 99 | sub_mem_imm, | ||
| 100 | sub_scale_src, | ||
| 101 | sub_scale_dst, | ||
| 102 | sub_scale_imm, | ||
| 103 | sub_mem_index_imm, | ||
| 104 | xor, | ||
| 105 | xor_mem_imm, | ||
| 106 | xor_scale_src, | ||
| 107 | xor_scale_dst, | ||
| 108 | xor_scale_imm, | ||
| 109 | xor_mem_index_imm, | ||
| 110 | @"and", | 40 | @"and", |
| 111 | and_mem_imm, | 41 | /// Call |
| 112 | and_scale_src, | 42 | call, |
| 113 | and_scale_dst, | 43 | /// Convert byte to word |
| 114 | and_scale_imm, | 44 | cbw, |
| 115 | and_mem_index_imm, | 45 | /// Convert word to doubleword |
| 116 | @"or", | 46 | cwde, |
| 117 | or_mem_imm, | 47 | /// Convert doubleword to quadword |
| 118 | or_scale_src, | 48 | cdqe, |
| 119 | or_scale_dst, | 49 | /// Convert word to doubleword |
| 120 | or_scale_imm, | 50 | cwd, |
| 121 | or_mem_index_imm, | 51 | /// Convert doubleword to quadword |
| 122 | rol, | 52 | cdq, |
| 123 | rol_mem_imm, | 53 | /// Convert doubleword to quadword |
| 124 | rol_scale_src, | 54 | cqo, |
| 125 | rol_scale_dst, | 55 | /// Logical compare |
| 126 | rol_scale_imm, | ||
| 127 | rol_mem_index_imm, | ||
| 128 | ror, | ||
| 129 | ror_mem_imm, | ||
| 130 | ror_scale_src, | ||
| 131 | ror_scale_dst, | ||
| 132 | ror_scale_imm, | ||
| 133 | ror_mem_index_imm, | ||
| 134 | rcl, | ||
| 135 | rcl_mem_imm, | ||
| 136 | rcl_scale_src, | ||
| 137 | rcl_scale_dst, | ||
| 138 | rcl_scale_imm, | ||
| 139 | rcl_mem_index_imm, | ||
| 140 | rcr, | ||
| 141 | rcr_mem_imm, | ||
| 142 | rcr_scale_src, | ||
| 143 | rcr_scale_dst, | ||
| 144 | rcr_scale_imm, | ||
| 145 | rcr_mem_index_imm, | ||
| 146 | sbb, | ||
| 147 | sbb_mem_imm, | ||
| 148 | sbb_scale_src, | ||
| 149 | sbb_scale_dst, | ||
| 150 | sbb_scale_imm, | ||
| 151 | sbb_mem_index_imm, | ||
| 152 | cmp, | 56 | cmp, |
| 153 | cmp_mem_imm, | 57 | /// Unsigned division |
| 154 | cmp_scale_src, | ||
| 155 | cmp_scale_dst, | ||
| 156 | cmp_scale_imm, | ||
| 157 | cmp_mem_index_imm, | ||
| 158 | mov, | ||
| 159 | mov_mem_imm, | ||
| 160 | mov_scale_src, | ||
| 161 | mov_scale_dst, | ||
| 162 | mov_scale_imm, | ||
| 163 | mov_mem_index_imm, | ||
| 164 | |||
| 165 | /// ops flags: form: | ||
| 166 | /// 0b00 reg1, reg2, | ||
| 167 | /// 0b01 reg1, byte ptr [reg2 + imm32] | ||
| 168 | /// 0b10 reg1, word ptr [reg2 + imm32] | ||
| 169 | /// 0b11 reg1, dword ptr [reg2 + imm32] | ||
| 170 | mov_sign_extend, | ||
| 171 | |||
| 172 | /// ops flags: form: | ||
| 173 | /// 0b00 reg1, reg2 | ||
| 174 | /// 0b01 reg1, byte ptr [reg2 + imm32] | ||
| 175 | /// 0b10 reg1, word ptr [reg2 + imm32] | ||
| 176 | mov_zero_extend, | ||
| 177 | |||
| 178 | /// ops flags: form: | ||
| 179 | /// 0b00 reg1, [reg2 + imm32] | ||
| 180 | /// 0b00 reg1, [ds:imm32] | ||
| 181 | /// 0b01 reg1, [rip + imm32] | ||
| 182 | /// 0b10 reg1, [reg2 + index + imm32] | ||
| 183 | /// Notes: | ||
| 184 | /// * 0b10 uses `IndexRegisterDisp` payload | ||
| 185 | lea, | ||
| 186 | |||
| 187 | /// ops flags: form: | ||
| 188 | /// 0b00 reg1, [rip + reloc] // via GOT PIC | ||
| 189 | /// 0b01 reg1, [rip + reloc] // direct load PIC | ||
| 190 | /// 0b10 reg1, [rip + reloc] // via imports table PIC | ||
| 191 | /// Notes: | ||
| 192 | /// * `Data` contains `relocation` | ||
| 193 | lea_pic, | ||
| 194 | |||
| 195 | /// ops flags: form: | ||
| 196 | /// 0b00 reg1, 1 | ||
| 197 | /// 0b01 reg1, .cl | ||
| 198 | /// 0b10 reg1, imm8 | ||
| 199 | /// Notes: | ||
| 200 | /// * If flags == 0b10, uses `imm`. | ||
| 201 | shl, | ||
| 202 | shl_mem_imm, | ||
| 203 | shl_scale_src, | ||
| 204 | shl_scale_dst, | ||
| 205 | shl_scale_imm, | ||
| 206 | shl_mem_index_imm, | ||
| 207 | sal, | ||
| 208 | sal_mem_imm, | ||
| 209 | sal_scale_src, | ||
| 210 | sal_scale_dst, | ||
| 211 | sal_scale_imm, | ||
| 212 | sal_mem_index_imm, | ||
| 213 | shr, | ||
| 214 | shr_mem_imm, | ||
| 215 | shr_scale_src, | ||
| 216 | shr_scale_dst, | ||
| 217 | shr_scale_imm, | ||
| 218 | shr_mem_index_imm, | ||
| 219 | sar, | ||
| 220 | sar_mem_imm, | ||
| 221 | sar_scale_src, | ||
| 222 | sar_scale_dst, | ||
| 223 | sar_scale_imm, | ||
| 224 | sar_mem_index_imm, | ||
| 225 | |||
| 226 | /// ops flags: form: | ||
| 227 | /// 0b00 reg1 | ||
| 228 | /// 0b00 byte ptr [reg2 + imm32] | ||
| 229 | /// 0b01 word ptr [reg2 + imm32] | ||
| 230 | /// 0b10 dword ptr [reg2 + imm32] | ||
| 231 | /// 0b11 qword ptr [reg2 + imm32] | ||
| 232 | imul, | ||
| 233 | idiv, | ||
| 234 | mul, | ||
| 235 | div, | 58 | div, |
| 236 | 59 | /// Store integer with truncation | |
| 237 | /// ops flags: form: | ||
| 238 | /// 0b00 AX <- AL | ||
| 239 | /// 0b01 DX:AX <- AX | ||
| 240 | /// 0b10 EDX:EAX <- EAX | ||
| 241 | /// 0b11 RDX:RAX <- RAX | ||
| 242 | cwd, | ||
| 243 | |||
| 244 | /// ops flags: form: | ||
| 245 | /// 0b00 reg1, reg2 | ||
| 246 | /// 0b01 reg1, [reg2 + imm32] | ||
| 247 | /// 0b01 reg1, [imm32] if reg2 is none | ||
| 248 | /// 0b10 reg1, reg2, imm32 | ||
| 249 | /// 0b11 reg1, [reg2 + imm32], imm32 | ||
| 250 | imul_complex, | ||
| 251 | |||
| 252 | /// ops flags: form: | ||
| 253 | /// 0b00 reg1, imm64 | ||
| 254 | /// 0b01 rax, moffs64 | ||
| 255 | /// Notes: | ||
| 256 | /// * If reg1 is 64-bit, the immediate is 64-bit and stored | ||
| 257 | /// within extra data `Imm64`. | ||
| 258 | /// * For 0b01, reg1 (or reg2) need to be | ||
| 259 | /// a version of rax. If reg1 == .none, then reg2 == .rax, | ||
| 260 | /// or vice versa. | ||
| 261 | movabs, | ||
| 262 | |||
| 263 | /// ops flags: form: | ||
| 264 | /// 0b00 word ptr [reg1 + imm32] | ||
| 265 | /// 0b01 dword ptr [reg1 + imm32] | ||
| 266 | /// 0b10 qword ptr [reg1 + imm32] | ||
| 267 | /// Notes: | ||
| 268 | /// * source is always ST(0) | ||
| 269 | /// * only supports memory operands as destination | ||
| 270 | fisttp, | 60 | fisttp, |
| 271 | 61 | /// Load floating-point value | |
| 272 | /// ops flags: form: | ||
| 273 | /// 0b01 dword ptr [reg1 + imm32] | ||
| 274 | /// 0b10 qword ptr [reg1 + imm32] | ||
| 275 | fld, | 62 | fld, |
| 276 | 63 | /// Signed division | |
| 277 | /// ops flags: form: | 64 | idiv, |
| 278 | /// 0b00 inst | 65 | /// Signed multiplication |
| 279 | /// 0b01 reg1 | 66 | imul, |
| 280 | /// 0b01 [imm32] if reg1 is none | 67 | /// |
| 281 | /// 0b10 [reg1 + imm32] | 68 | int3, |
| 69 | /// Jump | ||
| 282 | jmp, | 70 | jmp, |
| 283 | call, | 71 | /// Load effective address |
| 284 | 72 | lea, | |
| 285 | /// ops flags: | 73 | /// Move |
| 286 | /// unused | 74 | mov, |
| 287 | /// Notes: | 75 | /// Move with sign extension |
| 288 | /// * uses `inst_cc` in Data. | 76 | movsx, |
| 289 | cond_jmp, | 77 | /// Move with zero extension |
| 290 | 78 | movzx, | |
| 291 | /// ops flags: | 79 | /// Multiply |
| 292 | /// 0b00 reg1 | 80 | mul, |
| 293 | /// Notes: | 81 | /// No-op |
| 294 | /// * uses condition code (CC) stored as part of data | 82 | nop, |
| 295 | cond_set_byte, | 83 | /// Logical or |
| 296 | 84 | @"or", | |
| 297 | /// ops flags: | 85 | /// Pop |
| 298 | /// 0b00 reg1, reg2, | ||
| 299 | /// 0b01 reg1, word ptr [reg2 + imm] | ||
| 300 | /// 0b10 reg1, dword ptr [reg2 + imm] | ||
| 301 | /// 0b11 reg1, qword ptr [reg2 + imm] | ||
| 302 | /// Notes: | ||
| 303 | /// * uses condition code (CC) stored as part of data | ||
| 304 | cond_mov, | ||
| 305 | |||
| 306 | /// ops flags: form: | ||
| 307 | /// 0b00 reg1 | ||
| 308 | /// 0b01 [reg1 + imm32] | ||
| 309 | /// 0b10 imm32 | ||
| 310 | /// Notes: | ||
| 311 | /// * If 0b10 is specified and the tag is push, pushes immediate onto the stack | ||
| 312 | /// using the mnemonic PUSH imm32. | ||
| 313 | push, | ||
| 314 | pop, | 86 | pop, |
| 315 | 87 | /// Push | |
| 316 | /// ops flags: form: | 88 | push, |
| 317 | /// 0b00 retf imm16 | 89 | /// Return |
| 318 | /// 0b01 retf | ||
| 319 | /// 0b10 retn imm16 | ||
| 320 | /// 0b11 retn | ||
| 321 | ret, | 90 | ret, |
| 322 | 91 | /// Arithmetic shift left | |
| 323 | /// Fast system call | 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 | ||
| 324 | syscall, | 104 | syscall, |
| 325 | 105 | /// Test condition | |
| 326 | /// ops flags: form: | ||
| 327 | /// 0b00 reg1, imm32 if reg2 == .none | ||
| 328 | /// 0b00 reg1, reg2 | ||
| 329 | /// TODO handle more cases | ||
| 330 | @"test", | 106 | @"test", |
| 107 | /// Undefined instruction | ||
| 108 | ud2, | ||
| 109 | /// Logical exclusive-or | ||
| 110 | xor, | ||
| 331 | 111 | ||
| 332 | /// Breakpoint form: | 112 | /// Add single precision floating point |
| 333 | /// 0b00 int3 | 113 | addss, |
| 334 | interrupt, | 114 | /// Compare scalar single-precision floating-point values |
| 335 | 115 | cmpss, | |
| 336 | /// Nop | 116 | /// Move scalar single-precision floating-point value |
| 337 | nop, | 117 | movss, |
| 338 | 118 | /// Unordered compare scalar single-precision floating-point values | |
| 339 | /// SSE instructions | 119 | ucomiss, |
| 340 | /// ops flags: form: | 120 | /// Add double precision floating point |
| 341 | /// 0b00 reg1, qword ptr [reg2 + imm32] | 121 | addsd, |
| 342 | /// 0b01 qword ptr [reg1 + imm32], reg2 | 122 | /// Compare scalar double-precision floating-point values |
| 343 | /// 0b10 reg1, reg2 | 123 | cmpsd, |
| 344 | mov_f64_sse, | 124 | /// Move scalar double-precision floating-point value |
| 345 | mov_f32_sse, | 125 | movsd, |
| 346 | 126 | /// Unordered compare scalar double-precision floating-point values | |
| 347 | /// ops flags: form: | 127 | ucomisd, |
| 348 | /// 0b00 reg1, reg2 | 128 | |
| 349 | add_f64_sse, | 129 | /// Conditional move |
| 350 | add_f32_sse, | 130 | cmovcc, |
| 351 | 131 | /// Conditional jump | |
| 352 | /// ops flags: form: | 132 | jcc, |
| 353 | /// 0b00 reg1, reg2 | 133 | /// Set byte on condition |
| 354 | cmp_f64_sse, | 134 | setcc, |
| 355 | cmp_f32_sse, | 135 | |
| 356 | 136 | /// Mov absolute to/from memory wrt segment register to/from rax | |
| 357 | /// AVX instructions | 137 | mov_moffs, |
| 358 | /// ops flags: form: | 138 | |
| 359 | /// 0b00 reg1, qword ptr [reg2 + imm32] | 139 | /// Jump with relocation to another local MIR instruction |
| 360 | /// 0b01 qword ptr [reg1 + imm32], reg2 | 140 | /// Uses `inst` payload. |
| 361 | /// 0b10 reg1, reg1, reg2 | 141 | jmp_reloc, |
| 362 | mov_f64_avx, | 142 | |
| 363 | mov_f32_avx, | 143 | /// Call to an extern symbol via linker relocation. |
| 364 | 144 | /// Uses `relocation` payload. | |
| 365 | /// ops flags: form: | ||
| 366 | /// 0b00 reg1, reg1, reg2 | ||
| 367 | add_f64_avx, | ||
| 368 | add_f32_avx, | ||
| 369 | |||
| 370 | /// ops flags: form: | ||
| 371 | /// 0b00 reg1, reg1, reg2 | ||
| 372 | cmp_f64_avx, | ||
| 373 | cmp_f32_avx, | ||
| 374 | |||
| 375 | /// Pseudo-instructions | ||
| 376 | /// call extern function | ||
| 377 | /// Notes: | ||
| 378 | /// * target of the call is stored as `relocation` in `Data` union. | ||
| 379 | call_extern, | 145 | call_extern, |
| 380 | 146 | ||
| 381 | /// end of prologue | 147 | /// Load effective address of a symbol not yet allocated in VM. |
| 382 | dbg_prologue_end, | 148 | lea_linker, |
| 383 | 149 | ||
| 384 | /// start of epilogue | 150 | /// End of prologue |
| 151 | dbg_prologue_end, | ||
| 152 | /// Start of epilogue | ||
| 385 | dbg_epilogue_begin, | 153 | dbg_epilogue_begin, |
| 386 | 154 | /// Update debug line | |
| 387 | /// update debug line | 155 | /// Uses `payload` payload with data of type `DbgLineColumn`. |
| 388 | dbg_line, | 156 | dbg_line, |
| 389 | 157 | /// Push registers | |
| 390 | /// push registers | 158 | /// Uses `payload` payload with data of type `SaveRegisterList`. |
| 391 | /// Uses `payload` field with `SaveRegisterList` as payload. | ||
| 392 | push_regs, | 159 | push_regs, |
| 393 | 160 | /// Pop registers | |
| 394 | /// pop registers | 161 | /// Uses `payload` payload with data of type `SaveRegisterList`. |
| 395 | /// Uses `payload` field with `SaveRegisterList` as payload. | ||
| 396 | pop_regs, | 162 | pop_regs, |
| 397 | }; | ||
| 398 | /// The position of an MIR instruction within the `Mir` instructions array. | ||
| 399 | pub const Index = u32; | ||
| 400 | 163 | ||
| 401 | pub const Ops = packed struct { | 164 | /// Tombstone |
| 402 | reg1: u7, | 165 | /// Emitter should skip this instruction. |
| 403 | reg2: u7, | 166 | dead, |
| 404 | flags: u2, | 167 | }; |
| 405 | |||
| 406 | pub fn encode(vals: struct { | ||
| 407 | reg1: Register = .none, | ||
| 408 | reg2: Register = .none, | ||
| 409 | flags: u2 = 0b00, | ||
| 410 | }) Ops { | ||
| 411 | return .{ | ||
| 412 | .reg1 = @enumToInt(vals.reg1), | ||
| 413 | .reg2 = @enumToInt(vals.reg2), | ||
| 414 | .flags = vals.flags, | ||
| 415 | }; | ||
| 416 | } | ||
| 417 | 168 | ||
| 418 | pub fn decode(ops: Ops) struct { | 169 | pub const Ops = enum(u8) { |
| 419 | reg1: Register, | 170 | /// No data associated with this instruction (only mnemonic is used). |
| 420 | reg2: Register, | 171 | none, |
| 421 | flags: u2, | 172 | /// Single register operand. |
| 422 | } { | 173 | /// Uses `r` payload. |
| 423 | return .{ | 174 | r, |
| 424 | .reg1 = @intToEnum(Register, ops.reg1), | 175 | /// Register, register operands. |
| 425 | .reg2 = @intToEnum(Register, ops.reg2), | 176 | /// Uses `rr` payload. |
| 426 | .flags = ops.flags, | 177 | rr, |
| 427 | }; | 178 | /// Register, register, register operands. |
| 428 | } | 179 | /// Uses `rrr` payload. |
| 180 | rrr, | ||
| 181 | /// Register, register, immediate (sign-extended) operands. | ||
| 182 | /// Uses `rri` payload. | ||
| 183 | rri_s, | ||
| 184 | /// Register, register, immediate (unsigned) operands. | ||
| 185 | /// Uses `rri` payload. | ||
| 186 | rri_u, | ||
| 187 | /// Register with condition code (CC). | ||
| 188 | /// Uses `r_c` payload. | ||
| 189 | r_c, | ||
| 190 | /// Register, register with condition code (CC). | ||
| 191 | /// Uses `rr_c` payload. | ||
| 192 | rr_c, | ||
| 193 | /// Register, immediate (sign-extended) operands. | ||
| 194 | /// Uses `ri` payload. | ||
| 195 | ri_s, | ||
| 196 | /// Register, immediate (unsigned) operands. | ||
| 197 | /// Uses `ri` payload. | ||
| 198 | ri_u, | ||
| 199 | /// Register, 64-bit unsigned immediate operands. | ||
| 200 | /// Uses `rx` payload with payload type `Imm64`. | ||
| 201 | ri64, | ||
| 202 | /// Immediate (sign-extended) operand. | ||
| 203 | /// Uses `imm` payload. | ||
| 204 | imm_s, | ||
| 205 | /// Immediate (unsigned) operand. | ||
| 206 | /// Uses `imm` payload. | ||
| 207 | imm_u, | ||
| 208 | /// Relative displacement operand. | ||
| 209 | /// Uses `imm` payload. | ||
| 210 | rel, | ||
| 211 | /// Register, memory (SIB) operands. | ||
| 212 | /// Uses `rx` payload. | ||
| 213 | rm_sib, | ||
| 214 | /// Register, memory (RIP) operands. | ||
| 215 | /// Uses `rx` payload. | ||
| 216 | rm_rip, | ||
| 217 | /// Single memory (SIB) operand. | ||
| 218 | /// Uses `payload` with extra data of type `MemorySib`. | ||
| 219 | m_sib, | ||
| 220 | /// Single memory (RIP) operand. | ||
| 221 | /// Uses `payload` with extra data of type `MemoryRip`. | ||
| 222 | m_rip, | ||
| 223 | /// Memory (SIB), immediate (unsigned) operands. | ||
| 224 | /// Uses `xi` payload with extra data of type `MemorySib`. | ||
| 225 | mi_u_sib, | ||
| 226 | /// Memory (RIP), immediate (unsigned) operands. | ||
| 227 | /// Uses `xi` payload with extra data of type `MemoryRip`. | ||
| 228 | mi_u_rip, | ||
| 229 | /// Memory (SIB), immediate (sign-extend) operands. | ||
| 230 | /// Uses `xi` payload with extra data of type `MemorySib`. | ||
| 231 | mi_s_sib, | ||
| 232 | /// Memory (RIP), immediate (sign-extend) operands. | ||
| 233 | /// Uses `xi` payload with extra data of type `MemoryRip`. | ||
| 234 | mi_s_rip, | ||
| 235 | /// Memory (SIB), register operands. | ||
| 236 | /// Uses `rx` payload with extra data of type `MemorySib`. | ||
| 237 | mr_sib, | ||
| 238 | /// Memory (RIP), register operands. | ||
| 239 | /// Uses `rx` payload with extra data of type `MemoryRip`. | ||
| 240 | mr_rip, | ||
| 241 | /// Rax, Memory moffs. | ||
| 242 | /// Uses `payload` with extra data of type `MemoryMoffs`. | ||
| 243 | rax_moffs, | ||
| 244 | /// Memory moffs, rax. | ||
| 245 | /// Uses `payload` with extra data of type `MemoryMoffs`. | ||
| 246 | moffs_rax, | ||
| 247 | /// References another Mir instruction directly. | ||
| 248 | /// Uses `inst` payload. | ||
| 249 | inst, | ||
| 250 | /// References another Mir instruction directly with condition code (CC). | ||
| 251 | /// Uses `inst_cc` payload. | ||
| 252 | inst_cc, | ||
| 253 | /// Uses `payload` payload with data of type `MemoryConditionCode`. | ||
| 254 | m_cc, | ||
| 255 | /// Uses `rx` payload with extra data of type `MemoryConditionCode`. | ||
| 256 | rm_cc, | ||
| 257 | /// Uses `reloc` payload. | ||
| 258 | reloc, | ||
| 259 | /// Linker relocation - GOT indirection. | ||
| 260 | /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. | ||
| 261 | got_reloc, | ||
| 262 | /// Linker relocation - direct reference. | ||
| 263 | /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. | ||
| 264 | direct_reloc, | ||
| 265 | /// Linker relocation - imports table indirection (binding). | ||
| 266 | /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. | ||
| 267 | import_reloc, | ||
| 429 | }; | 268 | }; |
| 430 | 269 | ||
| 431 | /// All instructions have a 4-byte payload, which is contained within | ||
| 432 | /// this union. `Tag` determines which union field is active, as well as | ||
| 433 | /// how to interpret the data within. | ||
| 434 | pub const Data = union { | 270 | pub const Data = union { |
| 435 | /// Another instruction. | 271 | /// References another Mir instruction. |
| 436 | inst: Index, | 272 | inst: Index, |
| 437 | /// A 32-bit immediate value. | 273 | /// Another instruction with condition code (CC). |
| 438 | imm: u32, | 274 | /// Used by `jcc`. |
| 439 | /// A condition code for use with EFLAGS register. | ||
| 440 | cc: bits.Condition, | ||
| 441 | /// Another instruction with condition code. | ||
| 442 | /// Used by `cond_jmp`. | ||
| 443 | inst_cc: struct { | 275 | inst_cc: struct { |
| 444 | /// Another instruction. | 276 | /// Another instruction. |
| 445 | inst: Index, | 277 | inst: Index, |
| 446 | /// A condition code for use with EFLAGS register. | 278 | /// A condition code for use with EFLAGS register. |
| 447 | cc: bits.Condition, | 279 | cc: bits.Condition, |
| 448 | }, | 280 | }, |
| 281 | /// A 32-bit immediate value. | ||
| 282 | imm: u32, | ||
| 283 | r: Register, | ||
| 284 | rr: struct { | ||
| 285 | r1: Register, | ||
| 286 | r2: Register, | ||
| 287 | }, | ||
| 288 | rrr: struct { | ||
| 289 | r1: Register, | ||
| 290 | r2: Register, | ||
| 291 | r3: Register, | ||
| 292 | }, | ||
| 293 | rri: struct { | ||
| 294 | r1: Register, | ||
| 295 | r2: Register, | ||
| 296 | imm: u32, | ||
| 297 | }, | ||
| 298 | /// Register with condition code (CC). | ||
| 299 | r_c: struct { | ||
| 300 | r1: Register, | ||
| 301 | cc: bits.Condition, | ||
| 302 | }, | ||
| 303 | /// Register, register with condition code (CC). | ||
| 304 | rr_c: struct { | ||
| 305 | r1: Register, | ||
| 306 | r2: Register, | ||
| 307 | cc: bits.Condition, | ||
| 308 | }, | ||
| 309 | /// Register, immediate. | ||
| 310 | ri: struct { | ||
| 311 | r1: Register, | ||
| 312 | imm: u32, | ||
| 313 | }, | ||
| 314 | /// Register, followed by custom payload found in extra. | ||
| 315 | rx: struct { | ||
| 316 | r1: Register, | ||
| 317 | payload: u32, | ||
| 318 | }, | ||
| 319 | /// Custom payload followed by an immediate. | ||
| 320 | xi: struct { | ||
| 321 | payload: u32, | ||
| 322 | imm: u32, | ||
| 323 | }, | ||
| 449 | /// Relocation for the linker where: | 324 | /// Relocation for the linker where: |
| 450 | /// * `atom_index` is the index of the source | 325 | /// * `atom_index` is the index of the source |
| 451 | /// * `sym_index` is the index of the target | 326 | /// * `sym_index` is the index of the target |
| ... | @@ -468,62 +343,13 @@ pub const Inst = struct { | ... | @@ -468,62 +343,13 @@ pub const Inst = struct { |
| 468 | } | 343 | } |
| 469 | }; | 344 | }; |
| 470 | 345 | ||
| 471 | pub const IndexRegisterDisp = struct { | 346 | pub const LeaRegisterReloc = struct { |
| 472 | /// Index register to use with SIB-based encoding | 347 | /// Destination register. |
| 473 | index: u32, | 348 | reg: u32, |
| 474 | 349 | /// Index of the containing atom. | |
| 475 | /// Displacement value | 350 | atom_index: u32, |
| 476 | disp: u32, | 351 | /// Index into the linker's symbol table. |
| 477 | 352 | sym_index: u32, | |
| 478 | pub fn encode(index: Register, disp: u32) IndexRegisterDisp { | ||
| 479 | return .{ | ||
| 480 | .index = @enumToInt(index), | ||
| 481 | .disp = disp, | ||
| 482 | }; | ||
| 483 | } | ||
| 484 | |||
| 485 | pub fn decode(this: IndexRegisterDisp) struct { | ||
| 486 | index: Register, | ||
| 487 | disp: u32, | ||
| 488 | } { | ||
| 489 | return .{ | ||
| 490 | .index = @intToEnum(Register, this.index), | ||
| 491 | .disp = this.disp, | ||
| 492 | }; | ||
| 493 | } | ||
| 494 | }; | ||
| 495 | |||
| 496 | /// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list | ||
| 497 | /// instead of having two structs, one a superset of the other one? | ||
| 498 | pub const IndexRegisterDispImm = struct { | ||
| 499 | /// Index register to use with SIB-based encoding | ||
| 500 | index: u32, | ||
| 501 | |||
| 502 | /// Displacement value | ||
| 503 | disp: u32, | ||
| 504 | |||
| 505 | /// Immediate | ||
| 506 | imm: u32, | ||
| 507 | |||
| 508 | pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm { | ||
| 509 | return .{ | ||
| 510 | .index = @enumToInt(index), | ||
| 511 | .disp = disp, | ||
| 512 | .imm = imm, | ||
| 513 | }; | ||
| 514 | } | ||
| 515 | |||
| 516 | pub fn decode(this: IndexRegisterDispImm) struct { | ||
| 517 | index: Register, | ||
| 518 | disp: u32, | ||
| 519 | imm: u32, | ||
| 520 | } { | ||
| 521 | return .{ | ||
| 522 | .index = @intToEnum(Register, this.index), | ||
| 523 | .disp = this.disp, | ||
| 524 | .imm = this.imm, | ||
| 525 | }; | ||
| 526 | } | ||
| 527 | }; | 353 | }; |
| 528 | 354 | ||
| 529 | /// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers | 355 | /// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers |
| ... | @@ -567,16 +393,13 @@ pub const RegisterList = struct { | ... | @@ -567,16 +393,13 @@ pub const RegisterList = struct { |
| 567 | }; | 393 | }; |
| 568 | 394 | ||
| 569 | pub const SaveRegisterList = struct { | 395 | pub const SaveRegisterList = struct { |
| 396 | /// Base register | ||
| 397 | base_reg: u32, | ||
| 570 | /// Use `RegisterList` to populate. | 398 | /// Use `RegisterList` to populate. |
| 571 | register_list: u32, | 399 | register_list: u32, |
| 572 | stack_end: u32, | 400 | stack_end: u32, |
| 573 | }; | 401 | }; |
| 574 | 402 | ||
| 575 | pub const ImmPair = struct { | ||
| 576 | dest_off: u32, | ||
| 577 | operand: u32, | ||
| 578 | }; | ||
| 579 | |||
| 580 | pub const Imm64 = struct { | 403 | pub const Imm64 = struct { |
| 581 | msb: u32, | 404 | msb: u32, |
| 582 | lsb: u32, | 405 | lsb: u32, |
| ... | @@ -596,6 +419,90 @@ pub const Imm64 = struct { | ... | @@ -596,6 +419,90 @@ pub const Imm64 = struct { |
| 596 | } | 419 | } |
| 597 | }; | 420 | }; |
| 598 | 421 | ||
| 422 | // TODO this can be further compacted using packed struct | ||
| 423 | pub 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 | |||
| 464 | pub 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 | |||
| 485 | pub 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 | |||
| 599 | pub const DbgLineColumn = struct { | 506 | pub const DbgLineColumn = struct { |
| 600 | line: u32, | 507 | line: u32, |
| 601 | column: u32, | 508 | column: u32, |
| ... | @@ -607,9 +514,9 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { | ... | @@ -607,9 +514,9 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { |
| 607 | mir.* = undefined; | 514 | mir.* = undefined; |
| 608 | } | 515 | } |
| 609 | 516 | ||
| 610 | pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { | 517 | pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } { |
| 611 | const fields = std.meta.fields(T); | 518 | const fields = std.meta.fields(T); |
| 612 | var i: usize = index; | 519 | var i: u32 = index; |
| 613 | var result: T = undefined; | 520 | var result: T = undefined; |
| 614 | inline for (fields) |field| { | 521 | inline for (fields) |field| { |
| 615 | @field(result, field.name) = switch (field.type) { | 522 | @field(result, field.name) = switch (field.type) { |
src/arch/x86_64/bits.zig+301-845| ... | @@ -1,9 +1,9 @@ | ... | @@ -1,9 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const testing = std.testing; | ||
| 3 | const mem = std.mem; | ||
| 4 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 5 | const ArrayList = std.ArrayList; | 3 | const expect = std.testing.expect; |
| 4 | |||
| 6 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const ArrayList = std.ArrayList; | ||
| 7 | const DW = std.dwarf; | 7 | const DW = std.dwarf; |
| 8 | 8 | ||
| 9 | /// EFLAGS condition codes | 9 | /// EFLAGS condition codes |
| ... | @@ -135,131 +135,199 @@ pub const Condition = enum(u5) { | ... | @@ -135,131 +135,199 @@ pub const Condition = enum(u5) { |
| 135 | } | 135 | } |
| 136 | }; | 136 | }; |
| 137 | 137 | ||
| 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. | ||
| 152 | pub const Register = enum(u7) { | 138 | pub const Register = enum(u7) { |
| 153 | // zig fmt: off | 139 | // zig fmt: off |
| 154 | // 0 through 15, 64-bit registers. 8-15 are extended. | ||
| 155 | // id is just the int value. | ||
| 156 | rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, | 140 | rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, |
| 157 | r8, r9, r10, r11, r12, r13, r14, r15, | 141 | r8, r9, r10, r11, r12, r13, r14, r15, |
| 158 | 142 | ||
| 159 | // 16 through 31, 32-bit registers. 24-31 are extended. | ||
| 160 | // id is int value - 16. | ||
| 161 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | 143 | eax, ecx, edx, ebx, esp, ebp, esi, edi, |
| 162 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, | 144 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, |
| 163 | 145 | ||
| 164 | // 32-47, 16-bit registers. 40-47 are extended. | ||
| 165 | // id is int value - 32. | ||
| 166 | ax, cx, dx, bx, sp, bp, si, di, | 146 | ax, cx, dx, bx, sp, bp, si, di, |
| 167 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, | 147 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, |
| 168 | 148 | ||
| 169 | // 48-63, 8-bit registers. 56-63 are extended. | 149 | al, cl, dl, bl, spl, bpl, sil, dil, |
| 170 | // id is int value - 48. | ||
| 171 | al, cl, dl, bl, ah, ch, dh, bh, | ||
| 172 | r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, | 150 | r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, |
| 173 | 151 | ||
| 174 | // 64-79, 256-bit registers. | 152 | ah, ch, dh, bh, |
| 175 | // id is int value - 64. | 153 | |
| 176 | ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, | 154 | ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, |
| 177 | ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, | 155 | ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, |
| 178 | 156 | ||
| 179 | // 80-95, 128-bit registers. | ||
| 180 | // id is int value - 80. | ||
| 181 | xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, | 157 | xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, |
| 182 | xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, | 158 | xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, |
| 183 | 159 | ||
| 184 | // Pseudo-value for MIR instructions. | 160 | es, cs, ss, ds, fs, gs, |
| 161 | |||
| 185 | none, | 162 | none, |
| 186 | // zig fmt: on | 163 | // zig fmt: on |
| 187 | 164 | ||
| 188 | pub fn id(self: Register) u7 { | 165 | pub const Class = enum(u2) { |
| 189 | return switch (@enumToInt(self)) { | 166 | general_purpose, |
| 190 | 0...63 => @as(u7, @truncate(u4, @enumToInt(self))), | 167 | floating_point, |
| 191 | 64...79 => @enumToInt(self), | 168 | segment, |
| 169 | }; | ||
| 170 | |||
| 171 | pub fn class(reg: Register) Class { | ||
| 172 | return switch (@enumToInt(reg)) { | ||
| 173 | // zig fmt: off | ||
| 174 | @enumToInt(Register.rax) ... @enumToInt(Register.r15) => .general_purpose, | ||
| 175 | @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => .general_purpose, | ||
| 176 | @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => .general_purpose, | ||
| 177 | @enumToInt(Register.al) ... @enumToInt(Register.r15b) => .general_purpose, | ||
| 178 | @enumToInt(Register.ah) ... @enumToInt(Register.bh) => .general_purpose, | ||
| 179 | |||
| 180 | @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => .floating_point, | ||
| 181 | @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => .floating_point, | ||
| 182 | |||
| 183 | @enumToInt(Register.es) ... @enumToInt(Register.gs) => .segment, | ||
| 184 | |||
| 192 | else => unreachable, | 185 | else => unreachable, |
| 186 | // zig fmt: on | ||
| 193 | }; | 187 | }; |
| 194 | } | 188 | } |
| 195 | 189 | ||
| 196 | /// Returns the bit-width of the register. | 190 | pub fn id(reg: Register) u6 { |
| 197 | pub fn size(self: Register) u9 { | 191 | const base = switch (@enumToInt(reg)) { |
| 198 | return switch (@enumToInt(self)) { | 192 | // zig fmt: off |
| 199 | 0...15 => 64, | 193 | @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax), |
| 200 | 16...31 => 32, | 194 | @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax), |
| 201 | 32...47 => 16, | 195 | @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax), |
| 202 | 48...63 => 8, | 196 | @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al), |
| 203 | 64...79 => 256, | 197 | @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4, |
| 204 | 80...95 => 128, | 198 | |
| 199 | @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => @enumToInt(Register.ymm0) - 16, | ||
| 200 | @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => @enumToInt(Register.xmm0) - 16, | ||
| 201 | |||
| 202 | @enumToInt(Register.es) ... @enumToInt(Register.gs) => @enumToInt(Register.es) - 32, | ||
| 203 | |||
| 205 | else => unreachable, | 204 | else => unreachable, |
| 205 | // zig fmt: on | ||
| 206 | }; | 206 | }; |
| 207 | return @intCast(u6, @enumToInt(reg) - base); | ||
| 207 | } | 208 | } |
| 208 | 209 | ||
| 209 | /// Returns whether the register is *extended*. Extended registers are the | 210 | pub fn bitSize(reg: Register) u64 { |
| 210 | /// new registers added with amd64, r8 through r15. This also includes any | 211 | return switch (@enumToInt(reg)) { |
| 211 | /// other variant of access to those registers, such as r8b, r15d, and so | 212 | // zig fmt: off |
| 212 | /// on. This is needed because access to these registers requires special | 213 | @enumToInt(Register.rax) ... @enumToInt(Register.r15) => 64, |
| 213 | /// handling via the REX prefix, via the B or R bits, depending on context. | 214 | @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => 32, |
| 214 | pub fn isExtended(self: Register) bool { | 215 | @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => 16, |
| 215 | return @enumToInt(self) & 0x08 != 0; | 216 | @enumToInt(Register.al) ... @enumToInt(Register.r15b) => 8, |
| 217 | @enumToInt(Register.ah) ... @enumToInt(Register.bh) => 8, | ||
| 218 | |||
| 219 | @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => 256, | ||
| 220 | @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => 128, | ||
| 221 | |||
| 222 | @enumToInt(Register.es) ... @enumToInt(Register.gs) => 16, | ||
| 223 | |||
| 224 | else => unreachable, | ||
| 225 | // zig fmt: on | ||
| 226 | }; | ||
| 216 | } | 227 | } |
| 217 | 228 | ||
| 218 | /// This returns the 4-bit register ID, which is used in practically every | 229 | pub fn isExtended(reg: Register) bool { |
| 219 | /// opcode. Note that bit 3 (the highest bit) is *never* used directly in | 230 | return switch (@enumToInt(reg)) { |
| 220 | /// an instruction (@see isExtended), and requires special handling. The | 231 | // zig fmt: off |
| 221 | /// lower three bits are often embedded directly in instructions (such as | 232 | @enumToInt(Register.r8) ... @enumToInt(Register.r15) => true, |
| 222 | /// the B8 variant of moves), or used in R/M bytes. | 233 | @enumToInt(Register.r8d) ... @enumToInt(Register.r15d) => true, |
| 223 | pub fn enc(self: Register) u4 { | 234 | @enumToInt(Register.r8w) ... @enumToInt(Register.r15w) => true, |
| 224 | return @truncate(u4, @enumToInt(self)); | 235 | @enumToInt(Register.r8b) ... @enumToInt(Register.r15b) => true, |
| 236 | |||
| 237 | @enumToInt(Register.ymm8) ... @enumToInt(Register.ymm15) => true, | ||
| 238 | @enumToInt(Register.xmm8) ... @enumToInt(Register.xmm15) => true, | ||
| 239 | |||
| 240 | else => false, | ||
| 241 | // zig fmt: on | ||
| 242 | }; | ||
| 225 | } | 243 | } |
| 226 | 244 | ||
| 227 | /// Like enc, but only returns the lower 3 bits. | 245 | pub fn enc(reg: Register) u4 { |
| 228 | pub fn lowEnc(self: Register) u3 { | 246 | const base = switch (@enumToInt(reg)) { |
| 229 | return @truncate(u3, @enumToInt(self)); | 247 | // zig fmt: off |
| 248 | @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax), | ||
| 249 | @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax), | ||
| 250 | @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax), | ||
| 251 | @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al), | ||
| 252 | @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4, | ||
| 253 | |||
| 254 | @enumToInt(Register.ymm0) ... @enumToInt(Register.ymm15) => @enumToInt(Register.ymm0), | ||
| 255 | @enumToInt(Register.xmm0) ... @enumToInt(Register.xmm15) => @enumToInt(Register.xmm0), | ||
| 256 | |||
| 257 | @enumToInt(Register.es) ... @enumToInt(Register.gs) => @enumToInt(Register.es), | ||
| 258 | |||
| 259 | else => unreachable, | ||
| 260 | // zig fmt: on | ||
| 261 | }; | ||
| 262 | return @truncate(u4, @enumToInt(reg) - base); | ||
| 263 | } | ||
| 264 | |||
| 265 | pub fn lowEnc(reg: Register) u3 { | ||
| 266 | return @truncate(u3, reg.enc()); | ||
| 267 | } | ||
| 268 | |||
| 269 | pub fn toBitSize(reg: Register, bit_size: u64) Register { | ||
| 270 | return switch (bit_size) { | ||
| 271 | 8 => reg.to8(), | ||
| 272 | 16 => reg.to16(), | ||
| 273 | 32 => reg.to32(), | ||
| 274 | 64 => reg.to64(), | ||
| 275 | 128 => reg.to128(), | ||
| 276 | 256 => reg.to256(), | ||
| 277 | else => unreachable, | ||
| 278 | }; | ||
| 279 | } | ||
| 280 | |||
| 281 | fn gpBase(reg: Register) u7 { | ||
| 282 | assert(reg.class() == .general_purpose); | ||
| 283 | return switch (@enumToInt(reg)) { | ||
| 284 | // zig fmt: off | ||
| 285 | @enumToInt(Register.rax) ... @enumToInt(Register.r15) => @enumToInt(Register.rax), | ||
| 286 | @enumToInt(Register.eax) ... @enumToInt(Register.r15d) => @enumToInt(Register.eax), | ||
| 287 | @enumToInt(Register.ax) ... @enumToInt(Register.r15w) => @enumToInt(Register.ax), | ||
| 288 | @enumToInt(Register.al) ... @enumToInt(Register.r15b) => @enumToInt(Register.al), | ||
| 289 | @enumToInt(Register.ah) ... @enumToInt(Register.bh) => @enumToInt(Register.ah) - 4, | ||
| 290 | else => unreachable, | ||
| 291 | // zig fmt: on | ||
| 292 | }; | ||
| 230 | } | 293 | } |
| 231 | 294 | ||
| 232 | pub fn to256(self: Register) Register { | 295 | pub fn to64(reg: Register) Register { |
| 233 | return @intToEnum(Register, @as(u8, self.enc()) + 64); | 296 | return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.rax)); |
| 234 | } | 297 | } |
| 235 | 298 | ||
| 236 | pub fn to128(self: Register) Register { | 299 | pub fn to32(reg: Register) Register { |
| 237 | return @intToEnum(Register, @as(u8, self.enc()) + 80); | 300 | return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.eax)); |
| 238 | } | 301 | } |
| 239 | 302 | ||
| 240 | /// Convert from any register to its 64 bit alias. | 303 | pub fn to16(reg: Register) Register { |
| 241 | pub fn to64(self: Register) Register { | 304 | return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.ax)); |
| 242 | return @intToEnum(Register, self.enc()); | ||
| 243 | } | 305 | } |
| 244 | 306 | ||
| 245 | /// Convert from any register to its 32 bit alias. | 307 | pub fn to8(reg: Register) Register { |
| 246 | pub fn to32(self: Register) Register { | 308 | return @intToEnum(Register, @enumToInt(reg) - reg.gpBase() + @enumToInt(Register.al)); |
| 247 | return @intToEnum(Register, @as(u8, self.enc()) + 16); | ||
| 248 | } | 309 | } |
| 249 | 310 | ||
| 250 | /// Convert from any register to its 16 bit alias. | 311 | fn fpBase(reg: Register) u7 { |
| 251 | pub fn to16(self: Register) Register { | 312 | assert(reg.class() == .floating_point); |
| 252 | return @intToEnum(Register, @as(u8, self.enc()) + 32); | 313 | return switch (@enumToInt(reg)) { |
| 314 | @enumToInt(Register.ymm0)...@enumToInt(Register.ymm15) => @enumToInt(Register.ymm0), | ||
| 315 | @enumToInt(Register.xmm0)...@enumToInt(Register.xmm15) => @enumToInt(Register.xmm0), | ||
| 316 | else => unreachable, | ||
| 317 | }; | ||
| 253 | } | 318 | } |
| 254 | 319 | ||
| 255 | /// Convert from any register to its 8 bit alias. | 320 | pub fn to256(reg: Register) Register { |
| 256 | pub fn to8(self: Register) Register { | 321 | return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.ymm0)); |
| 257 | return @intToEnum(Register, @as(u8, self.enc()) + 48); | ||
| 258 | } | 322 | } |
| 259 | 323 | ||
| 260 | pub fn dwarfLocOp(self: Register) u8 { | 324 | pub fn to128(reg: Register) Register { |
| 261 | switch (@enumToInt(self)) { | 325 | return @intToEnum(Register, @enumToInt(reg) - reg.fpBase() + @enumToInt(Register.xmm0)); |
| 262 | 0...63 => return switch (self.to64()) { | 326 | } |
| 327 | |||
| 328 | pub fn dwarfLocOp(reg: Register) u8 { | ||
| 329 | return switch (reg.class()) { | ||
| 330 | .general_purpose => switch (reg.to64()) { | ||
| 263 | .rax => DW.OP.reg0, | 331 | .rax => DW.OP.reg0, |
| 264 | .rdx => DW.OP.reg1, | 332 | .rdx => DW.OP.reg1, |
| 265 | .rcx => DW.OP.reg2, | 333 | .rcx => DW.OP.reg2, |
| ... | @@ -268,31 +336,19 @@ pub const Register = enum(u7) { | ... | @@ -268,31 +336,19 @@ pub const Register = enum(u7) { |
| 268 | .rdi => DW.OP.reg5, | 336 | .rdi => DW.OP.reg5, |
| 269 | .rbp => DW.OP.reg6, | 337 | .rbp => DW.OP.reg6, |
| 270 | .rsp => DW.OP.reg7, | 338 | .rsp => DW.OP.reg7, |
| 271 | 339 | else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.reg0, | |
| 272 | .r8 => DW.OP.reg8, | ||
| 273 | .r9 => DW.OP.reg9, | ||
| 274 | .r10 => DW.OP.reg10, | ||
| 275 | .r11 => DW.OP.reg11, | ||
| 276 | .r12 => DW.OP.reg12, | ||
| 277 | .r13 => DW.OP.reg13, | ||
| 278 | .r14 => DW.OP.reg14, | ||
| 279 | .r15 => DW.OP.reg15, | ||
| 280 | |||
| 281 | else => unreachable, | ||
| 282 | }, | 340 | }, |
| 283 | 341 | .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.reg17, | |
| 284 | 64...79 => return @as(u8, self.enc()) + DW.OP.reg17, | ||
| 285 | |||
| 286 | else => unreachable, | 342 | else => unreachable, |
| 287 | } | 343 | }; |
| 288 | } | 344 | } |
| 289 | 345 | ||
| 290 | /// DWARF encodings that push a value onto the DWARF stack that is either | 346 | /// DWARF encodings that push a value onto the DWARF stack that is either |
| 291 | /// the contents of a register or the result of adding the contents a given | 347 | /// the contents of a register or the result of adding the contents a given |
| 292 | /// register to a given signed offset. | 348 | /// register to a given signed offset. |
| 293 | pub fn dwarfLocOpDeref(self: Register) u8 { | 349 | pub fn dwarfLocOpDeref(reg: Register) u8 { |
| 294 | switch (@enumToInt(self)) { | 350 | return switch (reg.class()) { |
| 295 | 0...63 => return switch (self.to64()) { | 351 | .general_purpose => switch (reg.to64()) { |
| 296 | .rax => DW.OP.breg0, | 352 | .rax => DW.OP.breg0, |
| 297 | .rdx => DW.OP.breg1, | 353 | .rdx => DW.OP.breg1, |
| 298 | .rcx => DW.OP.breg2, | 354 | .rcx => DW.OP.breg2, |
| ... | @@ -300,795 +356,195 @@ pub const Register = enum(u7) { | ... | @@ -300,795 +356,195 @@ pub const Register = enum(u7) { |
| 300 | .rsi => DW.OP.breg4, | 356 | .rsi => DW.OP.breg4, |
| 301 | .rdi => DW.OP.breg5, | 357 | .rdi => DW.OP.breg5, |
| 302 | .rbp => DW.OP.breg6, | 358 | .rbp => DW.OP.breg6, |
| 303 | .rsp => DW.OP.fbreg, | 359 | .rsp => DW.OP.breg7, |
| 304 | 360 | else => @intCast(u8, @enumToInt(reg) - reg.gpBase()) + DW.OP.breg0, | |
| 305 | .r8 => DW.OP.breg8, | ||
| 306 | .r9 => DW.OP.breg9, | ||
| 307 | .r10 => DW.OP.breg10, | ||
| 308 | .r11 => DW.OP.breg11, | ||
| 309 | .r12 => DW.OP.breg12, | ||
| 310 | .r13 => DW.OP.breg13, | ||
| 311 | .r14 => DW.OP.breg14, | ||
| 312 | .r15 => DW.OP.breg15, | ||
| 313 | |||
| 314 | else => unreachable, | ||
| 315 | }, | 361 | }, |
| 316 | 362 | .floating_point => @intCast(u8, @enumToInt(reg) - reg.fpBase()) + DW.OP.breg17, | |
| 317 | 64...79 => return @as(u8, self.enc()) + DW.OP.breg17, | ||
| 318 | |||
| 319 | else => unreachable, | 363 | else => unreachable, |
| 320 | } | 364 | }; |
| 321 | } | 365 | } |
| 322 | }; | 366 | }; |
| 323 | 367 | ||
| 324 | // zig fmt: on | 368 | test "Register id - different classes" { |
| 325 | 369 | try expect(Register.al.id() == Register.ax.id()); | |
| 326 | /// Encoding helper functions for x86_64 instructions | 370 | try expect(Register.ah.id() == Register.spl.id()); |
| 327 | /// | 371 | try expect(Register.ax.id() == Register.eax.id()); |
| 328 | /// Many of these helpers do very little, but they can help make things | 372 | try expect(Register.eax.id() == Register.rax.id()); |
| 329 | /// slightly more readable with more descriptive field names / function names. | ||
| 330 | /// | ||
| 331 | /// Some of them also have asserts to ensure that we aren't doing dumb things. | ||
| 332 | /// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal, | ||
| 333 | /// you need to encode it with an SIB byte. | ||
| 334 | /// | ||
| 335 | /// Note that ALL of these helper functions will assume capacity, | ||
| 336 | /// so ensure that the `code` has sufficient capacity before using them. | ||
| 337 | /// The `init` method is the recommended way to ensure capacity. | ||
| 338 | pub 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 | } | ||
| 372 | 373 | ||
| 373 | // -------- | 374 | try expect(Register.ymm0.id() == 0b10000); |
| 374 | // Prefixes | 375 | try expect(Register.ymm0.id() != Register.rax.id()); |
| 375 | // -------- | 376 | try expect(Register.xmm0.id() == Register.ymm0.id()); |
| 376 | |||
| 377 | pub const LegacyPrefixes = packed struct { | ||
| 378 | /// LOCK | ||
| 379 | prefix_f0: bool = false, | ||
| 380 | /// REPNZ, REPNE, REP, Scalar Double-precision | ||
| 381 | prefix_f2: bool = false, | ||
| 382 | /// REPZ, REPE, REP, Scalar Single-precision | ||
| 383 | prefix_f3: bool = false, | ||
| 384 | |||
| 385 | /// CS segment override or Branch not taken | ||
| 386 | prefix_2e: bool = false, | ||
| 387 | /// DS segment override | ||
| 388 | prefix_36: bool = false, | ||
| 389 | /// ES segment override | ||
| 390 | prefix_26: bool = false, | ||
| 391 | /// FS segment override | ||
| 392 | prefix_64: bool = false, | ||
| 393 | /// GS segment override | ||
| 394 | prefix_65: bool = false, | ||
| 395 | |||
| 396 | /// Branch taken | ||
| 397 | prefix_3e: bool = false, | ||
| 398 | |||
| 399 | /// Operand size override (enables 16 bit operation) | ||
| 400 | prefix_66: bool = false, | ||
| 401 | |||
| 402 | /// Address size override (enables 16 bit address size) | ||
| 403 | prefix_67: bool = false, | ||
| 404 | |||
| 405 | padding: u5 = 0, | ||
| 406 | }; | ||
| 407 | 377 | ||
| 408 | /// Encodes legacy prefixes | 378 | try expect(Register.es.id() == 0b100000); |
| 409 | pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void { | 379 | } |
| 410 | if (@bitCast(u16, prefixes) != 0) { | ||
| 411 | // Hopefully this path isn't taken very often, so we'll do it the slow way for now | ||
| 412 | |||
| 413 | // LOCK | ||
| 414 | if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0); | ||
| 415 | // REPNZ, REPNE, REP, Scalar Double-precision | ||
| 416 | if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2); | ||
| 417 | // REPZ, REPE, REP, Scalar Single-precision | ||
| 418 | if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3); | ||
| 419 | |||
| 420 | // CS segment override or Branch not taken | ||
| 421 | if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e); | ||
| 422 | // DS segment override | ||
| 423 | if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36); | ||
| 424 | // ES segment override | ||
| 425 | if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26); | ||
| 426 | // FS segment override | ||
| 427 | if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64); | ||
| 428 | // GS segment override | ||
| 429 | if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65); | ||
| 430 | |||
| 431 | // Branch taken | ||
| 432 | if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e); | ||
| 433 | |||
| 434 | // Operand size override | ||
| 435 | if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66); | ||
| 436 | |||
| 437 | // Address size override | ||
| 438 | if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | /// Use 16 bit operand size | ||
| 443 | /// | ||
| 444 | /// Note that this flag is overridden by REX.W, if both are present. | ||
| 445 | pub fn prefix16BitMode(self: Self) void { | ||
| 446 | self.code.appendAssumeCapacity(0x66); | ||
| 447 | } | ||
| 448 | |||
| 449 | pub const Vex = struct { | ||
| 450 | rex_prefix: Rex = .{}, | ||
| 451 | lead_opc: u5 = 0b0_0001, | ||
| 452 | register: u4 = 0b1111, | ||
| 453 | length: u1 = 0b0, | ||
| 454 | simd_prefix: u2 = 0b00, | ||
| 455 | wig_desc: bool = false, | ||
| 456 | lig_desc: bool = false, | ||
| 457 | lz_desc: bool = false, | ||
| 458 | |||
| 459 | pub fn rex(self: *Vex, r: Rex) void { | ||
| 460 | self.rex_prefix = r; | ||
| 461 | } | ||
| 462 | |||
| 463 | pub fn lead_opc_0f(self: *Vex) void { | ||
| 464 | self.lead_opc = 0b0_0001; | ||
| 465 | } | ||
| 466 | |||
| 467 | pub fn lead_opc_0f_38(self: *Vex) void { | ||
| 468 | self.lead_opc = 0b0_0010; | ||
| 469 | } | ||
| 470 | |||
| 471 | pub fn lead_opc_0f_3a(self: *Vex) void { | ||
| 472 | self.lead_opc = 0b0_0011; | ||
| 473 | } | ||
| 474 | |||
| 475 | pub fn reg(self: *Vex, register: u4) void { | ||
| 476 | self.register = ~register; | ||
| 477 | } | ||
| 478 | |||
| 479 | pub fn len_128(self: *Vex) void { | ||
| 480 | self.length = 0; | ||
| 481 | } | ||
| 482 | |||
| 483 | pub fn len_256(self: *Vex) void { | ||
| 484 | assert(!self.lz_desc); | ||
| 485 | self.length = 1; | ||
| 486 | } | ||
| 487 | 380 | ||
| 488 | pub fn simd_prefix_66(self: *Vex) void { | 381 | test "Register enc - different classes" { |
| 489 | self.simd_prefix = 0b01; | 382 | try expect(Register.al.enc() == Register.ax.enc()); |
| 490 | } | 383 | try expect(Register.ax.enc() == Register.eax.enc()); |
| 384 | try expect(Register.eax.enc() == Register.rax.enc()); | ||
| 385 | try expect(Register.ymm0.enc() == Register.rax.enc()); | ||
| 386 | try expect(Register.xmm0.enc() == Register.ymm0.enc()); | ||
| 387 | try expect(Register.es.enc() == Register.rax.enc()); | ||
| 388 | } | ||
| 491 | 389 | ||
| 492 | pub fn simd_prefix_f3(self: *Vex) void { | 390 | test "Register classes" { |
| 493 | self.simd_prefix = 0b10; | 391 | try expect(Register.r11.class() == .general_purpose); |
| 494 | } | 392 | try expect(Register.ymm11.class() == .floating_point); |
| 393 | try expect(Register.fs.class() == .segment); | ||
| 394 | } | ||
| 495 | 395 | ||
| 496 | pub fn simd_prefix_f2(self: *Vex) void { | 396 | pub const Memory = union(enum) { |
| 497 | self.simd_prefix = 0b11; | 397 | sib: Sib, |
| 498 | } | 398 | rip: Rip, |
| 399 | moffs: Moffs, | ||
| 499 | 400 | ||
| 500 | pub fn wig(self: *Vex) void { | 401 | pub const ScaleIndex = packed struct { |
| 501 | self.wig_desc = true; | 402 | scale: u4, |
| 502 | } | 403 | index: Register, |
| 404 | }; | ||
| 503 | 405 | ||
| 504 | pub fn lig(self: *Vex) void { | 406 | pub const PtrSize = enum { |
| 505 | self.lig_desc = true; | 407 | byte, |
| 408 | word, | ||
| 409 | dword, | ||
| 410 | qword, | ||
| 411 | tbyte, | ||
| 412 | |||
| 413 | pub fn fromSize(size: u32) PtrSize { | ||
| 414 | return if (size <= 1) | ||
| 415 | .byte | ||
| 416 | else if (size <= 2) | ||
| 417 | .word | ||
| 418 | else if (size <= 4) | ||
| 419 | .dword | ||
| 420 | else if (size <= 8) | ||
| 421 | .qword | ||
| 422 | else if (size == 10) | ||
| 423 | .tbyte | ||
| 424 | else | ||
| 425 | unreachable; | ||
| 506 | } | 426 | } |
| 507 | 427 | ||
| 508 | pub fn lz(self: *Vex) void { | 428 | pub fn fromBitSize(bit_size: u64) PtrSize { |
| 509 | self.lz_desc = true; | 429 | return switch (bit_size) { |
| 430 | 8 => .byte, | ||
| 431 | 16 => .word, | ||
| 432 | 32 => .dword, | ||
| 433 | 64 => .qword, | ||
| 434 | 80 => .tbyte, | ||
| 435 | else => unreachable, | ||
| 436 | }; | ||
| 510 | } | 437 | } |
| 511 | 438 | ||
| 512 | pub fn write(self: Vex, writer: anytype) usize { | 439 | pub fn bitSize(s: PtrSize) u64 { |
| 513 | var buf: [3]u8 = .{0} ** 3; | 440 | return switch (s) { |
| 514 | const form_3byte: bool = blk: { | 441 | .byte => 8, |
| 515 | if (self.rex_prefix.w and !self.wig_desc) break :blk true; | 442 | .word => 16, |
| 516 | if (self.rex_prefix.x or self.rex_prefix.b) break :blk true; | 443 | .dword => 32, |
| 517 | break :blk self.lead_opc != 0b0_0001; | 444 | .qword => 64, |
| 445 | .tbyte => 80, | ||
| 518 | }; | 446 | }; |
| 519 | |||
| 520 | if (self.lz_desc) { | ||
| 521 | assert(self.length == 0); | ||
| 522 | } | ||
| 523 | |||
| 524 | if (form_3byte) { | ||
| 525 | // First byte | ||
| 526 | buf[0] = 0xc4; | ||
| 527 | // Second byte | ||
| 528 | const rxb_mask: u3 = @intCast(u3, @boolToInt(!self.rex_prefix.r)) << 2 | | ||
| 529 | @intCast(u2, @boolToInt(!self.rex_prefix.x)) << 1 | | ||
| 530 | @boolToInt(!self.rex_prefix.b); | ||
| 531 | buf[1] |= @intCast(u8, rxb_mask) << 5; | ||
| 532 | buf[1] |= self.lead_opc; | ||
| 533 | // Third byte | ||
| 534 | buf[2] |= @intCast(u8, @boolToInt(!self.rex_prefix.w)) << 7; | ||
| 535 | buf[2] |= @intCast(u7, self.register) << 3; | ||
| 536 | buf[2] |= @intCast(u3, self.length) << 2; | ||
| 537 | buf[2] |= self.simd_prefix; | ||
| 538 | } else { | ||
| 539 | // First byte | ||
| 540 | buf[0] = 0xc5; | ||
| 541 | // Second byte | ||
| 542 | buf[1] |= @intCast(u8, @boolToInt(!self.rex_prefix.r)) << 7; | ||
| 543 | buf[1] |= @intCast(u7, self.register) << 3; | ||
| 544 | buf[1] |= @intCast(u3, self.length) << 2; | ||
| 545 | buf[1] |= self.simd_prefix; | ||
| 546 | } | ||
| 547 | |||
| 548 | const count: usize = if (form_3byte) 3 else 2; | ||
| 549 | _ = writer.writeAll(buf[0..count]) catch unreachable; | ||
| 550 | return count; | ||
| 551 | } | 447 | } |
| 552 | }; | 448 | }; |
| 553 | 449 | ||
| 554 | pub fn vex(self: Self, prefix: Vex) void { | 450 | pub const Sib = struct { |
| 555 | _ = prefix.write(self.code.writer()); | 451 | ptr_size: PtrSize, |
| 556 | } | 452 | base: ?Register, |
| 557 | 453 | scale_index: ?ScaleIndex, | |
| 558 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB | 454 | disp: i32, |
| 559 | pub const Rex = struct { | ||
| 560 | /// Wide, enables 64-bit operation | ||
| 561 | w: bool = false, | ||
| 562 | /// Extends the reg field in the ModR/M byte | ||
| 563 | r: bool = false, | ||
| 564 | /// Extends the index field in the SIB byte | ||
| 565 | x: bool = false, | ||
| 566 | /// Extends the r/m field in the ModR/M byte, | ||
| 567 | /// or the base field in the SIB byte, | ||
| 568 | /// or the reg field in the Opcode byte | ||
| 569 | b: bool = false, | ||
| 570 | }; | 455 | }; |
| 571 | 456 | ||
| 572 | /// Encodes a REX prefix byte given all the fields | 457 | pub const Rip = struct { |
| 573 | /// | 458 | ptr_size: PtrSize, |
| 574 | /// Use this byte whenever you need 64 bit operation, | 459 | disp: i32, |
| 575 | /// or one of reg, index, r/m, base, or opcode-reg might be extended. | 460 | }; |
| 576 | /// | ||
| 577 | /// See struct `Rex` for a description of each field. | ||
| 578 | /// | ||
| 579 | /// Does not add a prefix byte if none of the fields are set! | ||
| 580 | pub fn rex(self: Self, byte: Rex) void { | ||
| 581 | var value: u8 = 0b0100_0000; | ||
| 582 | |||
| 583 | if (byte.w) value |= 0b1000; | ||
| 584 | if (byte.r) value |= 0b0100; | ||
| 585 | if (byte.x) value |= 0b0010; | ||
| 586 | if (byte.b) value |= 0b0001; | ||
| 587 | |||
| 588 | if (value != 0b0100_0000) { | ||
| 589 | self.code.appendAssumeCapacity(value); | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | // ------ | ||
| 594 | // Opcode | ||
| 595 | // ------ | ||
| 596 | |||
| 597 | /// Encodes a 1 byte opcode | ||
| 598 | pub fn opcode_1byte(self: Self, opcode: u8) void { | ||
| 599 | self.code.appendAssumeCapacity(opcode); | ||
| 600 | } | ||
| 601 | |||
| 602 | /// Encodes a 2 byte opcode | ||
| 603 | /// | ||
| 604 | /// e.g. IMUL has the opcode 0x0f 0xaf, so you use | ||
| 605 | /// | ||
| 606 | /// encoder.opcode_2byte(0x0f, 0xaf); | ||
| 607 | pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void { | ||
| 608 | self.code.appendAssumeCapacity(prefix); | ||
| 609 | self.code.appendAssumeCapacity(opcode); | ||
| 610 | } | ||
| 611 | |||
| 612 | /// Encodes a 3 byte opcode | ||
| 613 | /// | ||
| 614 | /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10 | ||
| 615 | /// | ||
| 616 | /// encoder.opcode_3byte(0xf2, 0x0f, 0x10); | ||
| 617 | pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) void { | ||
| 618 | self.code.appendAssumeCapacity(prefix_1); | ||
| 619 | self.code.appendAssumeCapacity(prefix_2); | ||
| 620 | self.code.appendAssumeCapacity(opcode); | ||
| 621 | } | ||
| 622 | |||
| 623 | /// Encodes a 1 byte opcode with a reg field | ||
| 624 | /// | ||
| 625 | /// Remember to add a REX prefix byte if reg is extended! | ||
| 626 | pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void { | ||
| 627 | assert(opcode & 0b111 == 0); | ||
| 628 | self.code.appendAssumeCapacity(opcode | reg); | ||
| 629 | } | ||
| 630 | |||
| 631 | // ------ | ||
| 632 | // ModR/M | ||
| 633 | // ------ | ||
| 634 | |||
| 635 | /// Construct a ModR/M byte given all the fields | ||
| 636 | /// | ||
| 637 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 638 | pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void { | ||
| 639 | self.code.appendAssumeCapacity( | ||
| 640 | @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm, | ||
| 641 | ); | ||
| 642 | } | ||
| 643 | |||
| 644 | /// Construct a ModR/M byte using direct r/m addressing | ||
| 645 | /// r/m effective address: r/m | ||
| 646 | /// | ||
| 647 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 648 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 649 | pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 650 | self.modRm(0b11, reg_or_opx, rm); | ||
| 651 | } | ||
| 652 | |||
| 653 | /// Construct a ModR/M byte using indirect r/m addressing | ||
| 654 | /// r/m effective address: [r/m] | ||
| 655 | /// | ||
| 656 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 657 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 658 | pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 659 | assert(rm != 4 and rm != 5); | ||
| 660 | self.modRm(0b00, reg_or_opx, rm); | ||
| 661 | } | ||
| 662 | |||
| 663 | /// Construct a ModR/M byte using indirect SIB addressing | ||
| 664 | /// r/m effective address: [SIB] | ||
| 665 | /// | ||
| 666 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 667 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 668 | pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void { | ||
| 669 | self.modRm(0b00, reg_or_opx, 0b100); | ||
| 670 | } | ||
| 671 | |||
| 672 | /// Construct a ModR/M byte using RIP-relative addressing | ||
| 673 | /// r/m effective address: [RIP + disp32] | ||
| 674 | /// | ||
| 675 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 676 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 677 | pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void { | ||
| 678 | self.modRm(0b00, reg_or_opx, 0b101); | ||
| 679 | } | ||
| 680 | |||
| 681 | /// Construct a ModR/M byte using indirect r/m with a 8bit displacement | ||
| 682 | /// r/m effective address: [r/m + disp8] | ||
| 683 | /// | ||
| 684 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 685 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 686 | pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 687 | assert(rm != 4); | ||
| 688 | self.modRm(0b01, reg_or_opx, rm); | ||
| 689 | } | ||
| 690 | |||
| 691 | /// Construct a ModR/M byte using indirect SIB with a 8bit displacement | ||
| 692 | /// r/m effective address: [SIB + disp8] | ||
| 693 | /// | ||
| 694 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 695 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 696 | pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void { | ||
| 697 | self.modRm(0b01, reg_or_opx, 0b100); | ||
| 698 | } | ||
| 699 | |||
| 700 | /// Construct a ModR/M byte using indirect r/m with a 32bit displacement | ||
| 701 | /// r/m effective address: [r/m + disp32] | ||
| 702 | /// | ||
| 703 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 704 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 705 | pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 706 | assert(rm != 4); | ||
| 707 | self.modRm(0b10, reg_or_opx, rm); | ||
| 708 | } | ||
| 709 | |||
| 710 | /// Construct a ModR/M byte using indirect SIB with a 32bit displacement | ||
| 711 | /// r/m effective address: [SIB + disp32] | ||
| 712 | /// | ||
| 713 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 714 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 715 | pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void { | ||
| 716 | self.modRm(0b10, reg_or_opx, 0b100); | ||
| 717 | } | ||
| 718 | |||
| 719 | // --- | ||
| 720 | // SIB | ||
| 721 | // --- | ||
| 722 | |||
| 723 | /// Construct a SIB byte given all the fields | ||
| 724 | /// | ||
| 725 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 726 | pub fn sib(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 727 | self.code.appendAssumeCapacity( | ||
| 728 | @as(u8, scale) << 6 | @as(u8, index) << 3 | base, | ||
| 729 | ); | ||
| 730 | } | ||
| 731 | |||
| 732 | /// Construct a SIB byte with scale * index + base, no frills. | ||
| 733 | /// r/m effective address: [base + scale * index] | ||
| 734 | /// | ||
| 735 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 736 | pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 737 | assert(base != 5); | ||
| 738 | |||
| 739 | self.sib(scale, index, base); | ||
| 740 | } | ||
| 741 | |||
| 742 | /// Construct a SIB byte with scale * index + disp32 | ||
| 743 | /// r/m effective address: [scale * index + disp32] | ||
| 744 | /// | ||
| 745 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 746 | pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void { | ||
| 747 | assert(index != 4); | ||
| 748 | |||
| 749 | // scale is actually ignored | ||
| 750 | // index = 4 means no index | ||
| 751 | // base = 5 means no base, if mod == 0. | ||
| 752 | self.sib(scale, index, 5); | ||
| 753 | } | ||
| 754 | |||
| 755 | /// Construct a SIB byte with just base | ||
| 756 | /// r/m effective address: [base] | ||
| 757 | /// | ||
| 758 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 759 | pub fn sib_base(self: Self, base: u3) void { | ||
| 760 | assert(base != 5); | ||
| 761 | |||
| 762 | // scale is actually ignored | ||
| 763 | // index = 4 means no index | ||
| 764 | self.sib(0, 4, base); | ||
| 765 | } | ||
| 766 | |||
| 767 | /// Construct a SIB byte with just disp32 | ||
| 768 | /// r/m effective address: [disp32] | ||
| 769 | /// | ||
| 770 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 771 | pub fn sib_disp32(self: Self) void { | ||
| 772 | // scale is actually ignored | ||
| 773 | // index = 4 means no index | ||
| 774 | // base = 5 means no base, if mod == 0. | ||
| 775 | self.sib(0, 4, 5); | ||
| 776 | } | ||
| 777 | |||
| 778 | /// Construct a SIB byte with scale * index + base + disp8 | ||
| 779 | /// r/m effective address: [base + scale * index + disp8] | ||
| 780 | /// | ||
| 781 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 782 | pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 783 | self.sib(scale, index, base); | ||
| 784 | } | ||
| 785 | |||
| 786 | /// Construct a SIB byte with base + disp8, no index | ||
| 787 | /// r/m effective address: [base + disp8] | ||
| 788 | /// | ||
| 789 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 790 | pub fn sib_baseDisp8(self: Self, base: u3) void { | ||
| 791 | // scale is ignored | ||
| 792 | // index = 4 means no index | ||
| 793 | self.sib(0, 4, base); | ||
| 794 | } | ||
| 795 | 461 | ||
| 796 | /// Construct a SIB byte with scale * index + base + disp32 | 462 | pub const Moffs = struct { |
| 797 | /// r/m effective address: [base + scale * index + disp32] | 463 | seg: Register, |
| 798 | /// | 464 | offset: u64, |
| 799 | /// Remember to add a REX prefix byte if index or base are extended! | 465 | }; |
| 800 | pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 801 | self.sib(scale, index, base); | ||
| 802 | } | ||
| 803 | 466 | ||
| 804 | /// Construct a SIB byte with base + disp32, no index | 467 | pub fn moffs(reg: Register, offset: u64) Memory { |
| 805 | /// r/m effective address: [base + disp32] | 468 | assert(reg.class() == .segment); |
| 806 | /// | 469 | return .{ .moffs = .{ .seg = reg, .offset = offset } }; |
| 807 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 808 | pub fn sib_baseDisp32(self: Self, base: u3) void { | ||
| 809 | // scale is ignored | ||
| 810 | // index = 4 means no index | ||
| 811 | self.sib(0, 4, base); | ||
| 812 | } | 470 | } |
| 813 | 471 | ||
| 814 | // ------------------------- | 472 | pub fn sib(ptr_size: PtrSize, args: struct { |
| 815 | // Trivial (no bit fiddling) | 473 | disp: i32, |
| 816 | // ------------------------- | 474 | base: ?Register = null, |
| 817 | 475 | scale_index: ?ScaleIndex = null, | |
| 818 | /// Encode an 8 bit immediate | 476 | }) Memory { |
| 819 | /// | 477 | return .{ .sib = .{ |
| 820 | /// It is sign-extended to 64 bits by the cpu. | 478 | .base = args.base, |
| 821 | pub fn imm8(self: Self, imm: i8) void { | 479 | .disp = args.disp, |
| 822 | self.code.appendAssumeCapacity(@bitCast(u8, imm)); | 480 | .ptr_size = ptr_size, |
| 481 | .scale_index = args.scale_index, | ||
| 482 | } }; | ||
| 823 | } | 483 | } |
| 824 | 484 | ||
| 825 | /// Encode an 8 bit displacement | 485 | pub fn rip(ptr_size: PtrSize, disp: i32) Memory { |
| 826 | /// | 486 | return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } }; |
| 827 | /// It is sign-extended to 64 bits by the cpu. | ||
| 828 | pub fn disp8(self: Self, disp: i8) void { | ||
| 829 | self.code.appendAssumeCapacity(@bitCast(u8, disp)); | ||
| 830 | } | 487 | } |
| 831 | 488 | ||
| 832 | /// Encode an 16 bit immediate | 489 | pub fn isSegmentRegister(mem: Memory) bool { |
| 833 | /// | 490 | return switch (mem) { |
| 834 | /// It is sign-extended to 64 bits by the cpu. | 491 | .moffs => true, |
| 835 | pub fn imm16(self: Self, imm: i16) void { | 492 | .rip => false, |
| 836 | self.writeIntLittle(i16, imm); | 493 | .sib => |s| if (s.base) |r| r.class() == .segment else false, |
| 494 | }; | ||
| 837 | } | 495 | } |
| 838 | 496 | ||
| 839 | /// Encode an 32 bit immediate | 497 | pub fn base(mem: Memory) ?Register { |
| 840 | /// | 498 | return switch (mem) { |
| 841 | /// It is sign-extended to 64 bits by the cpu. | 499 | .moffs => |m| m.seg, |
| 842 | pub fn imm32(self: Self, imm: i32) void { | 500 | .sib => |s| s.base, |
| 843 | self.writeIntLittle(i32, imm); | 501 | .rip => null, |
| 502 | }; | ||
| 844 | } | 503 | } |
| 845 | 504 | ||
| 846 | /// Encode an 32 bit displacement | 505 | pub fn scaleIndex(mem: Memory) ?ScaleIndex { |
| 847 | /// | 506 | return switch (mem) { |
| 848 | /// It is sign-extended to 64 bits by the cpu. | 507 | .moffs, .rip => null, |
| 849 | pub fn disp32(self: Self, disp: i32) void { | 508 | .sib => |s| s.scale_index, |
| 850 | self.writeIntLittle(i32, disp); | 509 | }; |
| 851 | } | 510 | } |
| 852 | 511 | ||
| 853 | /// Encode an 64 bit immediate | 512 | pub fn bitSize(mem: Memory) u64 { |
| 854 | /// | 513 | return switch (mem) { |
| 855 | /// It is sign-extended to 64 bits by the cpu. | 514 | .rip => |r| r.ptr_size.bitSize(), |
| 856 | pub fn imm64(self: Self, imm: u64) void { | 515 | .sib => |s| s.ptr_size.bitSize(), |
| 857 | self.writeIntLittle(u64, imm); | 516 | .moffs => unreachable, |
| 517 | }; | ||
| 858 | } | 518 | } |
| 859 | }; | 519 | }; |
| 860 | 520 | ||
| 861 | test "Encoder helpers - general purpose registers" { | 521 | pub const Immediate = union(enum) { |
| 862 | var code = ArrayList(u8).init(testing.allocator); | 522 | signed: i32, |
| 863 | defer code.deinit(); | 523 | unsigned: u64, |
| 864 | |||
| 865 | // simple integer multiplication | ||
| 866 | |||
| 867 | // imul eax,edi | ||
| 868 | // 0faf c7 | ||
| 869 | { | ||
| 870 | try code.resize(0); | ||
| 871 | const encoder = try Encoder.init(&code, 4); | ||
| 872 | encoder.rex(.{ | ||
| 873 | .r = Register.eax.isExtended(), | ||
| 874 | .b = Register.edi.isExtended(), | ||
| 875 | }); | ||
| 876 | encoder.opcode_2byte(0x0f, 0xaf); | ||
| 877 | encoder.modRm_direct( | ||
| 878 | Register.eax.lowEnc(), | ||
| 879 | Register.edi.lowEnc(), | ||
| 880 | ); | ||
| 881 | |||
| 882 | try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); | ||
| 883 | } | ||
| 884 | |||
| 885 | // simple mov | ||
| 886 | |||
| 887 | // mov eax,edi | ||
| 888 | // 89 f8 | ||
| 889 | { | ||
| 890 | try code.resize(0); | ||
| 891 | const encoder = try Encoder.init(&code, 3); | ||
| 892 | encoder.rex(.{ | ||
| 893 | .r = Register.edi.isExtended(), | ||
| 894 | .b = Register.eax.isExtended(), | ||
| 895 | }); | ||
| 896 | encoder.opcode_1byte(0x89); | ||
| 897 | encoder.modRm_direct( | ||
| 898 | Register.edi.lowEnc(), | ||
| 899 | Register.eax.lowEnc(), | ||
| 900 | ); | ||
| 901 | |||
| 902 | try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); | ||
| 903 | } | ||
| 904 | |||
| 905 | // signed integer addition of 32-bit sign extended immediate to 64 bit register | ||
| 906 | |||
| 907 | // add rcx, 2147483647 | ||
| 908 | // | ||
| 909 | // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding | ||
| 910 | // | ||
| 911 | // 48 : REX.W set for 64 bit operand (*r*cx) | ||
| 912 | // 81 : opcode for "<arithmetic> with immediate" | ||
| 913 | // c1 : id = rcx, | ||
| 914 | // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx) | ||
| 915 | // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD | ||
| 916 | // : 001 <-- 001 is rcx | ||
| 917 | // ffffff7f : 2147483647 | ||
| 918 | { | ||
| 919 | try code.resize(0); | ||
| 920 | const encoder = try Encoder.init(&code, 7); | ||
| 921 | encoder.rex(.{ .w = true }); // use 64 bit operation | ||
| 922 | encoder.opcode_1byte(0x81); | ||
| 923 | encoder.modRm_direct( | ||
| 924 | 0, | ||
| 925 | Register.rcx.lowEnc(), | ||
| 926 | ); | ||
| 927 | encoder.imm32(2147483647); | ||
| 928 | |||
| 929 | try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items); | ||
| 930 | } | ||
| 931 | } | ||
| 932 | |||
| 933 | test "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 | } | ||
| 954 | 524 | ||
| 955 | { | 525 | pub fn u(x: u64) Immediate { |
| 956 | stream.reset(); | 526 | return .{ .unsigned = x }; |
| 957 | var vex_prefix = Encoder.Vex{}; | ||
| 958 | vex_prefix.rex(.{ | ||
| 959 | .w = true, | ||
| 960 | .x = true, | ||
| 961 | }); | ||
| 962 | const nwritten = vex_prefix.write(writer); | ||
| 963 | try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, buf[0..nwritten]); | ||
| 964 | } | 527 | } |
| 965 | 528 | ||
| 966 | { | 529 | pub fn s(x: i32) Immediate { |
| 967 | stream.reset(); | 530 | return .{ .signed = x }; |
| 968 | var vex_prefix = Encoder.Vex{}; | ||
| 969 | vex_prefix.rex(.{ | ||
| 970 | .w = true, | ||
| 971 | .r = true, | ||
| 972 | }); | ||
| 973 | vex_prefix.len_256(); | ||
| 974 | vex_prefix.lead_opc_0f(); | ||
| 975 | vex_prefix.simd_prefix_66(); | ||
| 976 | const nwritten = vex_prefix.write(writer); | ||
| 977 | try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, buf[0..nwritten]); | ||
| 978 | } | 531 | } |
| 979 | 532 | ||
| 980 | var code = ArrayList(u8).init(testing.allocator); | 533 | pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 { |
| 981 | defer code.deinit(); | 534 | return switch (imm) { |
| 982 | 535 | .signed => |x| switch (bit_size) { | |
| 983 | { | 536 | 1, 8 => @bitCast(u8, @intCast(i8, x)), |
| 984 | // vmovapd xmm1, xmm2 | 537 | 16 => @bitCast(u16, @intCast(i16, x)), |
| 985 | const encoder = try Encoder.init(&code, 4); | 538 | 32, 64 => @bitCast(u32, x), |
| 986 | var vex = Encoder.Vex{}; | 539 | else => unreachable, |
| 987 | vex.simd_prefix_66(); | 540 | }, |
| 988 | encoder.vex(vex); // use 64 bit operation | 541 | .unsigned => |x| switch (bit_size) { |
| 989 | encoder.opcode_1byte(0x28); | 542 | 1, 8 => @intCast(u8, x), |
| 990 | encoder.modRm_direct(0, Register.xmm1.lowEnc()); | 543 | 16 => @intCast(u16, x), |
| 991 | try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); | 544 | 32 => @intCast(u32, x), |
| 992 | } | 545 | 64 => x, |
| 993 | 546 | else => unreachable, | |
| 994 | { | 547 | }, |
| 995 | try code.resize(0); | 548 | }; |
| 996 | |||
| 997 | // vmovhpd xmm13, xmm1, qword ptr [rip] | ||
| 998 | const encoder = try Encoder.init(&code, 9); | ||
| 999 | var vex = Encoder.Vex{}; | ||
| 1000 | vex.len_128(); | ||
| 1001 | vex.simd_prefix_66(); | ||
| 1002 | vex.lead_opc_0f(); | ||
| 1003 | vex.rex(.{ .r = true }); | ||
| 1004 | vex.reg(Register.xmm1.enc()); | ||
| 1005 | encoder.vex(vex); | ||
| 1006 | encoder.opcode_1byte(0x16); | ||
| 1007 | encoder.modRm_RIPDisp32(Register.xmm13.lowEnc()); | ||
| 1008 | encoder.disp32(0); | ||
| 1009 | try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items); | ||
| 1010 | } | 549 | } |
| 1011 | } | 550 | }; |
| 1012 | |||
| 1013 | // TODO add these registers to the enum and populate dwarfLocOp | ||
| 1014 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. | ||
| 1015 | // RA = (16, "RA"), | ||
| 1016 | // | ||
| 1017 | // XMM0 = (17, "xmm0"), | ||
| 1018 | // XMM1 = (18, "xmm1"), | ||
| 1019 | // XMM2 = (19, "xmm2"), | ||
| 1020 | // XMM3 = (20, "xmm3"), | ||
| 1021 | // XMM4 = (21, "xmm4"), | ||
| 1022 | // XMM5 = (22, "xmm5"), | ||
| 1023 | // XMM6 = (23, "xmm6"), | ||
| 1024 | // XMM7 = (24, "xmm7"), | ||
| 1025 | // | ||
| 1026 | // XMM8 = (25, "xmm8"), | ||
| 1027 | // XMM9 = (26, "xmm9"), | ||
| 1028 | // XMM10 = (27, "xmm10"), | ||
| 1029 | // XMM11 = (28, "xmm11"), | ||
| 1030 | // XMM12 = (29, "xmm12"), | ||
| 1031 | // XMM13 = (30, "xmm13"), | ||
| 1032 | // XMM14 = (31, "xmm14"), | ||
| 1033 | // XMM15 = (32, "xmm15"), | ||
| 1034 | // | ||
| 1035 | // ST0 = (33, "st0"), | ||
| 1036 | // ST1 = (34, "st1"), | ||
| 1037 | // ST2 = (35, "st2"), | ||
| 1038 | // ST3 = (36, "st3"), | ||
| 1039 | // ST4 = (37, "st4"), | ||
| 1040 | // ST5 = (38, "st5"), | ||
| 1041 | // ST6 = (39, "st6"), | ||
| 1042 | // ST7 = (40, "st7"), | ||
| 1043 | // | ||
| 1044 | // MM0 = (41, "mm0"), | ||
| 1045 | // MM1 = (42, "mm1"), | ||
| 1046 | // MM2 = (43, "mm2"), | ||
| 1047 | // MM3 = (44, "mm3"), | ||
| 1048 | // MM4 = (45, "mm4"), | ||
| 1049 | // MM5 = (46, "mm5"), | ||
| 1050 | // MM6 = (47, "mm6"), | ||
| 1051 | // MM7 = (48, "mm7"), | ||
| 1052 | // | ||
| 1053 | // RFLAGS = (49, "rFLAGS"), | ||
| 1054 | // ES = (50, "es"), | ||
| 1055 | // CS = (51, "cs"), | ||
| 1056 | // SS = (52, "ss"), | ||
| 1057 | // DS = (53, "ds"), | ||
| 1058 | // FS = (54, "fs"), | ||
| 1059 | // GS = (55, "gs"), | ||
| 1060 | // | ||
| 1061 | // FS_BASE = (58, "fs.base"), | ||
| 1062 | // GS_BASE = (59, "gs.base"), | ||
| 1063 | // | ||
| 1064 | // TR = (62, "tr"), | ||
| 1065 | // LDTR = (63, "ldtr"), | ||
| 1066 | // MXCSR = (64, "mxcsr"), | ||
| 1067 | // FCW = (65, "fcw"), | ||
| 1068 | // FSW = (66, "fsw"), | ||
| 1069 | // | ||
| 1070 | // XMM16 = (67, "xmm16"), | ||
| 1071 | // XMM17 = (68, "xmm17"), | ||
| 1072 | // XMM18 = (69, "xmm18"), | ||
| 1073 | // XMM19 = (70, "xmm19"), | ||
| 1074 | // XMM20 = (71, "xmm20"), | ||
| 1075 | // XMM21 = (72, "xmm21"), | ||
| 1076 | // XMM22 = (73, "xmm22"), | ||
| 1077 | // XMM23 = (74, "xmm23"), | ||
| 1078 | // XMM24 = (75, "xmm24"), | ||
| 1079 | // XMM25 = (76, "xmm25"), | ||
| 1080 | // XMM26 = (77, "xmm26"), | ||
| 1081 | // XMM27 = (78, "xmm27"), | ||
| 1082 | // XMM28 = (79, "xmm28"), | ||
| 1083 | // XMM29 = (80, "xmm29"), | ||
| 1084 | // XMM30 = (81, "xmm30"), | ||
| 1085 | // XMM31 = (82, "xmm31"), | ||
| 1086 | // | ||
| 1087 | // K0 = (118, "k0"), | ||
| 1088 | // K1 = (119, "k1"), | ||
| 1089 | // K2 = (120, "k2"), | ||
| 1090 | // K3 = (121, "k3"), | ||
| 1091 | // K4 = (122, "k4"), | ||
| 1092 | // K5 = (123, "k5"), | ||
| 1093 | // K6 = (124, "k6"), | ||
| 1094 | // K7 = (125, "k7"), |
src/arch/x86_64/encoder.zig created+2275| ... | @@ -0,0 +1,2275 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const log = std.log.scoped(.x86_64_encoder); | ||
| 4 | const math = std.math; | ||
| 5 | const testing = std.testing; | ||
| 6 | |||
| 7 | const bits = @import("bits.zig"); | ||
| 8 | const Encoding = @import("Encoding.zig"); | ||
| 9 | const Immediate = bits.Immediate; | ||
| 10 | const Memory = bits.Memory; | ||
| 11 | const Register = bits.Register; | ||
| 12 | |||
| 13 | pub 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 | |||
| 394 | inline fn sign(i: anytype) @TypeOf(i) { | ||
| 395 | return @as(@TypeOf(i), @boolToInt(i > 0)) - @boolToInt(i < 0); | ||
| 396 | } | ||
| 397 | |||
| 398 | pub 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 | |||
| 446 | fn 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 | |||
| 777 | pub 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 | ||
| 790 | fn 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 | |||
| 810 | const 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 | |||
| 837 | test "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 | |||
| 849 | test "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 | |||
| 877 | test "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 | |||
| 1014 | test "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 | |||
| 1187 | test "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 | |||
| 1237 | test "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 | |||
| 1300 | test "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 | |||
| 1366 | test "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 | |||
| 1379 | test "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 | |||
| 1406 | test "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 | |||
| 1434 | test "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 | |||
| 1450 | fn 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 | |||
| 1465 | test "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 | |||
| 1482 | fn 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 | |||
| 1496 | test "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 | |||
| 1511 | const 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 | |||
| 2009 | test "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 | |||
| 2142 | test "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 | |||
| 2187 | test "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 | |||
| 2232 | test "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 @@ | ||
| 1 | const Encoding = @import("Encoding.zig"); | ||
| 2 | const Mnemonic = Encoding.Mnemonic; | ||
| 3 | const OpEn = Encoding.OpEn; | ||
| 4 | const Op = Encoding.Op; | ||
| 5 | const Mode = Encoding.Mode; | ||
| 6 | |||
| 7 | const opcode_len = u2; | ||
| 8 | const modrm_ext = u3; | ||
| 9 | |||
| 10 | const 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 | ||
| 14 | pub const table = &[_]Entry{ | ||
| 15 | // General-purpose | ||
| 16 | .{ .adc, .zi, .al, .imm8, .none, .none, 1, 0x14, 0x00, 0x00, 0, .none }, | ||
| 17 | .{ .adc, .zi, .ax, .imm16, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, | ||
| 18 | .{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none }, | ||
| 19 | .{ .adc, .zi, .rax, .imm32s, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long }, | ||
| 20 | .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none }, | ||
| 21 | .{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .rex }, | ||
| 22 | .{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, | ||
| 23 | .{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none }, | ||
| 24 | .{ .adc, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long }, | ||
| 25 | .{ .adc, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, | ||
| 26 | .{ .adc, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none }, | ||
| 27 | .{ .adc, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long }, | ||
| 28 | .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none }, | ||
| 29 | .{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .rex }, | ||
| 30 | .{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, | ||
| 31 | .{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none }, | ||
| 32 | .{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long }, | ||
| 33 | .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none }, | ||
| 34 | .{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .rex }, | ||
| 35 | .{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, | ||
| 36 | .{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none }, | ||
| 37 | .{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long }, | ||
| 38 | |||
| 39 | .{ .add, .zi, .al, .imm8, .none, .none, 1, 0x04, 0x00, 0x00, 0, .none }, | ||
| 40 | .{ .add, .zi, .ax, .imm16, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, | ||
| 41 | .{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none }, | ||
| 42 | .{ .add, .zi, .rax, .imm32s, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long }, | ||
| 43 | .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none }, | ||
| 44 | .{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .rex }, | ||
| 45 | .{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, | ||
| 46 | .{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none }, | ||
| 47 | .{ .add, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long }, | ||
| 48 | .{ .add, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, | ||
| 49 | .{ .add, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none }, | ||
| 50 | .{ .add, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long }, | ||
| 51 | .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none }, | ||
| 52 | .{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .rex }, | ||
| 53 | .{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, | ||
| 54 | .{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none }, | ||
| 55 | .{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long }, | ||
| 56 | .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none }, | ||
| 57 | .{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .rex }, | ||
| 58 | .{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, | ||
| 59 | .{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none }, | ||
| 60 | .{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long }, | ||
| 61 | |||
| 62 | .{ .@"and", .zi, .al, .imm8, .none, .none, 1, 0x24, 0x00, 0x00, 0, .none }, | ||
| 63 | .{ .@"and", .zi, .ax, .imm16, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, | ||
| 64 | .{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none }, | ||
| 65 | .{ .@"and", .zi, .rax, .imm32s, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long }, | ||
| 66 | .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none }, | ||
| 67 | .{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .rex }, | ||
| 68 | .{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, | ||
| 69 | .{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none }, | ||
| 70 | .{ .@"and", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long }, | ||
| 71 | .{ .@"and", .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, | ||
| 72 | .{ .@"and", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none }, | ||
| 73 | .{ .@"and", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long }, | ||
| 74 | .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none }, | ||
| 75 | .{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .rex }, | ||
| 76 | .{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, | ||
| 77 | .{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none }, | ||
| 78 | .{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long }, | ||
| 79 | .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none }, | ||
| 80 | .{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .rex }, | ||
| 81 | .{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, | ||
| 82 | .{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none }, | ||
| 83 | .{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long }, | ||
| 84 | |||
| 85 | // This is M encoding according to Intel, but D makes more sense here. | ||
| 86 | .{ .call, .d, .rel32, .none, .none, .none, 1, 0xe8, 0x00, 0x00, 0, .none }, | ||
| 87 | .{ .call, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 2, .none }, | ||
| 88 | |||
| 89 | .{ .cbw, .np, .o16, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none }, | ||
| 90 | .{ .cwde, .np, .o32, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none }, | ||
| 91 | .{ .cdqe, .np, .o64, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .long }, | ||
| 92 | |||
| 93 | .{ .cwd, .np, .o16, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none }, | ||
| 94 | .{ .cdq, .np, .o32, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none }, | ||
| 95 | .{ .cqo, .np, .o64, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .long }, | ||
| 96 | |||
| 97 | .{ .cmova, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, | ||
| 98 | .{ .cmova, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, | ||
| 99 | .{ .cmova, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long }, | ||
| 100 | .{ .cmovae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, | ||
| 101 | .{ .cmovae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, | ||
| 102 | .{ .cmovae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long }, | ||
| 103 | .{ .cmovb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, | ||
| 104 | .{ .cmovb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, | ||
| 105 | .{ .cmovb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long }, | ||
| 106 | .{ .cmovbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, | ||
| 107 | .{ .cmovbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, | ||
| 108 | .{ .cmovbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long }, | ||
| 109 | .{ .cmovc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, | ||
| 110 | .{ .cmovc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, | ||
| 111 | .{ .cmovc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long }, | ||
| 112 | .{ .cmove, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, | ||
| 113 | .{ .cmove, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, | ||
| 114 | .{ .cmove, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long }, | ||
| 115 | .{ .cmovg, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, | ||
| 116 | .{ .cmovg, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, | ||
| 117 | .{ .cmovg, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long }, | ||
| 118 | .{ .cmovge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, | ||
| 119 | .{ .cmovge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, | ||
| 120 | .{ .cmovge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long }, | ||
| 121 | .{ .cmovl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, | ||
| 122 | .{ .cmovl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, | ||
| 123 | .{ .cmovl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long }, | ||
| 124 | .{ .cmovle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, | ||
| 125 | .{ .cmovle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, | ||
| 126 | .{ .cmovle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long }, | ||
| 127 | .{ .cmovna, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, | ||
| 128 | .{ .cmovna, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none }, | ||
| 129 | .{ .cmovna, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long }, | ||
| 130 | .{ .cmovnae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, | ||
| 131 | .{ .cmovnae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none }, | ||
| 132 | .{ .cmovnae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long }, | ||
| 133 | .{ .cmovnb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, | ||
| 134 | .{ .cmovnb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, | ||
| 135 | .{ .cmovnb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long }, | ||
| 136 | .{ .cmovnbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, | ||
| 137 | .{ .cmovnbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none }, | ||
| 138 | .{ .cmovnbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long }, | ||
| 139 | .{ .cmovnc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, | ||
| 140 | .{ .cmovnc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none }, | ||
| 141 | .{ .cmovnc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long }, | ||
| 142 | .{ .cmovne, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, | ||
| 143 | .{ .cmovne, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, | ||
| 144 | .{ .cmovne, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long }, | ||
| 145 | .{ .cmovng, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, | ||
| 146 | .{ .cmovng, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none }, | ||
| 147 | .{ .cmovng, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long }, | ||
| 148 | .{ .cmovnge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, | ||
| 149 | .{ .cmovnge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none }, | ||
| 150 | .{ .cmovnge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long }, | ||
| 151 | .{ .cmovnl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, | ||
| 152 | .{ .cmovnl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none }, | ||
| 153 | .{ .cmovnl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long }, | ||
| 154 | .{ .cmovnle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, | ||
| 155 | .{ .cmovnle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none }, | ||
| 156 | .{ .cmovnle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long }, | ||
| 157 | .{ .cmovno, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none }, | ||
| 158 | .{ .cmovno, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none }, | ||
| 159 | .{ .cmovno, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .long }, | ||
| 160 | .{ .cmovnp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, | ||
| 161 | .{ .cmovnp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, | ||
| 162 | .{ .cmovnp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long }, | ||
| 163 | .{ .cmovns, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none }, | ||
| 164 | .{ .cmovns, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none }, | ||
| 165 | .{ .cmovns, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .long }, | ||
| 166 | .{ .cmovnz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, | ||
| 167 | .{ .cmovnz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none }, | ||
| 168 | .{ .cmovnz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long }, | ||
| 169 | .{ .cmovo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none }, | ||
| 170 | .{ .cmovo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none }, | ||
| 171 | .{ .cmovo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .long }, | ||
| 172 | .{ .cmovp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, | ||
| 173 | .{ .cmovp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, | ||
| 174 | .{ .cmovp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long }, | ||
| 175 | .{ .cmovpe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, | ||
| 176 | .{ .cmovpe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none }, | ||
| 177 | .{ .cmovpe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long }, | ||
| 178 | .{ .cmovpo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, | ||
| 179 | .{ .cmovpo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none }, | ||
| 180 | .{ .cmovpo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long }, | ||
| 181 | .{ .cmovs, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none }, | ||
| 182 | .{ .cmovs, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none }, | ||
| 183 | .{ .cmovs, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .long }, | ||
| 184 | .{ .cmovz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, | ||
| 185 | .{ .cmovz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none }, | ||
| 186 | .{ .cmovz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long }, | ||
| 187 | |||
| 188 | .{ .cmp, .zi, .al, .imm8, .none, .none, 1, 0x3c, 0x00, 0x00, 0, .none }, | ||
| 189 | .{ .cmp, .zi, .ax, .imm16, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, | ||
| 190 | .{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none }, | ||
| 191 | .{ .cmp, .zi, .rax, .imm32s, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long }, | ||
| 192 | .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none }, | ||
| 193 | .{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .rex }, | ||
| 194 | .{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, | ||
| 195 | .{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none }, | ||
| 196 | .{ .cmp, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long }, | ||
| 197 | .{ .cmp, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, | ||
| 198 | .{ .cmp, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none }, | ||
| 199 | .{ .cmp, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long }, | ||
| 200 | .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none }, | ||
| 201 | .{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .rex }, | ||
| 202 | .{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, | ||
| 203 | .{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none }, | ||
| 204 | .{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long }, | ||
| 205 | .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none }, | ||
| 206 | .{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .rex }, | ||
| 207 | .{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, | ||
| 208 | .{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none }, | ||
| 209 | .{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long }, | ||
| 210 | |||
| 211 | .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .none }, | ||
| 212 | .{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .rex }, | ||
| 213 | .{ .div, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, | ||
| 214 | .{ .div, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none }, | ||
| 215 | .{ .div, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .long }, | ||
| 216 | |||
| 217 | .{ .fisttp, .m, .m16, .none, .none, .none, 1, 0xdf, 0x00, 0x00, 1, .fpu }, | ||
| 218 | .{ .fisttp, .m, .m32, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 1, .fpu }, | ||
| 219 | .{ .fisttp, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 1, .fpu }, | ||
| 220 | |||
| 221 | .{ .fld, .m, .m32, .none, .none, .none, 1, 0xd9, 0x00, 0x00, 0, .fpu }, | ||
| 222 | .{ .fld, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 0, .fpu }, | ||
| 223 | .{ .fld, .m, .m80, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 5, .fpu }, | ||
| 224 | |||
| 225 | .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .none }, | ||
| 226 | .{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .rex }, | ||
| 227 | .{ .idiv, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, | ||
| 228 | .{ .idiv, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none }, | ||
| 229 | .{ .idiv, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .long }, | ||
| 230 | |||
| 231 | .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none }, | ||
| 232 | .{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .rex }, | ||
| 233 | .{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, | ||
| 234 | .{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none }, | ||
| 235 | .{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long }, | ||
| 236 | .{ .imul, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, | ||
| 237 | .{ .imul, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none }, | ||
| 238 | .{ .imul, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .long }, | ||
| 239 | .{ .imul, .rmi, .r16, .rm16, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, | ||
| 240 | .{ .imul, .rmi, .r32, .rm32, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .none }, | ||
| 241 | .{ .imul, .rmi, .r64, .rm64, .imm8s, .none, 1, 0x6b, 0x00, 0x00, 0, .long }, | ||
| 242 | .{ .imul, .rmi, .r16, .rm16, .imm16, .none, 1, 0x69, 0x00, 0x00, 0, .none }, | ||
| 243 | .{ .imul, .rmi, .r32, .rm32, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .none }, | ||
| 244 | .{ .imul, .rmi, .r64, .rm64, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .long }, | ||
| 245 | |||
| 246 | .{ .int3, .np, .none, .none, .none, .none, 1, 0xcc, 0x00, 0x00, 0, .none }, | ||
| 247 | |||
| 248 | .{ .ja, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none }, | ||
| 249 | .{ .jae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none }, | ||
| 250 | .{ .jb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none }, | ||
| 251 | .{ .jbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none }, | ||
| 252 | .{ .jc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none }, | ||
| 253 | .{ .jrcxz, .d, .rel32, .none, .none, .none, 1, 0xe3, 0x00, 0x00, 0, .none }, | ||
| 254 | .{ .je, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none }, | ||
| 255 | .{ .jg, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none }, | ||
| 256 | .{ .jge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none }, | ||
| 257 | .{ .jl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none }, | ||
| 258 | .{ .jle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none }, | ||
| 259 | .{ .jna, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none }, | ||
| 260 | .{ .jnae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none }, | ||
| 261 | .{ .jnb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none }, | ||
| 262 | .{ .jnbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none }, | ||
| 263 | .{ .jnc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none }, | ||
| 264 | .{ .jne, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none }, | ||
| 265 | .{ .jng, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none }, | ||
| 266 | .{ .jnge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none }, | ||
| 267 | .{ .jnl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none }, | ||
| 268 | .{ .jnle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none }, | ||
| 269 | .{ .jno, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x81, 0x00, 0, .none }, | ||
| 270 | .{ .jnp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none }, | ||
| 271 | .{ .jns, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x89, 0x00, 0, .none }, | ||
| 272 | .{ .jnz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none }, | ||
| 273 | .{ .jo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x80, 0x00, 0, .none }, | ||
| 274 | .{ .jp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none }, | ||
| 275 | .{ .jpe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none }, | ||
| 276 | .{ .jpo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none }, | ||
| 277 | .{ .js, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x88, 0x00, 0, .none }, | ||
| 278 | .{ .jz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none }, | ||
| 279 | |||
| 280 | .{ .jmp, .d, .rel32, .none, .none, .none, 1, 0xe9, 0x00, 0x00, 0, .none }, | ||
| 281 | .{ .jmp, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 4, .none }, | ||
| 282 | |||
| 283 | .{ .lea, .rm, .r16, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none }, | ||
| 284 | .{ .lea, .rm, .r32, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none }, | ||
| 285 | .{ .lea, .rm, .r64, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .long }, | ||
| 286 | |||
| 287 | .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none }, | ||
| 288 | .{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .rex }, | ||
| 289 | .{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, | ||
| 290 | .{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none }, | ||
| 291 | .{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long }, | ||
| 292 | .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none }, | ||
| 293 | .{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .rex }, | ||
| 294 | .{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, | ||
| 295 | .{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none }, | ||
| 296 | .{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long }, | ||
| 297 | .{ .mov, .mr, .rm16, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .none }, | ||
| 298 | .{ .mov, .mr, .rm64, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .long }, | ||
| 299 | .{ .mov, .rm, .sreg, .rm16, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .none }, | ||
| 300 | .{ .mov, .rm, .sreg, .rm64, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .long }, | ||
| 301 | .{ .mov, .fd, .al, .moffs, .none, .none, 1, 0xa0, 0x00, 0x00, 0, .none }, | ||
| 302 | .{ .mov, .fd, .ax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, | ||
| 303 | .{ .mov, .fd, .eax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none }, | ||
| 304 | .{ .mov, .fd, .rax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .long }, | ||
| 305 | .{ .mov, .td, .moffs, .al, .none, .none, 1, 0xa2, 0x00, 0x00, 0, .none }, | ||
| 306 | .{ .mov, .td, .moffs, .ax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, | ||
| 307 | .{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none }, | ||
| 308 | .{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long }, | ||
| 309 | .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none }, | ||
| 310 | .{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .rex }, | ||
| 311 | .{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, | ||
| 312 | .{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none }, | ||
| 313 | .{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long }, | ||
| 314 | .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none }, | ||
| 315 | .{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .rex }, | ||
| 316 | .{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, | ||
| 317 | .{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none }, | ||
| 318 | .{ .mov, .mi, .rm64, .imm32s, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long }, | ||
| 319 | |||
| 320 | .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, | ||
| 321 | .{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .rex }, | ||
| 322 | .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none }, | ||
| 323 | .{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .rex }, | ||
| 324 | .{ .movsx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .long }, | ||
| 325 | .{ .movsx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .none }, | ||
| 326 | .{ .movsx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .long }, | ||
| 327 | |||
| 328 | // This instruction is discouraged. | ||
| 329 | .{ .movsxd, .rm, .r32, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .none }, | ||
| 330 | .{ .movsxd, .rm, .r64, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .long }, | ||
| 331 | |||
| 332 | .{ .movzx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none }, | ||
| 333 | .{ .movzx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none }, | ||
| 334 | .{ .movzx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .long }, | ||
| 335 | .{ .movzx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .none }, | ||
| 336 | .{ .movzx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .long }, | ||
| 337 | |||
| 338 | .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .none }, | ||
| 339 | .{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .rex }, | ||
| 340 | .{ .mul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none }, | ||
| 341 | .{ .mul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none }, | ||
| 342 | .{ .mul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .long }, | ||
| 343 | |||
| 344 | .{ .nop, .np, .none, .none, .none, .none, 1, 0x90, 0x00, 0x00, 0, .none }, | ||
| 345 | |||
| 346 | .{ .@"or", .zi, .al, .imm8, .none, .none, 1, 0x0c, 0x00, 0x00, 0, .none }, | ||
| 347 | .{ .@"or", .zi, .ax, .imm16, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, | ||
| 348 | .{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none }, | ||
| 349 | .{ .@"or", .zi, .rax, .imm32s, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long }, | ||
| 350 | .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none }, | ||
| 351 | .{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .rex }, | ||
| 352 | .{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, | ||
| 353 | .{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none }, | ||
| 354 | .{ .@"or", .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long }, | ||
| 355 | .{ .@"or", .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, | ||
| 356 | .{ .@"or", .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none }, | ||
| 357 | .{ .@"or", .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long }, | ||
| 358 | .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none }, | ||
| 359 | .{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .rex }, | ||
| 360 | .{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, | ||
| 361 | .{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none }, | ||
| 362 | .{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long }, | ||
| 363 | .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none }, | ||
| 364 | .{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .rex }, | ||
| 365 | .{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, | ||
| 366 | .{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none }, | ||
| 367 | .{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long }, | ||
| 368 | |||
| 369 | .{ .pop, .o, .r16, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none }, | ||
| 370 | .{ .pop, .o, .r64, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none }, | ||
| 371 | .{ .pop, .m, .rm16, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none }, | ||
| 372 | .{ .pop, .m, .rm64, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none }, | ||
| 373 | |||
| 374 | .{ .push, .o, .r16, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none }, | ||
| 375 | .{ .push, .o, .r64, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none }, | ||
| 376 | .{ .push, .m, .rm16, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none }, | ||
| 377 | .{ .push, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none }, | ||
| 378 | .{ .push, .i, .imm8, .none, .none, .none, 1, 0x6a, 0x00, 0x00, 0, .none }, | ||
| 379 | .{ .push, .i, .imm16, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none }, | ||
| 380 | .{ .push, .i, .imm32, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none }, | ||
| 381 | |||
| 382 | .{ .ret, .np, .none, .none, .none, .none, 1, 0xc3, 0x00, 0x00, 0, .none }, | ||
| 383 | |||
| 384 | .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none }, | ||
| 385 | .{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .rex }, | ||
| 386 | .{ .sal, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, | ||
| 387 | .{ .sal, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, | ||
| 388 | .{ .sal, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long }, | ||
| 389 | .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none }, | ||
| 390 | .{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .rex }, | ||
| 391 | .{ .sal, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, | ||
| 392 | .{ .sal, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, | ||
| 393 | .{ .sal, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long }, | ||
| 394 | .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none }, | ||
| 395 | .{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .rex }, | ||
| 396 | .{ .sal, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, | ||
| 397 | .{ .sal, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, | ||
| 398 | .{ .sal, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long }, | ||
| 399 | |||
| 400 | .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .none }, | ||
| 401 | .{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .rex }, | ||
| 402 | .{ .sar, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none }, | ||
| 403 | .{ .sar, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none }, | ||
| 404 | .{ .sar, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .long }, | ||
| 405 | .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .none }, | ||
| 406 | .{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .rex }, | ||
| 407 | .{ .sar, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none }, | ||
| 408 | .{ .sar, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none }, | ||
| 409 | .{ .sar, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .long }, | ||
| 410 | .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .none }, | ||
| 411 | .{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .rex }, | ||
| 412 | .{ .sar, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, | ||
| 413 | .{ .sar, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none }, | ||
| 414 | .{ .sar, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .long }, | ||
| 415 | |||
| 416 | .{ .sbb, .zi, .al, .imm8, .none, .none, 1, 0x1c, 0x00, 0x00, 0, .none }, | ||
| 417 | .{ .sbb, .zi, .ax, .imm16, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, | ||
| 418 | .{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none }, | ||
| 419 | .{ .sbb, .zi, .rax, .imm32s, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long }, | ||
| 420 | .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none }, | ||
| 421 | .{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .rex }, | ||
| 422 | .{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, | ||
| 423 | .{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none }, | ||
| 424 | .{ .sbb, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long }, | ||
| 425 | .{ .sbb, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, | ||
| 426 | .{ .sbb, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none }, | ||
| 427 | .{ .sbb, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long }, | ||
| 428 | .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none }, | ||
| 429 | .{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .rex }, | ||
| 430 | .{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, | ||
| 431 | .{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none }, | ||
| 432 | .{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long }, | ||
| 433 | .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none }, | ||
| 434 | .{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .rex }, | ||
| 435 | .{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, | ||
| 436 | .{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none }, | ||
| 437 | .{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long }, | ||
| 438 | |||
| 439 | .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, | ||
| 440 | .{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .rex }, | ||
| 441 | .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, | ||
| 442 | .{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex }, | ||
| 443 | .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, | ||
| 444 | .{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex }, | ||
| 445 | .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none }, | ||
| 446 | .{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .rex }, | ||
| 447 | .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, | ||
| 448 | .{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex }, | ||
| 449 | .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none }, | ||
| 450 | .{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .rex }, | ||
| 451 | .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none }, | ||
| 452 | .{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .rex }, | ||
| 453 | .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none }, | ||
| 454 | .{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .rex }, | ||
| 455 | .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none }, | ||
| 456 | .{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .rex }, | ||
| 457 | .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none }, | ||
| 458 | .{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .rex }, | ||
| 459 | .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none }, | ||
| 460 | .{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .rex }, | ||
| 461 | .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none }, | ||
| 462 | .{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .rex }, | ||
| 463 | .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, | ||
| 464 | .{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex }, | ||
| 465 | .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none }, | ||
| 466 | .{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .rex }, | ||
| 467 | .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none }, | ||
| 468 | .{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .rex }, | ||
| 469 | .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none }, | ||
| 470 | .{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .rex }, | ||
| 471 | .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none }, | ||
| 472 | .{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .rex }, | ||
| 473 | .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none }, | ||
| 474 | .{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .rex }, | ||
| 475 | .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none }, | ||
| 476 | .{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .rex }, | ||
| 477 | .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none }, | ||
| 478 | .{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .rex }, | ||
| 479 | .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .none }, | ||
| 480 | .{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .rex }, | ||
| 481 | .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none }, | ||
| 482 | .{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .rex }, | ||
| 483 | .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .none }, | ||
| 484 | .{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .rex }, | ||
| 485 | .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none }, | ||
| 486 | .{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .rex }, | ||
| 487 | .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .none }, | ||
| 488 | .{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .rex }, | ||
| 489 | .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none }, | ||
| 490 | .{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .rex }, | ||
| 491 | .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none }, | ||
| 492 | .{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .rex }, | ||
| 493 | .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none }, | ||
| 494 | .{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .rex }, | ||
| 495 | .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .none }, | ||
| 496 | .{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .rex }, | ||
| 497 | .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none }, | ||
| 498 | .{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .rex }, | ||
| 499 | |||
| 500 | .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none }, | ||
| 501 | .{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .rex }, | ||
| 502 | .{ .shl, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, | ||
| 503 | .{ .shl, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none }, | ||
| 504 | .{ .shl, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long }, | ||
| 505 | .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none }, | ||
| 506 | .{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .rex }, | ||
| 507 | .{ .shl, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, | ||
| 508 | .{ .shl, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none }, | ||
| 509 | .{ .shl, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long }, | ||
| 510 | .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none }, | ||
| 511 | .{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .rex }, | ||
| 512 | .{ .shl, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, | ||
| 513 | .{ .shl, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none }, | ||
| 514 | .{ .shl, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long }, | ||
| 515 | |||
| 516 | .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .none }, | ||
| 517 | .{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .rex }, | ||
| 518 | .{ .shr, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none }, | ||
| 519 | .{ .shr, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none }, | ||
| 520 | .{ .shr, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .long }, | ||
| 521 | .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .none }, | ||
| 522 | .{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .rex }, | ||
| 523 | .{ .shr, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none }, | ||
| 524 | .{ .shr, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none }, | ||
| 525 | .{ .shr, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .long }, | ||
| 526 | .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .none }, | ||
| 527 | .{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .rex }, | ||
| 528 | .{ .shr, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, | ||
| 529 | .{ .shr, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none }, | ||
| 530 | .{ .shr, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .long }, | ||
| 531 | |||
| 532 | .{ .sub, .zi, .al, .imm8, .none, .none, 1, 0x2c, 0x00, 0x00, 0, .none }, | ||
| 533 | .{ .sub, .zi, .ax, .imm16, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, | ||
| 534 | .{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none }, | ||
| 535 | .{ .sub, .zi, .rax, .imm32s, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long }, | ||
| 536 | .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none }, | ||
| 537 | .{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .rex }, | ||
| 538 | .{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, | ||
| 539 | .{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none }, | ||
| 540 | .{ .sub, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long }, | ||
| 541 | .{ .sub, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, | ||
| 542 | .{ .sub, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none }, | ||
| 543 | .{ .sub, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long }, | ||
| 544 | .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none }, | ||
| 545 | .{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .rex }, | ||
| 546 | .{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, | ||
| 547 | .{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none }, | ||
| 548 | .{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long }, | ||
| 549 | .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none }, | ||
| 550 | .{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .rex }, | ||
| 551 | .{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, | ||
| 552 | .{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none }, | ||
| 553 | .{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long }, | ||
| 554 | |||
| 555 | .{ .syscall, .np, .none, .none, .none, .none, 2, 0x0f, 0x05, 0x00, 0, .none }, | ||
| 556 | |||
| 557 | .{ .@"test", .zi, .al, .imm8, .none, .none, 1, 0xa8, 0x00, 0x00, 0, .none }, | ||
| 558 | .{ .@"test", .zi, .ax, .imm16, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, | ||
| 559 | .{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none }, | ||
| 560 | .{ .@"test", .zi, .rax, .imm32s, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long }, | ||
| 561 | .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none }, | ||
| 562 | .{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .rex }, | ||
| 563 | .{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, | ||
| 564 | .{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none }, | ||
| 565 | .{ .@"test", .mi, .rm64, .imm32s, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long }, | ||
| 566 | .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none }, | ||
| 567 | .{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .rex }, | ||
| 568 | .{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, | ||
| 569 | .{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none }, | ||
| 570 | .{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long }, | ||
| 571 | |||
| 572 | .{ .ud2, .np, .none, .none, .none, .none, 2, 0x0f, 0x0b, 0x00, 0, .none }, | ||
| 573 | |||
| 574 | .{ .xor, .zi, .al, .imm8, .none, .none, 1, 0x34, 0x00, 0x00, 0, .none }, | ||
| 575 | .{ .xor, .zi, .ax, .imm16, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, | ||
| 576 | .{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none }, | ||
| 577 | .{ .xor, .zi, .rax, .imm32s, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long }, | ||
| 578 | .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none }, | ||
| 579 | .{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .rex }, | ||
| 580 | .{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, | ||
| 581 | .{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none }, | ||
| 582 | .{ .xor, .mi, .rm64, .imm32s, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long }, | ||
| 583 | .{ .xor, .mi, .rm16, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, | ||
| 584 | .{ .xor, .mi, .rm32, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none }, | ||
| 585 | .{ .xor, .mi, .rm64, .imm8s, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long }, | ||
| 586 | .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none }, | ||
| 587 | .{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .rex }, | ||
| 588 | .{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, | ||
| 589 | .{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none }, | ||
| 590 | .{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long }, | ||
| 591 | .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none }, | ||
| 592 | .{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .rex }, | ||
| 593 | .{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, | ||
| 594 | .{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none }, | ||
| 595 | .{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long }, | ||
| 596 | |||
| 597 | // SSE | ||
| 598 | .{ .addss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x58, 0, .sse }, | ||
| 599 | |||
| 600 | .{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, 3, 0xf3, 0x0f, 0xc2, 0, .sse }, | ||
| 601 | |||
| 602 | .{ .movss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x10, 0, .sse }, | ||
| 603 | .{ .movss, .mr, .xmm_m32, .xmm, .none, .none, 3, 0xf3, 0x0f, 0x11, 0, .sse }, | ||
| 604 | |||
| 605 | .{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, 2, 0x0f, 0x2e, 0x00, 0, .sse }, | ||
| 606 | |||
| 607 | // SSE2 | ||
| 608 | .{ .addsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x58, 0, .sse2 }, | ||
| 609 | |||
| 610 | .{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, 3, 0xf2, 0x0f, 0xc2, 0, .sse2 }, | ||
| 611 | |||
| 612 | .{ .movq, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf3, 0x0f, 0x7e, 0, .sse2 }, | ||
| 613 | .{ .movq, .mr, .xmm_m64, .xmm, .none, .none, 3, 0x66, 0x0f, 0xd6, 0, .sse2 }, | ||
| 614 | |||
| 615 | .{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x10, 0, .sse2 }, | ||
| 616 | .{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, 3, 0xf2, 0x0f, 0x11, 0, .sse2 }, | ||
| 617 | |||
| 618 | .{ .ucomisd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0x66, 0x0f, 0x2e, 0, .sse2 }, | ||
| 619 | }; | ||
| 620 | // zig fmt: on | ||
| 621 | |||
src/codegen.zig+287-19| ... | @@ -29,11 +29,10 @@ pub const Result = union(enum) { | ... | @@ -29,11 +29,10 @@ pub const Result = union(enum) { |
| 29 | fail: *ErrorMsg, | 29 | fail: *ErrorMsg, |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | pub const GenerateSymbolError = error{ | 32 | pub const CodeGenError = error{ |
| 33 | OutOfMemory, | 33 | OutOfMemory, |
| 34 | Overflow, | 34 | Overflow, |
| 35 | /// A Decl that this symbol depends on had a semantic analysis failure. | 35 | CodegenFail, |
| 36 | AnalysisFail, | ||
| 37 | }; | 36 | }; |
| 38 | 37 | ||
| 39 | pub const DebugInfoOutput = union(enum) { | 38 | pub const DebugInfoOutput = union(enum) { |
| ... | @@ -63,19 +62,6 @@ pub const DebugInfoOutput = union(enum) { | ... | @@ -63,19 +62,6 @@ pub const DebugInfoOutput = union(enum) { |
| 63 | none, | 62 | none, |
| 64 | }; | 63 | }; |
| 65 | 64 | ||
| 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) | ||
| 70 | pub const LinkerLoad = struct { | ||
| 71 | type: enum { | ||
| 72 | got, | ||
| 73 | direct, | ||
| 74 | import, | ||
| 75 | }, | ||
| 76 | sym_index: u32, | ||
| 77 | }; | ||
| 78 | |||
| 79 | pub fn generateFunction( | 65 | pub fn generateFunction( |
| 80 | bin_file: *link.File, | 66 | bin_file: *link.File, |
| 81 | src_loc: Module.SrcLoc, | 67 | src_loc: Module.SrcLoc, |
| ... | @@ -84,7 +70,7 @@ pub fn generateFunction( | ... | @@ -84,7 +70,7 @@ pub fn generateFunction( |
| 84 | liveness: Liveness, | 70 | liveness: Liveness, |
| 85 | code: *std.ArrayList(u8), | 71 | code: *std.ArrayList(u8), |
| 86 | debug_output: DebugInfoOutput, | 72 | debug_output: DebugInfoOutput, |
| 87 | ) GenerateSymbolError!Result { | 73 | ) CodeGenError!Result { |
| 88 | switch (bin_file.options.target.cpu.arch) { | 74 | switch (bin_file.options.target.cpu.arch) { |
| 89 | .arm, | 75 | .arm, |
| 90 | .armeb, | 76 | .armeb, |
| ... | @@ -120,7 +106,7 @@ pub fn generateSymbol( | ... | @@ -120,7 +106,7 @@ pub fn generateSymbol( |
| 120 | code: *std.ArrayList(u8), | 106 | code: *std.ArrayList(u8), |
| 121 | debug_output: DebugInfoOutput, | 107 | debug_output: DebugInfoOutput, |
| 122 | reloc_info: RelocInfo, | 108 | reloc_info: RelocInfo, |
| 123 | ) GenerateSymbolError!Result { | 109 | ) CodeGenError!Result { |
| 124 | const tracy = trace(@src()); | 110 | const tracy = trace(@src()); |
| 125 | defer tracy.end(); | 111 | defer tracy.end(); |
| 126 | 112 | ||
| ... | @@ -823,7 +809,7 @@ fn lowerDeclRef( | ... | @@ -823,7 +809,7 @@ fn lowerDeclRef( |
| 823 | code: *std.ArrayList(u8), | 809 | code: *std.ArrayList(u8), |
| 824 | debug_output: DebugInfoOutput, | 810 | debug_output: DebugInfoOutput, |
| 825 | reloc_info: RelocInfo, | 811 | reloc_info: RelocInfo, |
| 826 | ) GenerateSymbolError!Result { | 812 | ) CodeGenError!Result { |
| 827 | const target = bin_file.options.target; | 813 | const target = bin_file.options.target; |
| 828 | const module = bin_file.options.module.?; | 814 | const module = bin_file.options.module.?; |
| 829 | if (typed_value.ty.isSlice()) { | 815 | if (typed_value.ty.isSlice()) { |
| ... | @@ -880,6 +866,288 @@ fn lowerDeclRef( | ... | @@ -880,6 +866,288 @@ fn lowerDeclRef( |
| 880 | return Result.ok; | 866 | return Result.ok; |
| 881 | } | 867 | } |
| 882 | 868 | ||
| 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) | ||
| 873 | pub const LinkerLoad = struct { | ||
| 874 | type: enum { | ||
| 875 | got, | ||
| 876 | direct, | ||
| 877 | import, | ||
| 878 | }, | ||
| 879 | sym_index: u32, | ||
| 880 | }; | ||
| 881 | |||
| 882 | pub 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 | |||
| 912 | fn 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 | |||
| 976 | fn 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 | |||
| 1011 | pub 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 | |||
| 883 | pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 { | 1151 | pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 { |
| 884 | const payload_align = payload_ty.abiAlignment(target); | 1152 | const payload_align = payload_ty.abiAlignment(target); |
| 885 | const error_align = Type.anyerror.abiAlignment(target); | 1153 | const error_align = Type.anyerror.abiAlignment(target); |
src/codegen/c.zig+912-515| ... | @@ -17,12 +17,6 @@ const LazySrcLoc = Module.LazySrcLoc; | ... | @@ -17,12 +17,6 @@ const LazySrcLoc = Module.LazySrcLoc; |
| 17 | const Air = @import("../Air.zig"); | 17 | const Air = @import("../Air.zig"); |
| 18 | const Liveness = @import("../Liveness.zig"); | 18 | const Liveness = @import("../Liveness.zig"); |
| 19 | 19 | ||
| 20 | const target_util = @import("../target.zig"); | ||
| 21 | const libcFloatPrefix = target_util.libcFloatPrefix; | ||
| 22 | const libcFloatSuffix = target_util.libcFloatSuffix; | ||
| 23 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; | ||
| 24 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; | ||
| 25 | |||
| 26 | const BigIntLimb = std.math.big.Limb; | 20 | const BigIntLimb = std.math.big.Limb; |
| 27 | const BigInt = std.math.big.int; | 21 | const BigInt = std.math.big.int; |
| 28 | 22 | ||
| ... | @@ -112,11 +106,7 @@ const ValueRenderLocation = enum { | ... | @@ -112,11 +106,7 @@ const ValueRenderLocation = enum { |
| 112 | } | 106 | } |
| 113 | }; | 107 | }; |
| 114 | 108 | ||
| 115 | const BuiltinInfo = enum { | 109 | const BuiltinInfo = enum { none, bits }; |
| 116 | None, | ||
| 117 | Range, | ||
| 118 | Bits, | ||
| 119 | }; | ||
| 120 | 110 | ||
| 121 | const reserved_idents = std.ComptimeStringMap(void, .{ | 111 | const reserved_idents = std.ComptimeStringMap(void, .{ |
| 122 | // C language | 112 | // C language |
| ... | @@ -440,16 +430,24 @@ pub const Function = struct { | ... | @@ -440,16 +430,24 @@ pub const Function = struct { |
| 440 | return f.object.dg.typeToCType(ty, kind); | 430 | return f.object.dg.typeToCType(ty, kind); |
| 441 | } | 431 | } |
| 442 | 432 | ||
| 433 | fn byteSize(f: *Function, cty: CType) u64 { | ||
| 434 | return f.object.dg.byteSize(cty); | ||
| 435 | } | ||
| 436 | |||
| 443 | fn renderType(f: *Function, w: anytype, t: Type) !void { | 437 | fn renderType(f: *Function, w: anytype, t: Type) !void { |
| 444 | return f.object.dg.renderType(w, t); | 438 | return f.object.dg.renderType(w, t); |
| 445 | } | 439 | } |
| 446 | 440 | ||
| 447 | fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void { | 441 | fn renderCType(f: *Function, w: anytype, t: CType.Index) !void { |
| 448 | return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src } }, src_ty, location); | 442 | return f.object.dg.renderCType(w, t); |
| 443 | } | ||
| 444 | |||
| 445 | fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorizer, src_ty: Type, location: ValueRenderLocation) !void { | ||
| 446 | return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); | ||
| 449 | } | 447 | } |
| 450 | 448 | ||
| 451 | fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { | 449 | fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { |
| 452 | return f.object.dg.fmtIntLiteral(ty, val); | 450 | return f.object.dg.fmtIntLiteral(ty, val, .Other); |
| 453 | } | 451 | } |
| 454 | 452 | ||
| 455 | fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { | 453 | fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { |
| ... | @@ -574,9 +572,9 @@ pub const DeclGen = struct { | ... | @@ -574,9 +572,9 @@ pub const DeclGen = struct { |
| 574 | const len_val = Value.initPayload(&len_pl.base); | 572 | const len_val = Value.initPayload(&len_pl.base); |
| 575 | 573 | ||
| 576 | if (location == .StaticInitializer) { | 574 | if (location == .StaticInitializer) { |
| 577 | return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)}); | 575 | return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)}); |
| 578 | } else { | 576 | } else { |
| 579 | return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)}); | 577 | return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)}); |
| 580 | } | 578 | } |
| 581 | } | 579 | } |
| 582 | 580 | ||
| ... | @@ -606,7 +604,7 @@ pub const DeclGen = struct { | ... | @@ -606,7 +604,7 @@ pub const DeclGen = struct { |
| 606 | try writer.writeByte(')'); | 604 | try writer.writeByte(')'); |
| 607 | } | 605 | } |
| 608 | switch (ptr_val.tag()) { | 606 | switch (ptr_val.tag()) { |
| 609 | .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val)}), | 607 | .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}), |
| 610 | .decl_ref_mut, .decl_ref, .variable => { | 608 | .decl_ref_mut, .decl_ref, .variable => { |
| 611 | const decl_index = switch (ptr_val.tag()) { | 609 | const decl_index = switch (ptr_val.tag()) { |
| 612 | .decl_ref => ptr_val.castTag(.decl_ref).?.data, | 610 | .decl_ref => ptr_val.castTag(.decl_ref).?.data, |
| ... | @@ -670,7 +668,9 @@ pub const DeclGen = struct { | ... | @@ -670,7 +668,9 @@ pub const DeclGen = struct { |
| 670 | container_ptr_ty, | 668 | container_ptr_ty, |
| 671 | location, | 669 | location, |
| 672 | ); | 670 | ); |
| 673 | try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); | 671 | try writer.print(" + {})", .{ |
| 672 | try dg.fmtIntLiteral(Type.usize, byte_offset_val, .Other), | ||
| 673 | }); | ||
| 674 | }, | 674 | }, |
| 675 | .end => { | 675 | .end => { |
| 676 | try writer.writeAll("(("); | 676 | try writer.writeAll("(("); |
| ... | @@ -680,7 +680,9 @@ pub const DeclGen = struct { | ... | @@ -680,7 +680,9 @@ pub const DeclGen = struct { |
| 680 | container_ptr_ty, | 680 | container_ptr_ty, |
| 681 | location, | 681 | location, |
| 682 | ); | 682 | ); |
| 683 | try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)}); | 683 | try writer.print(") + {})", .{ |
| 684 | try dg.fmtIntLiteral(Type.usize, Value.one, .Other), | ||
| 685 | }); | ||
| 684 | }, | 686 | }, |
| 685 | } | 687 | } |
| 686 | }, | 688 | }, |
| ... | @@ -746,7 +748,7 @@ pub const DeclGen = struct { | ... | @@ -746,7 +748,7 @@ pub const DeclGen = struct { |
| 746 | return writer.writeAll("false"); | 748 | return writer.writeAll("false"); |
| 747 | } | 749 | } |
| 748 | }, | 750 | }, |
| 749 | .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteralLoc(ty, val, location)}), | 751 | .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}), |
| 750 | .Float => { | 752 | .Float => { |
| 751 | const bits = ty.floatBits(target); | 753 | const bits = ty.floatBits(target); |
| 752 | var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits }; | 754 | var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits }; |
| ... | @@ -780,11 +782,11 @@ pub const DeclGen = struct { | ... | @@ -780,11 +782,11 @@ pub const DeclGen = struct { |
| 780 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 782 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 781 | const ptr_ty = ty.slicePtrFieldType(&buf); | 783 | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 782 | try dg.renderType(writer, ptr_ty); | 784 | try dg.renderType(writer, ptr_ty); |
| 783 | return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); | 785 | return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); |
| 784 | } else { | 786 | } else { |
| 785 | try writer.writeAll("(("); | 787 | try writer.writeAll("(("); |
| 786 | try dg.renderType(writer, ty); | 788 | try dg.renderType(writer, ty); |
| 787 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); | 789 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); |
| 788 | }, | 790 | }, |
| 789 | .Optional => { | 791 | .Optional => { |
| 790 | var opt_buf: Type.Payload.ElemType = undefined; | 792 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | @@ -831,7 +833,7 @@ pub const DeclGen = struct { | ... | @@ -831,7 +833,7 @@ pub const DeclGen = struct { |
| 831 | 833 | ||
| 832 | return writer.writeByte('}'); | 834 | return writer.writeByte('}'); |
| 833 | }, | 835 | }, |
| 834 | .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}), | 836 | .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef, .Other)}), |
| 835 | }, | 837 | }, |
| 836 | .Union => { | 838 | .Union => { |
| 837 | if (!location.isInitializer()) { | 839 | if (!location.isInitializer()) { |
| ... | @@ -854,7 +856,7 @@ pub const DeclGen = struct { | ... | @@ -854,7 +856,7 @@ pub const DeclGen = struct { |
| 854 | if (!field.ty.hasRuntimeBits()) continue; | 856 | if (!field.ty.hasRuntimeBits()) continue; |
| 855 | try dg.renderValue(writer, field.ty, val, initializer_type); | 857 | try dg.renderValue(writer, field.ty, val, initializer_type); |
| 856 | break; | 858 | break; |
| 857 | } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); | 859 | } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef, .Other)}); |
| 858 | if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); | 860 | if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); |
| 859 | return writer.writeByte('}'); | 861 | return writer.writeByte('}'); |
| 860 | }, | 862 | }, |
| ... | @@ -868,7 +870,7 @@ pub const DeclGen = struct { | ... | @@ -868,7 +870,7 @@ pub const DeclGen = struct { |
| 868 | try writer.writeAll("{ .payload = "); | 870 | try writer.writeAll("{ .payload = "); |
| 869 | try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type); | 871 | try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type); |
| 870 | return writer.print(", .error = {x} }}", .{ | 872 | return writer.print(", .error = {x} }}", .{ |
| 871 | try dg.fmtIntLiteral(ty.errorUnionSet(), val), | 873 | try dg.fmtIntLiteral(ty.errorUnionSet(), val, .Other), |
| 872 | }); | 874 | }); |
| 873 | }, | 875 | }, |
| 874 | .Array, .Vector => { | 876 | .Array, .Vector => { |
| ... | @@ -927,7 +929,7 @@ pub const DeclGen = struct { | ... | @@ -927,7 +929,7 @@ pub const DeclGen = struct { |
| 927 | .decl_ref_mut, | 929 | .decl_ref_mut, |
| 928 | .decl_ref, | 930 | .decl_ref, |
| 929 | => try dg.renderParentPtr(writer, val, ty, location), | 931 | => try dg.renderParentPtr(writer, val, ty, location), |
| 930 | else => try writer.print("{}", .{try dg.fmtIntLiteralLoc(ty, val, location)}), | 932 | else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}), |
| 931 | }, | 933 | }, |
| 932 | .Float => { | 934 | .Float => { |
| 933 | const bits = ty.floatBits(target); | 935 | const bits = ty.floatBits(target); |
| ... | @@ -999,8 +1001,9 @@ pub const DeclGen = struct { | ... | @@ -999,8 +1001,9 @@ pub const DeclGen = struct { |
| 999 | // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); | 1001 | // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); |
| 1000 | } | 1002 | } |
| 1001 | 1003 | ||
| 1002 | try writer.writeAll("zig_make_special_"); | 1004 | try writer.writeAll("zig_"); |
| 1003 | if (location == .StaticInitializer) try writer.writeAll("constant_"); | 1005 | try writer.writeAll(if (location == .StaticInitializer) "init" else "make"); |
| 1006 | try writer.writeAll("_special_"); | ||
| 1004 | try dg.renderTypeForBuiltinFnName(writer, ty); | 1007 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 1005 | try writer.writeByte('('); | 1008 | try writer.writeByte('('); |
| 1006 | if (std.math.signbit(f128_val)) try writer.writeByte('-'); | 1009 | if (std.math.signbit(f128_val)) try writer.writeByte('-'); |
| ... | @@ -1020,7 +1023,7 @@ pub const DeclGen = struct { | ... | @@ -1020,7 +1023,7 @@ pub const DeclGen = struct { |
| 1020 | try writer.writeAll(", "); | 1023 | try writer.writeAll(", "); |
| 1021 | empty = false; | 1024 | empty = false; |
| 1022 | } | 1025 | } |
| 1023 | try writer.print("{x}", .{try dg.fmtIntLiteralLoc(int_ty, int_val, location)}); | 1026 | try writer.print("{x}", .{try dg.fmtIntLiteral(int_ty, int_val, location)}); |
| 1024 | if (!empty) try writer.writeByte(')'); | 1027 | if (!empty) try writer.writeByte(')'); |
| 1025 | return; | 1028 | return; |
| 1026 | }, | 1029 | }, |
| ... | @@ -1069,7 +1072,7 @@ pub const DeclGen = struct { | ... | @@ -1069,7 +1072,7 @@ pub const DeclGen = struct { |
| 1069 | .int_u64, .one => { | 1072 | .int_u64, .one => { |
| 1070 | try writer.writeAll("(("); | 1073 | try writer.writeAll("(("); |
| 1071 | try dg.renderType(writer, ty); | 1074 | try dg.renderType(writer, ty); |
| 1072 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); | 1075 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); |
| 1073 | }, | 1076 | }, |
| 1074 | .field_ptr, | 1077 | .field_ptr, |
| 1075 | .elem_ptr, | 1078 | .elem_ptr, |
| ... | @@ -1561,6 +1564,10 @@ pub const DeclGen = struct { | ... | @@ -1561,6 +1564,10 @@ pub const DeclGen = struct { |
| 1561 | return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); | 1564 | return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); |
| 1562 | } | 1565 | } |
| 1563 | 1566 | ||
| 1567 | fn byteSize(dg: *DeclGen, cty: CType) u64 { | ||
| 1568 | return cty.byteSize(dg.ctypes.set, dg.module.getTarget()); | ||
| 1569 | } | ||
| 1570 | |||
| 1564 | /// Renders a type as a single identifier, generating intermediate typedefs | 1571 | /// Renders a type as a single identifier, generating intermediate typedefs |
| 1565 | /// if necessary. | 1572 | /// if necessary. |
| 1566 | /// | 1573 | /// |
| ... | @@ -1573,9 +1580,12 @@ pub const DeclGen = struct { | ... | @@ -1573,9 +1580,12 @@ pub const DeclGen = struct { |
| 1573 | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | | 1580 | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | |
| 1574 | /// | 1581 | /// |
| 1575 | fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { | 1582 | fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { |
| 1583 | try dg.renderCType(w, try dg.typeToIndex(t, .complete)); | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | fn renderCType(dg: *DeclGen, w: anytype, idx: CType.Index) error{ OutOfMemory, AnalysisFail }!void { | ||
| 1576 | const store = &dg.ctypes.set; | 1587 | const store = &dg.ctypes.set; |
| 1577 | const module = dg.module; | 1588 | const module = dg.module; |
| 1578 | const idx = try dg.typeToIndex(t, .complete); | ||
| 1579 | _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); | 1589 | _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); |
| 1580 | try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); | 1590 | try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); |
| 1581 | } | 1591 | } |
| ... | @@ -1584,6 +1594,7 @@ pub const DeclGen = struct { | ... | @@ -1584,6 +1594,7 @@ pub const DeclGen = struct { |
| 1584 | c_value: struct { | 1594 | c_value: struct { |
| 1585 | f: *Function, | 1595 | f: *Function, |
| 1586 | value: CValue, | 1596 | value: CValue, |
| 1597 | v: Vectorizer, | ||
| 1587 | }, | 1598 | }, |
| 1588 | value: struct { | 1599 | value: struct { |
| 1589 | value: Value, | 1600 | value: Value, |
| ... | @@ -1593,6 +1604,7 @@ pub const DeclGen = struct { | ... | @@ -1593,6 +1604,7 @@ pub const DeclGen = struct { |
| 1593 | switch (self.*) { | 1604 | switch (self.*) { |
| 1594 | .c_value => |v| { | 1605 | .c_value => |v| { |
| 1595 | try v.f.writeCValue(w, v.value, location); | 1606 | try v.f.writeCValue(w, v.value, location); |
| 1607 | try v.v.elem(v.f, w); | ||
| 1596 | }, | 1608 | }, |
| 1597 | .value => |v| { | 1609 | .value => |v| { |
| 1598 | try dg.renderValue(w, value_ty, v.value, location); | 1610 | try dg.renderValue(w, value_ty, v.value, location); |
| ... | @@ -1829,111 +1841,93 @@ pub const DeclGen = struct { | ... | @@ -1829,111 +1841,93 @@ pub const DeclGen = struct { |
| 1829 | dg.module.markDeclAlive(decl); | 1841 | dg.module.markDeclAlive(decl); |
| 1830 | 1842 | ||
| 1831 | if (dg.module.decl_exports.get(decl_index)) |exports| { | 1843 | if (dg.module.decl_exports.get(decl_index)) |exports| { |
| 1832 | return writer.writeAll(exports.items[export_index].options.name); | 1844 | try writer.writeAll(exports.items[export_index].options.name); |
| 1833 | } else if (decl.isExtern()) { | 1845 | } else if (decl.isExtern()) { |
| 1834 | return writer.writeAll(mem.sliceTo(decl.name, 0)); | 1846 | try writer.writeAll(mem.sliceTo(decl.name, 0)); |
| 1835 | } else if (dg.module.test_functions.get(decl_index)) |_| { | ||
| 1836 | const gpa = dg.gpa; | ||
| 1837 | const name = try decl.getFullyQualifiedName(dg.module); | ||
| 1838 | defer gpa.free(name); | ||
| 1839 | return writer.print("{}_{d}", .{ fmtIdent(name), @enumToInt(decl_index) }); | ||
| 1840 | } else { | 1847 | } else { |
| 1841 | const gpa = dg.gpa; | 1848 | // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), |
| 1842 | const name = try decl.getFullyQualifiedName(dg.module); | 1849 | // expand to 3x the length of its input, but let's cut it off at a much shorter limit. |
| 1843 | defer gpa.free(name); | 1850 | var name: [100]u8 = undefined; |
| 1844 | 1851 | var name_stream = std.io.fixedBufferStream(&name); | |
| 1845 | // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), expand | 1852 | decl.renderFullyQualifiedName(dg.module, name_stream.writer()) catch |err| switch (err) { |
| 1846 | // to 3x the length of its input | 1853 | error.NoSpaceLeft => {}, |
| 1847 | if (name.len > 1365) { | 1854 | }; |
| 1848 | var hash = ident_hasher_init; | 1855 | try writer.print("{}__{d}", .{ |
| 1849 | hash.update(name); | 1856 | fmtIdent(name_stream.getWritten()), |
| 1850 | const ident_hash = hash.finalInt(); | 1857 | @enumToInt(decl_index), |
| 1851 | try writer.writeAll("zig_D_"); | 1858 | }); |
| 1852 | return std.fmt.formatIntValue(ident_hash, "x", .{}, writer); | ||
| 1853 | } else { | ||
| 1854 | return writer.print("{}", .{fmtIdent(name)}); | ||
| 1855 | } | ||
| 1856 | } | 1859 | } |
| 1857 | } | 1860 | } |
| 1858 | 1861 | ||
| 1859 | fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { | 1862 | fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { |
| 1860 | const target = dg.module.getTarget(); | 1863 | try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete)); |
| 1861 | if (ty.isAbiInt()) { | 1864 | } |
| 1862 | const int_info = ty.intInfo(target); | 1865 | |
| 1863 | const c_bits = toCIntBits(int_info.bits) orelse | 1866 | fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void { |
| 1864 | return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 1867 | switch (cty.tag()) { |
| 1865 | try writer.print("{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }); | 1868 | else => try writer.print("{c}{d}", .{ |
| 1866 | } else if (ty.isRuntimeFloat()) { | 1869 | if (cty.isBool()) |
| 1867 | try ty.print(writer, dg.module); | 1870 | signAbbrev(.unsigned) |
| 1868 | } else if (ty.isPtrAtRuntime()) { | 1871 | else if (cty.isInteger()) |
| 1869 | try writer.print("p{d}", .{ty.bitSize(target)}); | 1872 | signAbbrev(cty.signedness() orelse .unsigned) |
| 1870 | } else if (ty.zigTypeTag() == .Bool) { | 1873 | else if (cty.isFloat()) |
| 1871 | try writer.print("u8", .{}); | 1874 | @as(u8, 'f') |
| 1872 | } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ | 1875 | else if (cty.isPointer()) |
| 1873 | ty.fmt(dg.module), | 1876 | @as(u8, 'p') |
| 1874 | }); | 1877 | else |
| 1878 | return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ | ||
| 1879 | cty.tag(), | ||
| 1880 | }), | ||
| 1881 | if (cty.isFloat()) cty.floatActiveBits(dg.module.getTarget()) else dg.byteSize(cty) * 8, | ||
| 1882 | }), | ||
| 1883 | .array => try writer.writeAll("big"), | ||
| 1884 | } | ||
| 1875 | } | 1885 | } |
| 1876 | 1886 | ||
| 1877 | fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { | 1887 | fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { |
| 1878 | const target = dg.module.getTarget(); | 1888 | const cty = try dg.typeToCType(ty, .complete); |
| 1879 | switch (info) { | 1889 | const is_big = cty.tag() == .array; |
| 1880 | .None => {}, | ||
| 1881 | .Range => { | ||
| 1882 | var arena = std.heap.ArenaAllocator.init(dg.gpa); | ||
| 1883 | defer arena.deinit(); | ||
| 1884 | |||
| 1885 | const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; | ||
| 1886 | var stack align(@alignOf(ExpectedContents)) = | ||
| 1887 | std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); | ||
| 1888 | |||
| 1889 | const int_info = ty.intInfo(target); | ||
| 1890 | if (int_info.signedness == .signed) { | ||
| 1891 | const min_val = try ty.minInt(stack.get(), target); | ||
| 1892 | try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val)}); | ||
| 1893 | } | ||
| 1894 | 1890 | ||
| 1895 | const max_val = try ty.maxInt(stack.get(), target); | 1891 | switch (info) { |
| 1896 | try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)}); | 1892 | .none => if (!is_big) return, |
| 1897 | }, | 1893 | .bits => {}, |
| 1898 | .Bits => { | ||
| 1899 | var bits_pl = Value.Payload.U64{ | ||
| 1900 | .base = .{ .tag = .int_u64 }, | ||
| 1901 | .data = ty.bitSize(target), | ||
| 1902 | }; | ||
| 1903 | const bits_val = Value.initPayload(&bits_pl.base); | ||
| 1904 | try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val)}); | ||
| 1905 | }, | ||
| 1906 | } | 1894 | } |
| 1895 | |||
| 1896 | const target = dg.module.getTarget(); | ||
| 1897 | const int_info = if (ty.isAbiInt()) ty.intInfo(target) else std.builtin.Type.Int{ | ||
| 1898 | .signedness = .unsigned, | ||
| 1899 | .bits = @intCast(u16, ty.bitSize(target)), | ||
| 1900 | }; | ||
| 1901 | |||
| 1902 | if (is_big) try writer.print(", {}", .{int_info.signedness == .signed}); | ||
| 1903 | |||
| 1904 | var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; | ||
| 1905 | try writer.print(", {}", .{try dg.fmtIntLiteral( | ||
| 1906 | if (is_big) Type.u16 else Type.u8, | ||
| 1907 | Value.initPayload(&bits_pl.base), | ||
| 1908 | .FunctionArgument, | ||
| 1909 | )}); | ||
| 1907 | } | 1910 | } |
| 1908 | 1911 | ||
| 1909 | fn fmtIntLiteral( | 1912 | fn fmtIntLiteral( |
| 1910 | dg: *DeclGen, | 1913 | dg: *DeclGen, |
| 1911 | ty: Type, | 1914 | ty: Type, |
| 1912 | val: Value, | 1915 | val: Value, |
| 1916 | loc: ValueRenderLocation, | ||
| 1913 | ) !std.fmt.Formatter(formatIntLiteral) { | 1917 | ) !std.fmt.Formatter(formatIntLiteral) { |
| 1914 | const int_info = ty.intInfo(dg.module.getTarget()); | 1918 | const kind: CType.Kind = switch (loc) { |
| 1915 | const c_bits = toCIntBits(int_info.bits); | 1919 | .FunctionArgument => .parameter, |
| 1916 | if (c_bits == null or c_bits.? > 128) | 1920 | .Initializer, .Other => .complete, |
| 1917 | return dg.fail("TODO implement integer constants larger than 128 bits", .{}); | 1921 | .StaticInitializer => .global, |
| 1922 | }; | ||
| 1918 | return std.fmt.Formatter(formatIntLiteral){ .data = .{ | 1923 | return std.fmt.Formatter(formatIntLiteral){ .data = .{ |
| 1919 | .ty = ty, | 1924 | .dg = dg, |
| 1925 | .int_info = ty.intInfo(dg.module.getTarget()), | ||
| 1926 | .kind = kind, | ||
| 1927 | .cty = try dg.typeToCType(ty, kind), | ||
| 1920 | .val = val, | 1928 | .val = val, |
| 1921 | .mod = dg.module, | ||
| 1922 | } }; | 1929 | } }; |
| 1923 | } | 1930 | } |
| 1924 | |||
| 1925 | fn fmtIntLiteralLoc( | ||
| 1926 | dg: *DeclGen, | ||
| 1927 | ty: Type, | ||
| 1928 | val: Value, | ||
| 1929 | location: ValueRenderLocation, // TODO: Instead add this as optional arg to fmtIntLiteral | ||
| 1930 | ) !std.fmt.Formatter(formatIntLiteral) { | ||
| 1931 | const int_info = ty.intInfo(dg.module.getTarget()); | ||
| 1932 | const c_bits = toCIntBits(int_info.bits); | ||
| 1933 | if (c_bits == null or c_bits.? > 128) | ||
| 1934 | return dg.fail("TODO implement integer constants larger than 128 bits", .{}); | ||
| 1935 | return std.fmt.Formatter(formatIntLiteral){ .data = .{ .ty = ty, .val = val, .mod = dg.module, .location = location } }; | ||
| 1936 | } | ||
| 1937 | }; | 1931 | }; |
| 1938 | 1932 | ||
| 1939 | const CTypeFix = enum { prefix, suffix }; | 1933 | const CTypeFix = enum { prefix, suffix }; |
| ... | @@ -2450,7 +2444,7 @@ pub fn genErrDecls(o: *Object) !void { | ... | @@ -2450,7 +2444,7 @@ pub fn genErrDecls(o: *Object) !void { |
| 2450 | const len_val = Value.initPayload(&len_pl.base); | 2444 | const len_val = Value.initPayload(&len_pl.base); |
| 2451 | 2445 | ||
| 2452 | try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ | 2446 | try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ |
| 2453 | fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), | 2447 | fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), |
| 2454 | }); | 2448 | }); |
| 2455 | } | 2449 | } |
| 2456 | try writer.writeAll("};\n"); | 2450 | try writer.writeAll("};\n"); |
| ... | @@ -2501,7 +2495,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { | ... | @@ -2501,7 +2495,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { |
| 2501 | var int_pl: Value.Payload.U64 = undefined; | 2495 | var int_pl: Value.Payload.U64 = undefined; |
| 2502 | const int_val = tag_val.enumToInt(enum_ty, &int_pl); | 2496 | const int_val = tag_val.enumToInt(enum_ty, &int_pl); |
| 2503 | 2497 | ||
| 2504 | var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; | 2498 | var name_ty_pl = Type.Payload.Len{ |
| 2499 | .base = .{ .tag = .array_u8_sentinel_0 }, | ||
| 2500 | .data = name.len, | ||
| 2501 | }; | ||
| 2505 | const name_ty = Type.initPayload(&name_ty_pl.base); | 2502 | const name_ty = Type.initPayload(&name_ty_pl.base); |
| 2506 | 2503 | ||
| 2507 | var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; | 2504 | var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; |
| ... | @@ -2510,14 +2507,16 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { | ... | @@ -2510,14 +2507,16 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { |
| 2510 | var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; | 2507 | var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; |
| 2511 | const len_val = Value.initPayload(&len_pl.base); | 2508 | const len_val = Value.initPayload(&len_pl.base); |
| 2512 | 2509 | ||
| 2513 | try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)}); | 2510 | try w.print(" case {}: {{\n static ", .{ |
| 2511 | try o.dg.fmtIntLiteral(enum_ty, int_val, .Other), | ||
| 2512 | }); | ||
| 2514 | try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); | 2513 | try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); |
| 2515 | try w.writeAll(" = "); | 2514 | try w.writeAll(" = "); |
| 2516 | try o.dg.renderValue(w, name_ty, name_val, .Initializer); | 2515 | try o.dg.renderValue(w, name_ty, name_val, .Initializer); |
| 2517 | try w.writeAll(";\n return ("); | 2516 | try w.writeAll(";\n return ("); |
| 2518 | try o.dg.renderType(w, name_slice_ty); | 2517 | try o.dg.renderType(w, name_slice_ty); |
| 2519 | try w.print("){{{}, {}}};\n", .{ | 2518 | try w.print("){{{}, {}}};\n", .{ |
| 2520 | fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val), | 2519 | fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), |
| 2521 | }); | 2520 | }); |
| 2522 | 2521 | ||
| 2523 | try w.writeAll(" }\n"); | 2522 | try w.writeAll(" }\n"); |
| ... | @@ -2535,7 +2534,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { | ... | @@ -2535,7 +2534,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { |
| 2535 | 2534 | ||
| 2536 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2535 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 2537 | try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); | 2536 | try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); |
| 2538 | try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name }); | 2537 | try o.dg.renderFunctionSignature( |
| 2538 | fwd_decl_writer, | ||
| 2539 | fn_decl_index, | ||
| 2540 | .forward, | ||
| 2541 | .{ .string = fn_name }, | ||
| 2542 | ); | ||
| 2539 | try fwd_decl_writer.writeAll(";\n"); | 2543 | try fwd_decl_writer.writeAll(";\n"); |
| 2540 | 2544 | ||
| 2541 | try w.print("static zig_{s} ", .{@tagName(key)}); | 2545 | try w.print("static zig_{s} ", .{@tagName(key)}); |
| ... | @@ -2741,6 +2745,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -2741,6 +2745,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2741 | .const_ty => unreachable, // excluded from function bodies | 2745 | .const_ty => unreachable, // excluded from function bodies |
| 2742 | .arg => try airArg(f, inst), | 2746 | .arg => try airArg(f, inst), |
| 2743 | 2747 | ||
| 2748 | .trap => try airTrap(f.object.writer()), | ||
| 2744 | .breakpoint => try airBreakpoint(f.object.writer()), | 2749 | .breakpoint => try airBreakpoint(f.object.writer()), |
| 2745 | .ret_addr => try airRetAddr(f, inst), | 2750 | .ret_addr => try airRetAddr(f, inst), |
| 2746 | .frame_addr => try airFrameAddress(f, inst), | 2751 | .frame_addr => try airFrameAddress(f, inst), |
| ... | @@ -2752,35 +2757,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -2752,35 +2757,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2752 | 2757 | ||
| 2753 | // TODO use a different strategy for add, sub, mul, div | 2758 | // TODO use a different strategy for add, sub, mul, div |
| 2754 | // that communicates to the optimizer that wrapping is UB. | 2759 | // that communicates to the optimizer that wrapping is UB. |
| 2755 | .add => try airBinOp(f, inst, "+", "add", .None), | 2760 | .add => try airBinOp(f, inst, "+", "add", .none), |
| 2756 | .sub => try airBinOp(f, inst, "-", "sub", .None), | 2761 | .sub => try airBinOp(f, inst, "-", "sub", .none), |
| 2757 | .mul => try airBinOp(f, inst, "*", "mul", .None), | 2762 | .mul => try airBinOp(f, inst, "*", "mul", .none), |
| 2758 | 2763 | ||
| 2759 | .neg => try airFloatNeg(f, inst), | 2764 | .neg => try airFloatNeg(f, inst), |
| 2760 | .div_float => try airBinBuiltinCall(f, inst, "div", .None), | 2765 | .div_float => try airBinBuiltinCall(f, inst, "div", .none), |
| 2761 | 2766 | ||
| 2762 | .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None), | 2767 | .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none), |
| 2763 | .rem => blk: { | 2768 | .rem => blk: { |
| 2764 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 2769 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 2765 | const lhs_ty = f.air.typeOf(bin_op.lhs); | 2770 | const lhs_scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); |
| 2766 | // For binary operations @TypeOf(lhs)==@TypeOf(rhs), | 2771 | // For binary operations @TypeOf(lhs)==@TypeOf(rhs), |
| 2767 | // so we only check one. | 2772 | // so we only check one. |
| 2768 | break :blk if (lhs_ty.isInt()) | 2773 | break :blk if (lhs_scalar_ty.isInt()) |
| 2769 | try airBinOp(f, inst, "%", "rem", .None) | 2774 | try airBinOp(f, inst, "%", "rem", .none) |
| 2770 | else | 2775 | else |
| 2771 | try airBinFloatOp(f, inst, "fmod"); | 2776 | try airBinFloatOp(f, inst, "fmod"); |
| 2772 | }, | 2777 | }, |
| 2773 | .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None), | 2778 | .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), |
| 2774 | .mod => try airBinBuiltinCall(f, inst, "mod", .None), | 2779 | .mod => try airBinBuiltinCall(f, inst, "mod", .none), |
| 2775 | 2780 | ||
| 2776 | .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits), | 2781 | .addwrap => try airBinBuiltinCall(f, inst, "addw", .bits), |
| 2777 | .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits), | 2782 | .subwrap => try airBinBuiltinCall(f, inst, "subw", .bits), |
| 2778 | .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits), | 2783 | .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .bits), |
| 2779 | 2784 | ||
| 2780 | .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits), | 2785 | .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits), |
| 2781 | .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits), | 2786 | .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits), |
| 2782 | .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits), | 2787 | .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits), |
| 2783 | .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits), | 2788 | .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits), |
| 2784 | 2789 | ||
| 2785 | .sqrt => try airUnFloatOp(f, inst, "sqrt"), | 2790 | .sqrt => try airUnFloatOp(f, inst, "sqrt"), |
| 2786 | .sin => try airUnFloatOp(f, inst, "sin"), | 2791 | .sin => try airUnFloatOp(f, inst, "sin"), |
| ... | @@ -2799,34 +2804,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -2799,34 +2804,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2799 | 2804 | ||
| 2800 | .mul_add => try airMulAdd(f, inst), | 2805 | .mul_add => try airMulAdd(f, inst), |
| 2801 | 2806 | ||
| 2802 | .add_with_overflow => try airOverflow(f, inst, "add", .Bits), | 2807 | .add_with_overflow => try airOverflow(f, inst, "add", .bits), |
| 2803 | .sub_with_overflow => try airOverflow(f, inst, "sub", .Bits), | 2808 | .sub_with_overflow => try airOverflow(f, inst, "sub", .bits), |
| 2804 | .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits), | 2809 | .mul_with_overflow => try airOverflow(f, inst, "mul", .bits), |
| 2805 | .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits), | 2810 | .shl_with_overflow => try airOverflow(f, inst, "shl", .bits), |
| 2806 | 2811 | ||
| 2807 | .min => try airMinMax(f, inst, '<', "fmin"), | 2812 | .min => try airMinMax(f, inst, '<', "fmin"), |
| 2808 | .max => try airMinMax(f, inst, '>', "fmax"), | 2813 | .max => try airMinMax(f, inst, '>', "fmax"), |
| 2809 | 2814 | ||
| 2810 | .slice => try airSlice(f, inst), | 2815 | .slice => try airSlice(f, inst), |
| 2811 | 2816 | ||
| 2812 | .cmp_gt => try airCmpOp(f, inst, ">", "gt"), | 2817 | .cmp_gt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gt), |
| 2813 | .cmp_gte => try airCmpOp(f, inst, ">=", "ge"), | 2818 | .cmp_gte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gte), |
| 2814 | .cmp_lt => try airCmpOp(f, inst, "<", "lt"), | 2819 | .cmp_lt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lt), |
| 2815 | .cmp_lte => try airCmpOp(f, inst, "<=", "le"), | 2820 | .cmp_lte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lte), |
| 2816 | 2821 | ||
| 2817 | .cmp_eq => try airEquality(f, inst, "((", "==", "eq"), | 2822 | .cmp_eq => try airEquality(f, inst, .eq), |
| 2818 | .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"), | 2823 | .cmp_neq => try airEquality(f, inst, .neq), |
| 2819 | 2824 | ||
| 2820 | .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}), | 2825 | .cmp_vector => blk: { |
| 2826 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | ||
| 2827 | const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data; | ||
| 2828 | break :blk try airCmpOp(f, inst, extra, extra.compareOperator()); | ||
| 2829 | }, | ||
| 2821 | .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), | 2830 | .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), |
| 2822 | 2831 | ||
| 2823 | // bool_and and bool_or are non-short-circuit operations | 2832 | // bool_and and bool_or are non-short-circuit operations |
| 2824 | .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None), | 2833 | .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .none), |
| 2825 | .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None), | 2834 | .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .none), |
| 2826 | .xor => try airBinOp(f, inst, "^", "xor", .None), | 2835 | .xor => try airBinOp(f, inst, "^", "xor", .none), |
| 2827 | .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None), | 2836 | .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .none), |
| 2828 | .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits), | 2837 | .shl, => try airBinBuiltinCall(f, inst, "shlw", .bits), |
| 2829 | .shl_exact => try airBinOp(f, inst, "<<", "shl", .None), | 2838 | .shl_exact => try airBinOp(f, inst, "<<", "shl", .none), |
| 2830 | .not => try airNot (f, inst), | 2839 | .not => try airNot (f, inst), |
| 2831 | 2840 | ||
| 2832 | .optional_payload => try airOptionalPayload(f, inst), | 2841 | .optional_payload => try airOptionalPayload(f, inst), |
| ... | @@ -2871,11 +2880,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -2871,11 +2880,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2871 | .memcpy => try airMemcpy(f, inst), | 2880 | .memcpy => try airMemcpy(f, inst), |
| 2872 | .set_union_tag => try airSetUnionTag(f, inst), | 2881 | .set_union_tag => try airSetUnionTag(f, inst), |
| 2873 | .get_union_tag => try airGetUnionTag(f, inst), | 2882 | .get_union_tag => try airGetUnionTag(f, inst), |
| 2874 | .clz => try airUnBuiltinCall(f, inst, "clz", .Bits), | 2883 | .clz => try airUnBuiltinCall(f, inst, "clz", .bits), |
| 2875 | .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits), | 2884 | .ctz => try airUnBuiltinCall(f, inst, "ctz", .bits), |
| 2876 | .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits), | 2885 | .popcount => try airUnBuiltinCall(f, inst, "popcount", .bits), |
| 2877 | .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits), | 2886 | .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .bits), |
| 2878 | .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits), | 2887 | .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .bits), |
| 2879 | .tag_name => try airTagName(f, inst), | 2888 | .tag_name => try airTagName(f, inst), |
| 2880 | .error_name => try airErrorName(f, inst), | 2889 | .error_name => try airErrorName(f, inst), |
| 2881 | .splat => try airSplat(f, inst), | 2890 | .splat => try airSplat(f, inst), |
| ... | @@ -3266,7 +3275,10 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3266,7 +3275,10 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3266 | 3275 | ||
| 3267 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | 3276 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3268 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 3277 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 3269 | const ptr_info = f.air.typeOf(ty_op.operand).ptrInfo().data; | 3278 | |
| 3279 | const ptr_ty = f.air.typeOf(ty_op.operand); | ||
| 3280 | const ptr_scalar_ty = ptr_ty.scalarType(); | ||
| 3281 | const ptr_info = ptr_scalar_ty.ptrInfo().data; | ||
| 3270 | const src_ty = ptr_info.pointee_type; | 3282 | const src_ty = ptr_info.pointee_type; |
| 3271 | 3283 | ||
| 3272 | if (!src_ty.hasRuntimeBitsIgnoreComptime() or | 3284 | if (!src_ty.hasRuntimeBitsIgnoreComptime() or |
| ... | @@ -3284,20 +3296,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3284,20 +3296,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3284 | const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target); | 3296 | const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target); |
| 3285 | const is_array = lowersToArray(src_ty, target); | 3297 | const is_array = lowersToArray(src_ty, target); |
| 3286 | const need_memcpy = !is_aligned or is_array; | 3298 | const need_memcpy = !is_aligned or is_array; |
| 3287 | const writer = f.object.writer(); | ||
| 3288 | 3299 | ||
| 3300 | const writer = f.object.writer(); | ||
| 3289 | const local = try f.allocLocal(inst, src_ty); | 3301 | const local = try f.allocLocal(inst, src_ty); |
| 3302 | const v = try Vectorizer.start(f, inst, writer, ptr_ty); | ||
| 3290 | 3303 | ||
| 3291 | if (need_memcpy) { | 3304 | if (need_memcpy) { |
| 3292 | try writer.writeAll("memcpy("); | 3305 | try writer.writeAll("memcpy("); |
| 3293 | if (!is_array) try writer.writeByte('&'); | 3306 | if (!is_array) try writer.writeByte('&'); |
| 3294 | try f.writeCValue(writer, local, .FunctionArgument); | 3307 | try f.writeCValue(writer, local, .Other); |
| 3308 | try v.elem(f, writer); | ||
| 3295 | try writer.writeAll(", (const char *)"); | 3309 | try writer.writeAll(", (const char *)"); |
| 3296 | try f.writeCValue(writer, operand, .Other); | 3310 | try f.writeCValue(writer, operand, .Other); |
| 3311 | try v.elem(f, writer); | ||
| 3297 | try writer.writeAll(", sizeof("); | 3312 | try writer.writeAll(", sizeof("); |
| 3298 | try f.renderType(writer, src_ty); | 3313 | try f.renderType(writer, src_ty); |
| 3299 | try writer.writeAll("))"); | 3314 | try writer.writeAll("))"); |
| 3300 | } else if (ptr_info.host_size != 0) { | 3315 | } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) { |
| 3301 | var host_pl = Type.Payload.Bits{ | 3316 | var host_pl = Type.Payload.Bits{ |
| 3302 | .base = .{ .tag = .int_unsigned }, | 3317 | .base = .{ .tag = .int_unsigned }, |
| 3303 | .data = ptr_info.host_size * 8, | 3318 | .data = ptr_info.host_size * 8, |
| ... | @@ -3323,6 +3338,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3323,6 +3338,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3323 | const field_ty = Type.initPayload(&field_pl.base); | 3338 | const field_ty = Type.initPayload(&field_pl.base); |
| 3324 | 3339 | ||
| 3325 | try f.writeCValue(writer, local, .Other); | 3340 | try f.writeCValue(writer, local, .Other); |
| 3341 | try v.elem(f, writer); | ||
| 3326 | try writer.writeAll(" = ("); | 3342 | try writer.writeAll(" = ("); |
| 3327 | try f.renderType(writer, src_ty); | 3343 | try f.renderType(writer, src_ty); |
| 3328 | try writer.writeAll(")zig_wrap_"); | 3344 | try writer.writeAll(")zig_wrap_"); |
| ... | @@ -3341,16 +3357,21 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3341,16 +3357,21 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3341 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); | 3357 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); |
| 3342 | try writer.writeByte('('); | 3358 | try writer.writeByte('('); |
| 3343 | try f.writeCValueDeref(writer, operand); | 3359 | try f.writeCValueDeref(writer, operand); |
| 3360 | try v.elem(f, writer); | ||
| 3344 | try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); | 3361 | try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); |
| 3345 | if (cant_cast) try writer.writeByte(')'); | 3362 | if (cant_cast) try writer.writeByte(')'); |
| 3346 | try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits); | 3363 | try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits); |
| 3347 | try writer.writeByte(')'); | 3364 | try writer.writeByte(')'); |
| 3348 | } else { | 3365 | } else { |
| 3349 | try f.writeCValue(writer, local, .Other); | 3366 | try f.writeCValue(writer, local, .Other); |
| 3367 | try v.elem(f, writer); | ||
| 3350 | try writer.writeAll(" = "); | 3368 | try writer.writeAll(" = "); |
| 3351 | try f.writeCValueDeref(writer, operand); | 3369 | try f.writeCValueDeref(writer, operand); |
| 3370 | try v.elem(f, writer); | ||
| 3352 | } | 3371 | } |
| 3353 | try writer.writeAll(";\n"); | 3372 | try writer.writeAll(";\n"); |
| 3373 | try v.end(f, inst, writer); | ||
| 3374 | |||
| 3354 | return local; | 3375 | return local; |
| 3355 | } | 3376 | } |
| 3356 | 3377 | ||
| ... | @@ -3416,15 +3437,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3416,15 +3437,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3416 | 3437 | ||
| 3417 | const operand = try f.resolveInst(ty_op.operand); | 3438 | const operand = try f.resolveInst(ty_op.operand); |
| 3418 | try reap(f, inst, &.{ty_op.operand}); | 3439 | try reap(f, inst, &.{ty_op.operand}); |
| 3419 | const writer = f.object.writer(); | 3440 | |
| 3420 | const inst_ty = f.air.typeOfIndex(inst); | 3441 | const inst_ty = f.air.typeOfIndex(inst); |
| 3421 | const local = try f.allocLocal(inst, inst_ty); | 3442 | const inst_scalar_ty = inst_ty.scalarType(); |
| 3422 | const operand_ty = f.air.typeOf(ty_op.operand); | 3443 | const operand_ty = f.air.typeOf(ty_op.operand); |
| 3444 | const scalar_ty = operand_ty.scalarType(); | ||
| 3423 | 3445 | ||
| 3446 | const writer = f.object.writer(); | ||
| 3447 | const local = try f.allocLocal(inst, inst_ty); | ||
| 3448 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | ||
| 3424 | try f.writeCValue(writer, local, .Other); | 3449 | try f.writeCValue(writer, local, .Other); |
| 3450 | try v.elem(f, writer); | ||
| 3425 | try writer.writeAll(" = "); | 3451 | try writer.writeAll(" = "); |
| 3426 | try f.renderIntCast(writer, inst_ty, operand, operand_ty, .Other); | 3452 | try f.renderIntCast(writer, inst_scalar_ty, operand, v, scalar_ty, .Other); |
| 3427 | try writer.writeAll(";\n"); | 3453 | try writer.writeAll(";\n"); |
| 3454 | try v.end(f, inst, writer); | ||
| 3455 | |||
| 3428 | return local; | 3456 | return local; |
| 3429 | } | 3457 | } |
| 3430 | 3458 | ||
| ... | @@ -3438,34 +3466,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3438,34 +3466,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3438 | const operand = try f.resolveInst(ty_op.operand); | 3466 | const operand = try f.resolveInst(ty_op.operand); |
| 3439 | try reap(f, inst, &.{ty_op.operand}); | 3467 | try reap(f, inst, &.{ty_op.operand}); |
| 3440 | const inst_ty = f.air.typeOfIndex(inst); | 3468 | const inst_ty = f.air.typeOfIndex(inst); |
| 3441 | const writer = f.object.writer(); | 3469 | const inst_scalar_ty = inst_ty.scalarType(); |
| 3442 | const local = try f.allocLocal(inst, inst_ty); | ||
| 3443 | const target = f.object.dg.module.getTarget(); | 3470 | const target = f.object.dg.module.getTarget(); |
| 3444 | const dest_int_info = inst_ty.intInfo(target); | 3471 | const dest_int_info = inst_scalar_ty.intInfo(target); |
| 3445 | const dest_bits = dest_int_info.bits; | 3472 | const dest_bits = dest_int_info.bits; |
| 3446 | const dest_c_bits = toCIntBits(dest_int_info.bits) orelse | 3473 | const dest_c_bits = toCIntBits(dest_int_info.bits) orelse |
| 3447 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 3474 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 3448 | const operand_ty = f.air.typeOf(ty_op.operand); | 3475 | const operand_ty = f.air.typeOf(ty_op.operand); |
| 3449 | const operand_int_info = operand_ty.intInfo(target); | 3476 | const scalar_ty = operand_ty.scalarType(); |
| 3477 | const scalar_int_info = scalar_ty.intInfo(target); | ||
| 3478 | |||
| 3479 | const writer = f.object.writer(); | ||
| 3480 | const local = try f.allocLocal(inst, inst_ty); | ||
| 3481 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | ||
| 3450 | 3482 | ||
| 3451 | try f.writeCValue(writer, local, .Other); | 3483 | try f.writeCValue(writer, local, .Other); |
| 3484 | try v.elem(f, writer); | ||
| 3452 | try writer.writeAll(" = "); | 3485 | try writer.writeAll(" = "); |
| 3453 | 3486 | ||
| 3454 | if (dest_c_bits < 64) { | 3487 | if (dest_c_bits < 64) { |
| 3455 | try writer.writeByte('('); | 3488 | try writer.writeByte('('); |
| 3456 | try f.renderType(writer, inst_ty); | 3489 | try f.renderType(writer, inst_scalar_ty); |
| 3457 | try writer.writeByte(')'); | 3490 | try writer.writeByte(')'); |
| 3458 | } | 3491 | } |
| 3459 | 3492 | ||
| 3460 | const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; | 3493 | const needs_lo = scalar_int_info.bits > 64 and dest_bits <= 64; |
| 3461 | if (needs_lo) { | 3494 | if (needs_lo) { |
| 3462 | try writer.writeAll("zig_lo_"); | 3495 | try writer.writeAll("zig_lo_"); |
| 3463 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 3496 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); |
| 3464 | try writer.writeByte('('); | 3497 | try writer.writeByte('('); |
| 3465 | } | 3498 | } |
| 3466 | 3499 | ||
| 3467 | if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { | 3500 | if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { |
| 3468 | try f.writeCValue(writer, operand, .Other); | 3501 | try f.writeCValue(writer, operand, .Other); |
| 3502 | try v.elem(f, writer); | ||
| 3469 | } else switch (dest_int_info.signedness) { | 3503 | } else switch (dest_int_info.signedness) { |
| 3470 | .unsigned => { | 3504 | .unsigned => { |
| 3471 | var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); | 3505 | var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); |
| ... | @@ -3475,15 +3509,16 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3475,15 +3509,16 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3475 | var stack align(@alignOf(ExpectedContents)) = | 3509 | var stack align(@alignOf(ExpectedContents)) = |
| 3476 | std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); | 3510 | std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); |
| 3477 | 3511 | ||
| 3478 | const mask_val = try inst_ty.maxInt(stack.get(), target); | 3512 | const mask_val = try inst_scalar_ty.maxInt(stack.get(), target); |
| 3479 | try writer.writeAll("zig_and_"); | 3513 | try writer.writeAll("zig_and_"); |
| 3480 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 3514 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); |
| 3481 | try writer.writeByte('('); | 3515 | try writer.writeByte('('); |
| 3482 | try f.writeCValue(writer, operand, .FunctionArgument); | 3516 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 3483 | try writer.print(", {x})", .{try f.fmtIntLiteral(operand_ty, mask_val)}); | 3517 | try v.elem(f, writer); |
| 3518 | try writer.print(", {x})", .{try f.fmtIntLiteral(scalar_ty, mask_val)}); | ||
| 3484 | }, | 3519 | }, |
| 3485 | .signed => { | 3520 | .signed => { |
| 3486 | const c_bits = toCIntBits(operand_int_info.bits) orelse | 3521 | const c_bits = toCIntBits(scalar_int_info.bits) orelse |
| 3487 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 3522 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 3488 | var shift_pl = Value.Payload.U64{ | 3523 | var shift_pl = Value.Payload.U64{ |
| 3489 | .base = .{ .tag = .int_u64 }, | 3524 | .base = .{ .tag = .int_u64 }, |
| ... | @@ -3492,7 +3527,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3492,7 +3527,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3492 | const shift_val = Value.initPayload(&shift_pl.base); | 3527 | const shift_val = Value.initPayload(&shift_pl.base); |
| 3493 | 3528 | ||
| 3494 | try writer.writeAll("zig_shr_"); | 3529 | try writer.writeAll("zig_shr_"); |
| 3495 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 3530 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); |
| 3496 | if (c_bits == 128) { | 3531 | if (c_bits == 128) { |
| 3497 | try writer.print("(zig_bitcast_i{d}(", .{c_bits}); | 3532 | try writer.print("(zig_bitcast_i{d}(", .{c_bits}); |
| 3498 | } else { | 3533 | } else { |
| ... | @@ -3505,6 +3540,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3505,6 +3540,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3505 | try writer.print("(uint{d}_t)", .{c_bits}); | 3540 | try writer.print("(uint{d}_t)", .{c_bits}); |
| 3506 | } | 3541 | } |
| 3507 | try f.writeCValue(writer, operand, .FunctionArgument); | 3542 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 3543 | try v.elem(f, writer); | ||
| 3508 | if (c_bits == 128) try writer.writeByte(')'); | 3544 | if (c_bits == 128) try writer.writeByte(')'); |
| 3509 | try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)}); | 3545 | try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)}); |
| 3510 | if (c_bits == 128) try writer.writeByte(')'); | 3546 | if (c_bits == 128) try writer.writeByte(')'); |
| ... | @@ -3514,6 +3550,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3514,6 +3550,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3514 | 3550 | ||
| 3515 | if (needs_lo) try writer.writeByte(')'); | 3551 | if (needs_lo) try writer.writeByte(')'); |
| 3516 | try writer.writeAll(";\n"); | 3552 | try writer.writeAll(";\n"); |
| 3553 | try v.end(f, inst, writer); | ||
| 3554 | |||
| 3517 | return local; | 3555 | return local; |
| 3518 | } | 3556 | } |
| 3519 | 3557 | ||
| ... | @@ -3550,7 +3588,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { | ... | @@ -3550,7 +3588,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { |
| 3550 | fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | 3588 | fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3551 | // *a = b; | 3589 | // *a = b; |
| 3552 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 3590 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 3553 | const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; | 3591 | |
| 3592 | const ptr_ty = f.air.typeOf(bin_op.lhs); | ||
| 3593 | const ptr_scalar_ty = ptr_ty.scalarType(); | ||
| 3594 | const ptr_info = ptr_scalar_ty.ptrInfo().data; | ||
| 3554 | if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { | 3595 | if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { |
| 3555 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3596 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3556 | return .none; | 3597 | return .none; |
| ... | @@ -3573,11 +3614,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3573,11 +3614,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3573 | ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target); | 3614 | ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target); |
| 3574 | const is_array = lowersToArray(ptr_info.pointee_type, target); | 3615 | const is_array = lowersToArray(ptr_info.pointee_type, target); |
| 3575 | const need_memcpy = !is_aligned or is_array; | 3616 | const need_memcpy = !is_aligned or is_array; |
| 3576 | const writer = f.object.writer(); | ||
| 3577 | 3617 | ||
| 3578 | const src_val = try f.resolveInst(bin_op.rhs); | 3618 | const src_val = try f.resolveInst(bin_op.rhs); |
| 3579 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3619 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3580 | 3620 | ||
| 3621 | const writer = f.object.writer(); | ||
| 3622 | const v = try Vectorizer.start(f, inst, writer, ptr_ty); | ||
| 3623 | |||
| 3581 | if (need_memcpy) { | 3624 | if (need_memcpy) { |
| 3582 | // For this memcpy to safely work we need the rhs to have the same | 3625 | // For this memcpy to safely work we need the rhs to have the same |
| 3583 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). | 3626 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). |
| ... | @@ -3598,16 +3641,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3598,16 +3641,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3598 | 3641 | ||
| 3599 | try writer.writeAll("memcpy((char *)"); | 3642 | try writer.writeAll("memcpy((char *)"); |
| 3600 | try f.writeCValue(writer, ptr_val, .FunctionArgument); | 3643 | try f.writeCValue(writer, ptr_val, .FunctionArgument); |
| 3644 | try v.elem(f, writer); | ||
| 3601 | try writer.writeAll(", "); | 3645 | try writer.writeAll(", "); |
| 3602 | if (!is_array) try writer.writeByte('&'); | 3646 | if (!is_array) try writer.writeByte('&'); |
| 3603 | try f.writeCValue(writer, array_src, .FunctionArgument); | 3647 | try f.writeCValue(writer, array_src, .FunctionArgument); |
| 3648 | try v.elem(f, writer); | ||
| 3604 | try writer.writeAll(", sizeof("); | 3649 | try writer.writeAll(", sizeof("); |
| 3605 | try f.renderType(writer, src_ty); | 3650 | try f.renderType(writer, src_ty); |
| 3606 | try writer.writeAll("))"); | 3651 | try writer.writeAll("))"); |
| 3607 | if (src_val == .constant) { | 3652 | if (src_val == .constant) { |
| 3608 | try freeLocal(f, inst, array_src.new_local, 0); | 3653 | try freeLocal(f, inst, array_src.new_local, 0); |
| 3609 | } | 3654 | } |
| 3610 | } else if (ptr_info.host_size != 0) { | 3655 | } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) { |
| 3611 | const host_bits = ptr_info.host_size * 8; | 3656 | const host_bits = ptr_info.host_size * 8; |
| 3612 | var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits }; | 3657 | var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits }; |
| 3613 | const host_ty = Type.initPayload(&host_pl.base); | 3658 | const host_ty = Type.initPayload(&host_pl.base); |
| ... | @@ -3644,12 +3689,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3644,12 +3689,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3644 | const mask_val = Value.initPayload(&mask_pl.base); | 3689 | const mask_val = Value.initPayload(&mask_pl.base); |
| 3645 | 3690 | ||
| 3646 | try f.writeCValueDeref(writer, ptr_val); | 3691 | try f.writeCValueDeref(writer, ptr_val); |
| 3692 | try v.elem(f, writer); | ||
| 3647 | try writer.writeAll(" = zig_or_"); | 3693 | try writer.writeAll(" = zig_or_"); |
| 3648 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); | 3694 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); |
| 3649 | try writer.writeAll("(zig_and_"); | 3695 | try writer.writeAll("(zig_and_"); |
| 3650 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); | 3696 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); |
| 3651 | try writer.writeByte('('); | 3697 | try writer.writeByte('('); |
| 3652 | try f.writeCValueDeref(writer, ptr_val); | 3698 | try f.writeCValueDeref(writer, ptr_val); |
| 3699 | try v.elem(f, writer); | ||
| 3653 | try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)}); | 3700 | try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)}); |
| 3654 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); | 3701 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); |
| 3655 | try writer.writeByte('('); | 3702 | try writer.writeByte('('); |
| ... | @@ -3671,14 +3718,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3671,14 +3718,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3671 | try writer.writeByte(')'); | 3718 | try writer.writeByte(')'); |
| 3672 | } | 3719 | } |
| 3673 | try f.writeCValue(writer, src_val, .Other); | 3720 | try f.writeCValue(writer, src_val, .Other); |
| 3721 | try v.elem(f, writer); | ||
| 3674 | if (cant_cast) try writer.writeByte(')'); | 3722 | if (cant_cast) try writer.writeByte(')'); |
| 3675 | try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); | 3723 | try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); |
| 3676 | } else { | 3724 | } else { |
| 3677 | try f.writeCValueDeref(writer, ptr_val); | 3725 | try f.writeCValueDeref(writer, ptr_val); |
| 3726 | try v.elem(f, writer); | ||
| 3678 | try writer.writeAll(" = "); | 3727 | try writer.writeAll(" = "); |
| 3679 | try f.writeCValue(writer, src_val, .Other); | 3728 | try f.writeCValue(writer, src_val, .Other); |
| 3729 | try v.elem(f, writer); | ||
| 3680 | } | 3730 | } |
| 3681 | try writer.writeAll(";\n"); | 3731 | try writer.writeAll(";\n"); |
| 3732 | try v.end(f, inst, writer); | ||
| 3733 | |||
| 3682 | return .none; | 3734 | return .none; |
| 3683 | } | 3735 | } |
| 3684 | 3736 | ||
| ... | @@ -3696,51 +3748,39 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: | ... | @@ -3696,51 +3748,39 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: |
| 3696 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3748 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3697 | 3749 | ||
| 3698 | const inst_ty = f.air.typeOfIndex(inst); | 3750 | const inst_ty = f.air.typeOfIndex(inst); |
| 3699 | const vector_ty = f.air.typeOf(bin_op.lhs); | 3751 | const operand_ty = f.air.typeOf(bin_op.lhs); |
| 3700 | const scalar_ty = vector_ty.scalarType(); | 3752 | const scalar_ty = operand_ty.scalarType(); |
| 3701 | const w = f.object.writer(); | ||
| 3702 | 3753 | ||
| 3754 | const w = f.object.writer(); | ||
| 3703 | const local = try f.allocLocal(inst, inst_ty); | 3755 | const local = try f.allocLocal(inst, inst_ty); |
| 3704 | 3756 | const v = try Vectorizer.start(f, inst, w, operand_ty); | |
| 3705 | switch (vector_ty.zigTypeTag()) { | 3757 | try f.writeCValueMember(w, local, .{ .field = 1 }); |
| 3706 | .Vector => { | 3758 | try v.elem(f, w); |
| 3707 | try w.writeAll("zig_v"); | 3759 | try w.writeAll(" = zig_"); |
| 3708 | try w.writeAll(operation); | 3760 | try w.writeAll(operation); |
| 3709 | try w.writeAll("o_"); | 3761 | try w.writeAll("o_"); |
| 3710 | try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); | 3762 | try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); |
| 3711 | try w.writeAll("("); | 3763 | try w.writeAll("(&"); |
| 3712 | try f.writeCValueMember(w, local, .{ .field = 1 }); | 3764 | try f.writeCValueMember(w, local, .{ .field = 0 }); |
| 3713 | try w.writeAll(", "); | 3765 | try v.elem(f, w); |
| 3714 | try f.writeCValueMember(w, local, .{ .field = 0 }); | 3766 | try w.writeAll(", "); |
| 3715 | try w.print(", {d}, ", .{vector_ty.vectorLen()}); | ||
| 3716 | }, | ||
| 3717 | else => { | ||
| 3718 | try f.writeCValueMember(w, local, .{ .field = 1 }); | ||
| 3719 | try w.writeAll(" = zig_"); | ||
| 3720 | try w.writeAll(operation); | ||
| 3721 | try w.writeAll("o_"); | ||
| 3722 | try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); | ||
| 3723 | try w.writeAll("(&"); | ||
| 3724 | try f.writeCValueMember(w, local, .{ .field = 0 }); | ||
| 3725 | try w.writeAll(", "); | ||
| 3726 | }, | ||
| 3727 | } | ||
| 3728 | |||
| 3729 | try f.writeCValue(w, lhs, .FunctionArgument); | 3767 | try f.writeCValue(w, lhs, .FunctionArgument); |
| 3768 | try v.elem(f, w); | ||
| 3730 | try w.writeAll(", "); | 3769 | try w.writeAll(", "); |
| 3731 | try f.writeCValue(w, rhs, .FunctionArgument); | 3770 | try f.writeCValue(w, rhs, .FunctionArgument); |
| 3771 | try v.elem(f, w); | ||
| 3732 | try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); | 3772 | try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); |
| 3733 | try w.writeAll(");\n"); | 3773 | try w.writeAll(");\n"); |
| 3774 | try v.end(f, inst, w); | ||
| 3734 | 3775 | ||
| 3735 | return local; | 3776 | return local; |
| 3736 | } | 3777 | } |
| 3737 | 3778 | ||
| 3738 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | 3779 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3739 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 3740 | if (inst_ty.tag() != .bool) | ||
| 3741 | return try airUnBuiltinCall(f, inst, "not", .Bits); | ||
| 3742 | |||
| 3743 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 3780 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 3781 | const operand_ty = f.air.typeOf(ty_op.operand); | ||
| 3782 | const scalar_ty = operand_ty.scalarType(); | ||
| 3783 | if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits); | ||
| 3744 | 3784 | ||
| 3745 | if (f.liveness.isUnused(inst)) { | 3785 | if (f.liveness.isUnused(inst)) { |
| 3746 | try reap(f, inst, &.{ty_op.operand}); | 3786 | try reap(f, inst, &.{ty_op.operand}); |
| ... | @@ -3750,14 +3790,20 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3750,14 +3790,20 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3750 | const op = try f.resolveInst(ty_op.operand); | 3790 | const op = try f.resolveInst(ty_op.operand); |
| 3751 | try reap(f, inst, &.{ty_op.operand}); | 3791 | try reap(f, inst, &.{ty_op.operand}); |
| 3752 | 3792 | ||
| 3793 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 3794 | |||
| 3753 | const writer = f.object.writer(); | 3795 | const writer = f.object.writer(); |
| 3754 | const local = try f.allocLocal(inst, inst_ty); | 3796 | const local = try f.allocLocal(inst, inst_ty); |
| 3755 | 3797 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | |
| 3756 | try f.writeCValue(writer, local, .Other); | 3798 | try f.writeCValue(writer, local, .Other); |
| 3799 | try v.elem(f, writer); | ||
| 3757 | try writer.writeAll(" = "); | 3800 | try writer.writeAll(" = "); |
| 3758 | try writer.writeByte('!'); | 3801 | try writer.writeByte('!'); |
| 3759 | try f.writeCValue(writer, op, .Other); | 3802 | try f.writeCValue(writer, op, .Other); |
| 3803 | try v.elem(f, writer); | ||
| 3760 | try writer.writeAll(";\n"); | 3804 | try writer.writeAll(";\n"); |
| 3805 | try v.end(f, inst, writer); | ||
| 3806 | |||
| 3761 | return local; | 3807 | return local; |
| 3762 | } | 3808 | } |
| 3763 | 3809 | ||
| ... | @@ -3770,63 +3816,89 @@ fn airBinOp( | ... | @@ -3770,63 +3816,89 @@ fn airBinOp( |
| 3770 | ) !CValue { | 3816 | ) !CValue { |
| 3771 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 3817 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 3772 | const operand_ty = f.air.typeOf(bin_op.lhs); | 3818 | const operand_ty = f.air.typeOf(bin_op.lhs); |
| 3819 | const scalar_ty = operand_ty.scalarType(); | ||
| 3773 | const target = f.object.dg.module.getTarget(); | 3820 | const target = f.object.dg.module.getTarget(); |
| 3774 | if ((operand_ty.isInt() and operand_ty.bitSize(target) > 64) or operand_ty.isRuntimeFloat()) | 3821 | if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat()) |
| 3775 | return try airBinBuiltinCall(f, inst, operation, info); | 3822 | return try airBinBuiltinCall(f, inst, operation, info); |
| 3776 | 3823 | ||
| 3824 | if (f.liveness.isUnused(inst)) { | ||
| 3825 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | ||
| 3826 | return .none; | ||
| 3827 | } | ||
| 3828 | |||
| 3777 | const lhs = try f.resolveInst(bin_op.lhs); | 3829 | const lhs = try f.resolveInst(bin_op.lhs); |
| 3778 | const rhs = try f.resolveInst(bin_op.rhs); | 3830 | const rhs = try f.resolveInst(bin_op.rhs); |
| 3779 | |||
| 3780 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3831 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3781 | 3832 | ||
| 3782 | if (f.liveness.isUnused(inst)) return .none; | ||
| 3783 | |||
| 3784 | const inst_ty = f.air.typeOfIndex(inst); | 3833 | const inst_ty = f.air.typeOfIndex(inst); |
| 3785 | 3834 | ||
| 3786 | const writer = f.object.writer(); | 3835 | const writer = f.object.writer(); |
| 3787 | const local = try f.allocLocal(inst, inst_ty); | 3836 | const local = try f.allocLocal(inst, inst_ty); |
| 3837 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | ||
| 3788 | try f.writeCValue(writer, local, .Other); | 3838 | try f.writeCValue(writer, local, .Other); |
| 3839 | try v.elem(f, writer); | ||
| 3789 | try writer.writeAll(" = "); | 3840 | try writer.writeAll(" = "); |
| 3790 | try f.writeCValue(writer, lhs, .Other); | 3841 | try f.writeCValue(writer, lhs, .Other); |
| 3842 | try v.elem(f, writer); | ||
| 3791 | try writer.writeByte(' '); | 3843 | try writer.writeByte(' '); |
| 3792 | try writer.writeAll(operator); | 3844 | try writer.writeAll(operator); |
| 3793 | try writer.writeByte(' '); | 3845 | try writer.writeByte(' '); |
| 3794 | try f.writeCValue(writer, rhs, .Other); | 3846 | try f.writeCValue(writer, rhs, .Other); |
| 3847 | try v.elem(f, writer); | ||
| 3795 | try writer.writeAll(";\n"); | 3848 | try writer.writeAll(";\n"); |
| 3849 | try v.end(f, inst, writer); | ||
| 3796 | 3850 | ||
| 3797 | return local; | 3851 | return local; |
| 3798 | } | 3852 | } |
| 3799 | 3853 | ||
| 3800 | fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8) !CValue { | 3854 | fn airCmpOp( |
| 3801 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 3855 | f: *Function, |
| 3802 | 3856 | inst: Air.Inst.Index, | |
| 3857 | data: anytype, | ||
| 3858 | operator: std.math.CompareOperator, | ||
| 3859 | ) !CValue { | ||
| 3803 | if (f.liveness.isUnused(inst)) { | 3860 | if (f.liveness.isUnused(inst)) { |
| 3804 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3861 | try reap(f, inst, &.{ data.lhs, data.rhs }); |
| 3805 | return .none; | 3862 | return .none; |
| 3806 | } | 3863 | } |
| 3807 | 3864 | ||
| 3808 | const operand_ty = f.air.typeOf(bin_op.lhs); | 3865 | const operand_ty = f.air.typeOf(data.lhs); |
| 3866 | const scalar_ty = operand_ty.scalarType(); | ||
| 3867 | |||
| 3809 | const target = f.object.dg.module.getTarget(); | 3868 | const target = f.object.dg.module.getTarget(); |
| 3810 | if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) | 3869 | const scalar_bits = scalar_ty.bitSize(target); |
| 3811 | return try cmpBuiltinCall(f, inst, operator, "cmp"); | 3870 | if (scalar_ty.isInt() and scalar_bits > 64) |
| 3812 | if (operand_ty.isRuntimeFloat()) | 3871 | return airCmpBuiltinCall( |
| 3813 | return try cmpBuiltinCall(f, inst, operator, operation); | 3872 | f, |
| 3873 | inst, | ||
| 3874 | data, | ||
| 3875 | operator, | ||
| 3876 | .cmp, | ||
| 3877 | if (scalar_bits > 128) .bits else .none, | ||
| 3878 | ); | ||
| 3879 | if (scalar_ty.isRuntimeFloat()) | ||
| 3880 | return airCmpBuiltinCall(f, inst, data, operator, .operator, .none); | ||
| 3814 | 3881 | ||
| 3815 | const inst_ty = f.air.typeOfIndex(inst); | 3882 | const inst_ty = f.air.typeOfIndex(inst); |
| 3816 | const lhs = try f.resolveInst(bin_op.lhs); | 3883 | const lhs = try f.resolveInst(data.lhs); |
| 3817 | const rhs = try f.resolveInst(bin_op.rhs); | 3884 | const rhs = try f.resolveInst(data.rhs); |
| 3818 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3885 | try reap(f, inst, &.{ data.lhs, data.rhs }); |
| 3819 | 3886 | ||
| 3820 | const writer = f.object.writer(); | 3887 | const writer = f.object.writer(); |
| 3821 | const local = try f.allocLocal(inst, inst_ty); | 3888 | const local = try f.allocLocal(inst, inst_ty); |
| 3889 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | ||
| 3822 | try f.writeCValue(writer, local, .Other); | 3890 | try f.writeCValue(writer, local, .Other); |
| 3891 | try v.elem(f, writer); | ||
| 3823 | try writer.writeAll(" = "); | 3892 | try writer.writeAll(" = "); |
| 3824 | try f.writeCValue(writer, lhs, .Other); | 3893 | try f.writeCValue(writer, lhs, .Other); |
| 3894 | try v.elem(f, writer); | ||
| 3825 | try writer.writeByte(' '); | 3895 | try writer.writeByte(' '); |
| 3826 | try writer.writeAll(operator); | 3896 | try writer.writeAll(compareOperatorC(operator)); |
| 3827 | try writer.writeByte(' '); | 3897 | try writer.writeByte(' '); |
| 3828 | try f.writeCValue(writer, rhs, .Other); | 3898 | try f.writeCValue(writer, rhs, .Other); |
| 3899 | try v.elem(f, writer); | ||
| 3829 | try writer.writeAll(";\n"); | 3900 | try writer.writeAll(";\n"); |
| 3901 | try v.end(f, inst, writer); | ||
| 3830 | 3902 | ||
| 3831 | return local; | 3903 | return local; |
| 3832 | } | 3904 | } |
| ... | @@ -3834,9 +3906,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: | ... | @@ -3834,9 +3906,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: |
| 3834 | fn airEquality( | 3906 | fn airEquality( |
| 3835 | f: *Function, | 3907 | f: *Function, |
| 3836 | inst: Air.Inst.Index, | 3908 | inst: Air.Inst.Index, |
| 3837 | negate_prefix: []const u8, | 3909 | operator: std.math.CompareOperator, |
| 3838 | operator: []const u8, | ||
| 3839 | operation: []const u8, | ||
| 3840 | ) !CValue { | 3910 | ) !CValue { |
| 3841 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 3911 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 3842 | 3912 | ||
| ... | @@ -3847,10 +3917,18 @@ fn airEquality( | ... | @@ -3847,10 +3917,18 @@ fn airEquality( |
| 3847 | 3917 | ||
| 3848 | const operand_ty = f.air.typeOf(bin_op.lhs); | 3918 | const operand_ty = f.air.typeOf(bin_op.lhs); |
| 3849 | const target = f.object.dg.module.getTarget(); | 3919 | const target = f.object.dg.module.getTarget(); |
| 3850 | if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) | 3920 | const operand_bits = operand_ty.bitSize(target); |
| 3851 | return try cmpBuiltinCall(f, inst, operator, "cmp"); | 3921 | if (operand_ty.isInt() and operand_bits > 64) |
| 3922 | return airCmpBuiltinCall( | ||
| 3923 | f, | ||
| 3924 | inst, | ||
| 3925 | bin_op, | ||
| 3926 | operator, | ||
| 3927 | .cmp, | ||
| 3928 | if (operand_bits > 128) .bits else .none, | ||
| 3929 | ); | ||
| 3852 | if (operand_ty.isRuntimeFloat()) | 3930 | if (operand_ty.isRuntimeFloat()) |
| 3853 | return try cmpBuiltinCall(f, inst, operator, operation); | 3931 | return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); |
| 3854 | 3932 | ||
| 3855 | const lhs = try f.resolveInst(bin_op.lhs); | 3933 | const lhs = try f.resolveInst(bin_op.lhs); |
| 3856 | const rhs = try f.resolveInst(bin_op.rhs); | 3934 | const rhs = try f.resolveInst(bin_op.rhs); |
| ... | @@ -3866,7 +3944,12 @@ fn airEquality( | ... | @@ -3866,7 +3944,12 @@ fn airEquality( |
| 3866 | // (A && B) || (C && (A == B)) | 3944 | // (A && B) || (C && (A == B)) |
| 3867 | // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload | 3945 | // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload |
| 3868 | 3946 | ||
| 3869 | try writer.writeAll(negate_prefix); | 3947 | switch (operator) { |
| 3948 | .eq => {}, | ||
| 3949 | .neq => try writer.writeByte('!'), | ||
| 3950 | else => unreachable, | ||
| 3951 | } | ||
| 3952 | try writer.writeAll("(("); | ||
| 3870 | try f.writeCValue(writer, lhs, .Other); | 3953 | try f.writeCValue(writer, lhs, .Other); |
| 3871 | try writer.writeAll(".is_null && "); | 3954 | try writer.writeAll(".is_null && "); |
| 3872 | try f.writeCValue(writer, rhs, .Other); | 3955 | try f.writeCValue(writer, rhs, .Other); |
| ... | @@ -3885,7 +3968,7 @@ fn airEquality( | ... | @@ -3885,7 +3968,7 @@ fn airEquality( |
| 3885 | 3968 | ||
| 3886 | try f.writeCValue(writer, lhs, .Other); | 3969 | try f.writeCValue(writer, lhs, .Other); |
| 3887 | try writer.writeByte(' '); | 3970 | try writer.writeByte(' '); |
| 3888 | try writer.writeAll(operator); | 3971 | try writer.writeAll(compareOperatorC(operator)); |
| 3889 | try writer.writeByte(' '); | 3972 | try writer.writeByte(' '); |
| 3890 | try f.writeCValue(writer, rhs, .Other); | 3973 | try f.writeCValue(writer, rhs, .Other); |
| 3891 | try writer.writeAll(";\n"); | 3974 | try writer.writeAll(";\n"); |
| ... | @@ -3927,11 +4010,14 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { | ... | @@ -3927,11 +4010,14 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { |
| 3927 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 4010 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3928 | 4011 | ||
| 3929 | const inst_ty = f.air.typeOfIndex(inst); | 4012 | const inst_ty = f.air.typeOfIndex(inst); |
| 3930 | const elem_ty = inst_ty.elemType2(); | 4013 | const inst_scalar_ty = inst_ty.scalarType(); |
| 4014 | const elem_ty = inst_scalar_ty.elemType2(); | ||
| 3931 | 4015 | ||
| 3932 | const local = try f.allocLocal(inst, inst_ty); | 4016 | const local = try f.allocLocal(inst, inst_ty); |
| 3933 | const writer = f.object.writer(); | 4017 | const writer = f.object.writer(); |
| 4018 | const v = try Vectorizer.start(f, inst, writer, inst_ty); | ||
| 3934 | try f.writeCValue(writer, local, .Other); | 4019 | try f.writeCValue(writer, local, .Other); |
| 4020 | try v.elem(f, writer); | ||
| 3935 | try writer.writeAll(" = "); | 4021 | try writer.writeAll(" = "); |
| 3936 | 4022 | ||
| 3937 | if (elem_ty.hasRuntimeBitsIgnoreComptime()) { | 4023 | if (elem_ty.hasRuntimeBitsIgnoreComptime()) { |
| ... | @@ -3939,19 +4025,26 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { | ... | @@ -3939,19 +4025,26 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { |
| 3939 | // results in a NULL pointer, or if LHS is NULL. The operation is only UB | 4025 | // results in a NULL pointer, or if LHS is NULL. The operation is only UB |
| 3940 | // if the result is NULL and then dereferenced. | 4026 | // if the result is NULL and then dereferenced. |
| 3941 | try writer.writeByte('('); | 4027 | try writer.writeByte('('); |
| 3942 | try f.renderType(writer, inst_ty); | 4028 | try f.renderType(writer, inst_scalar_ty); |
| 3943 | try writer.writeAll(")(((uintptr_t)"); | 4029 | try writer.writeAll(")(((uintptr_t)"); |
| 3944 | try f.writeCValue(writer, lhs, .Other); | 4030 | try f.writeCValue(writer, lhs, .Other); |
| 4031 | try v.elem(f, writer); | ||
| 3945 | try writer.writeAll(") "); | 4032 | try writer.writeAll(") "); |
| 3946 | try writer.writeByte(operator); | 4033 | try writer.writeByte(operator); |
| 3947 | try writer.writeAll(" ("); | 4034 | try writer.writeAll(" ("); |
| 3948 | try f.writeCValue(writer, rhs, .Other); | 4035 | try f.writeCValue(writer, rhs, .Other); |
| 4036 | try v.elem(f, writer); | ||
| 3949 | try writer.writeAll("*sizeof("); | 4037 | try writer.writeAll("*sizeof("); |
| 3950 | try f.renderType(writer, elem_ty); | 4038 | try f.renderType(writer, elem_ty); |
| 3951 | try writer.writeAll(")))"); | 4039 | try writer.writeAll(")))"); |
| 3952 | } else try f.writeCValue(writer, lhs, .Initializer); | 4040 | } else { |
| 4041 | try f.writeCValue(writer, lhs, .Other); | ||
| 4042 | try v.elem(f, writer); | ||
| 4043 | } | ||
| 3953 | 4044 | ||
| 3954 | try writer.writeAll(";\n"); | 4045 | try writer.writeAll(";\n"); |
| 4046 | try v.end(f, inst, writer); | ||
| 4047 | |||
| 3955 | return local; | 4048 | return local; |
| 3956 | } | 4049 | } |
| 3957 | 4050 | ||
| ... | @@ -3964,10 +4057,12 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons | ... | @@ -3964,10 +4057,12 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons |
| 3964 | } | 4057 | } |
| 3965 | 4058 | ||
| 3966 | const inst_ty = f.air.typeOfIndex(inst); | 4059 | const inst_ty = f.air.typeOfIndex(inst); |
| 4060 | const inst_scalar_ty = inst_ty.scalarType(); | ||
| 4061 | |||
| 3967 | const target = f.object.dg.module.getTarget(); | 4062 | const target = f.object.dg.module.getTarget(); |
| 3968 | if (inst_ty.isInt() and inst_ty.bitSize(target) > 64) | 4063 | if (inst_scalar_ty.isInt() and inst_scalar_ty.bitSize(target) > 64) |
| 3969 | return try airBinBuiltinCall(f, inst, operation[1..], .None); | 4064 | return try airBinBuiltinCall(f, inst, operation[1..], .none); |
| 3970 | if (inst_ty.isRuntimeFloat()) | 4065 | if (inst_scalar_ty.isRuntimeFloat()) |
| 3971 | return try airBinFloatOp(f, inst, operation); | 4066 | return try airBinFloatOp(f, inst, operation); |
| 3972 | 4067 | ||
| 3973 | const lhs = try f.resolveInst(bin_op.lhs); | 4068 | const lhs = try f.resolveInst(bin_op.lhs); |
| ... | @@ -3976,19 +4071,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons | ... | @@ -3976,19 +4071,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons |
| 3976 | 4071 | ||
| 3977 | const writer = f.object.writer(); | 4072 | const writer = f.object.writer(); |
| 3978 | const local = try f.allocLocal(inst, inst_ty); | 4073 | const local = try f.allocLocal(inst, inst_ty); |
| 4074 | const v = try Vectorizer.start(f, inst, writer, inst_ty); | ||
| 3979 | try f.writeCValue(writer, local, .Other); | 4075 | try f.writeCValue(writer, local, .Other); |
| 4076 | try v.elem(f, writer); | ||
| 3980 | // (lhs <> rhs) ? lhs : rhs | 4077 | // (lhs <> rhs) ? lhs : rhs |
| 3981 | try writer.writeAll(" = ("); | 4078 | try writer.writeAll(" = ("); |
| 3982 | try f.writeCValue(writer, lhs, .Other); | 4079 | try f.writeCValue(writer, lhs, .Other); |
| 4080 | try v.elem(f, writer); | ||
| 3983 | try writer.writeByte(' '); | 4081 | try writer.writeByte(' '); |
| 3984 | try writer.writeByte(operator); | 4082 | try writer.writeByte(operator); |
| 3985 | try writer.writeByte(' '); | 4083 | try writer.writeByte(' '); |
| 3986 | try f.writeCValue(writer, rhs, .Other); | 4084 | try f.writeCValue(writer, rhs, .Other); |
| 4085 | try v.elem(f, writer); | ||
| 3987 | try writer.writeAll(") ? "); | 4086 | try writer.writeAll(") ? "); |
| 3988 | try f.writeCValue(writer, lhs, .Other); | 4087 | try f.writeCValue(writer, lhs, .Other); |
| 4088 | try v.elem(f, writer); | ||
| 3989 | try writer.writeAll(" : "); | 4089 | try writer.writeAll(" : "); |
| 3990 | try f.writeCValue(writer, rhs, .Other); | 4090 | try f.writeCValue(writer, rhs, .Other); |
| 4091 | try v.elem(f, writer); | ||
| 3991 | try writer.writeAll(";\n"); | 4092 | try writer.writeAll(";\n"); |
| 4093 | try v.end(f, inst, writer); | ||
| 3992 | 4094 | ||
| 3993 | return local; | 4095 | return local; |
| 3994 | } | 4096 | } |
| ... | @@ -4412,12 +4514,56 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4412,12 +4514,56 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4412 | 4514 | ||
| 4413 | // Ensure padding bits have the expected value. | 4515 | // Ensure padding bits have the expected value. |
| 4414 | if (dest_ty.isAbiInt()) { | 4516 | if (dest_ty.isAbiInt()) { |
| 4517 | const dest_cty = try f.typeToCType(dest_ty, .complete); | ||
| 4518 | const dest_info = dest_ty.intInfo(target); | ||
| 4519 | var info_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) { | ||
| 4520 | .unsigned => .int_unsigned, | ||
| 4521 | .signed => .int_signed, | ||
| 4522 | } }, .data = dest_info.bits }; | ||
| 4523 | var wrap_cty: ?CType = null; | ||
| 4524 | var need_bitcasts = false; | ||
| 4525 | |||
| 4415 | try f.writeCValue(writer, local, .Other); | 4526 | try f.writeCValue(writer, local, .Other); |
| 4416 | try writer.writeAll(" = zig_wrap_"); | 4527 | if (dest_cty.castTag(.array)) |pl| { |
| 4417 | try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty); | 4528 | try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { |
| 4529 | .Little => pl.data.len - 1, | ||
| 4530 | .Big => 0, | ||
| 4531 | }}); | ||
| 4532 | const elem_cty = f.indexToCType(pl.data.elem_type); | ||
| 4533 | wrap_cty = elem_cty.toSignedness(dest_info.signedness); | ||
| 4534 | need_bitcasts = wrap_cty.?.tag() == .zig_i128; | ||
| 4535 | info_ty_pl.data -= 1; | ||
| 4536 | info_ty_pl.data %= @intCast(u16, f.byteSize(elem_cty) * 8); | ||
| 4537 | info_ty_pl.data += 1; | ||
| 4538 | } | ||
| 4539 | try writer.writeAll(" = "); | ||
| 4540 | if (need_bitcasts) { | ||
| 4541 | try writer.writeAll("zig_bitcast_"); | ||
| 4542 | try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?.toUnsigned()); | ||
| 4543 | try writer.writeByte('('); | ||
| 4544 | } | ||
| 4545 | try writer.writeAll("zig_wrap_"); | ||
| 4546 | const info_ty = Type.initPayload(&info_ty_pl.base); | ||
| 4547 | if (wrap_cty) |cty| | ||
| 4548 | try f.object.dg.renderCTypeForBuiltinFnName(writer, cty) | ||
| 4549 | else | ||
| 4550 | try f.object.dg.renderTypeForBuiltinFnName(writer, info_ty); | ||
| 4418 | try writer.writeByte('('); | 4551 | try writer.writeByte('('); |
| 4552 | if (need_bitcasts) { | ||
| 4553 | try writer.writeAll("zig_bitcast_"); | ||
| 4554 | try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?); | ||
| 4555 | try writer.writeByte('('); | ||
| 4556 | } | ||
| 4419 | try f.writeCValue(writer, local, .Other); | 4557 | try f.writeCValue(writer, local, .Other); |
| 4420 | try f.object.dg.renderBuiltinInfo(writer, dest_ty, .Bits); | 4558 | if (dest_cty.castTag(.array)) |pl| { |
| 4559 | try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { | ||
| 4560 | .Little => pl.data.len - 1, | ||
| 4561 | .Big => 0, | ||
| 4562 | }}); | ||
| 4563 | } | ||
| 4564 | if (need_bitcasts) try writer.writeByte(')'); | ||
| 4565 | try f.object.dg.renderBuiltinInfo(writer, info_ty, .bits); | ||
| 4566 | if (need_bitcasts) try writer.writeByte(')'); | ||
| 4421 | try writer.writeAll(");\n"); | 4567 | try writer.writeAll(");\n"); |
| 4422 | } | 4568 | } |
| 4423 | 4569 | ||
| ... | @@ -4428,6 +4574,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4428,6 +4574,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4428 | return local; | 4574 | return local; |
| 4429 | } | 4575 | } |
| 4430 | 4576 | ||
| 4577 | fn airTrap(writer: anytype) !CValue { | ||
| 4578 | try writer.writeAll("zig_trap();\n"); | ||
| 4579 | return .none; | ||
| 4580 | } | ||
| 4581 | |||
| 4431 | fn airBreakpoint(writer: anytype) !CValue { | 4582 | fn airBreakpoint(writer: anytype) !CValue { |
| 4432 | try writer.writeAll("zig_breakpoint();\n"); | 4583 | try writer.writeAll("zig_breakpoint();\n"); |
| 4433 | return .none; | 4584 | return .none; |
| ... | @@ -5427,7 +5578,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5427,7 +5578,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5427 | try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); | 5578 | try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); |
| 5428 | try writer.writeByte(')'); | 5579 | try writer.writeByte(')'); |
| 5429 | if (cant_cast) try writer.writeByte(')'); | 5580 | if (cant_cast) try writer.writeByte(')'); |
| 5430 | try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .Bits); | 5581 | try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits); |
| 5431 | try writer.writeAll(");\n"); | 5582 | try writer.writeAll(");\n"); |
| 5432 | if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; | 5583 | if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; |
| 5433 | 5584 | ||
| ... | @@ -5860,7 +6011,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5860,7 +6011,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5860 | try f.writeCValue(writer, operand, .FunctionArgument); | 6011 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 5861 | try writer.writeByte(')'); | 6012 | try writer.writeByte(')'); |
| 5862 | if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) { | 6013 | if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) { |
| 5863 | try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits); | 6014 | try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); |
| 5864 | try writer.writeByte(')'); | 6015 | try writer.writeByte(')'); |
| 5865 | } | 6016 | } |
| 5866 | try writer.writeAll(";\n"); | 6017 | try writer.writeAll(";\n"); |
| ... | @@ -5906,19 +6057,35 @@ fn airUnBuiltinCall( | ... | @@ -5906,19 +6057,35 @@ fn airUnBuiltinCall( |
| 5906 | const operand = try f.resolveInst(ty_op.operand); | 6057 | const operand = try f.resolveInst(ty_op.operand); |
| 5907 | try reap(f, inst, &.{ty_op.operand}); | 6058 | try reap(f, inst, &.{ty_op.operand}); |
| 5908 | const inst_ty = f.air.typeOfIndex(inst); | 6059 | const inst_ty = f.air.typeOfIndex(inst); |
| 6060 | const inst_scalar_ty = inst_ty.scalarType(); | ||
| 5909 | const operand_ty = f.air.typeOf(ty_op.operand); | 6061 | const operand_ty = f.air.typeOf(ty_op.operand); |
| 6062 | const scalar_ty = operand_ty.scalarType(); | ||
| 6063 | |||
| 6064 | const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); | ||
| 6065 | const ref_ret = inst_scalar_cty.tag() == .array; | ||
| 5910 | 6066 | ||
| 5911 | const writer = f.object.writer(); | 6067 | const writer = f.object.writer(); |
| 5912 | const local = try f.allocLocal(inst, inst_ty); | 6068 | const local = try f.allocLocal(inst, inst_ty); |
| 5913 | try f.writeCValue(writer, local, .Other); | 6069 | const v = try Vectorizer.start(f, inst, writer, operand_ty); |
| 5914 | try writer.writeAll(" = zig_"); | 6070 | if (!ref_ret) { |
| 5915 | try writer.writeAll(operation); | 6071 | try f.writeCValue(writer, local, .Other); |
| 5916 | try writer.writeByte('_'); | 6072 | try v.elem(f, writer); |
| 5917 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 6073 | try writer.writeAll(" = "); |
| 6074 | } | ||
| 6075 | try writer.print("zig_{s}_", .{operation}); | ||
| 6076 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); | ||
| 5918 | try writer.writeByte('('); | 6077 | try writer.writeByte('('); |
| 6078 | if (ref_ret) { | ||
| 6079 | try f.writeCValue(writer, local, .FunctionArgument); | ||
| 6080 | try v.elem(f, writer); | ||
| 6081 | try writer.writeAll(", "); | ||
| 6082 | } | ||
| 5919 | try f.writeCValue(writer, operand, .FunctionArgument); | 6083 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 5920 | try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); | 6084 | try v.elem(f, writer); |
| 6085 | try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); | ||
| 5921 | try writer.writeAll(");\n"); | 6086 | try writer.writeAll(");\n"); |
| 6087 | try v.end(f, inst, writer); | ||
| 6088 | |||
| 5922 | return local; | 6089 | return local; |
| 5923 | } | 6090 | } |
| 5924 | 6091 | ||
| ... | @@ -5935,55 +6102,108 @@ fn airBinBuiltinCall( | ... | @@ -5935,55 +6102,108 @@ fn airBinBuiltinCall( |
| 5935 | return .none; | 6102 | return .none; |
| 5936 | } | 6103 | } |
| 5937 | 6104 | ||
| 6105 | const operand_ty = f.air.typeOf(bin_op.lhs); | ||
| 6106 | const operand_cty = try f.typeToCType(operand_ty, .complete); | ||
| 6107 | const is_big = operand_cty.tag() == .array; | ||
| 6108 | |||
| 5938 | const lhs = try f.resolveInst(bin_op.lhs); | 6109 | const lhs = try f.resolveInst(bin_op.lhs); |
| 5939 | const rhs = try f.resolveInst(bin_op.rhs); | 6110 | const rhs = try f.resolveInst(bin_op.rhs); |
| 5940 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 6111 | if (!is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 5941 | 6112 | ||
| 5942 | const inst_ty = f.air.typeOfIndex(inst); | 6113 | const inst_ty = f.air.typeOfIndex(inst); |
| 5943 | const operand_ty = f.air.typeOf(bin_op.lhs); | 6114 | const inst_scalar_ty = inst_ty.scalarType(); |
| 6115 | const scalar_ty = operand_ty.scalarType(); | ||
| 6116 | |||
| 6117 | const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); | ||
| 6118 | const ref_ret = inst_scalar_cty.tag() == .array; | ||
| 5944 | 6119 | ||
| 5945 | const writer = f.object.writer(); | 6120 | const writer = f.object.writer(); |
| 5946 | const local = try f.allocLocal(inst, inst_ty); | 6121 | const local = try f.allocLocal(inst, inst_ty); |
| 5947 | try f.writeCValue(writer, local, .Other); | 6122 | if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 5948 | try writer.writeAll(" = zig_"); | 6123 | const v = try Vectorizer.start(f, inst, writer, operand_ty); |
| 5949 | try writer.writeAll(operation); | 6124 | if (!ref_ret) { |
| 5950 | try writer.writeByte('_'); | 6125 | try f.writeCValue(writer, local, .Other); |
| 5951 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 6126 | try v.elem(f, writer); |
| 6127 | try writer.writeAll(" = "); | ||
| 6128 | } | ||
| 6129 | try writer.print("zig_{s}_", .{operation}); | ||
| 6130 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); | ||
| 5952 | try writer.writeByte('('); | 6131 | try writer.writeByte('('); |
| 6132 | if (ref_ret) { | ||
| 6133 | try f.writeCValue(writer, local, .FunctionArgument); | ||
| 6134 | try v.elem(f, writer); | ||
| 6135 | try writer.writeAll(", "); | ||
| 6136 | } | ||
| 5953 | try f.writeCValue(writer, lhs, .FunctionArgument); | 6137 | try f.writeCValue(writer, lhs, .FunctionArgument); |
| 6138 | try v.elem(f, writer); | ||
| 5954 | try writer.writeAll(", "); | 6139 | try writer.writeAll(", "); |
| 5955 | try f.writeCValue(writer, rhs, .FunctionArgument); | 6140 | try f.writeCValue(writer, rhs, .FunctionArgument); |
| 5956 | try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); | 6141 | try v.elem(f, writer); |
| 6142 | try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); | ||
| 5957 | try writer.writeAll(");\n"); | 6143 | try writer.writeAll(");\n"); |
| 6144 | try v.end(f, inst, writer); | ||
| 6145 | |||
| 5958 | return local; | 6146 | return local; |
| 5959 | } | 6147 | } |
| 5960 | 6148 | ||
| 5961 | fn cmpBuiltinCall( | 6149 | fn airCmpBuiltinCall( |
| 5962 | f: *Function, | 6150 | f: *Function, |
| 5963 | inst: Air.Inst.Index, | 6151 | inst: Air.Inst.Index, |
| 5964 | operator: []const u8, | 6152 | data: anytype, |
| 5965 | operation: []const u8, | 6153 | operator: std.math.CompareOperator, |
| 6154 | operation: enum { cmp, operator }, | ||
| 6155 | info: BuiltinInfo, | ||
| 5966 | ) !CValue { | 6156 | ) !CValue { |
| 6157 | if (f.liveness.isUnused(inst)) { | ||
| 6158 | try reap(f, inst, &.{ data.lhs, data.rhs }); | ||
| 6159 | return .none; | ||
| 6160 | } | ||
| 6161 | |||
| 6162 | const lhs = try f.resolveInst(data.lhs); | ||
| 6163 | const rhs = try f.resolveInst(data.rhs); | ||
| 6164 | try reap(f, inst, &.{ data.lhs, data.rhs }); | ||
| 6165 | |||
| 5967 | const inst_ty = f.air.typeOfIndex(inst); | 6166 | const inst_ty = f.air.typeOfIndex(inst); |
| 5968 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 6167 | const inst_scalar_ty = inst_ty.scalarType(); |
| 5969 | const operand_ty = f.air.typeOf(bin_op.lhs); | 6168 | const operand_ty = f.air.typeOf(data.lhs); |
| 6169 | const scalar_ty = operand_ty.scalarType(); | ||
| 5970 | 6170 | ||
| 5971 | const lhs = try f.resolveInst(bin_op.lhs); | 6171 | const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); |
| 5972 | const rhs = try f.resolveInst(bin_op.rhs); | 6172 | const ref_ret = inst_scalar_cty.tag() == .array; |
| 5973 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | ||
| 5974 | 6173 | ||
| 5975 | const writer = f.object.writer(); | 6174 | const writer = f.object.writer(); |
| 5976 | const local = try f.allocLocal(inst, inst_ty); | 6175 | const local = try f.allocLocal(inst, inst_ty); |
| 5977 | try f.writeCValue(writer, local, .Other); | 6176 | const v = try Vectorizer.start(f, inst, writer, operand_ty); |
| 5978 | try writer.writeAll(" = zig_"); | 6177 | if (!ref_ret) { |
| 5979 | try writer.writeAll(operation); | 6178 | try f.writeCValue(writer, local, .Other); |
| 5980 | try writer.writeByte('_'); | 6179 | try v.elem(f, writer); |
| 5981 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 6180 | try writer.writeAll(" = "); |
| 6181 | } | ||
| 6182 | try writer.print("zig_{s}_", .{switch (operation) { | ||
| 6183 | else => @tagName(operation), | ||
| 6184 | .operator => compareOperatorAbbrev(operator), | ||
| 6185 | }}); | ||
| 6186 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); | ||
| 5982 | try writer.writeByte('('); | 6187 | try writer.writeByte('('); |
| 6188 | if (ref_ret) { | ||
| 6189 | try f.writeCValue(writer, local, .FunctionArgument); | ||
| 6190 | try v.elem(f, writer); | ||
| 6191 | try writer.writeAll(", "); | ||
| 6192 | } | ||
| 5983 | try f.writeCValue(writer, lhs, .FunctionArgument); | 6193 | try f.writeCValue(writer, lhs, .FunctionArgument); |
| 6194 | try v.elem(f, writer); | ||
| 5984 | try writer.writeAll(", "); | 6195 | try writer.writeAll(", "); |
| 5985 | try f.writeCValue(writer, rhs, .FunctionArgument); | 6196 | try f.writeCValue(writer, rhs, .FunctionArgument); |
| 5986 | try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) }); | 6197 | try v.elem(f, writer); |
| 6198 | try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); | ||
| 6199 | try writer.writeByte(')'); | ||
| 6200 | if (!ref_ret) try writer.print(" {s} {}", .{ | ||
| 6201 | compareOperatorC(operator), | ||
| 6202 | try f.fmtIntLiteral(Type.initTag(.i32), Value.zero), | ||
| 6203 | }); | ||
| 6204 | try writer.writeAll(";\n"); | ||
| 6205 | try v.end(f, inst, writer); | ||
| 6206 | |||
| 5987 | return local; | 6207 | return local; |
| 5988 | } | 6208 | } |
| 5989 | 6209 | ||
| ... | @@ -6328,33 +6548,120 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6328,33 +6548,120 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6328 | 6548 | ||
| 6329 | fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { | 6549 | fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6330 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 6550 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 6551 | |||
| 6331 | if (f.liveness.isUnused(inst)) { | 6552 | if (f.liveness.isUnused(inst)) { |
| 6332 | try reap(f, inst, &.{ty_op.operand}); | 6553 | try reap(f, inst, &.{ty_op.operand}); |
| 6333 | return .none; | 6554 | return .none; |
| 6334 | } | 6555 | } |
| 6335 | 6556 | ||
| 6336 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6337 | const operand = try f.resolveInst(ty_op.operand); | 6557 | const operand = try f.resolveInst(ty_op.operand); |
| 6338 | try reap(f, inst, &.{ty_op.operand}); | 6558 | try reap(f, inst, &.{ty_op.operand}); |
| 6559 | |||
| 6560 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6561 | const inst_scalar_ty = inst_ty.scalarType(); | ||
| 6562 | const inst_scalar_cty = try f.typeToIndex(inst_scalar_ty, .complete); | ||
| 6563 | const need_memcpy = f.indexToCType(inst_scalar_cty).tag() == .array; | ||
| 6564 | |||
| 6339 | const writer = f.object.writer(); | 6565 | const writer = f.object.writer(); |
| 6340 | const local = try f.allocLocal(inst, inst_ty); | 6566 | const local = try f.allocLocal(inst, inst_ty); |
| 6567 | const v = try Vectorizer.start(f, inst, writer, inst_ty); | ||
| 6568 | if (need_memcpy) try writer.writeAll("memcpy(&"); | ||
| 6341 | try f.writeCValue(writer, local, .Other); | 6569 | try f.writeCValue(writer, local, .Other); |
| 6342 | try writer.writeAll(" = "); | 6570 | try v.elem(f, writer); |
| 6571 | try writer.writeAll(if (need_memcpy) ", &" else " = "); | ||
| 6572 | try f.writeCValue(writer, operand, .Other); | ||
| 6573 | if (need_memcpy) { | ||
| 6574 | try writer.writeAll(", sizeof("); | ||
| 6575 | try f.renderCType(writer, inst_scalar_cty); | ||
| 6576 | try writer.writeAll("))"); | ||
| 6577 | } | ||
| 6578 | try writer.writeAll(";\n"); | ||
| 6579 | try v.end(f, inst, writer); | ||
| 6343 | 6580 | ||
| 6344 | _ = operand; | 6581 | return local; |
| 6345 | return f.fail("TODO: C backend: implement airSplat", .{}); | ||
| 6346 | } | 6582 | } |
| 6347 | 6583 | ||
| 6348 | fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { | 6584 | fn 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); | ||
| 6350 | 6616 | ||
| 6351 | return f.fail("TODO: C backend: implement airSelect", .{}); | 6617 | return local; |
| 6352 | } | 6618 | } |
| 6353 | 6619 | ||
| 6354 | fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { | 6620 | fn 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; | ||
| 6356 | 6623 | ||
| 6357 | return f.fail("TODO: C backend: implement airShuffle", .{}); | 6624 | if (f.liveness.isUnused(inst)) { |
| 6625 | try reap(f, inst, &.{ extra.a, extra.b }); | ||
| 6626 | return .none; | ||
| 6627 | } | ||
| 6628 | |||
| 6629 | const mask = f.air.values[extra.mask]; | ||
| 6630 | const lhs = try f.resolveInst(extra.a); | ||
| 6631 | const rhs = try f.resolveInst(extra.b); | ||
| 6632 | |||
| 6633 | const module = f.object.dg.module; | ||
| 6634 | const target = module.getTarget(); | ||
| 6635 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6636 | |||
| 6637 | const writer = f.object.writer(); | ||
| 6638 | const local = try f.allocLocal(inst, inst_ty); | ||
| 6639 | try reap(f, inst, &.{ extra.a, extra.b }); // local cannot alias operands | ||
| 6640 | for (0..extra.mask_len) |index| { | ||
| 6641 | var dst_pl = Value.Payload.U64{ | ||
| 6642 | .base = .{ .tag = .int_u64 }, | ||
| 6643 | .data = @intCast(u64, index), | ||
| 6644 | }; | ||
| 6645 | |||
| 6646 | try f.writeCValue(writer, local, .Other); | ||
| 6647 | try writer.writeByte('['); | ||
| 6648 | try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&dst_pl.base), .Other); | ||
| 6649 | try writer.writeAll("] = "); | ||
| 6650 | |||
| 6651 | var buf: Value.ElemValueBuffer = undefined; | ||
| 6652 | const mask_elem = mask.elemValueBuffer(module, index, &buf).toSignedInt(target); | ||
| 6653 | var src_pl = Value.Payload.U64{ | ||
| 6654 | .base = .{ .tag = .int_u64 }, | ||
| 6655 | .data = @intCast(u64, mask_elem ^ mask_elem >> 63), | ||
| 6656 | }; | ||
| 6657 | |||
| 6658 | try f.writeCValue(writer, if (mask_elem >= 0) lhs else rhs, .Other); | ||
| 6659 | try writer.writeByte('['); | ||
| 6660 | try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&src_pl.base), .Other); | ||
| 6661 | try writer.writeAll("];\n"); | ||
| 6662 | } | ||
| 6663 | |||
| 6664 | return local; | ||
| 6358 | } | 6665 | } |
| 6359 | 6666 | ||
| 6360 | fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | 6667 | fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -6370,65 +6677,45 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6370,65 +6677,45 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6370 | const operand = try f.resolveInst(reduce.operand); | 6677 | const operand = try f.resolveInst(reduce.operand); |
| 6371 | try reap(f, inst, &.{reduce.operand}); | 6678 | try reap(f, inst, &.{reduce.operand}); |
| 6372 | const operand_ty = f.air.typeOf(reduce.operand); | 6679 | const operand_ty = f.air.typeOf(reduce.operand); |
| 6373 | const vector_len = operand_ty.vectorLen(); | ||
| 6374 | const writer = f.object.writer(); | 6680 | const writer = f.object.writer(); |
| 6375 | 6681 | ||
| 6376 | const Op = union(enum) { | 6682 | const use_operator = scalar_ty.bitSize(target) <= 64; |
| 6377 | call_fn: []const u8, | 6683 | const op: union(enum) { |
| 6684 | const Func = struct { operation: []const u8, info: BuiltinInfo = .none }; | ||
| 6685 | float_op: Func, | ||
| 6686 | builtin: Func, | ||
| 6378 | infix: []const u8, | 6687 | infix: []const u8, |
| 6379 | ternary: []const u8, | 6688 | ternary: []const u8, |
| 6380 | }; | 6689 | } = switch (reduce.operation) { |
| 6381 | var fn_name_buf: [64]u8 = undefined; | 6690 | .And => if (use_operator) .{ .infix = " &= " } else .{ .builtin = .{ .operation = "and" } }, |
| 6382 | const op: Op = switch (reduce.operation) { | 6691 | .Or => if (use_operator) .{ .infix = " |= " } else .{ .builtin = .{ .operation = "or" } }, |
| 6383 | .And => .{ .infix = " &= " }, | 6692 | .Xor => if (use_operator) .{ .infix = " ^= " } else .{ .builtin = .{ .operation = "xor" } }, |
| 6384 | .Or => .{ .infix = " |= " }, | ||
| 6385 | .Xor => .{ .infix = " ^= " }, | ||
| 6386 | .Min => switch (scalar_ty.zigTypeTag()) { | 6693 | .Min => switch (scalar_ty.zigTypeTag()) { |
| 6387 | .Int => Op{ .ternary = " < " }, | 6694 | .Int => if (use_operator) .{ .ternary = " < " } else .{ |
| 6388 | .Float => op: { | 6695 | .builtin = .{ .operation = "min" }, |
| 6389 | const float_bits = scalar_ty.floatBits(target); | ||
| 6390 | break :op Op{ | ||
| 6391 | .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "{s}fmin{s}", .{ | ||
| 6392 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), | ||
| 6393 | }) catch unreachable, | ||
| 6394 | }; | ||
| 6395 | }, | 6696 | }, |
| 6697 | .Float => .{ .float_op = .{ .operation = "fmin" } }, | ||
| 6396 | else => unreachable, | 6698 | else => unreachable, |
| 6397 | }, | 6699 | }, |
| 6398 | .Max => switch (scalar_ty.zigTypeTag()) { | 6700 | .Max => switch (scalar_ty.zigTypeTag()) { |
| 6399 | .Int => Op{ .ternary = " > " }, | 6701 | .Int => if (use_operator) .{ .ternary = " > " } else .{ |
| 6400 | .Float => op: { | 6702 | .builtin = .{ .operation = "max" }, |
| 6401 | const float_bits = scalar_ty.floatBits(target); | ||
| 6402 | break :op Op{ | ||
| 6403 | .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "{s}fmax{s}", .{ | ||
| 6404 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), | ||
| 6405 | }) catch unreachable, | ||
| 6406 | }; | ||
| 6407 | }, | 6703 | }, |
| 6704 | .Float => .{ .float_op = .{ .operation = "fmax" } }, | ||
| 6408 | else => unreachable, | 6705 | else => unreachable, |
| 6409 | }, | 6706 | }, |
| 6410 | .Add => switch (scalar_ty.zigTypeTag()) { | 6707 | .Add => switch (scalar_ty.zigTypeTag()) { |
| 6411 | .Int => Op{ .infix = " += " }, | 6708 | .Int => if (use_operator) .{ .infix = " += " } else .{ |
| 6412 | .Float => op: { | 6709 | .builtin = .{ .operation = "addw", .info = .bits }, |
| 6413 | const float_bits = scalar_ty.floatBits(target); | ||
| 6414 | break :op Op{ | ||
| 6415 | .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "__add{s}f3", .{ | ||
| 6416 | compilerRtFloatAbbrev(float_bits), | ||
| 6417 | }) catch unreachable, | ||
| 6418 | }; | ||
| 6419 | }, | 6710 | }, |
| 6711 | .Float => .{ .builtin = .{ .operation = "add" } }, | ||
| 6420 | else => unreachable, | 6712 | else => unreachable, |
| 6421 | }, | 6713 | }, |
| 6422 | .Mul => switch (scalar_ty.zigTypeTag()) { | 6714 | .Mul => switch (scalar_ty.zigTypeTag()) { |
| 6423 | .Int => Op{ .infix = " *= " }, | 6715 | .Int => if (use_operator) .{ .infix = " *= " } else .{ |
| 6424 | .Float => op: { | 6716 | .builtin = .{ .operation = "mulw", .info = .bits }, |
| 6425 | const float_bits = scalar_ty.floatBits(target); | ||
| 6426 | break :op Op{ | ||
| 6427 | .call_fn = std.fmt.bufPrintZ(&fn_name_buf, "__mul{s}f3", .{ | ||
| 6428 | compilerRtFloatAbbrev(float_bits), | ||
| 6429 | }) catch unreachable, | ||
| 6430 | }; | ||
| 6431 | }, | 6717 | }, |
| 6718 | .Float => .{ .builtin = .{ .operation = "mul" } }, | ||
| 6432 | else => unreachable, | 6719 | else => unreachable, |
| 6433 | }, | 6720 | }, |
| 6434 | }; | 6721 | }; |
| ... | @@ -6444,75 +6731,96 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6444,75 +6731,96 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6444 | // } | 6731 | // } |
| 6445 | // break :reduce accum; | 6732 | // break :reduce accum; |
| 6446 | // } | 6733 | // } |
| 6447 | const it = try f.allocLocal(inst, Type.usize); | ||
| 6448 | try f.writeCValue(writer, it, .Other); | ||
| 6449 | try writer.writeAll(" = 0;\n"); | ||
| 6450 | 6734 | ||
| 6451 | const accum = try f.allocLocal(inst, scalar_ty); | 6735 | const accum = try f.allocLocal(inst, scalar_ty); |
| 6452 | try f.writeCValue(writer, accum, .Other); | 6736 | try f.writeCValue(writer, accum, .Other); |
| 6453 | try writer.writeAll(" = "); | 6737 | try writer.writeAll(" = "); |
| 6454 | 6738 | ||
| 6455 | const init_val = switch (reduce.operation) { | 6739 | var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); |
| 6456 | .And, .Or, .Xor, .Add => "0", | 6740 | defer arena.deinit(); |
| 6741 | |||
| 6742 | const ExpectedContents = union { | ||
| 6743 | u: Value.Payload.U64, | ||
| 6744 | i: Value.Payload.I64, | ||
| 6745 | f16: Value.Payload.Float_16, | ||
| 6746 | f32: Value.Payload.Float_32, | ||
| 6747 | f64: Value.Payload.Float_64, | ||
| 6748 | f80: Value.Payload.Float_80, | ||
| 6749 | f128: Value.Payload.Float_128, | ||
| 6750 | }; | ||
| 6751 | var stack align(@alignOf(ExpectedContents)) = | ||
| 6752 | std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); | ||
| 6753 | |||
| 6754 | try f.object.dg.renderValue(writer, scalar_ty, switch (reduce.operation) { | ||
| 6755 | .Or, .Xor, .Add => Value.zero, | ||
| 6756 | .And => switch (scalar_ty.zigTypeTag()) { | ||
| 6757 | .Bool => Value.one, | ||
| 6758 | else => switch (scalar_ty.intInfo(target).signedness) { | ||
| 6759 | .unsigned => try scalar_ty.maxInt(stack.get(), target), | ||
| 6760 | .signed => Value.negative_one, | ||
| 6761 | }, | ||
| 6762 | }, | ||
| 6457 | .Min => switch (scalar_ty.zigTypeTag()) { | 6763 | .Min => switch (scalar_ty.zigTypeTag()) { |
| 6458 | .Int => "TODO_intmax", | 6764 | .Bool => Value.one, |
| 6459 | .Float => "TODO_nan", | 6765 | .Int => try scalar_ty.maxInt(stack.get(), target), |
| 6766 | .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target), | ||
| 6460 | else => unreachable, | 6767 | else => unreachable, |
| 6461 | }, | 6768 | }, |
| 6462 | .Max => switch (scalar_ty.zigTypeTag()) { | 6769 | .Max => switch (scalar_ty.zigTypeTag()) { |
| 6463 | .Int => "TODO_intmin", | 6770 | .Bool => Value.zero, |
| 6464 | .Float => "TODO_nan", | 6771 | .Int => try scalar_ty.minInt(stack.get(), target), |
| 6772 | .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target), | ||
| 6465 | else => unreachable, | 6773 | else => unreachable, |
| 6466 | }, | 6774 | }, |
| 6467 | .Mul => "1", | 6775 | .Mul => Value.one, |
| 6468 | }; | 6776 | }, .Initializer); |
| 6469 | try writer.writeAll(init_val); | 6777 | try writer.writeAll(";\n"); |
| 6470 | try writer.writeAll(";"); | ||
| 6471 | try f.object.indent_writer.insertNewline(); | ||
| 6472 | try writer.writeAll("for (;"); | ||
| 6473 | try f.writeCValue(writer, it, .Other); | ||
| 6474 | try writer.print("<{d};++", .{vector_len}); | ||
| 6475 | try f.writeCValue(writer, it, .Other); | ||
| 6476 | try writer.writeAll(") "); | ||
| 6477 | try f.writeCValue(writer, accum, .Other); | ||
| 6478 | 6778 | ||
| 6779 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | ||
| 6780 | try f.writeCValue(writer, accum, .Other); | ||
| 6479 | switch (op) { | 6781 | switch (op) { |
| 6480 | .call_fn => |fn_name| { | 6782 | .float_op => |func| { |
| 6481 | try writer.print(" = {s}(", .{fn_name}); | 6783 | try writer.writeAll(" = zig_libc_name_"); |
| 6784 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); | ||
| 6785 | try writer.print("({s})(", .{func.operation}); | ||
| 6786 | try f.writeCValue(writer, accum, .FunctionArgument); | ||
| 6787 | try writer.writeAll(", "); | ||
| 6788 | try f.writeCValue(writer, operand, .Other); | ||
| 6789 | try v.elem(f, writer); | ||
| 6790 | try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info); | ||
| 6791 | try writer.writeByte(')'); | ||
| 6792 | }, | ||
| 6793 | .builtin => |func| { | ||
| 6794 | try writer.print(" = zig_{s}_", .{func.operation}); | ||
| 6795 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); | ||
| 6796 | try writer.writeByte('('); | ||
| 6482 | try f.writeCValue(writer, accum, .FunctionArgument); | 6797 | try f.writeCValue(writer, accum, .FunctionArgument); |
| 6483 | try writer.writeAll(", "); | 6798 | try writer.writeAll(", "); |
| 6484 | try f.writeCValue(writer, operand, .Other); | 6799 | try f.writeCValue(writer, operand, .Other); |
| 6485 | try writer.writeAll("["); | 6800 | try v.elem(f, writer); |
| 6486 | try f.writeCValue(writer, it, .Other); | 6801 | try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info); |
| 6487 | try writer.writeAll("])"); | 6802 | try writer.writeByte(')'); |
| 6488 | }, | 6803 | }, |
| 6489 | .infix => |ass| { | 6804 | .infix => |ass| { |
| 6490 | try writer.writeAll(ass); | 6805 | try writer.writeAll(ass); |
| 6491 | try f.writeCValue(writer, operand, .Other); | 6806 | try f.writeCValue(writer, operand, .Other); |
| 6492 | try writer.writeAll("["); | 6807 | try v.elem(f, writer); |
| 6493 | try f.writeCValue(writer, it, .Other); | ||
| 6494 | try writer.writeAll("]"); | ||
| 6495 | }, | 6808 | }, |
| 6496 | .ternary => |cmp| { | 6809 | .ternary => |cmp| { |
| 6497 | try writer.writeAll(" = "); | 6810 | try writer.writeAll(" = "); |
| 6498 | try f.writeCValue(writer, accum, .Other); | 6811 | try f.writeCValue(writer, accum, .Other); |
| 6499 | try writer.writeAll(cmp); | 6812 | try writer.writeAll(cmp); |
| 6500 | try f.writeCValue(writer, operand, .Other); | 6813 | try f.writeCValue(writer, operand, .Other); |
| 6501 | try writer.writeAll("["); | 6814 | try v.elem(f, writer); |
| 6502 | try f.writeCValue(writer, it, .Other); | 6815 | try writer.writeAll(" ? "); |
| 6503 | try writer.writeAll("] ? "); | ||
| 6504 | try f.writeCValue(writer, accum, .Other); | 6816 | try f.writeCValue(writer, accum, .Other); |
| 6505 | try writer.writeAll(" : "); | 6817 | try writer.writeAll(" : "); |
| 6506 | try f.writeCValue(writer, operand, .Other); | 6818 | try f.writeCValue(writer, operand, .Other); |
| 6507 | try writer.writeAll("["); | 6819 | try v.elem(f, writer); |
| 6508 | try f.writeCValue(writer, it, .Other); | ||
| 6509 | try writer.writeAll("]"); | ||
| 6510 | }, | 6820 | }, |
| 6511 | } | 6821 | } |
| 6512 | |||
| 6513 | try writer.writeAll(";\n"); | 6822 | try writer.writeAll(";\n"); |
| 6514 | 6823 | try v.end(f, inst, writer); | |
| 6515 | try freeLocal(f, inst, it.new_local, 0); | ||
| 6516 | 6824 | ||
| 6517 | return accum; | 6825 | return accum; |
| 6518 | } | 6826 | } |
| ... | @@ -6646,7 +6954,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6646,7 +6954,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6646 | try writer.writeByte('('); | 6954 | try writer.writeByte('('); |
| 6647 | 6955 | ||
| 6648 | if (inst_ty.isAbiInt() and (field_ty.isAbiInt() or field_ty.isPtrAtRuntime())) { | 6956 | if (inst_ty.isAbiInt() and (field_ty.isAbiInt() or field_ty.isPtrAtRuntime())) { |
| 6649 | try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument); | 6957 | try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument); |
| 6650 | } else { | 6958 | } else { |
| 6651 | try writer.writeByte('('); | 6959 | try writer.writeByte('('); |
| 6652 | try f.renderType(writer, inst_ty); | 6960 | try f.renderType(writer, inst_ty); |
| ... | @@ -6664,7 +6972,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6664,7 +6972,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6664 | 6972 | ||
| 6665 | try writer.writeAll(", "); | 6973 | try writer.writeAll(", "); |
| 6666 | try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); | 6974 | try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); |
| 6667 | try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits); | 6975 | try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); |
| 6668 | try writer.writeByte(')'); | 6976 | try writer.writeByte(')'); |
| 6669 | if (!empty) try writer.writeByte(')'); | 6977 | if (!empty) try writer.writeByte(')'); |
| 6670 | 6978 | ||
| ... | @@ -6788,7 +7096,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6788,7 +7096,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6788 | } | 7096 | } |
| 6789 | 7097 | ||
| 6790 | fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { | 7098 | fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6791 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6792 | const un_op = f.air.instructions.items(.data)[inst].un_op; | 7099 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 6793 | if (f.liveness.isUnused(inst)) { | 7100 | if (f.liveness.isUnused(inst)) { |
| 6794 | try reap(f, inst, &.{un_op}); | 7101 | try reap(f, inst, &.{un_op}); |
| ... | @@ -6797,16 +7104,23 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6797,16 +7104,23 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6797 | 7104 | ||
| 6798 | const operand = try f.resolveInst(un_op); | 7105 | const operand = try f.resolveInst(un_op); |
| 6799 | try reap(f, inst, &.{un_op}); | 7106 | try reap(f, inst, &.{un_op}); |
| 7107 | |||
| 6800 | const operand_ty = f.air.typeOf(un_op); | 7108 | const operand_ty = f.air.typeOf(un_op); |
| 7109 | const scalar_ty = operand_ty.scalarType(); | ||
| 6801 | 7110 | ||
| 6802 | const writer = f.object.writer(); | 7111 | const writer = f.object.writer(); |
| 6803 | const local = try f.allocLocal(inst, inst_ty); | 7112 | const local = try f.allocLocal(inst, operand_ty); |
| 7113 | const v = try Vectorizer.start(f, inst, writer, operand_ty); | ||
| 6804 | try f.writeCValue(writer, local, .Other); | 7114 | try f.writeCValue(writer, local, .Other); |
| 7115 | try v.elem(f, writer); | ||
| 6805 | try writer.writeAll(" = zig_neg_"); | 7116 | try writer.writeAll(" = zig_neg_"); |
| 6806 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 7117 | try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); |
| 6807 | try writer.writeByte('('); | 7118 | try writer.writeByte('('); |
| 6808 | try f.writeCValue(writer, operand, .FunctionArgument); | 7119 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 7120 | try v.elem(f, writer); | ||
| 6809 | try writer.writeAll(");\n"); | 7121 | try writer.writeAll(");\n"); |
| 7122 | try v.end(f, inst, writer); | ||
| 7123 | |||
| 6810 | return local; | 7124 | return local; |
| 6811 | } | 7125 | } |
| 6812 | 7126 | ||
| ... | @@ -6816,19 +7130,28 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal | ... | @@ -6816,19 +7130,28 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal |
| 6816 | try reap(f, inst, &.{un_op}); | 7130 | try reap(f, inst, &.{un_op}); |
| 6817 | return .none; | 7131 | return .none; |
| 6818 | } | 7132 | } |
| 7133 | |||
| 6819 | const operand = try f.resolveInst(un_op); | 7134 | const operand = try f.resolveInst(un_op); |
| 6820 | try reap(f, inst, &.{un_op}); | 7135 | try reap(f, inst, &.{un_op}); |
| 6821 | const writer = f.object.writer(); | 7136 | |
| 6822 | const inst_ty = f.air.typeOfIndex(inst); | 7137 | const inst_ty = f.air.typeOfIndex(inst); |
| 7138 | const inst_scalar_ty = inst_ty.scalarType(); | ||
| 7139 | |||
| 7140 | const writer = f.object.writer(); | ||
| 6823 | const local = try f.allocLocal(inst, inst_ty); | 7141 | const local = try f.allocLocal(inst, inst_ty); |
| 7142 | const v = try Vectorizer.start(f, inst, writer, inst_ty); | ||
| 6824 | try f.writeCValue(writer, local, .Other); | 7143 | try f.writeCValue(writer, local, .Other); |
| 7144 | try v.elem(f, writer); | ||
| 6825 | try writer.writeAll(" = zig_libc_name_"); | 7145 | try writer.writeAll(" = zig_libc_name_"); |
| 6826 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); | 7146 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); |
| 6827 | try writer.writeByte('('); | 7147 | try writer.writeByte('('); |
| 6828 | try writer.writeAll(operation); | 7148 | try writer.writeAll(operation); |
| 6829 | try writer.writeAll(")("); | 7149 | try writer.writeAll(")("); |
| 6830 | try f.writeCValue(writer, operand, .FunctionArgument); | 7150 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 7151 | try v.elem(f, writer); | ||
| 6831 | try writer.writeAll(");\n"); | 7152 | try writer.writeAll(");\n"); |
| 7153 | try v.end(f, inst, writer); | ||
| 7154 | |||
| 6832 | return local; | 7155 | return local; |
| 6833 | } | 7156 | } |
| 6834 | 7157 | ||
| ... | @@ -6838,23 +7161,32 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa | ... | @@ -6838,23 +7161,32 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa |
| 6838 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 7161 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 6839 | return .none; | 7162 | return .none; |
| 6840 | } | 7163 | } |
| 7164 | |||
| 6841 | const lhs = try f.resolveInst(bin_op.lhs); | 7165 | const lhs = try f.resolveInst(bin_op.lhs); |
| 6842 | const rhs = try f.resolveInst(bin_op.rhs); | 7166 | const rhs = try f.resolveInst(bin_op.rhs); |
| 6843 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 7167 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 6844 | 7168 | ||
| 6845 | const writer = f.object.writer(); | ||
| 6846 | const inst_ty = f.air.typeOfIndex(inst); | 7169 | const inst_ty = f.air.typeOfIndex(inst); |
| 7170 | const inst_scalar_ty = inst_ty.scalarType(); | ||
| 7171 | |||
| 7172 | const writer = f.object.writer(); | ||
| 6847 | const local = try f.allocLocal(inst, inst_ty); | 7173 | const local = try f.allocLocal(inst, inst_ty); |
| 7174 | const v = try Vectorizer.start(f, inst, writer, inst_ty); | ||
| 6848 | try f.writeCValue(writer, local, .Other); | 7175 | try f.writeCValue(writer, local, .Other); |
| 7176 | try v.elem(f, writer); | ||
| 6849 | try writer.writeAll(" = zig_libc_name_"); | 7177 | try writer.writeAll(" = zig_libc_name_"); |
| 6850 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); | 7178 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); |
| 6851 | try writer.writeByte('('); | 7179 | try writer.writeByte('('); |
| 6852 | try writer.writeAll(operation); | 7180 | try writer.writeAll(operation); |
| 6853 | try writer.writeAll(")("); | 7181 | try writer.writeAll(")("); |
| 6854 | try f.writeCValue(writer, lhs, .FunctionArgument); | 7182 | try f.writeCValue(writer, lhs, .FunctionArgument); |
| 7183 | try v.elem(f, writer); | ||
| 6855 | try writer.writeAll(", "); | 7184 | try writer.writeAll(", "); |
| 6856 | try f.writeCValue(writer, rhs, .FunctionArgument); | 7185 | try f.writeCValue(writer, rhs, .FunctionArgument); |
| 7186 | try v.elem(f, writer); | ||
| 6857 | try writer.writeAll(");\n"); | 7187 | try writer.writeAll(");\n"); |
| 7188 | try v.end(f, inst, writer); | ||
| 7189 | |||
| 6858 | return local; | 7190 | return local; |
| 6859 | } | 7191 | } |
| 6860 | 7192 | ||
| ... | @@ -6865,23 +7197,34 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6865,23 +7197,34 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6865 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); | 7197 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); |
| 6866 | return .none; | 7198 | return .none; |
| 6867 | } | 7199 | } |
| 6868 | const inst_ty = f.air.typeOfIndex(inst); | 7200 | |
| 6869 | const mulend1 = try f.resolveInst(bin_op.lhs); | 7201 | const mulend1 = try f.resolveInst(bin_op.lhs); |
| 6870 | const mulend2 = try f.resolveInst(bin_op.rhs); | 7202 | const mulend2 = try f.resolveInst(bin_op.rhs); |
| 6871 | const addend = try f.resolveInst(pl_op.operand); | 7203 | const addend = try f.resolveInst(pl_op.operand); |
| 6872 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); | 7204 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); |
| 7205 | |||
| 7206 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 7207 | const inst_scalar_ty = inst_ty.scalarType(); | ||
| 7208 | |||
| 6873 | const writer = f.object.writer(); | 7209 | const writer = f.object.writer(); |
| 6874 | const local = try f.allocLocal(inst, inst_ty); | 7210 | const local = try f.allocLocal(inst, inst_ty); |
| 7211 | const v = try Vectorizer.start(f, inst, writer, inst_ty); | ||
| 6875 | try f.writeCValue(writer, local, .Other); | 7212 | try f.writeCValue(writer, local, .Other); |
| 7213 | try v.elem(f, writer); | ||
| 6876 | try writer.writeAll(" = zig_libc_name_"); | 7214 | try writer.writeAll(" = zig_libc_name_"); |
| 6877 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); | 7215 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); |
| 6878 | try writer.writeAll("(fma)("); | 7216 | try writer.writeAll("(fma)("); |
| 6879 | try f.writeCValue(writer, mulend1, .FunctionArgument); | 7217 | try f.writeCValue(writer, mulend1, .FunctionArgument); |
| 7218 | try v.elem(f, writer); | ||
| 6880 | try writer.writeAll(", "); | 7219 | try writer.writeAll(", "); |
| 6881 | try f.writeCValue(writer, mulend2, .FunctionArgument); | 7220 | try f.writeCValue(writer, mulend2, .FunctionArgument); |
| 7221 | try v.elem(f, writer); | ||
| 6882 | try writer.writeAll(", "); | 7222 | try writer.writeAll(", "); |
| 6883 | try f.writeCValue(writer, addend, .FunctionArgument); | 7223 | try f.writeCValue(writer, addend, .FunctionArgument); |
| 7224 | try v.elem(f, writer); | ||
| 6884 | try writer.writeAll(");\n"); | 7225 | try writer.writeAll(");\n"); |
| 7226 | try v.end(f, inst, writer); | ||
| 7227 | |||
| 6885 | return local; | 7228 | return local; |
| 6886 | } | 7229 | } |
| 6887 | 7230 | ||
| ... | @@ -7083,6 +7426,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 { | ... | @@ -7083,6 +7426,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 { |
| 7083 | } else unreachable; | 7426 | } else unreachable; |
| 7084 | } | 7427 | } |
| 7085 | 7428 | ||
| 7429 | fn 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 | |||
| 7440 | fn 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 | |||
| 7086 | fn StringLiteral(comptime WriterType: type) type { | 7451 | fn StringLiteral(comptime WriterType: type) type { |
| 7087 | // MSVC has a length limit of 16380 per string literal (before concatenation) | 7452 | // MSVC has a length limit of 16380 per string literal (before concatenation) |
| 7088 | const max_char_len = 4; | 7453 | const max_char_len = 4; |
| ... | @@ -7171,30 +7536,33 @@ fn undefPattern(comptime IntType: type) IntType { | ... | @@ -7171,30 +7536,33 @@ fn undefPattern(comptime IntType: type) IntType { |
| 7171 | return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3)); | 7536 | return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3)); |
| 7172 | } | 7537 | } |
| 7173 | 7538 | ||
| 7174 | const FormatIntLiteralContext = struct { ty: Type, val: Value, mod: *Module, location: ?ValueRenderLocation = null }; | 7539 | const FormatIntLiteralContext = struct { |
| 7540 | dg: *DeclGen, | ||
| 7541 | int_info: std.builtin.Type.Int, | ||
| 7542 | kind: CType.Kind, | ||
| 7543 | cty: CType, | ||
| 7544 | val: Value, | ||
| 7545 | }; | ||
| 7175 | fn formatIntLiteral( | 7546 | fn formatIntLiteral( |
| 7176 | data: FormatIntLiteralContext, | 7547 | data: FormatIntLiteralContext, |
| 7177 | comptime fmt: []const u8, | 7548 | comptime fmt: []const u8, |
| 7178 | options: std.fmt.FormatOptions, | 7549 | options: std.fmt.FormatOptions, |
| 7179 | writer: anytype, | 7550 | writer: anytype, |
| 7180 | ) @TypeOf(writer).Error!void { | 7551 | ) @TypeOf(writer).Error!void { |
| 7181 | const target = data.mod.getTarget(); | 7552 | const target = data.dg.module.getTarget(); |
| 7182 | const int_info = data.ty.intInfo(target); | ||
| 7183 | 7553 | ||
| 7184 | const ExpectedContents = struct { | 7554 | const ExpectedContents = struct { |
| 7185 | const base = 10; | 7555 | const base = 10; |
| 7186 | const limbs_count_128 = BigInt.calcTwosCompLimbCount(128); | 7556 | const bits = 128; |
| 7187 | const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base); | 7557 | const limbs_count = BigInt.calcTwosCompLimbCount(bits); |
| 7188 | const worst_case_int = BigInt.Const{ | ||
| 7189 | .limbs = &([1]BigIntLimb{std.math.maxInt(BigIntLimb)} ** expected_needed_limbs_count), | ||
| 7190 | .positive = false, | ||
| 7191 | }; | ||
| 7192 | 7558 | ||
| 7193 | undef_limbs: [limbs_count_128]BigIntLimb, | 7559 | undef_limbs: [limbs_count]BigIntLimb, |
| 7194 | wrap_limbs: [limbs_count_128]BigIntLimb, | 7560 | wrap_limbs: [limbs_count]BigIntLimb, |
| 7561 | to_string_buf: [bits]u8, | ||
| 7562 | to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb, | ||
| 7195 | }; | 7563 | }; |
| 7196 | var stack align(@alignOf(ExpectedContents)) = | 7564 | var stack align(@alignOf(ExpectedContents)) = |
| 7197 | std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa); | 7565 | std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa); |
| 7198 | const allocator = stack.get(); | 7566 | const allocator = stack.get(); |
| 7199 | 7567 | ||
| 7200 | var undef_limbs: []BigIntLimb = &.{}; | 7568 | var undef_limbs: []BigIntLimb = &.{}; |
| ... | @@ -7202,7 +7570,7 @@ fn formatIntLiteral( | ... | @@ -7202,7 +7570,7 @@ fn formatIntLiteral( |
| 7202 | 7570 | ||
| 7203 | var int_buf: Value.BigIntSpace = undefined; | 7571 | var int_buf: Value.BigIntSpace = undefined; |
| 7204 | const int = if (data.val.isUndefDeep()) blk: { | 7572 | const int = if (data.val.isUndefDeep()) blk: { |
| 7205 | undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(int_info.bits)); | 7573 | undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)); |
| 7206 | std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb)); | 7574 | std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb)); |
| 7207 | 7575 | ||
| 7208 | var undef_int = BigInt.Mutable{ | 7576 | var undef_int = BigInt.Mutable{ |
| ... | @@ -7210,164 +7578,193 @@ fn formatIntLiteral( | ... | @@ -7210,164 +7578,193 @@ fn formatIntLiteral( |
| 7210 | .len = undef_limbs.len, | 7578 | .len = undef_limbs.len, |
| 7211 | .positive = true, | 7579 | .positive = true, |
| 7212 | }; | 7580 | }; |
| 7213 | undef_int.truncate(undef_int.toConst(), int_info.signedness, int_info.bits); | 7581 | undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits); |
| 7214 | break :blk undef_int.toConst(); | 7582 | break :blk undef_int.toConst(); |
| 7215 | } else data.val.toBigInt(&int_buf, target); | 7583 | } else data.val.toBigInt(&int_buf, target); |
| 7216 | assert(int.fitsInTwosComp(int_info.signedness, int_info.bits)); | 7584 | assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits)); |
| 7217 | 7585 | ||
| 7218 | const c_bits = toCIntBits(int_info.bits) orelse unreachable; | 7586 | const c_bits = @intCast(usize, data.cty.byteSize(data.dg.ctypes.set, target) * 8); |
| 7219 | var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined; | 7587 | var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined; |
| 7220 | const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); | 7588 | const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); |
| 7221 | 7589 | ||
| 7222 | const wrap_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)); | 7590 | var wrap = BigInt.Mutable{ |
| 7223 | defer allocator.free(wrap_limbs); | 7591 | .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)), |
| 7224 | var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined }; | 7592 | .len = undefined, |
| 7225 | if (wrap.addWrap(int, one, int_info.signedness, c_bits) or | 7593 | .positive = undefined, |
| 7226 | int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits)) | 7594 | }; |
| 7227 | { | 7595 | defer allocator.free(wrap.limbs); |
| 7228 | const abbrev = switch (data.ty.tag()) { | 7596 | |
| 7229 | .c_short, .c_ushort => "SHRT", | 7597 | const c_limb_info: struct { |
| 7230 | .c_int, .c_uint => "INT", | 7598 | cty: CType, |
| 7231 | .c_long, .c_ulong => "LONG", | 7599 | count: usize, |
| 7232 | .c_longlong, .c_ulonglong => "LLONG", | 7600 | endian: std.builtin.Endian, |
| 7233 | .isize, .usize => "INTPTR", | 7601 | homogeneous: bool, |
| 7234 | else => return writer.print("zig_{s}Int_{c}{d}", .{ | 7602 | } = switch (data.cty.tag()) { |
| 7235 | if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits, | 7603 | else => .{ |
| 7236 | }), | 7604 | .cty = CType.initTag(.void), |
| 7237 | }; | 7605 | .count = 1, |
| 7238 | if (int_info.signedness == .unsigned) try writer.writeByte('U'); | 7606 | .endian = .Little, |
| 7239 | return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" }); | 7607 | .homogeneous = true, |
| 7240 | } | ||
| 7241 | |||
| 7242 | var use_twos_comp = false; | ||
| 7243 | if (!int.positive) { | ||
| 7244 | if (c_bits > 64) { | ||
| 7245 | // TODO: Can this be done for decimal literals as well? | ||
| 7246 | if (fmt.len == 1 and fmt[0] != 'd') { | ||
| 7247 | use_twos_comp = true; | ||
| 7248 | } else { | ||
| 7249 | // TODO: Use fmtIntLiteral for 0? | ||
| 7250 | try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits }); | ||
| 7251 | } | ||
| 7252 | } else { | ||
| 7253 | try writer.writeByte('-'); | ||
| 7254 | } | ||
| 7255 | } | ||
| 7256 | |||
| 7257 | switch (data.ty.tag()) { | ||
| 7258 | .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, | ||
| 7259 | else => { | ||
| 7260 | if (int_info.bits <= 64) { | ||
| 7261 | try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) { | ||
| 7262 | .signed => "", | ||
| 7263 | .unsigned => "U", | ||
| 7264 | }, c_bits }); | ||
| 7265 | } else if (data.location != null and data.location.? == .StaticInitializer) { | ||
| 7266 | // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers | ||
| 7267 | try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); | ||
| 7268 | } else { | ||
| 7269 | try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); | ||
| 7270 | } | ||
| 7271 | }, | 7608 | }, |
| 7272 | } | 7609 | .zig_u128, .zig_i128 => .{ |
| 7610 | .cty = CType.initTag(.uint64_t), | ||
| 7611 | .count = 2, | ||
| 7612 | .endian = .Big, | ||
| 7613 | .homogeneous = false, | ||
| 7614 | }, | ||
| 7615 | .array => info: { | ||
| 7616 | const array_data = data.cty.castTag(.array).?.data; | ||
| 7617 | break :info .{ | ||
| 7618 | .cty = data.dg.indexToCType(array_data.elem_type), | ||
| 7619 | .count = @intCast(usize, array_data.len), | ||
| 7620 | .endian = target.cpu.arch.endian(), | ||
| 7621 | .homogeneous = true, | ||
| 7622 | }; | ||
| 7623 | }, | ||
| 7624 | }; | ||
| 7625 | if (c_limb_info.count == 1) { | ||
| 7626 | if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or | ||
| 7627 | data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) | ||
| 7628 | return writer.print("{s}_{s}", .{ | ||
| 7629 | data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ | ||
| 7630 | if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, | ||
| 7631 | }), | ||
| 7632 | if (int.positive) "MAX" else "MIN", | ||
| 7633 | }); | ||
| 7634 | |||
| 7635 | if (!int.positive) try writer.writeByte('-'); | ||
| 7636 | try data.cty.renderLiteralPrefix(writer, data.kind); | ||
| 7273 | 7637 | ||
| 7274 | const limbs_count_64 = @divExact(64, @bitSizeOf(BigIntLimb)); | 7638 | const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) { |
| 7275 | if (c_bits <= 64) { | 7639 | 0 => .{ .base = 10 }, |
| 7276 | var base: u8 = undefined; | ||
| 7277 | var case: std.fmt.Case = undefined; | ||
| 7278 | switch (fmt.len) { | ||
| 7279 | 0 => base = 10, | ||
| 7280 | 1 => switch (fmt[0]) { | 7640 | 1 => switch (fmt[0]) { |
| 7281 | 'b' => { | 7641 | 'b' => style: { |
| 7282 | base = 2; | ||
| 7283 | try writer.writeAll("0b"); | 7642 | try writer.writeAll("0b"); |
| 7643 | break :style .{ .base = 2 }; | ||
| 7284 | }, | 7644 | }, |
| 7285 | 'o' => { | 7645 | 'o' => style: { |
| 7286 | base = 8; | ||
| 7287 | try writer.writeByte('0'); | 7646 | try writer.writeByte('0'); |
| 7647 | break :style .{ .base = 8 }; | ||
| 7288 | }, | 7648 | }, |
| 7289 | 'd' => base = 10, | 7649 | 'd' => .{ .base = 10 }, |
| 7290 | 'x' => { | 7650 | 'x', 'X' => |base| style: { |
| 7291 | base = 16; | ||
| 7292 | case = .lower; | ||
| 7293 | try writer.writeAll("0x"); | ||
| 7294 | }, | ||
| 7295 | 'X' => { | ||
| 7296 | base = 16; | ||
| 7297 | case = .upper; | ||
| 7298 | try writer.writeAll("0x"); | 7651 | try writer.writeAll("0x"); |
| 7652 | break :style .{ .base = 16, .case = switch (base) { | ||
| 7653 | 'x' => .lower, | ||
| 7654 | 'X' => .upper, | ||
| 7655 | else => unreachable, | ||
| 7656 | } }; | ||
| 7299 | }, | 7657 | }, |
| 7300 | else => @compileError("Invalid fmt: " ++ fmt), | 7658 | else => @compileError("Invalid fmt: " ++ fmt), |
| 7301 | }, | 7659 | }, |
| 7302 | else => @compileError("Invalid fmt: " ++ fmt), | 7660 | else => @compileError("Invalid fmt: " ++ fmt), |
| 7303 | } | 7661 | }; |
| 7304 | 7662 | ||
| 7305 | var str: [64]u8 = undefined; | 7663 | const string = try int.abs().toStringAlloc(allocator, style.base, style.case); |
| 7306 | var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]BigIntLimb = undefined; | 7664 | defer allocator.free(string); |
| 7307 | try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]); | 7665 | try writer.writeAll(string); |
| 7308 | } else { | 7666 | } else { |
| 7309 | assert(c_bits == 128); | 7667 | try data.cty.renderLiteralPrefix(writer, data.kind); |
| 7310 | const split = std.math.min(int.limbs.len, limbs_count_64); | 7668 | wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits); |
| 7311 | var twos_comp_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined; | 7669 | std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0); |
| 7312 | 7670 | wrap.len = wrap.limbs.len; | |
| 7313 | // Adding a negation in the C code before the doesn't work in all cases: | 7671 | const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count); |
| 7314 | // - struct versions would require an extra zig_sub_ call to negate, which wouldn't work in constant expressions | 7672 | |
| 7315 | // - negating the f80 int representation (i128) doesn't make sense | 7673 | var c_limb_int_info = std.builtin.Type.Int{ |
| 7316 | // Instead we write out the literal as a negative number in twos complement | 7674 | .signedness = undefined, |
| 7317 | var limbs = int.limbs; | 7675 | .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)), |
| 7318 | 7676 | }; | |
| 7319 | if (use_twos_comp) { | 7677 | var c_limb_cty: CType = undefined; |
| 7320 | var twos_comp = BigInt.Mutable{ | 7678 | |
| 7321 | .limbs = &twos_comp_limbs, | 7679 | var limb_offset: usize = 0; |
| 7322 | .positive = undefined, | 7680 | const most_significant_limb_i = wrap.len - limbs_per_c_limb; |
| 7681 | while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) { | ||
| 7682 | const limb_i = switch (c_limb_info.endian) { | ||
| 7683 | .Little => limb_offset, | ||
| 7684 | .Big => most_significant_limb_i - limb_offset, | ||
| 7685 | }; | ||
| 7686 | var c_limb_mut = BigInt.Mutable{ | ||
| 7687 | .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb], | ||
| 7323 | .len = undefined, | 7688 | .len = undefined, |
| 7689 | .positive = true, | ||
| 7690 | }; | ||
| 7691 | c_limb_mut.normalize(limbs_per_c_limb); | ||
| 7692 | |||
| 7693 | if (limb_i == most_significant_limb_i and | ||
| 7694 | !c_limb_info.homogeneous and data.int_info.signedness == .signed) | ||
| 7695 | { | ||
| 7696 | // most significant limb is actually signed | ||
| 7697 | c_limb_int_info.signedness = .signed; | ||
| 7698 | c_limb_cty = c_limb_info.cty.toSigned(); | ||
| 7699 | |||
| 7700 | c_limb_mut.positive = wrap.positive; | ||
| 7701 | c_limb_mut.truncate( | ||
| 7702 | c_limb_mut.toConst(), | ||
| 7703 | .signed, | ||
| 7704 | data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb), | ||
| 7705 | ); | ||
| 7706 | } else { | ||
| 7707 | c_limb_int_info.signedness = .unsigned; | ||
| 7708 | c_limb_cty = c_limb_info.cty; | ||
| 7709 | } | ||
| 7710 | var c_limb_val_pl = Value.Payload.BigInt{ | ||
| 7711 | .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative }, | ||
| 7712 | .data = c_limb_mut.limbs[0..c_limb_mut.len], | ||
| 7324 | }; | 7713 | }; |
| 7325 | 7714 | ||
| 7326 | twos_comp.convertToTwosComplement(int, .signed, int_info.bits); | 7715 | if (limb_offset > 0) try writer.writeAll(", "); |
| 7327 | limbs = twos_comp.limbs; | 7716 | try formatIntLiteral(.{ |
| 7717 | .dg = data.dg, | ||
| 7718 | .int_info = c_limb_int_info, | ||
| 7719 | .kind = data.kind, | ||
| 7720 | .cty = c_limb_cty, | ||
| 7721 | .val = Value.initPayload(&c_limb_val_pl.base), | ||
| 7722 | }, fmt, options, writer); | ||
| 7328 | } | 7723 | } |
| 7724 | } | ||
| 7725 | try data.cty.renderLiteralSuffix(writer); | ||
| 7726 | } | ||
| 7329 | 7727 | ||
| 7330 | var upper_pl = Value.Payload.BigInt{ | 7728 | const Vectorizer = struct { |
| 7331 | .base = .{ .tag = .int_big_positive }, | 7729 | index: CValue = .none, |
| 7332 | .data = limbs[split..], | ||
| 7333 | }; | ||
| 7334 | const upper_val = Value.initPayload(&upper_pl.base); | ||
| 7335 | try formatIntLiteral(.{ | ||
| 7336 | .ty = switch (int_info.signedness) { | ||
| 7337 | .unsigned => Type.u64, | ||
| 7338 | .signed => if (use_twos_comp) Type.u64 else Type.i64, | ||
| 7339 | }, | ||
| 7340 | .val = upper_val, | ||
| 7341 | .mod = data.mod, | ||
| 7342 | }, fmt, options, writer); | ||
| 7343 | 7730 | ||
| 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() }; | ||
| 7345 | 7734 | ||
| 7346 | var lower_pl = Value.Payload.BigInt{ | 7735 | const local = try f.allocLocal(inst, Type.usize); |
| 7347 | .base = .{ .tag = .int_big_positive }, | ||
| 7348 | .data = limbs[0..split], | ||
| 7349 | }; | ||
| 7350 | const lower_val = Value.initPayload(&lower_pl.base); | ||
| 7351 | try formatIntLiteral(.{ | ||
| 7352 | .ty = Type.u64, | ||
| 7353 | .val = lower_val, | ||
| 7354 | .mod = data.mod, | ||
| 7355 | }, fmt, options, writer); | ||
| 7356 | 7736 | ||
| 7357 | if (!int.positive and c_bits > 64 and !use_twos_comp) try writer.writeByte(')'); | 7737 | try writer.writeAll("for ("); |
| 7358 | return writer.writeByte(')'); | 7738 | try f.writeCValue(writer, local, .Other); |
| 7739 | try writer.print(" = {d}; ", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); | ||
| 7740 | try f.writeCValue(writer, local, .Other); | ||
| 7741 | try writer.print(" < {d}; ", .{ | ||
| 7742 | try f.fmtIntLiteral(Type.usize, Value.initPayload(&len_pl.base)), | ||
| 7743 | }); | ||
| 7744 | try f.writeCValue(writer, local, .Other); | ||
| 7745 | try writer.print(" += {d}) {{\n", .{try f.fmtIntLiteral(Type.usize, Value.one)}); | ||
| 7746 | f.object.indent_writer.pushIndent(); | ||
| 7747 | |||
| 7748 | break :index .{ .index = local }; | ||
| 7749 | } else .{}; | ||
| 7359 | } | 7750 | } |
| 7360 | 7751 | ||
| 7361 | switch (data.ty.tag()) { | 7752 | pub fn elem(self: Vectorizer, f: *Function, writer: anytype) !void { |
| 7362 | .c_short, .c_ushort, .c_int => {}, | 7753 | if (self.index != .none) { |
| 7363 | .c_uint => try writer.writeAll("u"), | 7754 | try writer.writeByte('['); |
| 7364 | .c_long => try writer.writeAll("l"), | 7755 | try f.writeCValue(writer, self.index, .Other); |
| 7365 | .c_ulong => try writer.writeAll("ul"), | 7756 | try writer.writeByte(']'); |
| 7366 | .c_longlong => try writer.writeAll("ll"), | 7757 | } |
| 7367 | .c_ulonglong => try writer.writeAll("ull"), | ||
| 7368 | else => try writer.writeByte(')'), | ||
| 7369 | } | 7758 | } |
| 7370 | } | 7759 | |
| 7760 | pub fn end(self: Vectorizer, f: *Function, inst: Air.Inst.Index, writer: anytype) !void { | ||
| 7761 | if (self.index != .none) { | ||
| 7762 | f.object.indent_writer.popIndent(); | ||
| 7763 | try writer.writeAll("}\n"); | ||
| 7764 | try freeLocal(f, inst, self.index.new_local, 0); | ||
| 7765 | } | ||
| 7766 | } | ||
| 7767 | }; | ||
| 7371 | 7768 | ||
| 7372 | fn isByRef(ty: Type) bool { | 7769 | fn isByRef(ty: Type) bool { |
| 7373 | _ = ty; | 7770 | _ = ty; |
src/codegen/c/type.zig+449-32| ... | @@ -496,6 +496,427 @@ pub const CType = extern union { | ... | @@ -496,6 +496,427 @@ pub const CType = extern union { |
| 496 | } | 496 | } |
| 497 | }; | 497 | }; |
| 498 | 498 | ||
| 499 | pub fn isBool(self: CType) bool { | ||
| 500 | return switch (self.tag()) { | ||
| 501 | ._Bool, | ||
| 502 | .bool, | ||
| 503 | => true, | ||
| 504 | else => false, | ||
| 505 | }; | ||
| 506 | } | ||
| 507 | |||
| 508 | pub fn isInteger(self: CType) bool { | ||
| 509 | return switch (self.tag()) { | ||
| 510 | .char, | ||
| 511 | .@"signed char", | ||
| 512 | .short, | ||
| 513 | .int, | ||
| 514 | .long, | ||
| 515 | .@"long long", | ||
| 516 | .@"unsigned char", | ||
| 517 | .@"unsigned short", | ||
| 518 | .@"unsigned int", | ||
| 519 | .@"unsigned long", | ||
| 520 | .@"unsigned long long", | ||
| 521 | .size_t, | ||
| 522 | .ptrdiff_t, | ||
| 523 | .uint8_t, | ||
| 524 | .int8_t, | ||
| 525 | .uint16_t, | ||
| 526 | .int16_t, | ||
| 527 | .uint32_t, | ||
| 528 | .int32_t, | ||
| 529 | .uint64_t, | ||
| 530 | .int64_t, | ||
| 531 | .uintptr_t, | ||
| 532 | .intptr_t, | ||
| 533 | .zig_u128, | ||
| 534 | .zig_i128, | ||
| 535 | => true, | ||
| 536 | else => false, | ||
| 537 | }; | ||
| 538 | } | ||
| 539 | |||
| 540 | pub fn signedness(self: CType) ?std.builtin.Signedness { | ||
| 541 | return switch (self.tag()) { | ||
| 542 | .char => null, // unknown signedness | ||
| 543 | .@"signed char", | ||
| 544 | .short, | ||
| 545 | .int, | ||
| 546 | .long, | ||
| 547 | .@"long long", | ||
| 548 | .ptrdiff_t, | ||
| 549 | .int8_t, | ||
| 550 | .int16_t, | ||
| 551 | .int32_t, | ||
| 552 | .int64_t, | ||
| 553 | .intptr_t, | ||
| 554 | .zig_i128, | ||
| 555 | => .signed, | ||
| 556 | .@"unsigned char", | ||
| 557 | .@"unsigned short", | ||
| 558 | .@"unsigned int", | ||
| 559 | .@"unsigned long", | ||
| 560 | .@"unsigned long long", | ||
| 561 | .size_t, | ||
| 562 | .uint8_t, | ||
| 563 | .uint16_t, | ||
| 564 | .uint32_t, | ||
| 565 | .uint64_t, | ||
| 566 | .uintptr_t, | ||
| 567 | .zig_u128, | ||
| 568 | => .unsigned, | ||
| 569 | else => unreachable, | ||
| 570 | }; | ||
| 571 | } | ||
| 572 | |||
| 573 | pub fn isFloat(self: CType) bool { | ||
| 574 | return switch (self.tag()) { | ||
| 575 | .float, | ||
| 576 | .double, | ||
| 577 | .@"long double", | ||
| 578 | .zig_f16, | ||
| 579 | .zig_f32, | ||
| 580 | .zig_f64, | ||
| 581 | .zig_f80, | ||
| 582 | .zig_f128, | ||
| 583 | .zig_c_longdouble, | ||
| 584 | => true, | ||
| 585 | else => false, | ||
| 586 | }; | ||
| 587 | } | ||
| 588 | |||
| 589 | pub fn isPointer(self: CType) bool { | ||
| 590 | return switch (self.tag()) { | ||
| 591 | .pointer, | ||
| 592 | .pointer_const, | ||
| 593 | .pointer_volatile, | ||
| 594 | .pointer_const_volatile, | ||
| 595 | => true, | ||
| 596 | else => false, | ||
| 597 | }; | ||
| 598 | } | ||
| 599 | |||
| 600 | pub fn isFunction(self: CType) bool { | ||
| 601 | return switch (self.tag()) { | ||
| 602 | .function, | ||
| 603 | .varargs_function, | ||
| 604 | => true, | ||
| 605 | else => false, | ||
| 606 | }; | ||
| 607 | } | ||
| 608 | |||
| 609 | pub fn toSigned(self: CType) CType { | ||
| 610 | return CType.initTag(switch (self.tag()) { | ||
| 611 | .char, .@"signed char", .@"unsigned char" => .@"signed char", | ||
| 612 | .short, .@"unsigned short" => .short, | ||
| 613 | .int, .@"unsigned int" => .int, | ||
| 614 | .long, .@"unsigned long" => .long, | ||
| 615 | .@"long long", .@"unsigned long long" => .@"long long", | ||
| 616 | .size_t, .ptrdiff_t => .ptrdiff_t, | ||
| 617 | .uint8_t, .int8_t => .int8_t, | ||
| 618 | .uint16_t, .int16_t => .int16_t, | ||
| 619 | .uint32_t, .int32_t => .int32_t, | ||
| 620 | .uint64_t, .int64_t => .int64_t, | ||
| 621 | .uintptr_t, .intptr_t => .intptr_t, | ||
| 622 | .zig_u128, .zig_i128 => .zig_i128, | ||
| 623 | .float, | ||
| 624 | .double, | ||
| 625 | .@"long double", | ||
| 626 | .zig_f16, | ||
| 627 | .zig_f32, | ||
| 628 | .zig_f80, | ||
| 629 | .zig_f128, | ||
| 630 | .zig_c_longdouble, | ||
| 631 | => |t| t, | ||
| 632 | else => unreachable, | ||
| 633 | }); | ||
| 634 | } | ||
| 635 | |||
| 636 | pub fn toUnsigned(self: CType) CType { | ||
| 637 | return CType.initTag(switch (self.tag()) { | ||
| 638 | .char, .@"signed char", .@"unsigned char" => .@"unsigned char", | ||
| 639 | .short, .@"unsigned short" => .@"unsigned short", | ||
| 640 | .int, .@"unsigned int" => .@"unsigned int", | ||
| 641 | .long, .@"unsigned long" => .@"unsigned long", | ||
| 642 | .@"long long", .@"unsigned long long" => .@"unsigned long long", | ||
| 643 | .size_t, .ptrdiff_t => .size_t, | ||
| 644 | .uint8_t, .int8_t => .uint8_t, | ||
| 645 | .uint16_t, .int16_t => .uint16_t, | ||
| 646 | .uint32_t, .int32_t => .uint32_t, | ||
| 647 | .uint64_t, .int64_t => .uint64_t, | ||
| 648 | .uintptr_t, .intptr_t => .uintptr_t, | ||
| 649 | .zig_u128, .zig_i128 => .zig_u128, | ||
| 650 | else => unreachable, | ||
| 651 | }); | ||
| 652 | } | ||
| 653 | |||
| 654 | pub fn toSignedness(self: CType, s: std.builtin.Signedness) CType { | ||
| 655 | return switch (s) { | ||
| 656 | .unsigned => self.toUnsigned(), | ||
| 657 | .signed => self.toSigned(), | ||
| 658 | }; | ||
| 659 | } | ||
| 660 | |||
| 661 | pub fn getStandardDefineAbbrev(self: CType) ?[]const u8 { | ||
| 662 | return switch (self.tag()) { | ||
| 663 | .char => "CHAR", | ||
| 664 | .@"signed char" => "SCHAR", | ||
| 665 | .short => "SHRT", | ||
| 666 | .int => "INT", | ||
| 667 | .long => "LONG", | ||
| 668 | .@"long long" => "LLONG", | ||
| 669 | .@"unsigned char" => "UCHAR", | ||
| 670 | .@"unsigned short" => "USHRT", | ||
| 671 | .@"unsigned int" => "UINT", | ||
| 672 | .@"unsigned long" => "ULONG", | ||
| 673 | .@"unsigned long long" => "ULLONG", | ||
| 674 | .float => "FLT", | ||
| 675 | .double => "DBL", | ||
| 676 | .@"long double" => "LDBL", | ||
| 677 | .size_t => "SIZE", | ||
| 678 | .ptrdiff_t => "PTRDIFF", | ||
| 679 | .uint8_t => "UINT8", | ||
| 680 | .int8_t => "INT8", | ||
| 681 | .uint16_t => "UINT16", | ||
| 682 | .int16_t => "INT16", | ||
| 683 | .uint32_t => "UINT32", | ||
| 684 | .int32_t => "INT32", | ||
| 685 | .uint64_t => "UINT64", | ||
| 686 | .int64_t => "INT64", | ||
| 687 | .uintptr_t => "UINTPTR", | ||
| 688 | .intptr_t => "INTPTR", | ||
| 689 | else => null, | ||
| 690 | }; | ||
| 691 | } | ||
| 692 | |||
| 693 | pub fn renderLiteralPrefix(self: CType, writer: anytype, kind: Kind) @TypeOf(writer).Error!void { | ||
| 694 | switch (self.tag()) { | ||
| 695 | .void => unreachable, | ||
| 696 | ._Bool, | ||
| 697 | .char, | ||
| 698 | .@"signed char", | ||
| 699 | .short, | ||
| 700 | .@"unsigned short", | ||
| 701 | .bool, | ||
| 702 | .size_t, | ||
| 703 | .ptrdiff_t, | ||
| 704 | .uintptr_t, | ||
| 705 | .intptr_t, | ||
| 706 | => |t| switch (kind) { | ||
| 707 | else => try writer.print("({s})", .{@tagName(t)}), | ||
| 708 | .global => {}, | ||
| 709 | }, | ||
| 710 | .int, | ||
| 711 | .long, | ||
| 712 | .@"long long", | ||
| 713 | .@"unsigned char", | ||
| 714 | .@"unsigned int", | ||
| 715 | .@"unsigned long", | ||
| 716 | .@"unsigned long long", | ||
| 717 | .float, | ||
| 718 | .double, | ||
| 719 | .@"long double", | ||
| 720 | => {}, | ||
| 721 | .uint8_t, | ||
| 722 | .int8_t, | ||
| 723 | .uint16_t, | ||
| 724 | .int16_t, | ||
| 725 | .uint32_t, | ||
| 726 | .int32_t, | ||
| 727 | .uint64_t, | ||
| 728 | .int64_t, | ||
| 729 | => try writer.print("{s}_C(", .{self.getStandardDefineAbbrev().?}), | ||
| 730 | .zig_u128, | ||
| 731 | .zig_i128, | ||
| 732 | .zig_f16, | ||
| 733 | .zig_f32, | ||
| 734 | .zig_f64, | ||
| 735 | .zig_f80, | ||
| 736 | .zig_f128, | ||
| 737 | .zig_c_longdouble, | ||
| 738 | => |t| try writer.print("zig_{s}_{s}(", .{ | ||
| 739 | switch (kind) { | ||
| 740 | else => "make", | ||
| 741 | .global => "init", | ||
| 742 | }, | ||
| 743 | @tagName(t)["zig_".len..], | ||
| 744 | }), | ||
| 745 | .pointer, | ||
| 746 | .pointer_const, | ||
| 747 | .pointer_volatile, | ||
| 748 | .pointer_const_volatile, | ||
| 749 | => unreachable, | ||
| 750 | .array, | ||
| 751 | .vector, | ||
| 752 | => try writer.writeByte('{'), | ||
| 753 | .fwd_anon_struct, | ||
| 754 | .fwd_anon_union, | ||
| 755 | .fwd_struct, | ||
| 756 | .fwd_union, | ||
| 757 | .unnamed_struct, | ||
| 758 | .unnamed_union, | ||
| 759 | .packed_unnamed_struct, | ||
| 760 | .packed_unnamed_union, | ||
| 761 | .anon_struct, | ||
| 762 | .anon_union, | ||
| 763 | .@"struct", | ||
| 764 | .@"union", | ||
| 765 | .packed_struct, | ||
| 766 | .packed_union, | ||
| 767 | .function, | ||
| 768 | .varargs_function, | ||
| 769 | => unreachable, | ||
| 770 | } | ||
| 771 | } | ||
| 772 | |||
| 773 | pub fn renderLiteralSuffix(self: CType, writer: anytype) @TypeOf(writer).Error!void { | ||
| 774 | switch (self.tag()) { | ||
| 775 | .void => unreachable, | ||
| 776 | ._Bool => {}, | ||
| 777 | .char, | ||
| 778 | .@"signed char", | ||
| 779 | .short, | ||
| 780 | .int, | ||
| 781 | => {}, | ||
| 782 | .long => try writer.writeByte('l'), | ||
| 783 | .@"long long" => try writer.writeAll("ll"), | ||
| 784 | .@"unsigned char", | ||
| 785 | .@"unsigned short", | ||
| 786 | .@"unsigned int", | ||
| 787 | => try writer.writeByte('u'), | ||
| 788 | .@"unsigned long", | ||
| 789 | .size_t, | ||
| 790 | .uintptr_t, | ||
| 791 | => try writer.writeAll("ul"), | ||
| 792 | .@"unsigned long long" => try writer.writeAll("ull"), | ||
| 793 | .float => try writer.writeByte('f'), | ||
| 794 | .double => {}, | ||
| 795 | .@"long double" => try writer.writeByte('l'), | ||
| 796 | .bool, | ||
| 797 | .ptrdiff_t, | ||
| 798 | .intptr_t, | ||
| 799 | => {}, | ||
| 800 | .uint8_t, | ||
| 801 | .int8_t, | ||
| 802 | .uint16_t, | ||
| 803 | .int16_t, | ||
| 804 | .uint32_t, | ||
| 805 | .int32_t, | ||
| 806 | .uint64_t, | ||
| 807 | .int64_t, | ||
| 808 | .zig_u128, | ||
| 809 | .zig_i128, | ||
| 810 | .zig_f16, | ||
| 811 | .zig_f32, | ||
| 812 | .zig_f64, | ||
| 813 | .zig_f80, | ||
| 814 | .zig_f128, | ||
| 815 | .zig_c_longdouble, | ||
| 816 | => try writer.writeByte(')'), | ||
| 817 | .pointer, | ||
| 818 | .pointer_const, | ||
| 819 | .pointer_volatile, | ||
| 820 | .pointer_const_volatile, | ||
| 821 | => unreachable, | ||
| 822 | .array, | ||
| 823 | .vector, | ||
| 824 | => try writer.writeByte('}'), | ||
| 825 | .fwd_anon_struct, | ||
| 826 | .fwd_anon_union, | ||
| 827 | .fwd_struct, | ||
| 828 | .fwd_union, | ||
| 829 | .unnamed_struct, | ||
| 830 | .unnamed_union, | ||
| 831 | .packed_unnamed_struct, | ||
| 832 | .packed_unnamed_union, | ||
| 833 | .anon_struct, | ||
| 834 | .anon_union, | ||
| 835 | .@"struct", | ||
| 836 | .@"union", | ||
| 837 | .packed_struct, | ||
| 838 | .packed_union, | ||
| 839 | .function, | ||
| 840 | .varargs_function, | ||
| 841 | => unreachable, | ||
| 842 | } | ||
| 843 | } | ||
| 844 | |||
| 845 | pub fn floatActiveBits(self: CType, target: Target) u16 { | ||
| 846 | return switch (self.tag()) { | ||
| 847 | .float => target.c_type_bit_size(.float), | ||
| 848 | .double => target.c_type_bit_size(.double), | ||
| 849 | .@"long double", .zig_c_longdouble => target.c_type_bit_size(.longdouble), | ||
| 850 | .zig_f16 => 16, | ||
| 851 | .zig_f32 => 32, | ||
| 852 | .zig_f64 => 64, | ||
| 853 | .zig_f80 => 80, | ||
| 854 | .zig_f128 => 128, | ||
| 855 | else => unreachable, | ||
| 856 | }; | ||
| 857 | } | ||
| 858 | |||
| 859 | pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 { | ||
| 860 | return switch (self.tag()) { | ||
| 861 | .void => 0, | ||
| 862 | .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1, | ||
| 863 | .short => target.c_type_byte_size(.short), | ||
| 864 | .int => target.c_type_byte_size(.int), | ||
| 865 | .long => target.c_type_byte_size(.long), | ||
| 866 | .@"long long" => target.c_type_byte_size(.longlong), | ||
| 867 | .@"unsigned short" => target.c_type_byte_size(.ushort), | ||
| 868 | .@"unsigned int" => target.c_type_byte_size(.uint), | ||
| 869 | .@"unsigned long" => target.c_type_byte_size(.ulong), | ||
| 870 | .@"unsigned long long" => target.c_type_byte_size(.ulonglong), | ||
| 871 | .float => target.c_type_byte_size(.float), | ||
| 872 | .double => target.c_type_byte_size(.double), | ||
| 873 | .@"long double" => target.c_type_byte_size(.longdouble), | ||
| 874 | .size_t, | ||
| 875 | .ptrdiff_t, | ||
| 876 | .uintptr_t, | ||
| 877 | .intptr_t, | ||
| 878 | .pointer, | ||
| 879 | .pointer_const, | ||
| 880 | .pointer_volatile, | ||
| 881 | .pointer_const_volatile, | ||
| 882 | => @divExact(target.cpu.arch.ptrBitWidth(), 8), | ||
| 883 | .uint16_t, .int16_t, .zig_f16 => 2, | ||
| 884 | .uint32_t, .int32_t, .zig_f32 => 4, | ||
| 885 | .uint64_t, .int64_t, .zig_f64 => 8, | ||
| 886 | .zig_u128, .zig_i128, .zig_f128 => 16, | ||
| 887 | .zig_f80 => if (target.c_type_bit_size(.longdouble) == 80) | ||
| 888 | target.c_type_byte_size(.longdouble) | ||
| 889 | else | ||
| 890 | 16, | ||
| 891 | .zig_c_longdouble => target.c_type_byte_size(.longdouble), | ||
| 892 | |||
| 893 | .array, | ||
| 894 | .vector, | ||
| 895 | => { | ||
| 896 | const data = self.cast(Payload.Sequence).?.data; | ||
| 897 | return data.len * store.indexToCType(data.elem_type).byteSize(store, target); | ||
| 898 | }, | ||
| 899 | |||
| 900 | .fwd_anon_struct, | ||
| 901 | .fwd_anon_union, | ||
| 902 | .fwd_struct, | ||
| 903 | .fwd_union, | ||
| 904 | .unnamed_struct, | ||
| 905 | .unnamed_union, | ||
| 906 | .packed_unnamed_struct, | ||
| 907 | .packed_unnamed_union, | ||
| 908 | .anon_struct, | ||
| 909 | .anon_union, | ||
| 910 | .@"struct", | ||
| 911 | .@"union", | ||
| 912 | .packed_struct, | ||
| 913 | .packed_union, | ||
| 914 | .function, | ||
| 915 | .varargs_function, | ||
| 916 | => unreachable, | ||
| 917 | }; | ||
| 918 | } | ||
| 919 | |||
| 499 | pub fn isPacked(self: CType) bool { | 920 | pub fn isPacked(self: CType) bool { |
| 500 | return switch (self.tag()) { | 921 | return switch (self.tag()) { |
| 501 | else => false, | 922 | else => false, |
| ... | @@ -787,26 +1208,26 @@ pub const CType = extern union { | ... | @@ -787,26 +1208,26 @@ pub const CType = extern union { |
| 787 | }; | 1208 | }; |
| 788 | } | 1209 | } |
| 789 | 1210 | ||
| 790 | fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag { | 1211 | fn tagFromIntInfo(int_info: std.builtin.Type.Int) Tag { |
| 791 | return switch (bits) { | 1212 | return switch (int_info.bits) { |
| 792 | 0 => .void, | 1213 | 0 => .void, |
| 793 | 1...8 => switch (signedness) { | 1214 | 1...8 => switch (int_info.signedness) { |
| 794 | .unsigned => .uint8_t, | 1215 | .unsigned => .uint8_t, |
| 795 | .signed => .int8_t, | 1216 | .signed => .int8_t, |
| 796 | }, | 1217 | }, |
| 797 | 9...16 => switch (signedness) { | 1218 | 9...16 => switch (int_info.signedness) { |
| 798 | .unsigned => .uint16_t, | 1219 | .unsigned => .uint16_t, |
| 799 | .signed => .int16_t, | 1220 | .signed => .int16_t, |
| 800 | }, | 1221 | }, |
| 801 | 17...32 => switch (signedness) { | 1222 | 17...32 => switch (int_info.signedness) { |
| 802 | .unsigned => .uint32_t, | 1223 | .unsigned => .uint32_t, |
| 803 | .signed => .int32_t, | 1224 | .signed => .int32_t, |
| 804 | }, | 1225 | }, |
| 805 | 33...64 => switch (signedness) { | 1226 | 33...64 => switch (int_info.signedness) { |
| 806 | .unsigned => .uint64_t, | 1227 | .unsigned => .uint64_t, |
| 807 | .signed => .int64_t, | 1228 | .signed => .int64_t, |
| 808 | }, | 1229 | }, |
| 809 | 65...128 => switch (signedness) { | 1230 | 65...128 => switch (int_info.signedness) { |
| 810 | .unsigned => .zig_u128, | 1231 | .unsigned => .zig_u128, |
| 811 | .signed => .zig_i128, | 1232 | .signed => .zig_i128, |
| 812 | }, | 1233 | }, |
| ... | @@ -945,31 +1366,27 @@ pub const CType = extern union { | ... | @@ -945,31 +1366,27 @@ pub const CType = extern union { |
| 945 | .c_ulong => self.init(.@"unsigned long"), | 1366 | .c_ulong => self.init(.@"unsigned long"), |
| 946 | .c_longlong => self.init(.@"long long"), | 1367 | .c_longlong => self.init(.@"long long"), |
| 947 | .c_ulonglong => self.init(.@"unsigned long long"), | 1368 | .c_ulonglong => self.init(.@"unsigned long long"), |
| 948 | else => { | 1369 | else => switch (tagFromIntInfo(ty.intInfo(target))) { |
| 949 | const info = ty.intInfo(target); | 1370 | .void => unreachable, |
| 950 | const t = tagFromIntInfo(info.signedness, info.bits); | 1371 | else => |t| self.init(t), |
| 951 | switch (t) { | 1372 | .array => switch (kind) { |
| 952 | .void => unreachable, | 1373 | .forward, .complete, .global => { |
| 953 | else => self.init(t), | 1374 | const abi_size = ty.abiSize(target); |
| 954 | .array => switch (kind) { | 1375 | const abi_align = ty.abiAlignment(target); |
| 955 | .forward, .complete, .global => { | 1376 | self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ |
| 956 | const abi_size = ty.abiSize(target); | 1377 | .len = @divExact(abi_size, abi_align), |
| 957 | const abi_align = ty.abiAlignment(target); | 1378 | .elem_type = tagFromIntInfo(.{ |
| 958 | self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ | 1379 | .signedness = .unsigned, |
| 959 | .len = @divExact(abi_size, abi_align), | 1380 | .bits = @intCast(u16, abi_align * 8), |
| 960 | .elem_type = tagFromIntInfo( | 1381 | }).toIndex(), |
| 961 | .unsigned, | 1382 | } } }; |
| 962 | @intCast(u16, abi_align * 8), | 1383 | self.value = .{ .cty = initPayload(&self.storage.seq) }; |
| 963 | ).toIndex(), | ||
| 964 | } } }; | ||
| 965 | self.value = .{ .cty = initPayload(&self.storage.seq) }; | ||
| 966 | }, | ||
| 967 | .forward_parameter, | ||
| 968 | .parameter, | ||
| 969 | => try self.initArrayParameter(ty, kind, lookup), | ||
| 970 | .payload => unreachable, | ||
| 971 | }, | 1384 | }, |
| 972 | } | 1385 | .forward_parameter, |
| 1386 | .parameter, | ||
| 1387 | => try self.initArrayParameter(ty, kind, lookup), | ||
| 1388 | .payload => unreachable, | ||
| 1389 | }, | ||
| 973 | }, | 1390 | }, |
| 974 | } else switch (ty.zigTypeTag()) { | 1391 | } else switch (ty.zigTypeTag()) { |
| 975 | .Frame => unreachable, | 1392 | .Frame => unreachable, |
| ... | @@ -1048,7 +1465,7 @@ pub const CType = extern union { | ... | @@ -1048,7 +1465,7 @@ pub const CType = extern union { |
| 1048 | .base = .{ .tag = .int_unsigned }, | 1465 | .base = .{ .tag = .int_unsigned }, |
| 1049 | .data = info.host_size * 8, | 1466 | .data = info.host_size * 8, |
| 1050 | }; | 1467 | }; |
| 1051 | const pointee_ty = if (info.host_size > 0) | 1468 | const pointee_ty = if (info.host_size > 0 and info.vector_index == .none) |
| 1052 | Type.initPayload(&host_int_pl.base) | 1469 | Type.initPayload(&host_int_pl.base) |
| 1053 | else | 1470 | else |
| 1054 | info.pointee_type; | 1471 | info.pointee_type; |
src/codegen/llvm.zig+10-1| ... | @@ -1778,7 +1778,7 @@ pub const Object = struct { | ... | @@ -1778,7 +1778,7 @@ pub const Object = struct { |
| 1778 | if (ty.optionalReprIsPayload()) { | 1778 | if (ty.optionalReprIsPayload()) { |
| 1779 | const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); | 1779 | const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); |
| 1780 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. | 1780 | // The recursive call to `lowerDebugType` means we can't use `gop` anymore. |
| 1781 | try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); | 1781 | try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module }); |
| 1782 | return ptr_di_ty; | 1782 | return ptr_di_ty; |
| 1783 | } | 1783 | } |
| 1784 | 1784 | ||
| ... | @@ -4595,6 +4595,7 @@ pub const FuncGen = struct { | ... | @@ -4595,6 +4595,7 @@ pub const FuncGen = struct { |
| 4595 | .block => try self.airBlock(inst), | 4595 | .block => try self.airBlock(inst), |
| 4596 | .br => try self.airBr(inst), | 4596 | .br => try self.airBr(inst), |
| 4597 | .switch_br => try self.airSwitchBr(inst), | 4597 | .switch_br => try self.airSwitchBr(inst), |
| 4598 | .trap => try self.airTrap(inst), | ||
| 4598 | .breakpoint => try self.airBreakpoint(inst), | 4599 | .breakpoint => try self.airBreakpoint(inst), |
| 4599 | .ret_addr => try self.airRetAddr(inst), | 4600 | .ret_addr => try self.airRetAddr(inst), |
| 4600 | .frame_addr => try self.airFrameAddress(inst), | 4601 | .frame_addr => try self.airFrameAddress(inst), |
| ... | @@ -8261,6 +8262,14 @@ pub const FuncGen = struct { | ... | @@ -8261,6 +8262,14 @@ pub const FuncGen = struct { |
| 8261 | return fg.load(ptr, ptr_ty); | 8262 | return fg.load(ptr, ptr_ty); |
| 8262 | } | 8263 | } |
| 8263 | 8264 | ||
| 8265 | fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | ||
| 8266 | _ = inst; | ||
| 8267 | const llvm_fn = self.getIntrinsic("llvm.trap", &.{}); | ||
| 8268 | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .Cold, .Auto, ""); | ||
| 8269 | _ = self.builder.buildUnreachable(); | ||
| 8270 | return null; | ||
| 8271 | } | ||
| 8272 | |||
| 8264 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | 8273 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8265 | _ = inst; | 8274 | _ = inst; |
| 8266 | const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{}); | 8275 | const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{}); |
src/glibc.zig+11-8| ... | @@ -161,7 +161,7 @@ pub const CRTFile = enum { | ... | @@ -161,7 +161,7 @@ pub const CRTFile = enum { |
| 161 | libc_nonshared_a, | 161 | libc_nonshared_a, |
| 162 | }; | 162 | }; |
| 163 | 163 | ||
| 164 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 164 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 165 | if (!build_options.have_llvm) { | 165 | if (!build_options.have_llvm) { |
| 166 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 166 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 167 | } | 167 | } |
| ... | @@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 196 | "-DASSEMBLER", | 196 | "-DASSEMBLER", |
| 197 | "-Wa,--noexecstack", | 197 | "-Wa,--noexecstack", |
| 198 | }); | 198 | }); |
| 199 | return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ | 199 | return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{ |
| 200 | .{ | 200 | .{ |
| 201 | .src_path = try start_asm_path(comp, arena, "crti.S"), | 201 | .src_path = try start_asm_path(comp, arena, "crti.S"), |
| 202 | .cache_exempt_flags = args.items, | 202 | .cache_exempt_flags = args.items, |
| ... | @@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 215 | "-DASSEMBLER", | 215 | "-DASSEMBLER", |
| 216 | "-Wa,--noexecstack", | 216 | "-Wa,--noexecstack", |
| 217 | }); | 217 | }); |
| 218 | return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ | 218 | return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{ |
| 219 | .{ | 219 | .{ |
| 220 | .src_path = try start_asm_path(comp, arena, "crtn.S"), | 220 | .src_path = try start_asm_path(comp, arena, "crtn.S"), |
| 221 | .cache_exempt_flags = args.items, | 221 | .cache_exempt_flags = args.items, |
| ... | @@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 265 | .cache_exempt_flags = args.items, | 265 | .cache_exempt_flags = args.items, |
| 266 | }; | 266 | }; |
| 267 | }; | 267 | }; |
| 268 | return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_o, abi_note_o }); | 268 | return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &.{ |
| 269 | start_o, abi_note_o, | ||
| 270 | }); | ||
| 269 | }, | 271 | }, |
| 270 | .libc_nonshared_a => { | 272 | .libc_nonshared_a => { |
| 271 | const s = path.sep_str; | 273 | const s = path.sep_str; |
| ... | @@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 366 | files_index += 1; | 368 | files_index += 1; |
| 367 | } | 369 | } |
| 368 | const files = files_buf[0..files_index]; | 370 | const files = files_buf[0..files_index]; |
| 369 | return comp.build_crt_file("c_nonshared", .Lib, files); | 371 | return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", prog_node, files); |
| 370 | }, | 372 | }, |
| 371 | } | 373 | } |
| 372 | } | 374 | } |
| ... | @@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct { | ... | @@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct { |
| 639 | 641 | ||
| 640 | const all_map_basename = "all.map"; | 642 | const all_map_basename = "all.map"; |
| 641 | 643 | ||
| 642 | pub fn buildSharedObjects(comp: *Compilation) !void { | 644 | pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 643 | const tracy = trace(@src()); | 645 | const tracy = trace(@src()); |
| 644 | defer tracy.end(); | 646 | defer tracy.end(); |
| 645 | 647 | ||
| ... | @@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { | ... | @@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 1023 | const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; | 1025 | const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; |
| 1024 | try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items); | 1026 | try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items); |
| 1025 | 1027 | ||
| 1026 | try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib); | 1028 | try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node); |
| 1027 | } | 1029 | } |
| 1028 | 1030 | ||
| 1029 | man.writeManifest() catch |err| { | 1031 | man.writeManifest() catch |err| { |
| ... | @@ -1046,6 +1048,7 @@ fn buildSharedLib( | ... | @@ -1046,6 +1048,7 @@ fn buildSharedLib( |
| 1046 | bin_directory: Compilation.Directory, | 1048 | bin_directory: Compilation.Directory, |
| 1047 | asm_file_basename: []const u8, | 1049 | asm_file_basename: []const u8, |
| 1048 | lib: Lib, | 1050 | lib: Lib, |
| 1051 | prog_node: *std.Progress.Node, | ||
| 1049 | ) !void { | 1052 | ) !void { |
| 1050 | const tracy = trace(@src()); | 1053 | const tracy = trace(@src()); |
| 1051 | defer tracy.end(); | 1054 | defer tracy.end(); |
| ... | @@ -1105,7 +1108,7 @@ fn buildSharedLib( | ... | @@ -1105,7 +1108,7 @@ fn buildSharedLib( |
| 1105 | }); | 1108 | }); |
| 1106 | defer sub_compilation.destroy(); | 1109 | defer sub_compilation.destroy(); |
| 1107 | 1110 | ||
| 1108 | try sub_compilation.updateSubCompilation(); | 1111 | try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node); |
| 1109 | } | 1112 | } |
| 1110 | 1113 | ||
| 1111 | // Return true if glibc has crti/crtn sources for that architecture. | 1114 | // Return true if glibc has crti/crtn sources for that architecture. |
src/libcxx.zig+4-4| ... | @@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{ | ... | @@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{ |
| 96 | "src/verbose_abort.cpp", | 96 | "src/verbose_abort.cpp", |
| 97 | }; | 97 | }; |
| 98 | 98 | ||
| 99 | pub fn buildLibCXX(comp: *Compilation) !void { | 99 | pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 100 | if (!build_options.have_llvm) { | 100 | if (!build_options.have_llvm) { |
| 101 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 101 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 102 | } | 102 | } |
| ... | @@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { | ... | @@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 258 | }); | 258 | }); |
| 259 | defer sub_compilation.destroy(); | 259 | defer sub_compilation.destroy(); |
| 260 | 260 | ||
| 261 | try sub_compilation.updateSubCompilation(); | 261 | try comp.updateSubCompilation(sub_compilation, .libcxx, prog_node); |
| 262 | 262 | ||
| 263 | assert(comp.libcxx_static_lib == null); | 263 | assert(comp.libcxx_static_lib == null); |
| 264 | comp.libcxx_static_lib = Compilation.CRTFile{ | 264 | comp.libcxx_static_lib = Compilation.CRTFile{ |
| ... | @@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { | ... | @@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 269 | }; | 269 | }; |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | pub fn buildLibCXXABI(comp: *Compilation) !void { | 272 | pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 273 | if (!build_options.have_llvm) { | 273 | if (!build_options.have_llvm) { |
| 274 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 274 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 275 | } | 275 | } |
| ... | @@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { | ... | @@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { |
| 418 | }); | 418 | }); |
| 419 | defer sub_compilation.destroy(); | 419 | defer sub_compilation.destroy(); |
| 420 | 420 | ||
| 421 | try sub_compilation.updateSubCompilation(); | 421 | try comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node); |
| 422 | 422 | ||
| 423 | assert(comp.libcxxabi_static_lib == null); | 423 | assert(comp.libcxxabi_static_lib == null); |
| 424 | comp.libcxxabi_static_lib = Compilation.CRTFile{ | 424 | comp.libcxxabi_static_lib = Compilation.CRTFile{ |
src/libtsan.zig+2-2| ... | @@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig"); | ... | @@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig"); |
| 5 | const build_options = @import("build_options"); | 5 | const build_options = @import("build_options"); |
| 6 | const trace = @import("tracy.zig").trace; | 6 | const trace = @import("tracy.zig").trace; |
| 7 | 7 | ||
| 8 | pub fn buildTsan(comp: *Compilation) !void { | 8 | pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 9 | if (!build_options.have_llvm) { | 9 | if (!build_options.have_llvm) { |
| 10 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 10 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 11 | } | 11 | } |
| ... | @@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void { | ... | @@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 235 | }); | 235 | }); |
| 236 | defer sub_compilation.destroy(); | 236 | defer sub_compilation.destroy(); |
| 237 | 237 | ||
| 238 | try sub_compilation.updateSubCompilation(); | 238 | try comp.updateSubCompilation(sub_compilation, .libtsan, prog_node); |
| 239 | 239 | ||
| 240 | assert(comp.tsan_static_lib == null); | 240 | assert(comp.tsan_static_lib == null); |
| 241 | comp.tsan_static_lib = Compilation.CRTFile{ | 241 | comp.tsan_static_lib = Compilation.CRTFile{ |
src/libunwind.zig+2-2| ... | @@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig"); | ... | @@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig"); |
| 7 | const build_options = @import("build_options"); | 7 | const build_options = @import("build_options"); |
| 8 | const trace = @import("tracy.zig").trace; | 8 | const trace = @import("tracy.zig").trace; |
| 9 | 9 | ||
| 10 | pub fn buildStaticLib(comp: *Compilation) !void { | 10 | pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 11 | if (!build_options.have_llvm) { | 11 | if (!build_options.have_llvm) { |
| 12 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 12 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 13 | } | 13 | } |
| ... | @@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { | ... | @@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { |
| 130 | }); | 130 | }); |
| 131 | defer sub_compilation.destroy(); | 131 | defer sub_compilation.destroy(); |
| 132 | 132 | ||
| 133 | try sub_compilation.updateSubCompilation(); | 133 | try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node); |
| 134 | 134 | ||
| 135 | assert(comp.libunwind_static_lib == null); | 135 | assert(comp.libunwind_static_lib == null); |
| 136 | 136 |
src/link.zig+42-3| ... | @@ -264,6 +264,8 @@ pub const File = struct { | ... | @@ -264,6 +264,8 @@ pub const File = struct { |
| 264 | /// of this linking operation. | 264 | /// of this linking operation. |
| 265 | lock: ?Cache.Lock = null, | 265 | lock: ?Cache.Lock = null, |
| 266 | 266 | ||
| 267 | child_pid: ?std.ChildProcess.Id = null, | ||
| 268 | |||
| 267 | /// Attempts incremental linking, if the file already exists. If | 269 | /// Attempts incremental linking, if the file already exists. If |
| 268 | /// incremental linking fails, falls back to truncating the file and | 270 | /// incremental linking fails, falls back to truncating the file and |
| 269 | /// rewriting it. A malicious file is detected as incremental link failure | 271 | /// rewriting it. A malicious file is detected as incremental link failure |
| ... | @@ -376,6 +378,26 @@ pub const File = struct { | ... | @@ -376,6 +378,26 @@ pub const File = struct { |
| 376 | if (build_options.only_c) unreachable; | 378 | if (build_options.only_c) unreachable; |
| 377 | if (base.file != null) return; | 379 | if (base.file != null) return; |
| 378 | const emit = base.options.emit orelse return; | 380 | const emit = base.options.emit orelse return; |
| 381 | if (base.child_pid) |pid| { | ||
| 382 | // If we try to open the output file in write mode while it is running, | ||
| 383 | // it will return ETXTBSY. So instead, we copy the file, atomically rename it | ||
| 384 | // over top of the exe path, and then proceed normally. This changes the inode, | ||
| 385 | // avoiding the error. | ||
| 386 | const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{ | ||
| 387 | emit.sub_path, std.crypto.random.int(u32), | ||
| 388 | }); | ||
| 389 | try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); | ||
| 390 | try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); | ||
| 391 | switch (builtin.os.tag) { | ||
| 392 | .linux => { | ||
| 393 | switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) { | ||
| 394 | .SUCCESS => {}, | ||
| 395 | else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), | ||
| 396 | } | ||
| 397 | }, | ||
| 398 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 399 | } | ||
| 400 | } | ||
| 379 | base.file = try emit.directory.handle.createFile(emit.sub_path, .{ | 401 | base.file = try emit.directory.handle.createFile(emit.sub_path, .{ |
| 380 | .truncate = false, | 402 | .truncate = false, |
| 381 | .read = true, | 403 | .read = true, |
| ... | @@ -424,6 +446,18 @@ pub const File = struct { | ... | @@ -424,6 +446,18 @@ pub const File = struct { |
| 424 | } | 446 | } |
| 425 | f.close(); | 447 | f.close(); |
| 426 | base.file = null; | 448 | base.file = null; |
| 449 | |||
| 450 | if (base.child_pid) |pid| { | ||
| 451 | switch (builtin.os.tag) { | ||
| 452 | .linux => { | ||
| 453 | switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) { | ||
| 454 | .SUCCESS => {}, | ||
| 455 | else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), | ||
| 456 | } | ||
| 457 | }, | ||
| 458 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 459 | } | ||
| 460 | } | ||
| 427 | }, | 461 | }, |
| 428 | .c, .spirv, .nvptx => {}, | 462 | .c, .spirv, .nvptx => {}, |
| 429 | } | 463 | } |
| ... | @@ -460,6 +494,9 @@ pub const File = struct { | ... | @@ -460,6 +494,9 @@ pub const File = struct { |
| 460 | CurrentWorkingDirectoryUnlinked, | 494 | CurrentWorkingDirectoryUnlinked, |
| 461 | LockViolation, | 495 | LockViolation, |
| 462 | NetNameDeleted, | 496 | NetNameDeleted, |
| 497 | DeviceBusy, | ||
| 498 | InvalidArgument, | ||
| 499 | HotSwapUnavailableOnHostOperatingSystem, | ||
| 463 | }; | 500 | }; |
| 464 | 501 | ||
| 465 | /// Called from within the CodeGen to lower a local variable instantion as an unnamed | 502 | /// Called from within the CodeGen to lower a local variable instantion as an unnamed |
| ... | @@ -1051,9 +1088,11 @@ pub const File = struct { | ... | @@ -1051,9 +1088,11 @@ pub const File = struct { |
| 1051 | log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); | 1088 | log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); |
| 1052 | }; | 1089 | }; |
| 1053 | 1090 | ||
| 1054 | man.writeManifest() catch |err| { | 1091 | if (man.have_exclusive_lock) { |
| 1055 | log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); | 1092 | man.writeManifest() catch |err| { |
| 1056 | }; | 1093 | log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); |
| 1094 | }; | ||
| 1095 | } | ||
| 1057 | 1096 | ||
| 1058 | base.lock = man.toOwnedLock(); | 1097 | base.lock = man.toOwnedLock(); |
| 1059 | } | 1098 | } |
src/link/C.zig+14-8| ... | @@ -221,14 +221,19 @@ pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void | ... | @@ -221,14 +221,19 @@ pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void |
| 221 | return self.flushModule(comp, prog_node); | 221 | return self.flushModule(comp, prog_node); |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | fn abiDefine(comp: *Compilation) ?[]const u8 { | 224 | fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { |
| 225 | return switch (comp.getTarget().abi) { | 225 | var defines = std.ArrayList(u8).init(self.base.allocator); |
| 226 | .msvc => "#define ZIG_TARGET_ABI_MSVC\n", | 226 | errdefer defines.deinit(); |
| 227 | else => null, | 227 | const writer = defines.writer(); |
| 228 | }; | 228 | switch (target.abi) { |
| 229 | .msvc => try writer.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), | ||
| 230 | else => {}, | ||
| 231 | } | ||
| 232 | try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{target.maxIntAlignment()}); | ||
| 233 | return defines; | ||
| 229 | } | 234 | } |
| 230 | 235 | ||
| 231 | pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 236 | pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !void { |
| 232 | const tracy = trace(@src()); | 237 | const tracy = trace(@src()); |
| 233 | defer tracy.end(); | 238 | defer tracy.end(); |
| 234 | 239 | ||
| ... | @@ -245,12 +250,13 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -245,12 +250,13 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) |
| 245 | var f: Flush = .{}; | 250 | var f: Flush = .{}; |
| 246 | defer f.deinit(gpa); | 251 | defer f.deinit(gpa); |
| 247 | 252 | ||
| 248 | const abi_define = abiDefine(comp); | 253 | const abi_defines = try self.abiDefines(module.getTarget()); |
| 254 | defer abi_defines.deinit(); | ||
| 249 | 255 | ||
| 250 | // Covers defines, zig.h, ctypes, asm, lazy fwd. | 256 | // Covers defines, zig.h, ctypes, asm, lazy fwd. |
| 251 | try f.all_buffers.ensureUnusedCapacity(gpa, 5); | 257 | try f.all_buffers.ensureUnusedCapacity(gpa, 5); |
| 252 | 258 | ||
| 253 | if (abi_define) |buf| f.appendBufAssumeCapacity(buf); | 259 | f.appendBufAssumeCapacity(abi_defines.items); |
| 254 | f.appendBufAssumeCapacity(zig_h); | 260 | f.appendBufAssumeCapacity(zig_h); |
| 255 | 261 | ||
| 256 | const ctypes_index = f.all_buffers.items.len; | 262 | const ctypes_index = f.all_buffers.items.len; |
src/link/Coff.zig+1-1| ... | @@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In | ... | @@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 1060 | decl.analysis = .codegen_failure; | 1060 | decl.analysis = .codegen_failure; |
| 1061 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 1061 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 1062 | log.err("{s}", .{em.msg}); | 1062 | log.err("{s}", .{em.msg}); |
| 1063 | return error.AnalysisFail; | 1063 | return error.CodegenFail; |
| 1064 | }, | 1064 | }, |
| 1065 | }; | 1065 | }; |
| 1066 | 1066 |
src/link/Elf.zig+60-8| ... | @@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 467 | .p_paddr = entry_addr, | 467 | .p_paddr = entry_addr, |
| 468 | .p_memsz = file_size, | 468 | .p_memsz = file_size, |
| 469 | .p_align = p_align, | 469 | .p_align = p_align, |
| 470 | .p_flags = elf.PF_X | elf.PF_R, | 470 | .p_flags = elf.PF_X | elf.PF_R | elf.PF_W, |
| 471 | }); | 471 | }); |
| 472 | self.entry_addr = null; | 472 | self.entry_addr = null; |
| 473 | self.phdr_table_dirty = true; | 473 | self.phdr_table_dirty = true; |
| ... | @@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 493 | .p_paddr = got_addr, | 493 | .p_paddr = got_addr, |
| 494 | .p_memsz = file_size, | 494 | .p_memsz = file_size, |
| 495 | .p_align = p_align, | 495 | .p_align = p_align, |
| 496 | .p_flags = elf.PF_R, | 496 | .p_flags = elf.PF_R | elf.PF_W, |
| 497 | }); | 497 | }); |
| 498 | self.phdr_table_dirty = true; | 498 | self.phdr_table_dirty = true; |
| 499 | } | 499 | } |
| ... | @@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 516 | .p_paddr = rodata_addr, | 516 | .p_paddr = rodata_addr, |
| 517 | .p_memsz = file_size, | 517 | .p_memsz = file_size, |
| 518 | .p_align = p_align, | 518 | .p_align = p_align, |
| 519 | .p_flags = elf.PF_R, | 519 | .p_flags = elf.PF_R | elf.PF_W, |
| 520 | }); | 520 | }); |
| 521 | self.phdr_table_dirty = true; | 521 | self.phdr_table_dirty = true; |
| 522 | } | 522 | } |
| ... | @@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { | ... | @@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { |
| 2097 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | 2097 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 2098 | const local_sym_index = atom.getSymbolIndex().?; | 2098 | const local_sym_index = atom.getSymbolIndex().?; |
| 2099 | 2099 | ||
| 2100 | log.debug("adding %{d} to local symbols free list", .{local_sym_index}); | ||
| 2100 | self.local_symbol_free_list.append(gpa, local_sym_index) catch {}; | 2101 | self.local_symbol_free_list.append(gpa, local_sym_index) catch {}; |
| 2101 | self.local_symbols.items[local_sym_index].st_info = 0; | 2102 | self.local_symbols.items[local_sym_index] = .{ |
| 2102 | self.local_symbols.items[local_sym_index].st_shndx = 0; | 2103 | .st_name = 0, |
| 2104 | .st_info = 0, | ||
| 2105 | .st_other = 0, | ||
| 2106 | .st_shndx = 0, | ||
| 2107 | .st_value = 0, | ||
| 2108 | .st_size = 0, | ||
| 2109 | }; | ||
| 2103 | _ = self.atom_by_index_table.remove(local_sym_index); | 2110 | _ = self.atom_by_index_table.remove(local_sym_index); |
| 2104 | self.getAtomPtr(atom_index).local_sym_index = 0; | 2111 | self.getAtomPtr(atom_index).local_sym_index = 0; |
| 2105 | 2112 | ||
| ... | @@ -2159,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme | ... | @@ -2159,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme |
| 2159 | // First we look for an appropriately sized free list node. | 2166 | // First we look for an appropriately sized free list node. |
| 2160 | // The list is unordered. We'll just take the first thing that works. | 2167 | // The list is unordered. We'll just take the first thing that works. |
| 2161 | const vaddr = blk: { | 2168 | const vaddr = blk: { |
| 2162 | var i: usize = 0; | 2169 | var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; |
| 2163 | while (i < free_list.items.len) { | 2170 | while (i < free_list.items.len) { |
| 2164 | const big_atom_index = free_list.items[i]; | 2171 | const big_atom_index = free_list.items[i]; |
| 2165 | const big_atom = self.getAtom(big_atom_index); | 2172 | const big_atom = self.getAtom(big_atom_index); |
| ... | @@ -2390,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s | ... | @@ -2390,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2390 | const atom = self.getAtom(atom_index); | 2397 | const atom = self.getAtom(atom_index); |
| 2391 | 2398 | ||
| 2392 | const shdr_index = decl_metadata.shdr; | 2399 | const shdr_index = decl_metadata.shdr; |
| 2393 | if (atom.getSymbol(self).st_size != 0) { | 2400 | if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) { |
| 2394 | const local_sym = atom.getSymbolPtr(self); | 2401 | const local_sym = atom.getSymbolPtr(self); |
| 2395 | local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); | 2402 | local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); |
| 2396 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | 2403 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; |
| ... | @@ -2444,6 +2451,28 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s | ... | @@ -2444,6 +2451,28 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2444 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; | 2451 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; |
| 2445 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; | 2452 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; |
| 2446 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; | 2453 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; |
| 2454 | |||
| 2455 | if (self.base.child_pid) |pid| { | ||
| 2456 | switch (builtin.os.tag) { | ||
| 2457 | .linux => { | ||
| 2458 | var code_vec: [1]std.os.iovec_const = .{.{ | ||
| 2459 | .iov_base = code.ptr, | ||
| 2460 | .iov_len = code.len, | ||
| 2461 | }}; | ||
| 2462 | var remote_vec: [1]std.os.iovec_const = .{.{ | ||
| 2463 | .iov_base = @intToPtr([*]u8, @intCast(usize, local_sym.st_value)), | ||
| 2464 | .iov_len = code.len, | ||
| 2465 | }}; | ||
| 2466 | const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); | ||
| 2467 | switch (std.os.errno(rc)) { | ||
| 2468 | .SUCCESS => assert(rc == code.len), | ||
| 2469 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | ||
| 2470 | } | ||
| 2471 | }, | ||
| 2472 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 2473 | } | ||
| 2474 | } | ||
| 2475 | |||
| 2447 | try self.base.file.?.pwriteAll(code, file_offset); | 2476 | try self.base.file.?.pwriteAll(code, file_offset); |
| 2448 | 2477 | ||
| 2449 | return local_sym; | 2478 | return local_sym; |
| ... | @@ -2618,7 +2647,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module | ... | @@ -2618,7 +2647,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module |
| 2618 | decl.analysis = .codegen_failure; | 2647 | decl.analysis = .codegen_failure; |
| 2619 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 2648 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 2620 | log.err("{s}", .{em.msg}); | 2649 | log.err("{s}", .{em.msg}); |
| 2621 | return error.AnalysisFail; | 2650 | return error.CodegenFail; |
| 2622 | }, | 2651 | }, |
| 2623 | }; | 2652 | }; |
| 2624 | 2653 | ||
| ... | @@ -2813,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | ... | @@ -2813,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2813 | const endian = self.base.options.target.cpu.arch.endian(); | 2842 | const endian = self.base.options.target.cpu.arch.endian(); |
| 2814 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; | 2843 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; |
| 2815 | const off = shdr.sh_offset + @as(u64, entry_size) * index; | 2844 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2845 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | ||
| 2846 | const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; | ||
| 2816 | switch (entry_size) { | 2847 | switch (entry_size) { |
| 2817 | 2 => { | 2848 | 2 => { |
| 2818 | var buf: [2]u8 = undefined; | 2849 | var buf: [2]u8 = undefined; |
| ... | @@ -2828,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | ... | @@ -2828,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2828 | var buf: [8]u8 = undefined; | 2859 | var buf: [8]u8 = undefined; |
| 2829 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | 2860 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| 2830 | try self.base.file.?.pwriteAll(&buf, off); | 2861 | try self.base.file.?.pwriteAll(&buf, off); |
| 2862 | |||
| 2863 | if (self.base.child_pid) |pid| { | ||
| 2864 | switch (builtin.os.tag) { | ||
| 2865 | .linux => { | ||
| 2866 | var local_vec: [1]std.os.iovec_const = .{.{ | ||
| 2867 | .iov_base = &buf, | ||
| 2868 | .iov_len = buf.len, | ||
| 2869 | }}; | ||
| 2870 | var remote_vec: [1]std.os.iovec_const = .{.{ | ||
| 2871 | .iov_base = @intToPtr([*]u8, @intCast(usize, vaddr)), | ||
| 2872 | .iov_len = buf.len, | ||
| 2873 | }}; | ||
| 2874 | const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); | ||
| 2875 | switch (std.os.errno(rc)) { | ||
| 2876 | .SUCCESS => assert(rc == buf.len), | ||
| 2877 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | ||
| 2878 | } | ||
| 2879 | }, | ||
| 2880 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 2881 | } | ||
| 2882 | } | ||
| 2831 | }, | 2883 | }, |
| 2832 | else => unreachable, | 2884 | else => unreachable, |
| 2833 | } | 2885 | } |
src/link/MachO.zig+1-1| ... | @@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu | ... | @@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2089 | decl.analysis = .codegen_failure; | 2089 | decl.analysis = .codegen_failure; |
| 2090 | try module.failed_decls.put(module.gpa, decl_index, em); | 2090 | try module.failed_decls.put(module.gpa, decl_index, em); |
| 2091 | log.err("{s}", .{em.msg}); | 2091 | log.err("{s}", .{em.msg}); |
| 2092 | return error.AnalysisFail; | 2092 | return error.CodegenFail; |
| 2093 | }, | 2093 | }, |
| 2094 | }; | 2094 | }; |
| 2095 | 2095 |
src/link/MachO/CodeSignature.zig+2-2| ... | @@ -7,12 +7,12 @@ const log = std.log.scoped(.link); | ... | @@ -7,12 +7,12 @@ const log = std.log.scoped(.link); |
| 7 | const macho = std.macho; | 7 | const macho = std.macho; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | const testing = std.testing; | 9 | const testing = std.testing; |
| 10 | const ThreadPool = std.Thread.Pool; | ||
| 11 | const WaitGroup = std.Thread.WaitGroup; | ||
| 10 | 12 | ||
| 11 | const Allocator = mem.Allocator; | 13 | const Allocator = mem.Allocator; |
| 12 | const Compilation = @import("../../Compilation.zig"); | 14 | const Compilation = @import("../../Compilation.zig"); |
| 13 | const Sha256 = std.crypto.hash.sha2.Sha256; | 15 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 14 | const ThreadPool = @import("../../ThreadPool.zig"); | ||
| 15 | const WaitGroup = @import("../../WaitGroup.zig"); | ||
| 16 | 16 | ||
| 17 | const hash_size = Sha256.digest_length; | 17 | const hash_size = Sha256.digest_length; |
| 18 | 18 |
src/link/Plan9.zig+1-1| ... | @@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I | ... | @@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I |
| 377 | decl.analysis = .codegen_failure; | 377 | decl.analysis = .codegen_failure; |
| 378 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 378 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 379 | log.err("{s}", .{em.msg}); | 379 | log.err("{s}", .{em.msg}); |
| 380 | return error.AnalysisFail; | 380 | return error.CodegenFail; |
| 381 | }, | 381 | }, |
| 382 | }; | 382 | }; |
| 383 | // duped_code is freed when the unnamed const is freed | 383 | // duped_code is freed when the unnamed const is freed |
src/link/Wasm.zig+79-46| ... | @@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol | ... | @@ -468,6 +468,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 468 | .flags = 0, | 468 | .flags = 0, |
| 469 | .tag = tag, | 469 | .tag = tag, |
| 470 | .index = undefined, | 470 | .index = undefined, |
| 471 | .virtual_address = undefined, | ||
| 471 | }); | 472 | }); |
| 472 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); | 473 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); |
| 473 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); | 474 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); |
| ... | @@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void { | ... | @@ -886,32 +887,12 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 886 | const loc = try wasm.createSyntheticSymbol("__heap_base", .data); | 887 | const loc = try wasm.createSyntheticSymbol("__heap_base", .data); |
| 887 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); | 888 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 888 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. | 889 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. |
| 889 | |||
| 890 | // TODO: Can we use `createAtom` here while also re-using the symbol | ||
| 891 | // from `createSyntheticSymbol`. | ||
| 892 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | ||
| 893 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | ||
| 894 | atom.* = Atom.empty; | ||
| 895 | atom.sym_index = loc.index; | ||
| 896 | atom.alignment = 1; | ||
| 897 | |||
| 898 | try wasm.parseAtom(atom_index, .{ .data = .synthetic }); | ||
| 899 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); | ||
| 900 | } | 890 | } |
| 901 | 891 | ||
| 902 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { | 892 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { |
| 903 | const loc = try wasm.createSyntheticSymbol("__heap_end", .data); | 893 | const loc = try wasm.createSyntheticSymbol("__heap_end", .data); |
| 904 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); | 894 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 905 | _ = wasm.resolved_symbols.swapRemove(loc); | 895 | _ = wasm.resolved_symbols.swapRemove(loc); |
| 906 | |||
| 907 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | ||
| 908 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | ||
| 909 | atom.* = Atom.empty; | ||
| 910 | atom.sym_index = loc.index; | ||
| 911 | atom.alignment = 1; | ||
| 912 | |||
| 913 | try wasm.parseAtom(atom_index, .{ .data = .synthetic }); | ||
| 914 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); | ||
| 915 | } | 896 | } |
| 916 | } | 897 | } |
| 917 | 898 | ||
| ... | @@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 { | ... | @@ -1011,6 +992,7 @@ pub fn allocateSymbol(wasm: *Wasm) !u32 { |
| 1011 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | 992 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1012 | .tag = undefined, // will be set after updateDecl | 993 | .tag = undefined, // will be set after updateDecl |
| 1013 | .index = undefined, // will be set after updateDecl | 994 | .index = undefined, // will be set after updateDecl |
| 995 | .virtual_address = undefined, // will be set during atom allocation | ||
| 1014 | }; | 996 | }; |
| 1015 | if (wasm.symbols_free_list.popOrNull()) |index| { | 997 | if (wasm.symbols_free_list.popOrNull()) |index| { |
| 1016 | wasm.symbols.items[index] = symbol; | 998 | wasm.symbols.items[index] = symbol; |
| ... | @@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In | ... | @@ -1246,6 +1228,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In |
| 1246 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | 1228 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1247 | .tag = .data, | 1229 | .tag = .data, |
| 1248 | .index = undefined, | 1230 | .index = undefined, |
| 1231 | .virtual_address = undefined, | ||
| 1249 | }; | 1232 | }; |
| 1250 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); | 1233 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); |
| 1251 | 1234 | ||
| ... | @@ -1265,7 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In | ... | @@ -1265,7 +1248,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In |
| 1265 | .fail => |em| { | 1248 | .fail => |em| { |
| 1266 | decl.analysis = .codegen_failure; | 1249 | decl.analysis = .codegen_failure; |
| 1267 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 1250 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| 1268 | return error.AnalysisFail; | 1251 | return error.CodegenFail; |
| 1269 | }, | 1252 | }, |
| 1270 | }; | 1253 | }; |
| 1271 | }; | 1254 | }; |
| ... | @@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 { | ... | @@ -1292,6 +1275,7 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8) !u32 { |
| 1292 | .flags = 0, | 1275 | .flags = 0, |
| 1293 | .index = undefined, // index to type will be set after merging function symbols | 1276 | .index = undefined, // index to type will be set after merging function symbols |
| 1294 | .tag = .function, | 1277 | .tag = .function, |
| 1278 | .virtual_address = undefined, | ||
| 1295 | }; | 1279 | }; |
| 1296 | symbol.setGlobal(true); | 1280 | symbol.setGlobal(true); |
| 1297 | symbol.setUndefined(true); | 1281 | symbol.setUndefined(true); |
| ... | @@ -1610,7 +1594,6 @@ const Kind = union(enum) { | ... | @@ -1610,7 +1594,6 @@ const Kind = union(enum) { |
| 1610 | read_only, | 1594 | read_only, |
| 1611 | uninitialized, | 1595 | uninitialized, |
| 1612 | initialized, | 1596 | initialized, |
| 1613 | synthetic, | ||
| 1614 | }, | 1597 | }, |
| 1615 | function: void, | 1598 | function: void, |
| 1616 | 1599 | ||
| ... | @@ -1621,7 +1604,6 @@ const Kind = union(enum) { | ... | @@ -1621,7 +1604,6 @@ const Kind = union(enum) { |
| 1621 | .read_only => return ".rodata.", | 1604 | .read_only => return ".rodata.", |
| 1622 | .uninitialized => return ".bss.", | 1605 | .uninitialized => return ".bss.", |
| 1623 | .initialized => return ".data.", | 1606 | .initialized => return ".data.", |
| 1624 | .synthetic => return ".synthetic", | ||
| 1625 | } | 1607 | } |
| 1626 | } | 1608 | } |
| 1627 | }; | 1609 | }; |
| ... | @@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -1788,6 +1770,30 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1788 | } | 1770 | } |
| 1789 | } | 1771 | } |
| 1790 | 1772 | ||
| 1773 | /// For each data symbol, sets the virtual address. | ||
| 1774 | fn 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 | |||
| 1791 | fn sortDataSegments(wasm: *Wasm) !void { | 1797 | fn sortDataSegments(wasm: *Wasm) !void { |
| 1792 | var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{}; | 1798 | var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{}; |
| 1793 | try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count()); | 1799 | try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count()); |
| ... | @@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void { | ... | @@ -1805,7 +1811,6 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1805 | if (mem.startsWith(u8, name, ".rodata")) return 0; | 1811 | if (mem.startsWith(u8, name, ".rodata")) return 0; |
| 1806 | if (mem.startsWith(u8, name, ".data")) return 1; | 1812 | if (mem.startsWith(u8, name, ".data")) return 1; |
| 1807 | if (mem.startsWith(u8, name, ".text")) return 2; | 1813 | if (mem.startsWith(u8, name, ".text")) return 2; |
| 1808 | if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end | ||
| 1809 | return 3; | 1814 | return 3; |
| 1810 | } | 1815 | } |
| 1811 | }; | 1816 | }; |
| ... | @@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -2137,13 +2142,10 @@ fn setupExports(wasm: *Wasm) !void { |
| 2137 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); | 2142 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); |
| 2138 | }; | 2143 | }; |
| 2139 | const exp: types.Export = if (symbol.tag == .data) exp: { | 2144 | const exp: types.Export = if (symbol.tag == .data) exp: { |
| 2140 | const atom_index = wasm.symbol_atom.get(sym_loc).?; | ||
| 2141 | const atom = wasm.getAtom(atom_index); | ||
| 2142 | const va = atom.getVA(wasm, symbol); | ||
| 2143 | const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len); | 2145 | const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| 2144 | try wasm.wasm_globals.append(wasm.base.allocator, .{ | 2146 | try wasm.wasm_globals.append(wasm.base.allocator, .{ |
| 2145 | .global_type = .{ .valtype = .i32, .mutable = false }, | 2147 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| 2146 | .init = .{ .i32_const = @intCast(i32, va) }, | 2148 | .init = .{ .i32_const = @intCast(i32, symbol.virtual_address) }, |
| 2147 | }); | 2149 | }); |
| 2148 | break :exp .{ | 2150 | break :exp .{ |
| 2149 | .name = export_name, | 2151 | .name = export_name, |
| ... | @@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2220,10 +2222,6 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2220 | var offset: u32 = @intCast(u32, memory_ptr); | 2222 | var offset: u32 = @intCast(u32, memory_ptr); |
| 2221 | var data_seg_it = wasm.data_segments.iterator(); | 2223 | var data_seg_it = wasm.data_segments.iterator(); |
| 2222 | while (data_seg_it.next()) |entry| { | 2224 | while (data_seg_it.next()) |entry| { |
| 2223 | if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) { | ||
| 2224 | // do not update synthetic segments as they are not part of the output | ||
| 2225 | continue; | ||
| 2226 | } | ||
| 2227 | const segment = &wasm.segments.items[entry.value_ptr.*]; | 2225 | const segment = &wasm.segments.items[entry.value_ptr.*]; |
| 2228 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); | 2226 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); |
| 2229 | memory_ptr += segment.size; | 2227 | memory_ptr += segment.size; |
| ... | @@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2240,12 +2238,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2240 | // One of the linked object files has a reference to the __heap_base symbol. | 2238 | // One of the linked object files has a reference to the __heap_base symbol. |
| 2241 | // We must set its virtual address so it can be used in relocations. | 2239 | // We must set its virtual address so it can be used in relocations. |
| 2242 | if (wasm.findGlobalSymbol("__heap_base")) |loc| { | 2240 | if (wasm.findGlobalSymbol("__heap_base")) |loc| { |
| 2243 | const segment_index = wasm.data_segments.get(".synthetic").?; | 2241 | const symbol = loc.getSymbol(wasm); |
| 2244 | const segment = &wasm.segments.items[segment_index]; | 2242 | symbol.virtual_address = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); |
| 2245 | segment.offset = 0; // for simplicity we store the entire VA into atom's offset. | ||
| 2246 | const atom_index = wasm.symbol_atom.get(loc).?; | ||
| 2247 | const atom = wasm.getAtomPtr(atom_index); | ||
| 2248 | atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); | ||
| 2249 | } | 2243 | } |
| 2250 | 2244 | ||
| 2251 | // Setup the max amount of pages | 2245 | // Setup the max amount of pages |
| ... | @@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2274,12 +2268,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2274 | log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); | 2268 | log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); |
| 2275 | 2269 | ||
| 2276 | if (wasm.findGlobalSymbol("__heap_end")) |loc| { | 2270 | if (wasm.findGlobalSymbol("__heap_end")) |loc| { |
| 2277 | const segment_index = wasm.data_segments.get(".synthetic").?; | 2271 | const symbol = loc.getSymbol(wasm); |
| 2278 | const segment = &wasm.segments.items[segment_index]; | 2272 | symbol.virtual_address = @intCast(u32, memory_ptr); |
| 2279 | segment.offset = 0; | ||
| 2280 | const atom_index = wasm.symbol_atom.get(loc).?; | ||
| 2281 | const atom = wasm.getAtomPtr(atom_index); | ||
| 2282 | atom.offset = @intCast(u32, memory_ptr); | ||
| 2283 | } | 2273 | } |
| 2284 | 2274 | ||
| 2285 | if (wasm.base.options.max_memory) |max_memory| { | 2275 | if (wasm.base.options.max_memory) |max_memory| { |
| ... | @@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { | ... | @@ -2417,6 +2407,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2417 | .tag = .data, | 2407 | .tag = .data, |
| 2418 | .flags = 0, | 2408 | .flags = 0, |
| 2419 | .index = 0, | 2409 | .index = 0, |
| 2410 | .virtual_address = undefined, | ||
| 2420 | }; | 2411 | }; |
| 2421 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 2412 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 2422 | 2413 | ||
| ... | @@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { | ... | @@ -2449,6 +2440,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2449 | .tag = .data, | 2440 | .tag = .data, |
| 2450 | .flags = 0, | 2441 | .flags = 0, |
| 2451 | .index = 0, | 2442 | .index = 0, |
| 2443 | .virtual_address = undefined, | ||
| 2452 | }; | 2444 | }; |
| 2453 | names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 2445 | names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 2454 | 2446 | ||
| ... | @@ -2635,6 +2627,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2635,6 +2627,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2635 | try man.addOptionalFile(compiler_rt_path); | 2627 | try man.addOptionalFile(compiler_rt_path); |
| 2636 | man.hash.addOptionalBytes(options.entry); | 2628 | man.hash.addOptionalBytes(options.entry); |
| 2637 | man.hash.addOptional(options.stack_size_override); | 2629 | man.hash.addOptional(options.stack_size_override); |
| 2630 | man.hash.add(wasm.base.options.build_id); | ||
| 2638 | man.hash.add(options.import_memory); | 2631 | man.hash.add(options.import_memory); |
| 2639 | man.hash.add(options.import_table); | 2632 | man.hash.add(options.import_table); |
| 2640 | man.hash.add(options.export_table); | 2633 | man.hash.add(options.export_table); |
| ... | @@ -2748,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2748,6 +2741,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2748 | 2741 | ||
| 2749 | try wasm.allocateAtoms(); | 2742 | try wasm.allocateAtoms(); |
| 2750 | try wasm.setupMemory(); | 2743 | try wasm.setupMemory(); |
| 2744 | wasm.allocateVirtualAddresses(); | ||
| 2751 | wasm.mapFunctionTable(); | 2745 | wasm.mapFunctionTable(); |
| 2752 | try wasm.mergeSections(); | 2746 | try wasm.mergeSections(); |
| 2753 | try wasm.mergeTypes(); | 2747 | try wasm.mergeTypes(); |
| ... | @@ -2866,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2866,6 +2860,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2866 | 2860 | ||
| 2867 | try wasm.allocateAtoms(); | 2861 | try wasm.allocateAtoms(); |
| 2868 | try wasm.setupMemory(); | 2862 | try wasm.setupMemory(); |
| 2863 | wasm.allocateVirtualAddresses(); | ||
| 2869 | wasm.mapFunctionTable(); | 2864 | wasm.mapFunctionTable(); |
| 2870 | try wasm.mergeSections(); | 2865 | try wasm.mergeSections(); |
| 2871 | try wasm.mergeTypes(); | 2866 | try wasm.mergeTypes(); |
| ... | @@ -3225,6 +3220,12 @@ fn writeToFile( | ... | @@ -3225,6 +3220,12 @@ fn writeToFile( |
| 3225 | } | 3220 | } |
| 3226 | 3221 | ||
| 3227 | if (!wasm.base.options.strip) { | 3222 | if (!wasm.base.options.strip) { |
| 3223 | // The build id must be computed on the main sections only, | ||
| 3224 | // so we have to do it now, before the debug sections. | ||
| 3225 | if (wasm.base.options.build_id) { | ||
| 3226 | try emitBuildIdSection(&binary_bytes); | ||
| 3227 | } | ||
| 3228 | |||
| 3228 | // if (wasm.dwarf) |*dwarf| { | 3229 | // if (wasm.dwarf) |*dwarf| { |
| 3229 | // const mod = wasm.base.options.module.?; | 3230 | // const mod = wasm.base.options.module.?; |
| 3230 | // try dwarf.writeDbgAbbrev(); | 3231 | // try dwarf.writeDbgAbbrev(); |
| ... | @@ -3363,6 +3364,33 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { | ... | @@ -3363,6 +3364,33 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { |
| 3363 | ); | 3364 | ); |
| 3364 | } | 3365 | } |
| 3365 | 3366 | ||
| 3367 | fn 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 | |||
| 3366 | fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void { | 3394 | fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void { |
| 3367 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | 3395 | const header_offset = try reserveCustomSectionHeader(binary_bytes); |
| 3368 | 3396 | ||
| ... | @@ -3426,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem | ... | @@ -3426,8 +3454,6 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem |
| 3426 | // bss section is not emitted when this condition holds true, so we also | 3454 | // bss section is not emitted when this condition holds true, so we also |
| 3427 | // do not output a name for it. | 3455 | // do not output a name for it. |
| 3428 | if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; | 3456 | if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; |
| 3429 | // Synthetic segments are not emitted | ||
| 3430 | if (std.mem.eql(u8, key, ".synthetic")) continue; | ||
| 3431 | segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); | 3457 | segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); |
| 3432 | data_segment_index += 1; | 3458 | data_segment_index += 1; |
| 3433 | } | 3459 | } |
| ... | @@ -3594,6 +3620,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! | ... | @@ -3594,6 +3620,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 3594 | try man.addOptionalFile(compiler_rt_path); | 3620 | try man.addOptionalFile(compiler_rt_path); |
| 3595 | man.hash.addOptionalBytes(wasm.base.options.entry); | 3621 | man.hash.addOptionalBytes(wasm.base.options.entry); |
| 3596 | man.hash.addOptional(wasm.base.options.stack_size_override); | 3622 | man.hash.addOptional(wasm.base.options.stack_size_override); |
| 3623 | man.hash.add(wasm.base.options.build_id); | ||
| 3597 | man.hash.add(wasm.base.options.import_memory); | 3624 | man.hash.add(wasm.base.options.import_memory); |
| 3598 | man.hash.add(wasm.base.options.import_table); | 3625 | man.hash.add(wasm.base.options.import_table); |
| 3599 | man.hash.add(wasm.base.options.export_table); | 3626 | man.hash.add(wasm.base.options.export_table); |
| ... | @@ -3760,6 +3787,12 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! | ... | @@ -3760,6 +3787,12 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 3760 | if (wasm.base.options.import_symbols) { | 3787 | if (wasm.base.options.import_symbols) { |
| 3761 | try argv.append("--allow-undefined"); | 3788 | try argv.append("--allow-undefined"); |
| 3762 | } | 3789 | } |
| 3790 | |||
| 3791 | // XXX - TODO: add when wasm-ld supports --build-id. | ||
| 3792 | // if (wasm.base.options.build_id) { | ||
| 3793 | // try argv.append("--build-id=tree"); | ||
| 3794 | // } | ||
| 3795 | |||
| 3763 | try argv.appendSlice(&.{ "-o", full_out_path }); | 3796 | try argv.appendSlice(&.{ "-o", full_out_path }); |
| 3764 | 3797 | ||
| 3765 | if (target.cpu.arch == .wasm64) { | 3798 | if (target.cpu.arch == .wasm64) { |
src/link/Wasm/Atom.zig+1-23| ... | @@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 { | ... | @@ -89,21 +89,6 @@ pub fn getSymbolIndex(atom: Atom) ?u32 { |
| 89 | return atom.sym_index; | 89 | return atom.sym_index; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | /// Returns the virtual address of the `Atom`. This is the address starting | ||
| 93 | /// from the first entry within a section. | ||
| 94 | pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 { | ||
| 95 | if (symbol.tag == .function) return atom.offset; | ||
| 96 | std.debug.assert(symbol.tag == .data); | ||
| 97 | const merge_segment = wasm.base.options.output_mode != .Obj; | ||
| 98 | const segment_info = if (atom.file) |object_index| blk: { | ||
| 99 | break :blk wasm.objects.items[object_index].segment_info; | ||
| 100 | } else wasm.segment_info.values(); | ||
| 101 | const segment_name = segment_info[symbol.index].outputName(merge_segment); | ||
| 102 | const segment_index = wasm.data_segments.get(segment_name).?; | ||
| 103 | const segment = wasm.segments.items[segment_index]; | ||
| 104 | return segment.offset + atom.offset; | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Resolves the relocations within the atom, writing the new value | 92 | /// Resolves the relocations within the atom, writing the new value |
| 108 | /// at the calculated offset. | 93 | /// at the calculated offset. |
| 109 | pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { | 94 | pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { |
| ... | @@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa | ... | @@ -186,14 +171,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa |
| 186 | if (symbol.isUndefined()) { | 171 | if (symbol.isUndefined()) { |
| 187 | return 0; | 172 | return 0; |
| 188 | } | 173 | } |
| 189 | const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse { | 174 | const va = @intCast(i64, symbol.virtual_address); |
| 190 | // this can only occur during incremental-compilation when a relocation | ||
| 191 | // still points to a freed decl. It is fine to emit the value 0 here | ||
| 192 | // as no actual code will point towards it. | ||
| 193 | return 0; | ||
| 194 | }; | ||
| 195 | const target_atom = wasm_bin.getAtom(target_atom_index); | ||
| 196 | const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol)); | ||
| 197 | return @intCast(u32, va + relocation.addend); | 175 | return @intCast(u32, va + relocation.addend); |
| 198 | }, | 176 | }, |
| 199 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, | 177 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, |
src/link/Wasm/Object.zig+2| ... | @@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol { | ... | @@ -270,6 +270,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol { |
| 270 | .name = table_import.name, | 270 | .name = table_import.name, |
| 271 | .tag = .table, | 271 | .tag = .table, |
| 272 | .index = 0, | 272 | .index = 0, |
| 273 | .virtual_address = undefined, | ||
| 273 | }; | 274 | }; |
| 274 | table_symbol.setFlag(.WASM_SYM_UNDEFINED); | 275 | table_symbol.setFlag(.WASM_SYM_UNDEFINED); |
| 275 | table_symbol.setFlag(.WASM_SYM_NO_STRIP); | 276 | table_symbol.setFlag(.WASM_SYM_NO_STRIP); |
| ... | @@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -758,6 +759,7 @@ fn Parser(comptime ReaderType: type) type { |
| 758 | .tag = tag, | 759 | .tag = tag, |
| 759 | .name = undefined, | 760 | .name = undefined, |
| 760 | .index = undefined, | 761 | .index = undefined, |
| 762 | .virtual_address = undefined, | ||
| 761 | }; | 763 | }; |
| 762 | 764 | ||
| 763 | switch (tag) { | 765 | switch (tag) { |
src/link/Wasm/Symbol.zig+3| ... | @@ -20,6 +20,9 @@ name: u32, | ... | @@ -20,6 +20,9 @@ name: u32, |
| 20 | index: u32, | 20 | index: u32, |
| 21 | /// Represents the kind of the symbol, such as a function or global. | 21 | /// Represents the kind of the symbol, such as a function or global. |
| 22 | tag: Tag, | 22 | tag: 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. | ||
| 25 | virtual_address: u32, | ||
| 23 | 26 | ||
| 24 | pub const Tag = enum { | 27 | pub const Tag = enum { |
| 25 | function, | 28 | function, |
src/main.zig+501-342| ... | @@ -9,6 +9,8 @@ const Allocator = mem.Allocator; | ... | @@ -9,6 +9,8 @@ const Allocator = mem.Allocator; |
| 9 | const ArrayList = std.ArrayList; | 9 | const ArrayList = std.ArrayList; |
| 10 | const Ast = std.zig.Ast; | 10 | const Ast = std.zig.Ast; |
| 11 | const warn = std.log.warn; | 11 | const warn = std.log.warn; |
| 12 | const ThreadPool = std.Thread.Pool; | ||
| 13 | const cleanExit = std.process.cleanExit; | ||
| 12 | 14 | ||
| 13 | const tracy = @import("tracy.zig"); | 15 | const tracy = @import("tracy.zig"); |
| 14 | const Compilation = @import("Compilation.zig"); | 16 | const Compilation = @import("Compilation.zig"); |
| ... | @@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig"); | ... | @@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig"); |
| 22 | const clang = @import("clang.zig"); | 24 | const clang = @import("clang.zig"); |
| 23 | const Cache = std.Build.Cache; | 25 | const Cache = std.Build.Cache; |
| 24 | const target_util = @import("target.zig"); | 26 | const target_util = @import("target.zig"); |
| 25 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 26 | const crash_report = @import("crash_report.zig"); | 27 | const crash_report = @import("crash_report.zig"); |
| 28 | const Module = @import("Module.zig"); | ||
| 29 | const AstGen = @import("AstGen.zig"); | ||
| 30 | const Server = std.zig.Server; | ||
| 27 | 31 | ||
| 28 | pub const std_options = struct { | 32 | pub const std_options = struct { |
| 29 | pub const wasiCwd = wasi_cwd; | 33 | pub const wasiCwd = wasi_cwd; |
| ... | @@ -361,7 +365,6 @@ const usage_build_generic = | ... | @@ -361,7 +365,6 @@ const usage_build_generic = |
| 361 | \\ | 365 | \\ |
| 362 | \\General Options: | 366 | \\General Options: |
| 363 | \\ -h, --help Print this help and exit | 367 | \\ -h, --help Print this help and exit |
| 364 | \\ --watch Enable compiler REPL | ||
| 365 | \\ --color [auto|off|on] Enable or disable colored error messages | 368 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 366 | \\ -femit-bin[=path] (default) Output machine code | 369 | \\ -femit-bin[=path] (default) Output machine code |
| 367 | \\ -fno-emit-bin Do not output machine code | 370 | \\ -fno-emit-bin Do not output machine code |
| ... | @@ -666,6 +669,16 @@ const ArgMode = union(enum) { | ... | @@ -666,6 +669,16 @@ const ArgMode = union(enum) { |
| 666 | run, | 669 | run, |
| 667 | }; | 670 | }; |
| 668 | 671 | ||
| 672 | /// Avoid dragging networking into zig2.c because it adds dependencies on some | ||
| 673 | /// linker symbols that are annoying to satisfy while bootstrapping. | ||
| 674 | const Ip4Address = if (build_options.omit_pkg_fetching_code) void else std.net.Ip4Address; | ||
| 675 | |||
| 676 | const Listen = union(enum) { | ||
| 677 | none, | ||
| 678 | ip4: Ip4Address, | ||
| 679 | stdio, | ||
| 680 | }; | ||
| 681 | |||
| 669 | fn buildOutputType( | 682 | fn buildOutputType( |
| 670 | gpa: Allocator, | 683 | gpa: Allocator, |
| 671 | arena: Allocator, | 684 | arena: Allocator, |
| ... | @@ -686,7 +699,7 @@ fn buildOutputType( | ... | @@ -686,7 +699,7 @@ fn buildOutputType( |
| 686 | var formatted_panics: ?bool = null; | 699 | var formatted_panics: ?bool = null; |
| 687 | var function_sections = false; | 700 | var function_sections = false; |
| 688 | var no_builtin = false; | 701 | var no_builtin = false; |
| 689 | var watch = false; | 702 | var listen: Listen = .none; |
| 690 | var debug_compile_errors = false; | 703 | var debug_compile_errors = false; |
| 691 | var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); | 704 | var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); |
| 692 | var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); | 705 | var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); |
| ... | @@ -1144,6 +1157,23 @@ fn buildOutputType( | ... | @@ -1144,6 +1157,23 @@ fn buildOutputType( |
| 1144 | } else { | 1157 | } else { |
| 1145 | try log_scopes.append(gpa, args_iter.nextOrFatal()); | 1158 | try log_scopes.append(gpa, args_iter.nextOrFatal()); |
| 1146 | } | 1159 | } |
| 1160 | } else if (mem.eql(u8, arg, "--listen")) { | ||
| 1161 | const next_arg = args_iter.nextOrFatal(); | ||
| 1162 | if (mem.eql(u8, next_arg, "-")) { | ||
| 1163 | listen = .stdio; | ||
| 1164 | } else { | ||
| 1165 | if (build_options.omit_pkg_fetching_code) unreachable; | ||
| 1166 | // example: --listen 127.0.0.1:9000 | ||
| 1167 | var it = std.mem.split(u8, next_arg, ":"); | ||
| 1168 | const host = it.next().?; | ||
| 1169 | const port_text = it.next() orelse "14735"; | ||
| 1170 | const port = std.fmt.parseInt(u16, port_text, 10) catch |err| | ||
| 1171 | fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) }); | ||
| 1172 | listen = .{ .ip4 = std.net.Ip4Address.parse(host, port) catch |err| | ||
| 1173 | fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) }; | ||
| 1174 | } | ||
| 1175 | } else if (mem.eql(u8, arg, "--listen=-")) { | ||
| 1176 | listen = .stdio; | ||
| 1147 | } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { | 1177 | } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { |
| 1148 | if (!build_options.enable_link_snapshots) { | 1178 | if (!build_options.enable_link_snapshots) { |
| 1149 | std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); | 1179 | std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); |
| ... | @@ -1172,8 +1202,6 @@ fn buildOutputType( | ... | @@ -1172,8 +1202,6 @@ fn buildOutputType( |
| 1172 | test_evented_io = true; | 1202 | test_evented_io = true; |
| 1173 | } else if (mem.eql(u8, arg, "--test-no-exec")) { | 1203 | } else if (mem.eql(u8, arg, "--test-no-exec")) { |
| 1174 | test_no_exec = true; | 1204 | test_no_exec = true; |
| 1175 | } else if (mem.eql(u8, arg, "--watch")) { | ||
| 1176 | watch = true; | ||
| 1177 | } else if (mem.eql(u8, arg, "-ftime-report")) { | 1205 | } else if (mem.eql(u8, arg, "-ftime-report")) { |
| 1178 | time_report = true; | 1206 | time_report = true; |
| 1179 | } else if (mem.eql(u8, arg, "-fstack-report")) { | 1207 | } else if (mem.eql(u8, arg, "-fstack-report")) { |
| ... | @@ -2999,7 +3027,7 @@ fn buildOutputType( | ... | @@ -2999,7 +3027,7 @@ fn buildOutputType( |
| 2999 | defer zig_lib_directory.handle.close(); | 3027 | defer zig_lib_directory.handle.close(); |
| 3000 | 3028 | ||
| 3001 | var thread_pool: ThreadPool = undefined; | 3029 | var thread_pool: ThreadPool = undefined; |
| 3002 | try thread_pool.init(gpa); | 3030 | try thread_pool.init(.{ .allocator = gpa }); |
| 3003 | defer thread_pool.deinit(); | 3031 | defer thread_pool.deinit(); |
| 3004 | 3032 | ||
| 3005 | var libc_installation: ?LibCInstallation = null; | 3033 | var libc_installation: ?LibCInstallation = null; |
| ... | @@ -3259,8 +3287,52 @@ fn buildOutputType( | ... | @@ -3259,8 +3287,52 @@ fn buildOutputType( |
| 3259 | if (show_builtin) { | 3287 | if (show_builtin) { |
| 3260 | return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); | 3288 | return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); |
| 3261 | } | 3289 | } |
| 3290 | switch (listen) { | ||
| 3291 | .none => {}, | ||
| 3292 | .stdio => { | ||
| 3293 | if (build_options.only_c) unreachable; | ||
| 3294 | try serve( | ||
| 3295 | comp, | ||
| 3296 | std.io.getStdIn(), | ||
| 3297 | std.io.getStdOut(), | ||
| 3298 | test_exec_args.items, | ||
| 3299 | self_exe_path, | ||
| 3300 | arg_mode, | ||
| 3301 | all_args, | ||
| 3302 | runtime_args_start, | ||
| 3303 | ); | ||
| 3304 | return cleanExit(); | ||
| 3305 | }, | ||
| 3306 | .ip4 => |ip4_addr| { | ||
| 3307 | if (build_options.omit_pkg_fetching_code) unreachable; | ||
| 3308 | |||
| 3309 | var server = std.net.StreamServer.init(.{ | ||
| 3310 | .reuse_address = true, | ||
| 3311 | }); | ||
| 3312 | defer server.deinit(); | ||
| 3313 | |||
| 3314 | try server.listen(.{ .in = ip4_addr }); | ||
| 3315 | |||
| 3316 | while (true) { | ||
| 3317 | const conn = try server.accept(); | ||
| 3318 | defer conn.stream.close(); | ||
| 3319 | |||
| 3320 | try serve( | ||
| 3321 | comp, | ||
| 3322 | .{ .handle = conn.stream.handle }, | ||
| 3323 | .{ .handle = conn.stream.handle }, | ||
| 3324 | test_exec_args.items, | ||
| 3325 | self_exe_path, | ||
| 3326 | arg_mode, | ||
| 3327 | all_args, | ||
| 3328 | runtime_args_start, | ||
| 3329 | ); | ||
| 3330 | } | ||
| 3331 | }, | ||
| 3332 | } | ||
| 3333 | |||
| 3262 | if (arg_mode == .translate_c) { | 3334 | if (arg_mode == .translate_c) { |
| 3263 | return cmdTranslateC(comp, arena, have_enable_cache); | 3335 | return cmdTranslateC(comp, arena, null); |
| 3264 | } | 3336 | } |
| 3265 | 3337 | ||
| 3266 | const hook: AfterUpdateHook = blk: { | 3338 | const hook: AfterUpdateHook = blk: { |
| ... | @@ -3276,7 +3348,7 @@ fn buildOutputType( | ... | @@ -3276,7 +3348,7 @@ fn buildOutputType( |
| 3276 | }; | 3348 | }; |
| 3277 | 3349 | ||
| 3278 | updateModule(gpa, comp, hook) catch |err| switch (err) { | 3350 | updateModule(gpa, comp, hook) catch |err| switch (err) { |
| 3279 | error.SemanticAnalyzeFail => if (!watch) process.exit(1), | 3351 | error.SemanticAnalyzeFail => if (listen == .none) process.exit(1), |
| 3280 | else => |e| return e, | 3352 | else => |e| return e, |
| 3281 | }; | 3353 | }; |
| 3282 | if (build_options.only_c) return cleanExit(); | 3354 | if (build_options.only_c) return cleanExit(); |
| ... | @@ -3332,7 +3404,6 @@ fn buildOutputType( | ... | @@ -3332,7 +3404,6 @@ fn buildOutputType( |
| 3332 | self_exe_path.?, | 3404 | self_exe_path.?, |
| 3333 | arg_mode, | 3405 | arg_mode, |
| 3334 | target_info, | 3406 | target_info, |
| 3335 | watch, | ||
| 3336 | &comp_destroyed, | 3407 | &comp_destroyed, |
| 3337 | all_args, | 3408 | all_args, |
| 3338 | runtime_args_start, | 3409 | runtime_args_start, |
| ... | @@ -3340,109 +3411,215 @@ fn buildOutputType( | ... | @@ -3340,109 +3411,215 @@ fn buildOutputType( |
| 3340 | ); | 3411 | ); |
| 3341 | } | 3412 | } |
| 3342 | 3413 | ||
| 3343 | const stdin = std.io.getStdIn().reader(); | 3414 | // Skip resource deallocation in release builds; let the OS do it. |
| 3344 | const stderr = std.io.getStdErr().writer(); | 3415 | return cleanExit(); |
| 3345 | var repl_buf: [1024]u8 = undefined; | 3416 | } |
| 3417 | |||
| 3418 | fn 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; | ||
| 3346 | 3429 | ||
| 3347 | const ReplCmd = enum { | 3430 | var server = try Server.init(.{ |
| 3348 | update, | 3431 | .gpa = gpa, |
| 3349 | help, | 3432 | .in = in, |
| 3350 | run, | 3433 | .out = out, |
| 3351 | update_and_run, | 3434 | .zig_version = build_options.version, |
| 3435 | }); | ||
| 3436 | defer server.deinit(); | ||
| 3437 | |||
| 3438 | var child_pid: ?std.ChildProcess.Id = null; | ||
| 3439 | |||
| 3440 | var progress: std.Progress = .{ | ||
| 3441 | .terminal = null, | ||
| 3442 | .root = .{ | ||
| 3443 | .context = undefined, | ||
| 3444 | .parent = null, | ||
| 3445 | .name = "", | ||
| 3446 | .unprotected_estimated_total_items = 0, | ||
| 3447 | .unprotected_completed_items = 0, | ||
| 3448 | }, | ||
| 3449 | .columns_written = 0, | ||
| 3450 | .prev_refresh_timestamp = 0, | ||
| 3451 | .timer = null, | ||
| 3452 | .done = false, | ||
| 3352 | }; | 3453 | }; |
| 3454 | const main_progress_node = &progress.root; | ||
| 3455 | main_progress_node.context = &progress; | ||
| 3353 | 3456 | ||
| 3354 | var last_cmd: ReplCmd = .help; | 3457 | while (true) { |
| 3458 | const hdr = try server.receiveMessage(); | ||
| 3355 | 3459 | ||
| 3356 | while (watch) { | 3460 | switch (hdr.tag) { |
| 3357 | try stderr.print("(zig) ", .{}); | 3461 | .exit => { |
| 3358 | try comp.makeBinFileExecutable(); | 3462 | return cleanExit(); |
| 3359 | if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { | 3463 | }, |
| 3360 | try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)}); | 3464 | .update => { |
| 3361 | continue; | 3465 | assert(main_progress_node.recently_updated_child == null); |
| 3362 | }) |line| { | 3466 | tracy.frameMark(); |
| 3363 | const actual_line = mem.trimRight(u8, line, "\r\n "); | 3467 | |
| 3364 | const cmd: ReplCmd = blk: { | 3468 | if (arg_mode == .translate_c) { |
| 3365 | if (mem.eql(u8, actual_line, "update")) { | 3469 | var arena_instance = std.heap.ArenaAllocator.init(gpa); |
| 3366 | break :blk .update; | 3470 | defer arena_instance.deinit(); |
| 3367 | } else if (mem.eql(u8, actual_line, "exit")) { | 3471 | const arena = arena_instance.allocator(); |
| 3368 | break; | 3472 | var output: TranslateCOutput = undefined; |
| 3369 | } else if (mem.eql(u8, actual_line, "help")) { | 3473 | try cmdTranslateC(comp, arena, &output); |
| 3370 | break :blk .help; | 3474 | try server.serveEmitBinPath(output.path, .{ |
| 3371 | } else if (mem.eql(u8, actual_line, "run")) { | 3475 | .flags = .{ .cache_hit = output.cache_hit }, |
| 3372 | break :blk .run; | 3476 | }); |
| 3373 | } else if (mem.eql(u8, actual_line, "update-and-run")) { | ||
| 3374 | break :blk .update_and_run; | ||
| 3375 | } else if (actual_line.len == 0) { | ||
| 3376 | break :blk last_cmd; | ||
| 3377 | } else { | ||
| 3378 | try stderr.print("unknown command: {s}\n", .{actual_line}); | ||
| 3379 | continue; | 3477 | continue; |
| 3380 | } | 3478 | } |
| 3381 | }; | 3479 | |
| 3382 | last_cmd = cmd; | 3480 | if (comp.bin_file.options.output_mode == .Exe) { |
| 3383 | switch (cmd) { | 3481 | try comp.makeBinFileWritable(); |
| 3384 | .update => { | 3482 | } |
| 3385 | tracy.frameMark(); | 3483 | |
| 3386 | if (output_mode == .Exe) { | 3484 | { |
| 3387 | try comp.makeBinFileWritable(); | 3485 | var reset: std.Thread.ResetEvent = .{}; |
| 3486 | |||
| 3487 | var progress_thread = try std.Thread.spawn(.{}, progressThread, .{ | ||
| 3488 | &progress, &server, &reset, | ||
| 3489 | }); | ||
| 3490 | defer { | ||
| 3491 | reset.set(); | ||
| 3492 | progress_thread.join(); | ||
| 3388 | } | 3493 | } |
| 3389 | updateModule(gpa, comp, hook) catch |err| switch (err) { | 3494 | |
| 3390 | error.SemanticAnalyzeFail => continue, | 3495 | try comp.update(main_progress_node); |
| 3391 | else => |e| return e, | 3496 | } |
| 3392 | }; | 3497 | |
| 3393 | }, | 3498 | try comp.makeBinFileExecutable(); |
| 3394 | .help => { | 3499 | try serveUpdateResults(&server, comp); |
| 3395 | try stderr.writeAll(repl_help); | 3500 | }, |
| 3396 | }, | 3501 | .run => { |
| 3397 | .run => { | 3502 | if (child_pid != null) { |
| 3398 | tracy.frameMark(); | 3503 | @panic("TODO block until the child exits"); |
| 3399 | try runOrTest( | 3504 | } |
| 3400 | comp, | 3505 | @panic("TODO call runOrTest"); |
| 3401 | gpa, | 3506 | //try runOrTest( |
| 3402 | arena, | 3507 | // comp, |
| 3403 | test_exec_args.items, | 3508 | // gpa, |
| 3404 | self_exe_path.?, | 3509 | // arena, |
| 3405 | arg_mode, | 3510 | // test_exec_args, |
| 3406 | target_info, | 3511 | // self_exe_path.?, |
| 3407 | watch, | 3512 | // arg_mode, |
| 3408 | &comp_destroyed, | 3513 | // target_info, |
| 3409 | all_args, | 3514 | // true, |
| 3410 | runtime_args_start, | 3515 | // &comp_destroyed, |
| 3411 | link_libc, | 3516 | // all_args, |
| 3412 | ); | 3517 | // runtime_args_start, |
| 3413 | }, | 3518 | // link_libc, |
| 3414 | .update_and_run => { | 3519 | //); |
| 3415 | tracy.frameMark(); | 3520 | }, |
| 3416 | if (output_mode == .Exe) { | 3521 | .hot_update => { |
| 3522 | tracy.frameMark(); | ||
| 3523 | assert(main_progress_node.recently_updated_child == null); | ||
| 3524 | if (child_pid) |pid| { | ||
| 3525 | try comp.hotCodeSwap(main_progress_node, pid); | ||
| 3526 | try serveUpdateResults(&server, comp); | ||
| 3527 | } else { | ||
| 3528 | if (comp.bin_file.options.output_mode == .Exe) { | ||
| 3417 | try comp.makeBinFileWritable(); | 3529 | try comp.makeBinFileWritable(); |
| 3418 | } | 3530 | } |
| 3419 | updateModule(gpa, comp, hook) catch |err| switch (err) { | 3531 | try comp.update(main_progress_node); |
| 3420 | error.SemanticAnalyzeFail => continue, | ||
| 3421 | else => |e| return e, | ||
| 3422 | }; | ||
| 3423 | try comp.makeBinFileExecutable(); | 3532 | try comp.makeBinFileExecutable(); |
| 3424 | try runOrTest( | 3533 | try serveUpdateResults(&server, comp); |
| 3534 | |||
| 3535 | child_pid = try runOrTestHotSwap( | ||
| 3425 | comp, | 3536 | comp, |
| 3426 | gpa, | 3537 | gpa, |
| 3427 | arena, | 3538 | test_exec_args, |
| 3428 | test_exec_args.items, | ||
| 3429 | self_exe_path.?, | 3539 | self_exe_path.?, |
| 3430 | arg_mode, | 3540 | arg_mode, |
| 3431 | target_info, | ||
| 3432 | watch, | ||
| 3433 | &comp_destroyed, | ||
| 3434 | all_args, | 3541 | all_args, |
| 3435 | runtime_args_start, | 3542 | runtime_args_start, |
| 3436 | link_libc, | ||
| 3437 | ); | 3543 | ); |
| 3438 | }, | 3544 | } |
| 3545 | }, | ||
| 3546 | else => { | ||
| 3547 | fatal("unrecognized message from client: 0x{x}", .{@enumToInt(hdr.tag)}); | ||
| 3548 | }, | ||
| 3549 | } | ||
| 3550 | } | ||
| 3551 | } | ||
| 3552 | |||
| 3553 | fn progressThread(progress: *std.Progress, server: *const Server, reset: *std.Thread.ResetEvent) void { | ||
| 3554 | while (true) { | ||
| 3555 | if (reset.timedWait(500 * std.time.ns_per_ms)) |_| { | ||
| 3556 | // The Compilation update has completed. | ||
| 3557 | return; | ||
| 3558 | } else |err| switch (err) { | ||
| 3559 | error.Timeout => {}, | ||
| 3560 | } | ||
| 3561 | |||
| 3562 | var buf: std.BoundedArray(u8, 160) = .{}; | ||
| 3563 | |||
| 3564 | { | ||
| 3565 | progress.update_mutex.lock(); | ||
| 3566 | defer progress.update_mutex.unlock(); | ||
| 3567 | |||
| 3568 | var need_ellipse = false; | ||
| 3569 | var maybe_node: ?*std.Progress.Node = &progress.root; | ||
| 3570 | while (maybe_node) |node| { | ||
| 3571 | if (need_ellipse) { | ||
| 3572 | buf.appendSlice("... ") catch {}; | ||
| 3573 | } | ||
| 3574 | need_ellipse = false; | ||
| 3575 | const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic); | ||
| 3576 | const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic); | ||
| 3577 | const current_item = completed_items + 1; | ||
| 3578 | if (node.name.len != 0 or eti > 0) { | ||
| 3579 | if (node.name.len != 0) { | ||
| 3580 | buf.appendSlice(node.name) catch {}; | ||
| 3581 | need_ellipse = true; | ||
| 3582 | } | ||
| 3583 | if (eti > 0) { | ||
| 3584 | if (need_ellipse) buf.appendSlice(" ") catch {}; | ||
| 3585 | buf.writer().print("[{d}/{d}] ", .{ current_item, eti }) catch {}; | ||
| 3586 | need_ellipse = false; | ||
| 3587 | } else if (completed_items != 0) { | ||
| 3588 | if (need_ellipse) buf.appendSlice(" ") catch {}; | ||
| 3589 | buf.writer().print("[{d}] ", .{current_item}) catch {}; | ||
| 3590 | need_ellipse = false; | ||
| 3591 | } | ||
| 3592 | } | ||
| 3593 | maybe_node = @atomicLoad(?*std.Progress.Node, &node.recently_updated_child, .Acquire); | ||
| 3439 | } | 3594 | } |
| 3440 | } else { | ||
| 3441 | break; | ||
| 3442 | } | 3595 | } |
| 3596 | |||
| 3597 | const progress_string = buf.slice(); | ||
| 3598 | |||
| 3599 | server.serveMessage(.{ | ||
| 3600 | .tag = .progress, | ||
| 3601 | .bytes_len = @intCast(u32, progress_string.len), | ||
| 3602 | }, &.{ | ||
| 3603 | progress_string, | ||
| 3604 | }) catch |err| { | ||
| 3605 | fatal("unable to write to client: {s}", .{@errorName(err)}); | ||
| 3606 | }; | ||
| 3607 | } | ||
| 3608 | } | ||
| 3609 | |||
| 3610 | fn serveUpdateResults(s: *Server, comp: *Compilation) !void { | ||
| 3611 | const gpa = comp.gpa; | ||
| 3612 | var error_bundle = try comp.getAllErrorsAlloc(); | ||
| 3613 | defer error_bundle.deinit(gpa); | ||
| 3614 | if (error_bundle.errorMessageCount() > 0) { | ||
| 3615 | try s.serveErrorBundle(error_bundle); | ||
| 3616 | } else if (comp.bin_file.options.emit) |emit| { | ||
| 3617 | const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); | ||
| 3618 | defer gpa.free(full_path); | ||
| 3619 | try s.serveEmitBinPath(full_path, .{ | ||
| 3620 | .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, | ||
| 3621 | }); | ||
| 3443 | } | 3622 | } |
| 3444 | // Skip resource deallocation in release builds; let the OS do it. | ||
| 3445 | return cleanExit(); | ||
| 3446 | } | 3623 | } |
| 3447 | 3624 | ||
| 3448 | const ModuleDepIterator = struct { | 3625 | const ModuleDepIterator = struct { |
| ... | @@ -3530,7 +3707,6 @@ fn runOrTest( | ... | @@ -3530,7 +3707,6 @@ fn runOrTest( |
| 3530 | self_exe_path: []const u8, | 3707 | self_exe_path: []const u8, |
| 3531 | arg_mode: ArgMode, | 3708 | arg_mode: ArgMode, |
| 3532 | target_info: std.zig.system.NativeTargetInfo, | 3709 | target_info: std.zig.system.NativeTargetInfo, |
| 3533 | watch: bool, | ||
| 3534 | comp_destroyed: *bool, | 3710 | comp_destroyed: *bool, |
| 3535 | all_args: []const []const u8, | 3711 | all_args: []const []const u8, |
| 3536 | runtime_args_start: ?usize, | 3712 | runtime_args_start: ?usize, |
| ... | @@ -3561,7 +3737,7 @@ fn runOrTest( | ... | @@ -3561,7 +3737,7 @@ fn runOrTest( |
| 3561 | 3737 | ||
| 3562 | // We do not execve for tests because if the test fails we want to print | 3738 | // We do not execve for tests because if the test fails we want to print |
| 3563 | // the error message and invocation below. | 3739 | // the error message and invocation below. |
| 3564 | if (std.process.can_execv and arg_mode == .run and !watch) { | 3740 | if (std.process.can_execv and arg_mode == .run) { |
| 3565 | // execv releases the locks; no need to destroy the Compilation here. | 3741 | // execv releases the locks; no need to destroy the Compilation here. |
| 3566 | const err = std.process.execve(gpa, argv.items, &env_map); | 3742 | const err = std.process.execve(gpa, argv.items, &env_map); |
| 3567 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); | 3743 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); |
| ... | @@ -3574,12 +3750,10 @@ fn runOrTest( | ... | @@ -3574,12 +3750,10 @@ fn runOrTest( |
| 3574 | child.stdout_behavior = .Inherit; | 3750 | child.stdout_behavior = .Inherit; |
| 3575 | child.stderr_behavior = .Inherit; | 3751 | child.stderr_behavior = .Inherit; |
| 3576 | 3752 | ||
| 3577 | if (!watch) { | 3753 | // Here we release all the locks associated with the Compilation so |
| 3578 | // Here we release all the locks associated with the Compilation so | 3754 | // that whatever this child process wants to do won't deadlock. |
| 3579 | // that whatever this child process wants to do won't deadlock. | 3755 | comp.destroy(); |
| 3580 | comp.destroy(); | 3756 | comp_destroyed.* = true; |
| 3581 | comp_destroyed.* = true; | ||
| 3582 | } | ||
| 3583 | 3757 | ||
| 3584 | const term = child.spawnAndWait() catch |err| { | 3758 | const term = child.spawnAndWait() catch |err| { |
| 3585 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); | 3759 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); |
| ... | @@ -3591,19 +3765,13 @@ fn runOrTest( | ... | @@ -3591,19 +3765,13 @@ fn runOrTest( |
| 3591 | switch (term) { | 3765 | switch (term) { |
| 3592 | .Exited => |code| { | 3766 | .Exited => |code| { |
| 3593 | if (code == 0) { | 3767 | if (code == 0) { |
| 3594 | if (!watch) return cleanExit(); | 3768 | return cleanExit(); |
| 3595 | } else if (watch) { | ||
| 3596 | warn("process exited with code {d}", .{code}); | ||
| 3597 | } else { | 3769 | } else { |
| 3598 | process.exit(code); | 3770 | process.exit(code); |
| 3599 | } | 3771 | } |
| 3600 | }, | 3772 | }, |
| 3601 | else => { | 3773 | else => { |
| 3602 | if (watch) { | 3774 | process.exit(1); |
| 3603 | warn("process aborted abnormally", .{}); | ||
| 3604 | } else { | ||
| 3605 | process.exit(1); | ||
| 3606 | } | ||
| 3607 | }, | 3775 | }, |
| 3608 | } | 3776 | } |
| 3609 | }, | 3777 | }, |
| ... | @@ -3611,7 +3779,7 @@ fn runOrTest( | ... | @@ -3611,7 +3779,7 @@ fn runOrTest( |
| 3611 | switch (term) { | 3779 | switch (term) { |
| 3612 | .Exited => |code| { | 3780 | .Exited => |code| { |
| 3613 | if (code == 0) { | 3781 | if (code == 0) { |
| 3614 | if (!watch) return cleanExit(); | 3782 | return cleanExit(); |
| 3615 | } else { | 3783 | } else { |
| 3616 | const cmd = try std.mem.join(arena, " ", argv.items); | 3784 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 3617 | fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); | 3785 | fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); |
| ... | @@ -3631,6 +3799,62 @@ fn runOrTest( | ... | @@ -3631,6 +3799,62 @@ fn runOrTest( |
| 3631 | } | 3799 | } |
| 3632 | } | 3800 | } |
| 3633 | 3801 | ||
| 3802 | fn 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 | |||
| 3634 | const AfterUpdateHook = union(enum) { | 3858 | const AfterUpdateHook = union(enum) { |
| 3635 | none, | 3859 | none, |
| 3636 | print_emit_bin_dir_path, | 3860 | print_emit_bin_dir_path, |
| ... | @@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) { | ... | @@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) { |
| 3638 | }; | 3862 | }; |
| 3639 | 3863 | ||
| 3640 | fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void { | 3864 | fn 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 | } | ||
| 3642 | 3883 | ||
| 3643 | var errors = try comp.getAllErrorsAlloc(); | 3884 | var errors = try comp.getAllErrorsAlloc(); |
| 3644 | defer errors.deinit(comp.gpa); | 3885 | defer errors.deinit(comp.gpa); |
| 3645 | 3886 | ||
| 3646 | if (errors.list.len != 0) { | 3887 | if (errors.errorMessageCount() > 0) { |
| 3647 | const ttyconf: std.debug.TTY.Config = switch (comp.color) { | 3888 | errors.renderToStdErr(renderOptions(comp.color)); |
| 3648 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 3649 | .on => .escape_codes, | ||
| 3650 | .off => .no_color, | ||
| 3651 | }; | ||
| 3652 | for (errors.list) |full_err_msg| { | ||
| 3653 | full_err_msg.renderToStdErr(ttyconf); | ||
| 3654 | } | ||
| 3655 | const log_text = comp.getCompileLogOutput(); | ||
| 3656 | if (log_text.len != 0) { | ||
| 3657 | std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); | ||
| 3658 | } | ||
| 3659 | return error.SemanticAnalyzeFail; | 3889 | return error.SemanticAnalyzeFail; |
| 3660 | } else switch (hook) { | 3890 | } else switch (hook) { |
| 3661 | .none => {}, | 3891 | .none => {}, |
| ... | @@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void | ... | @@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void |
| 3697 | } | 3927 | } |
| 3698 | } | 3928 | } |
| 3699 | 3929 | ||
| 3700 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { | 3930 | const TranslateCOutput = struct { |
| 3931 | path: []const u8, | ||
| 3932 | cache_hit: bool, | ||
| 3933 | }; | ||
| 3934 | |||
| 3935 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*TranslateCOutput) !void { | ||
| 3701 | if (!build_options.have_llvm) | 3936 | if (!build_options.have_llvm) |
| 3702 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); | 3937 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); |
| 3703 | 3938 | ||
| ... | @@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3708 | 3943 | ||
| 3709 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); | 3944 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); |
| 3710 | man.want_shared_lock = false; | 3945 | man.want_shared_lock = false; |
| 3711 | defer if (enable_cache) man.deinit(); | 3946 | defer man.deinit(); |
| 3712 | 3947 | ||
| 3713 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects | 3948 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |
| 3714 | Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { | 3949 | Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { |
| 3715 | fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); | 3950 | fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); |
| 3716 | }; | 3951 | }; |
| 3717 | 3952 | ||
| 3953 | if (fancy_output) |p| p.cache_hit = true; | ||
| 3718 | const digest = if (try man.hit()) man.final() else digest: { | 3954 | const digest = if (try man.hit()) man.final() else digest: { |
| 3955 | if (fancy_output) |p| p.cache_hit = false; | ||
| 3719 | var argv = std.ArrayList([]const u8).init(arena); | 3956 | var argv = std.ArrayList([]const u8).init(arena); |
| 3720 | try argv.append(""); // argv[0] is program name, actual args start at [1] | 3957 | try argv.append(""); // argv[0] is program name, actual args start at [1] |
| 3721 | 3958 | ||
| ... | @@ -3766,6 +4003,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3766,6 +4003,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3766 | error.OutOfMemory => return error.OutOfMemory, | 4003 | error.OutOfMemory => return error.OutOfMemory, |
| 3767 | error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), | 4004 | error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), |
| 3768 | error.SemanticAnalyzeFail => { | 4005 | error.SemanticAnalyzeFail => { |
| 4006 | // TODO convert these to zig errors | ||
| 3769 | for (clang_errors) |clang_err| { | 4007 | for (clang_errors) |clang_err| { |
| 3770 | std.debug.print("{s}:{d}:{d}: {s}\n", .{ | 4008 | std.debug.print("{s}:{d}:{d}: {s}\n", .{ |
| 3771 | if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", | 4009 | if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", |
| ... | @@ -3810,12 +4048,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3810,12 +4048,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3810 | break :digest digest; | 4048 | break :digest digest; |
| 3811 | }; | 4049 | }; |
| 3812 | 4050 | ||
| 3813 | if (enable_cache) { | 4051 | if (fancy_output) |p| { |
| 3814 | const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ | 4052 | const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ |
| 3815 | "o", &digest, translated_zig_basename, | 4053 | "o", &digest, translated_zig_basename, |
| 3816 | }); | 4054 | }); |
| 3817 | try io.getStdOut().writer().print("{s}\n", .{full_zig_path}); | 4055 | p.path = full_zig_path; |
| 3818 | return cleanExit(); | ||
| 3819 | } else { | 4056 | } else { |
| 3820 | const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); | 4057 | const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); |
| 3821 | const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { | 4058 | const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { |
| ... | @@ -4009,6 +4246,8 @@ pub const usage_build = | ... | @@ -4009,6 +4246,8 @@ pub const usage_build = |
| 4009 | \\Options: | 4246 | \\Options: |
| 4010 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error | 4247 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error |
| 4011 | \\ -fno-reference-trace Disable reference trace | 4248 | \\ -fno-reference-trace Disable reference trace |
| 4249 | \\ -fsummary Print the build summary, even on success | ||
| 4250 | \\ -fno-summary Omit the build summary, even on failure | ||
| 4012 | \\ --build-file [file] Override path to build.zig | 4251 | \\ --build-file [file] Override path to build.zig |
| 4013 | \\ --cache-dir [path] Override path to local Zig cache directory | 4252 | \\ --cache-dir [path] Override path to local Zig cache directory |
| 4014 | \\ --global-cache-dir [path] Override path to global Zig cache directory | 4253 | \\ --global-cache-dir [path] Override path to global Zig cache directory |
| ... | @@ -4021,7 +4260,6 @@ pub const usage_build = | ... | @@ -4021,7 +4260,6 @@ pub const usage_build = |
| 4021 | 4260 | ||
| 4022 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | 4261 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4023 | var color: Color = .auto; | 4262 | var color: Color = .auto; |
| 4024 | var prominent_compile_errors: bool = false; | ||
| 4025 | 4263 | ||
| 4026 | // We want to release all the locks before executing the child process, so we make a nice | 4264 | // We want to release all the locks before executing the child process, so we make a nice |
| 4027 | // big block here to ensure the cleanup gets run when we extract out our argv. | 4265 | // big block here to ensure the cleanup gets run when we extract out our argv. |
| ... | @@ -4082,8 +4320,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4082,8 +4320,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4082 | i += 1; | 4320 | i += 1; |
| 4083 | override_global_cache_dir = args[i]; | 4321 | override_global_cache_dir = args[i]; |
| 4084 | continue; | 4322 | continue; |
| 4085 | } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { | ||
| 4086 | prominent_compile_errors = true; | ||
| 4087 | } else if (mem.eql(u8, arg, "-freference-trace")) { | 4323 | } else if (mem.eql(u8, arg, "-freference-trace")) { |
| 4088 | try child_argv.append(arg); | 4324 | try child_argv.append(arg); |
| 4089 | reference_trace = 256; | 4325 | reference_trace = 256; |
| ... | @@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4201 | .basename = exe_basename, | 4437 | .basename = exe_basename, |
| 4202 | }; | 4438 | }; |
| 4203 | var thread_pool: ThreadPool = undefined; | 4439 | var thread_pool: ThreadPool = undefined; |
| 4204 | try thread_pool.init(gpa); | 4440 | try thread_pool.init(.{ .allocator = gpa }); |
| 4205 | defer thread_pool.deinit(); | 4441 | defer thread_pool.deinit(); |
| 4206 | 4442 | ||
| 4207 | var cleanup_build_runner_dir: ?fs.Dir = null; | 4443 | var cleanup_build_runner_dir: ?fs.Dir = null; |
| ... | @@ -4228,6 +4464,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4228,6 +4464,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4228 | .root_src_path = "build_runner.zig", | 4464 | .root_src_path = "build_runner.zig", |
| 4229 | }; | 4465 | }; |
| 4230 | 4466 | ||
| 4467 | var build_pkg: Package = .{ | ||
| 4468 | .root_src_directory = build_directory, | ||
| 4469 | .root_src_path = build_zig_basename, | ||
| 4470 | }; | ||
| 4231 | if (!build_options.omit_pkg_fetching_code) { | 4471 | if (!build_options.omit_pkg_fetching_code) { |
| 4232 | var http_client: std.http.Client = .{ .allocator = gpa }; | 4472 | var http_client: std.http.Client = .{ .allocator = gpa }; |
| 4233 | defer http_client.deinit(); | 4473 | defer http_client.deinit(); |
| ... | @@ -4247,9 +4487,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4247,9 +4487,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4247 | var all_modules: Package.AllModules = .{}; | 4487 | var all_modules: Package.AllModules = .{}; |
| 4248 | defer all_modules.deinit(gpa); | 4488 | defer all_modules.deinit(gpa); |
| 4249 | 4489 | ||
| 4490 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | ||
| 4491 | try wip_errors.init(gpa); | ||
| 4492 | defer wip_errors.deinit(); | ||
| 4493 | |||
| 4250 | // Here we borrow main package's table and will replace it with a fresh | 4494 | // Here we borrow main package's table and will replace it with a fresh |
| 4251 | // one after this process completes. | 4495 | // one after this process completes. |
| 4252 | main_pkg.fetchAndAddDependencies( | 4496 | const fetch_result = build_pkg.fetchAndAddDependencies( |
| 4497 | &main_pkg, | ||
| 4253 | arena, | 4498 | arena, |
| 4254 | &thread_pool, | 4499 | &thread_pool, |
| 4255 | &http_client, | 4500 | &http_client, |
| ... | @@ -4259,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4259,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4259 | &dependencies_source, | 4504 | &dependencies_source, |
| 4260 | &build_roots_source, | 4505 | &build_roots_source, |
| 4261 | "", | 4506 | "", |
| 4262 | color, | 4507 | &wip_errors, |
| 4263 | &all_modules, | 4508 | &all_modules, |
| 4264 | ) catch |err| switch (err) { | 4509 | ); |
| 4265 | error.PackageFetchFailed => process.exit(1), | 4510 | if (wip_errors.root_list.items.len > 0) { |
| 4266 | else => |e| return e, | 4511 | var errors = try wip_errors.toOwnedBundle(""); |
| 4267 | }; | 4512 | defer errors.deinit(gpa); |
| 4513 | errors.renderToStdErr(renderOptions(color)); | ||
| 4514 | process.exit(1); | ||
| 4515 | } | ||
| 4516 | try fetch_result; | ||
| 4268 | 4517 | ||
| 4269 | try dependencies_source.appendSlice("};\npub const build_root = struct {\n"); | 4518 | try dependencies_source.appendSlice("};\npub const build_root = struct {\n"); |
| 4270 | try dependencies_source.appendSlice(build_roots_source.items); | 4519 | try dependencies_source.appendSlice(build_roots_source.items); |
| ... | @@ -4280,11 +4529,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4280,11 +4529,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4280 | mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); | 4529 | mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); |
| 4281 | try main_pkg.add(gpa, "@dependencies", deps_pkg); | 4530 | try main_pkg.add(gpa, "@dependencies", deps_pkg); |
| 4282 | } | 4531 | } |
| 4283 | |||
| 4284 | var build_pkg: Package = .{ | ||
| 4285 | .root_src_directory = build_directory, | ||
| 4286 | .root_src_path = build_zig_basename, | ||
| 4287 | }; | ||
| 4288 | try main_pkg.add(gpa, "@build", &build_pkg); | 4532 | try main_pkg.add(gpa, "@build", &build_pkg); |
| 4289 | 4533 | ||
| 4290 | const comp = Compilation.create(gpa, .{ | 4534 | const comp = Compilation.create(gpa, .{ |
| ... | @@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4312 | defer comp.destroy(); | 4556 | defer comp.destroy(); |
| 4313 | 4557 | ||
| 4314 | updateModule(gpa, comp, .none) catch |err| switch (err) { | 4558 | updateModule(gpa, comp, .none) catch |err| switch (err) { |
| 4315 | error.SemanticAnalyzeFail => process.exit(1), | 4559 | error.SemanticAnalyzeFail => process.exit(2), |
| 4316 | else => |e| return e, | 4560 | else => |e| return e, |
| 4317 | }; | 4561 | }; |
| 4318 | try comp.makeBinFileExecutable(); | 4562 | try comp.makeBinFileExecutable(); |
| ... | @@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4336 | switch (term) { | 4580 | switch (term) { |
| 4337 | .Exited => |code| { | 4581 | .Exited => |code| { |
| 4338 | if (code == 0) return cleanExit(); | 4582 | if (code == 0) return cleanExit(); |
| 4583 | // Indicates that the build runner has reported compile errors | ||
| 4584 | // and this parent process does not need to report any further | ||
| 4585 | // diagnostics. | ||
| 4586 | if (code == 2) process.exit(2); | ||
| 4339 | 4587 | ||
| 4340 | if (prominent_compile_errors) { | 4588 | const cmd = try std.mem.join(arena, " ", child_argv); |
| 4341 | fatal("the build command failed with exit code {d}", .{code}); | 4589 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); |
| 4342 | } else { | ||
| 4343 | const cmd = try std.mem.join(arena, " ", child_argv); | ||
| 4344 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); | ||
| 4345 | } | ||
| 4346 | }, | 4590 | }, |
| 4347 | else => { | 4591 | else => { |
| 4348 | const cmd = try std.mem.join(arena, " ", child_argv); | 4592 | const cmd = try std.mem.join(arena, " ", child_argv); |
| ... | @@ -4356,7 +4600,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4356,7 +4600,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4356 | } | 4600 | } |
| 4357 | 4601 | ||
| 4358 | fn readSourceFileToEndAlloc( | 4602 | fn readSourceFileToEndAlloc( |
| 4359 | allocator: mem.Allocator, | 4603 | allocator: Allocator, |
| 4360 | input: *const fs.File, | 4604 | input: *const fs.File, |
| 4361 | size_hint: ?usize, | 4605 | size_hint: ?usize, |
| 4362 | ) ![:0]u8 { | 4606 | ) ![:0]u8 { |
| ... | @@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void | ... | @@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4500 | }; | 4744 | }; |
| 4501 | defer tree.deinit(gpa); | 4745 | defer tree.deinit(gpa); |
| 4502 | 4746 | ||
| 4503 | try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color); | ||
| 4504 | var has_ast_error = false; | ||
| 4505 | if (check_ast_flag) { | 4747 | if (check_ast_flag) { |
| 4506 | const Module = @import("Module.zig"); | ||
| 4507 | const AstGen = @import("AstGen.zig"); | ||
| 4508 | |||
| 4509 | var file: Module.File = .{ | 4748 | var file: Module.File = .{ |
| 4510 | .status = .never_loaded, | 4749 | .status = .never_loaded, |
| 4511 | .source_loaded = true, | 4750 | .source_loaded = true, |
| ... | @@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void | ... | @@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4528 | defer file.zir.deinit(gpa); | 4767 | defer file.zir.deinit(gpa); |
| 4529 | 4768 | ||
| 4530 | if (file.zir.hasCompileErrors()) { | 4769 | if (file.zir.hasCompileErrors()) { |
| 4531 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | 4770 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 4532 | defer arena_instance.deinit(); | 4771 | try wip_errors.init(gpa); |
| 4533 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa); | 4772 | defer wip_errors.deinit(); |
| 4534 | defer errors.deinit(); | 4773 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 4535 | 4774 | var error_bundle = try wip_errors.toOwnedBundle(""); | |
| 4536 | try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); | 4775 | defer error_bundle.deinit(gpa); |
| 4537 | const ttyconf: std.debug.TTY.Config = switch (color) { | 4776 | error_bundle.renderToStdErr(renderOptions(color)); |
| 4538 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | 4777 | process.exit(2); |
| 4539 | .on => .escape_codes, | ||
| 4540 | .off => .no_color, | ||
| 4541 | }; | ||
| 4542 | for (errors.items) |full_err_msg| { | ||
| 4543 | full_err_msg.renderToStdErr(ttyconf); | ||
| 4544 | } | ||
| 4545 | has_ast_error = true; | ||
| 4546 | } | 4778 | } |
| 4547 | } | 4779 | } else if (tree.errors.len != 0) { |
| 4548 | if (tree.errors.len != 0 or has_ast_error) { | 4780 | try printAstErrorsToStderr(gpa, tree, "<stdin>", color); |
| 4549 | process.exit(1); | 4781 | process.exit(2); |
| 4550 | } | 4782 | } |
| 4551 | const formatted = try tree.render(gpa); | 4783 | const formatted = try tree.render(gpa); |
| 4552 | defer gpa.free(formatted); | 4784 | defer gpa.free(formatted); |
| ... | @@ -4622,6 +4854,7 @@ const FmtError = error{ | ... | @@ -4622,6 +4854,7 @@ const FmtError = error{ |
| 4622 | ConnectionResetByPeer, | 4854 | ConnectionResetByPeer, |
| 4623 | LockViolation, | 4855 | LockViolation, |
| 4624 | NetNameDeleted, | 4856 | NetNameDeleted, |
| 4857 | InvalidArgument, | ||
| 4625 | } || fs.File.OpenError; | 4858 | } || fs.File.OpenError; |
| 4626 | 4859 | ||
| 4627 | fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { | 4860 | fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { |
| ... | @@ -4687,12 +4920,13 @@ fn fmtPathFile( | ... | @@ -4687,12 +4920,13 @@ fn fmtPathFile( |
| 4687 | if (stat.kind == .Directory) | 4920 | if (stat.kind == .Directory) |
| 4688 | return error.IsDir; | 4921 | return error.IsDir; |
| 4689 | 4922 | ||
| 4923 | const gpa = fmt.gpa; | ||
| 4690 | const source_code = try readSourceFileToEndAlloc( | 4924 | const source_code = try readSourceFileToEndAlloc( |
| 4691 | fmt.gpa, | 4925 | gpa, |
| 4692 | &source_file, | 4926 | &source_file, |
| 4693 | std.math.cast(usize, stat.size) orelse return error.FileTooBig, | 4927 | std.math.cast(usize, stat.size) orelse return error.FileTooBig, |
| 4694 | ); | 4928 | ); |
| 4695 | defer fmt.gpa.free(source_code); | 4929 | defer gpa.free(source_code); |
| 4696 | 4930 | ||
| 4697 | source_file.close(); | 4931 | source_file.close(); |
| 4698 | file_closed = true; | 4932 | file_closed = true; |
| ... | @@ -4700,19 +4934,16 @@ fn fmtPathFile( | ... | @@ -4700,19 +4934,16 @@ fn fmtPathFile( |
| 4700 | // Add to set after no longer possible to get error.IsDir. | 4934 | // Add to set after no longer possible to get error.IsDir. |
| 4701 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; | 4935 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; |
| 4702 | 4936 | ||
| 4703 | var tree = try Ast.parse(fmt.gpa, source_code, .zig); | 4937 | var tree = try Ast.parse(gpa, source_code, .zig); |
| 4704 | defer tree.deinit(fmt.gpa); | 4938 | defer tree.deinit(gpa); |
| 4705 | 4939 | ||
| 4706 | try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree, file_path, fmt.color); | ||
| 4707 | if (tree.errors.len != 0) { | 4940 | if (tree.errors.len != 0) { |
| 4941 | try printAstErrorsToStderr(gpa, tree, file_path, fmt.color); | ||
| 4708 | fmt.any_error = true; | 4942 | fmt.any_error = true; |
| 4709 | return; | 4943 | return; |
| 4710 | } | 4944 | } |
| 4711 | 4945 | ||
| 4712 | if (fmt.check_ast) { | 4946 | if (fmt.check_ast) { |
| 4713 | const Module = @import("Module.zig"); | ||
| 4714 | const AstGen = @import("AstGen.zig"); | ||
| 4715 | |||
| 4716 | var file: Module.File = .{ | 4947 | var file: Module.File = .{ |
| 4717 | .status = .never_loaded, | 4948 | .status = .never_loaded, |
| 4718 | .source_loaded = true, | 4949 | .source_loaded = true, |
| ... | @@ -4731,31 +4962,24 @@ fn fmtPathFile( | ... | @@ -4731,31 +4962,24 @@ fn fmtPathFile( |
| 4731 | .root_decl = .none, | 4962 | .root_decl = .none, |
| 4732 | }; | 4963 | }; |
| 4733 | 4964 | ||
| 4734 | file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); | 4965 | file.pkg = try Package.create(gpa, null, file.sub_file_path); |
| 4735 | defer file.pkg.destroy(fmt.gpa); | 4966 | defer file.pkg.destroy(gpa); |
| 4736 | 4967 | ||
| 4737 | if (stat.size > max_src_size) | 4968 | if (stat.size > max_src_size) |
| 4738 | return error.FileTooBig; | 4969 | return error.FileTooBig; |
| 4739 | 4970 | ||
| 4740 | file.zir = try AstGen.generate(fmt.gpa, file.tree); | 4971 | file.zir = try AstGen.generate(gpa, file.tree); |
| 4741 | file.zir_loaded = true; | 4972 | file.zir_loaded = true; |
| 4742 | defer file.zir.deinit(fmt.gpa); | 4973 | defer file.zir.deinit(gpa); |
| 4743 | 4974 | ||
| 4744 | if (file.zir.hasCompileErrors()) { | 4975 | if (file.zir.hasCompileErrors()) { |
| 4745 | var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa); | 4976 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 4746 | defer arena_instance.deinit(); | 4977 | try wip_errors.init(gpa); |
| 4747 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa); | 4978 | defer wip_errors.deinit(); |
| 4748 | defer errors.deinit(); | 4979 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 4749 | 4980 | var error_bundle = try wip_errors.toOwnedBundle(""); | |
| 4750 | try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); | 4981 | defer error_bundle.deinit(gpa); |
| 4751 | const ttyconf: std.debug.TTY.Config = switch (fmt.color) { | 4982 | error_bundle.renderToStdErr(renderOptions(fmt.color)); |
| 4752 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 4753 | .on => .escape_codes, | ||
| 4754 | .off => .no_color, | ||
| 4755 | }; | ||
| 4756 | for (errors.items) |full_err_msg| { | ||
| 4757 | full_err_msg.renderToStdErr(ttyconf); | ||
| 4758 | } | ||
| 4759 | fmt.any_error = true; | 4983 | fmt.any_error = true; |
| 4760 | } | 4984 | } |
| 4761 | } | 4985 | } |
| ... | @@ -4783,100 +5007,50 @@ fn fmtPathFile( | ... | @@ -4783,100 +5007,50 @@ fn fmtPathFile( |
| 4783 | } | 5007 | } |
| 4784 | } | 5008 | } |
| 4785 | 5009 | ||
| 4786 | pub fn printErrsMsgToStdErr( | 5010 | fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void { |
| 4787 | gpa: mem.Allocator, | 5011 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 4788 | arena: mem.Allocator, | 5012 | try wip_errors.init(gpa); |
| 5013 | defer wip_errors.deinit(); | ||
| 5014 | |||
| 5015 | try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); | ||
| 5016 | |||
| 5017 | var error_bundle = try wip_errors.toOwnedBundle(""); | ||
| 5018 | defer error_bundle.deinit(gpa); | ||
| 5019 | error_bundle.renderToStdErr(renderOptions(color)); | ||
| 5020 | } | ||
| 5021 | |||
| 5022 | pub fn putAstErrorsIntoBundle( | ||
| 5023 | gpa: Allocator, | ||
| 4789 | tree: Ast, | 5024 | tree: Ast, |
| 4790 | path: []const u8, | 5025 | path: []const u8, |
| 4791 | color: Color, | 5026 | wip_errors: *std.zig.ErrorBundle.Wip, |
| 4792 | ) !void { | 5027 | ) !void { |
| 4793 | const parse_errors: []const Ast.Error = tree.errors; | 5028 | var file: Module.File = .{ |
| 4794 | var i: usize = 0; | 5029 | .status = .never_loaded, |
| 4795 | while (i < parse_errors.len) : (i += 1) { | 5030 | .source_loaded = true, |
| 4796 | const parse_error = parse_errors[i]; | 5031 | .zir_loaded = false, |
| 4797 | const lok_token = parse_error.token; | 5032 | .sub_file_path = path, |
| 4798 | const token_tags = tree.tokens.items(.tag); | 5033 | .source = tree.source, |
| 4799 | const start_loc = tree.tokenLocation(0, lok_token); | 5034 | .stat = .{ |
| 4800 | const source_line = tree.source[start_loc.line_start..start_loc.line_end]; | 5035 | .size = 0, |
| 4801 | 5036 | .inode = 0, | |
| 4802 | var text_buf = std.ArrayList(u8).init(gpa); | 5037 | .mtime = 0, |
| 4803 | defer text_buf.deinit(); | 5038 | }, |
| 4804 | const writer = text_buf.writer(); | 5039 | .tree = tree, |
| 4805 | try tree.renderError(parse_error, writer); | 5040 | .tree_loaded = true, |
| 4806 | const text = try arena.dupe(u8, text_buf.items); | 5041 | .zir = undefined, |
| 4807 | 5042 | .pkg = undefined, | |
| 4808 | var notes_buffer: [2]Compilation.AllErrors.Message = undefined; | 5043 | .root_decl = .none, |
| 4809 | var notes_len: usize = 0; | 5044 | }; |
| 4810 | |||
| 4811 | if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) { | ||
| 4812 | const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len); | ||
| 4813 | const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off; | ||
| 4814 | notes_buffer[notes_len] = .{ | ||
| 4815 | .src = .{ | ||
| 4816 | .src_path = path, | ||
| 4817 | .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{ | ||
| 4818 | std.zig.fmtEscapes(tree.source[byte_offset..][0..1]), | ||
| 4819 | }), | ||
| 4820 | .span = .{ .start = byte_offset, .end = byte_offset + 1, .main = byte_offset }, | ||
| 4821 | .line = @intCast(u32, start_loc.line), | ||
| 4822 | .column = @intCast(u32, start_loc.column) + bad_off, | ||
| 4823 | .source_line = source_line, | ||
| 4824 | }, | ||
| 4825 | }; | ||
| 4826 | notes_len += 1; | ||
| 4827 | } | ||
| 4828 | |||
| 4829 | for (parse_errors[i + 1 ..]) |note| { | ||
| 4830 | if (!note.is_note) break; | ||
| 4831 | |||
| 4832 | text_buf.items.len = 0; | ||
| 4833 | try tree.renderError(note, writer); | ||
| 4834 | const note_loc = tree.tokenLocation(0, note.token); | ||
| 4835 | const byte_offset = @intCast(u32, note_loc.line_start); | ||
| 4836 | notes_buffer[notes_len] = .{ | ||
| 4837 | .src = .{ | ||
| 4838 | .src_path = path, | ||
| 4839 | .msg = try arena.dupe(u8, text_buf.items), | ||
| 4840 | .span = .{ | ||
| 4841 | .start = byte_offset, | ||
| 4842 | .end = byte_offset + @intCast(u32, tree.tokenSlice(note.token).len), | ||
| 4843 | .main = byte_offset, | ||
| 4844 | }, | ||
| 4845 | .line = @intCast(u32, note_loc.line), | ||
| 4846 | .column = @intCast(u32, note_loc.column), | ||
| 4847 | .source_line = tree.source[note_loc.line_start..note_loc.line_end], | ||
| 4848 | }, | ||
| 4849 | }; | ||
| 4850 | i += 1; | ||
| 4851 | notes_len += 1; | ||
| 4852 | } | ||
| 4853 | 5045 | ||
| 4854 | const extra_offset = tree.errorOffset(parse_error); | 5046 | file.pkg = try Package.create(gpa, null, path); |
| 4855 | const byte_offset = @intCast(u32, start_loc.line_start) + extra_offset; | 5047 | defer file.pkg.destroy(gpa); |
| 4856 | const message: Compilation.AllErrors.Message = .{ | ||
| 4857 | .src = .{ | ||
| 4858 | .src_path = path, | ||
| 4859 | .msg = text, | ||
| 4860 | .span = .{ | ||
| 4861 | .start = byte_offset, | ||
| 4862 | .end = byte_offset + @intCast(u32, tree.tokenSlice(lok_token).len), | ||
| 4863 | .main = byte_offset, | ||
| 4864 | }, | ||
| 4865 | .line = @intCast(u32, start_loc.line), | ||
| 4866 | .column = @intCast(u32, start_loc.column) + extra_offset, | ||
| 4867 | .source_line = source_line, | ||
| 4868 | .notes = notes_buffer[0..notes_len], | ||
| 4869 | }, | ||
| 4870 | }; | ||
| 4871 | 5048 | ||
| 4872 | const ttyconf: std.debug.TTY.Config = switch (color) { | 5049 | file.zir = try AstGen.generate(gpa, file.tree); |
| 4873 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | 5050 | file.zir_loaded = true; |
| 4874 | .on => .escape_codes, | 5051 | defer file.zir.deinit(gpa); |
| 4875 | .off => .no_color, | ||
| 4876 | }; | ||
| 4877 | 5052 | ||
| 4878 | message.renderToStdErr(ttyconf); | 5053 | try Compilation.addZirErrorMessages(wip_errors, &file); |
| 4879 | } | ||
| 4880 | } | 5054 | } |
| 4881 | 5055 | ||
| 4882 | pub const info_zen = | 5056 | pub const info_zen = |
| ... | @@ -5324,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat | ... | @@ -5324,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat |
| 5324 | return std.zig.system.NativeTargetInfo.detect(cross_target); | 5498 | return std.zig.system.NativeTargetInfo.detect(cross_target); |
| 5325 | } | 5499 | } |
| 5326 | 5500 | ||
| 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. | ||
| 5332 | pub fn cleanExit() void { | ||
| 5333 | if (builtin.mode == .Debug) { | ||
| 5334 | return; | ||
| 5335 | } else { | ||
| 5336 | process.exit(0); | ||
| 5337 | } | ||
| 5338 | } | ||
| 5339 | |||
| 5340 | const usage_ast_check = | 5501 | const usage_ast_check = |
| 5341 | \\Usage: zig ast-check [file] | 5502 | \\Usage: zig ast-check [file] |
| 5342 | \\ | 5503 | \\ |
| ... | @@ -5359,8 +5520,6 @@ pub fn cmdAstCheck( | ... | @@ -5359,8 +5520,6 @@ pub fn cmdAstCheck( |
| 5359 | arena: Allocator, | 5520 | arena: Allocator, |
| 5360 | args: []const []const u8, | 5521 | args: []const []const u8, |
| 5361 | ) !void { | 5522 | ) !void { |
| 5362 | const Module = @import("Module.zig"); | ||
| 5363 | const AstGen = @import("AstGen.zig"); | ||
| 5364 | const Zir = @import("Zir.zig"); | 5523 | const Zir = @import("Zir.zig"); |
| 5365 | 5524 | ||
| 5366 | var color: Color = .auto; | 5525 | var color: Color = .auto; |
| ... | @@ -5450,26 +5609,18 @@ pub fn cmdAstCheck( | ... | @@ -5450,26 +5609,18 @@ pub fn cmdAstCheck( |
| 5450 | file.tree_loaded = true; | 5609 | file.tree_loaded = true; |
| 5451 | defer file.tree.deinit(gpa); | 5610 | defer file.tree.deinit(gpa); |
| 5452 | 5611 | ||
| 5453 | try printErrsMsgToStdErr(gpa, arena, file.tree, file.sub_file_path, color); | ||
| 5454 | if (file.tree.errors.len != 0) { | ||
| 5455 | process.exit(1); | ||
| 5456 | } | ||
| 5457 | |||
| 5458 | file.zir = try AstGen.generate(gpa, file.tree); | 5612 | file.zir = try AstGen.generate(gpa, file.tree); |
| 5459 | file.zir_loaded = true; | 5613 | file.zir_loaded = true; |
| 5460 | defer file.zir.deinit(gpa); | 5614 | defer file.zir.deinit(gpa); |
| 5461 | 5615 | ||
| 5462 | if (file.zir.hasCompileErrors()) { | 5616 | if (file.zir.hasCompileErrors()) { |
| 5463 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | 5617 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 5464 | try Compilation.AllErrors.addZir(arena, &errors, &file); | 5618 | try wip_errors.init(gpa); |
| 5465 | const ttyconf: std.debug.TTY.Config = switch (color) { | 5619 | defer wip_errors.deinit(); |
| 5466 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | 5620 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 5467 | .on => .escape_codes, | 5621 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5468 | .off => .no_color, | 5622 | defer error_bundle.deinit(gpa); |
| 5469 | }; | 5623 | error_bundle.renderToStdErr(renderOptions(color)); |
| 5470 | for (errors.items) |full_err_msg| { | ||
| 5471 | full_err_msg.renderToStdErr(ttyconf); | ||
| 5472 | } | ||
| 5473 | process.exit(1); | 5624 | process.exit(1); |
| 5474 | } | 5625 | } |
| 5475 | 5626 | ||
| ... | @@ -5527,8 +5678,7 @@ pub fn cmdChangelist( | ... | @@ -5527,8 +5678,7 @@ pub fn cmdChangelist( |
| 5527 | arena: Allocator, | 5678 | arena: Allocator, |
| 5528 | args: []const []const u8, | 5679 | args: []const []const u8, |
| 5529 | ) !void { | 5680 | ) !void { |
| 5530 | const Module = @import("Module.zig"); | 5681 | const color: Color = .auto; |
| 5531 | const AstGen = @import("AstGen.zig"); | ||
| 5532 | const Zir = @import("Zir.zig"); | 5682 | const Zir = @import("Zir.zig"); |
| 5533 | 5683 | ||
| 5534 | const old_source_file = args[0]; | 5684 | const old_source_file = args[0]; |
| ... | @@ -5576,22 +5726,18 @@ pub fn cmdChangelist( | ... | @@ -5576,22 +5726,18 @@ pub fn cmdChangelist( |
| 5576 | file.tree_loaded = true; | 5726 | file.tree_loaded = true; |
| 5577 | defer file.tree.deinit(gpa); | 5727 | defer file.tree.deinit(gpa); |
| 5578 | 5728 | ||
| 5579 | try printErrsMsgToStdErr(gpa, arena, file.tree, old_source_file, .auto); | ||
| 5580 | if (file.tree.errors.len != 0) { | ||
| 5581 | process.exit(1); | ||
| 5582 | } | ||
| 5583 | |||
| 5584 | file.zir = try AstGen.generate(gpa, file.tree); | 5729 | file.zir = try AstGen.generate(gpa, file.tree); |
| 5585 | file.zir_loaded = true; | 5730 | file.zir_loaded = true; |
| 5586 | defer file.zir.deinit(gpa); | 5731 | defer file.zir.deinit(gpa); |
| 5587 | 5732 | ||
| 5588 | if (file.zir.hasCompileErrors()) { | 5733 | if (file.zir.hasCompileErrors()) { |
| 5589 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | 5734 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 5590 | try Compilation.AllErrors.addZir(arena, &errors, &file); | 5735 | try wip_errors.init(gpa); |
| 5591 | const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); | 5736 | defer wip_errors.deinit(); |
| 5592 | for (errors.items) |full_err_msg| { | 5737 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 5593 | full_err_msg.renderToStdErr(ttyconf); | 5738 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5594 | } | 5739 | defer error_bundle.deinit(gpa); |
| 5740 | error_bundle.renderToStdErr(renderOptions(color)); | ||
| 5595 | process.exit(1); | 5741 | process.exit(1); |
| 5596 | } | 5742 | } |
| 5597 | 5743 | ||
| ... | @@ -5613,11 +5759,6 @@ pub fn cmdChangelist( | ... | @@ -5613,11 +5759,6 @@ pub fn cmdChangelist( |
| 5613 | var new_tree = try Ast.parse(gpa, new_source, .zig); | 5759 | var new_tree = try Ast.parse(gpa, new_source, .zig); |
| 5614 | defer new_tree.deinit(gpa); | 5760 | defer new_tree.deinit(gpa); |
| 5615 | 5761 | ||
| 5616 | try printErrsMsgToStdErr(gpa, arena, new_tree, new_source_file, .auto); | ||
| 5617 | if (new_tree.errors.len != 0) { | ||
| 5618 | process.exit(1); | ||
| 5619 | } | ||
| 5620 | |||
| 5621 | var old_zir = file.zir; | 5762 | var old_zir = file.zir; |
| 5622 | defer old_zir.deinit(gpa); | 5763 | defer old_zir.deinit(gpa); |
| 5623 | file.zir_loaded = false; | 5764 | file.zir_loaded = false; |
| ... | @@ -5625,12 +5766,13 @@ pub fn cmdChangelist( | ... | @@ -5625,12 +5766,13 @@ pub fn cmdChangelist( |
| 5625 | file.zir_loaded = true; | 5766 | file.zir_loaded = true; |
| 5626 | 5767 | ||
| 5627 | if (file.zir.hasCompileErrors()) { | 5768 | if (file.zir.hasCompileErrors()) { |
| 5628 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | 5769 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 5629 | try Compilation.AllErrors.addZir(arena, &errors, &file); | 5770 | try wip_errors.init(gpa); |
| 5630 | const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); | 5771 | defer wip_errors.deinit(); |
| 5631 | for (errors.items) |full_err_msg| { | 5772 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 5632 | full_err_msg.renderToStdErr(ttyconf); | 5773 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5633 | } | 5774 | defer error_bundle.deinit(gpa); |
| 5775 | error_bundle.renderToStdErr(renderOptions(color)); | ||
| 5634 | process.exit(1); | 5776 | process.exit(1); |
| 5635 | } | 5777 | } |
| 5636 | 5778 | ||
| ... | @@ -5891,3 +6033,20 @@ const ClangSearchSanitizer = struct { | ... | @@ -5891,3 +6033,20 @@ const ClangSearchSanitizer = struct { |
| 5891 | iframework: bool = false, | 6033 | iframework: bool = false, |
| 5892 | }; | 6034 | }; |
| 5893 | }; | 6035 | }; |
| 6036 | |||
| 6037 | fn 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 | |||
| 6045 | fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions { | ||
| 6046 | const ttyconf = get_tty_conf(color); | ||
| 6047 | return .{ | ||
| 6048 | .ttyconf = ttyconf, | ||
| 6049 | .include_source_line = ttyconf != .no_color, | ||
| 6050 | .include_reference_trace = ttyconf != .no_color, | ||
| 6051 | }; | ||
| 6052 | } |
src/mingw.zig+7-7| ... | @@ -19,7 +19,7 @@ pub const CRTFile = enum { | ... | @@ -19,7 +19,7 @@ pub const CRTFile = enum { |
| 19 | uuid_lib, | 19 | uuid_lib, |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 22 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 23 | if (!build_options.have_llvm) { | 23 | if (!build_options.have_llvm) { |
| 24 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 24 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 25 | } | 25 | } |
| ... | @@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 41 | //"-D_UNICODE", | 41 | //"-D_UNICODE", |
| 42 | //"-DWPRFLAG=1", | 42 | //"-DWPRFLAG=1", |
| 43 | }); | 43 | }); |
| 44 | return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{ | 44 | return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &.{ |
| 45 | .{ | 45 | .{ |
| 46 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 46 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 47 | "libc", "mingw", "crt", "crtexe.c", | 47 | "libc", "mingw", "crt", "crtexe.c", |
| ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 60 | "-U__CRTDLL__", | 60 | "-U__CRTDLL__", |
| 61 | "-D__MSVCRT__", | 61 | "-D__MSVCRT__", |
| 62 | }); | 62 | }); |
| 63 | return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{ | 63 | return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &.{ |
| 64 | .{ | 64 | .{ |
| 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 66 | "libc", "mingw", "crt", "crtdll.c", | 66 | "libc", "mingw", "crt", "crtdll.c", |
| ... | @@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 100 | .extra_flags = args.items, | 100 | .extra_flags = args.items, |
| 101 | }; | 101 | }; |
| 102 | } | 102 | } |
| 103 | return comp.build_crt_file("mingw32", .Lib, &c_source_files); | 103 | return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, &c_source_files); |
| 104 | }, | 104 | }, |
| 105 | 105 | ||
| 106 | .msvcrt_os_lib => { | 106 | .msvcrt_os_lib => { |
| ... | @@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 148 | }; | 148 | }; |
| 149 | } | 149 | } |
| 150 | } | 150 | } |
| 151 | return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items); | 151 | return comp.build_crt_file("msvcrt-os", .Lib, .@"mingw-w64 msvcrt-os.lib", prog_node, c_source_files.items); |
| 152 | }, | 152 | }, |
| 153 | 153 | ||
| 154 | .mingwex_lib => { | 154 | .mingwex_lib => { |
| ... | @@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 211 | } else { | 211 | } else { |
| 212 | @panic("unsupported arch"); | 212 | @panic("unsupported arch"); |
| 213 | } | 213 | } |
| 214 | return comp.build_crt_file("mingwex", .Lib, c_source_files.items); | 214 | return comp.build_crt_file("mingwex", .Lib, .@"mingw-w64 mingwex.lib", prog_node, c_source_files.items); |
| 215 | }, | 215 | }, |
| 216 | 216 | ||
| 217 | .uuid_lib => { | 217 | .uuid_lib => { |
| ... | @@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 244 | .extra_flags = extra_flags, | 244 | .extra_flags = extra_flags, |
| 245 | }; | 245 | }; |
| 246 | } | 246 | } |
| 247 | return comp.build_crt_file("uuid", .Lib, &c_source_files); | 247 | return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", prog_node, &c_source_files); |
| 248 | }, | 248 | }, |
| 249 | } | 249 | } |
| 250 | } | 250 | } |
src/musl.zig+8-8| ... | @@ -17,7 +17,7 @@ pub const CRTFile = enum { | ... | @@ -17,7 +17,7 @@ pub const CRTFile = enum { |
| 17 | libc_so, | 17 | libc_so, |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 20 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 21 | if (!build_options.have_llvm) { | 21 | if (!build_options.have_llvm) { |
| 22 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 22 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 23 | } | 23 | } |
| ... | @@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 33 | try args.appendSlice(&[_][]const u8{ | 33 | try args.appendSlice(&[_][]const u8{ |
| 34 | "-Qunused-arguments", | 34 | "-Qunused-arguments", |
| 35 | }); | 35 | }); |
| 36 | return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ | 36 | return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{ |
| 37 | .{ | 37 | .{ |
| 38 | .src_path = try start_asm_path(comp, arena, "crti.s"), | 38 | .src_path = try start_asm_path(comp, arena, "crti.s"), |
| 39 | .extra_flags = args.items, | 39 | .extra_flags = args.items, |
| ... | @@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 46 | try args.appendSlice(&[_][]const u8{ | 46 | try args.appendSlice(&[_][]const u8{ |
| 47 | "-Qunused-arguments", | 47 | "-Qunused-arguments", |
| 48 | }); | 48 | }); |
| 49 | return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ | 49 | return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{ |
| 50 | .{ | 50 | .{ |
| 51 | .src_path = try start_asm_path(comp, arena, "crtn.s"), | 51 | .src_path = try start_asm_path(comp, arena, "crtn.s"), |
| 52 | .extra_flags = args.items, | 52 | .extra_flags = args.items, |
| ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 60 | "-fno-stack-protector", | 60 | "-fno-stack-protector", |
| 61 | "-DCRT", | 61 | "-DCRT", |
| 62 | }); | 62 | }); |
| 63 | return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{ | 63 | return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{ |
| 64 | .{ | 64 | .{ |
| 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 66 | "libc", "musl", "crt", "crt1.c", | 66 | "libc", "musl", "crt", "crt1.c", |
| ... | @@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 77 | "-fno-stack-protector", | 77 | "-fno-stack-protector", |
| 78 | "-DCRT", | 78 | "-DCRT", |
| 79 | }); | 79 | }); |
| 80 | return comp.build_crt_file("rcrt1", .Obj, &[1]Compilation.CSourceFile{ | 80 | return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{ |
| 81 | .{ | 81 | .{ |
| 82 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 82 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 83 | "libc", "musl", "crt", "rcrt1.c", | 83 | "libc", "musl", "crt", "rcrt1.c", |
| ... | @@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 94 | "-fno-stack-protector", | 94 | "-fno-stack-protector", |
| 95 | "-DCRT", | 95 | "-DCRT", |
| 96 | }); | 96 | }); |
| 97 | return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{ | 97 | return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{ |
| 98 | .{ | 98 | .{ |
| 99 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 99 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 100 | "libc", "musl", "crt", "Scrt1.c", | 100 | "libc", "musl", "crt", "Scrt1.c", |
| ... | @@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 187 | .extra_flags = args.items, | 187 | .extra_flags = args.items, |
| 188 | }; | 188 | }; |
| 189 | } | 189 | } |
| 190 | return comp.build_crt_file("c", .Lib, c_source_files.items); | 190 | return comp.build_crt_file("c", .Lib, .@"musl libc.a", prog_node, c_source_files.items); |
| 191 | }, | 191 | }, |
| 192 | .libc_so => { | 192 | .libc_so => { |
| 193 | const target = comp.getTarget(); | 193 | const target = comp.getTarget(); |
| ... | @@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 241 | }); | 241 | }); |
| 242 | defer sub_compilation.destroy(); | 242 | defer sub_compilation.destroy(); |
| 243 | 243 | ||
| 244 | try sub_compilation.updateSubCompilation(); | 244 | try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node); |
| 245 | 245 | ||
| 246 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | 246 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); |
| 247 | 247 |
src/objcopy.zig+45-5| ... | @@ -4,22 +4,25 @@ const fs = std.fs; | ... | @@ -4,22 +4,25 @@ const fs = std.fs; |
| 4 | const elf = std.elf; | 4 | const elf = std.elf; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const File = std.fs.File; | 6 | const File = std.fs.File; |
| 7 | const assert = std.debug.assert; | ||
| 8 | |||
| 7 | const main = @import("main.zig"); | 9 | const main = @import("main.zig"); |
| 8 | const fatal = main.fatal; | 10 | const fatal = main.fatal; |
| 9 | const cleanExit = main.cleanExit; | 11 | const Server = std.zig.Server; |
| 12 | const build_options = @import("build_options"); | ||
| 10 | 13 | ||
| 11 | pub fn cmdObjCopy( | 14 | pub fn cmdObjCopy( |
| 12 | gpa: Allocator, | 15 | gpa: Allocator, |
| 13 | arena: Allocator, | 16 | arena: Allocator, |
| 14 | args: []const []const u8, | 17 | args: []const []const u8, |
| 15 | ) !void { | 18 | ) !void { |
| 16 | _ = gpa; | ||
| 17 | var i: usize = 0; | 19 | var i: usize = 0; |
| 18 | var opt_out_fmt: ?std.Target.ObjectFormat = null; | 20 | var opt_out_fmt: ?std.Target.ObjectFormat = null; |
| 19 | var opt_input: ?[]const u8 = null; | 21 | var opt_input: ?[]const u8 = null; |
| 20 | var opt_output: ?[]const u8 = null; | 22 | var opt_output: ?[]const u8 = null; |
| 21 | var only_section: ?[]const u8 = null; | 23 | var only_section: ?[]const u8 = null; |
| 22 | var pad_to: ?u64 = null; | 24 | var pad_to: ?u64 = null; |
| 25 | var listen = false; | ||
| 23 | while (i < args.len) : (i += 1) { | 26 | while (i < args.len) : (i += 1) { |
| 24 | const arg = args[i]; | 27 | const arg = args[i]; |
| 25 | if (!mem.startsWith(u8, arg, "-")) { | 28 | if (!mem.startsWith(u8, arg, "-")) { |
| ... | @@ -54,6 +57,8 @@ pub fn cmdObjCopy( | ... | @@ -54,6 +57,8 @@ pub fn cmdObjCopy( |
| 54 | i += 1; | 57 | i += 1; |
| 55 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | 58 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| 56 | only_section = args[i]; | 59 | only_section = args[i]; |
| 60 | } else if (mem.eql(u8, arg, "--listen=-")) { | ||
| 61 | listen = true; | ||
| 57 | } else if (mem.startsWith(u8, arg, "--only-section=")) { | 62 | } else if (mem.startsWith(u8, arg, "--only-section=")) { |
| 58 | only_section = arg["--output-target=".len..]; | 63 | only_section = arg["--output-target=".len..]; |
| 59 | } else if (mem.eql(u8, arg, "--pad-to")) { | 64 | } else if (mem.eql(u8, arg, "--pad-to")) { |
| ... | @@ -102,10 +107,45 @@ pub fn cmdObjCopy( | ... | @@ -102,10 +107,45 @@ pub fn cmdObjCopy( |
| 102 | .only_section = only_section, | 107 | .only_section = only_section, |
| 103 | .pad_to = pad_to, | 108 | .pad_to = pad_to, |
| 104 | }); | 109 | }); |
| 105 | return cleanExit(); | ||
| 106 | }, | 110 | }, |
| 107 | else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), | 111 | else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), |
| 108 | } | 112 | } |
| 113 | |||
| 114 | if (listen) { | ||
| 115 | var server = try Server.init(.{ | ||
| 116 | .gpa = gpa, | ||
| 117 | .in = std.io.getStdIn(), | ||
| 118 | .out = std.io.getStdOut(), | ||
| 119 | .zig_version = build_options.version, | ||
| 120 | }); | ||
| 121 | defer server.deinit(); | ||
| 122 | |||
| 123 | var seen_update = false; | ||
| 124 | while (true) { | ||
| 125 | const hdr = try server.receiveMessage(); | ||
| 126 | switch (hdr.tag) { | ||
| 127 | .exit => { | ||
| 128 | return std.process.cleanExit(); | ||
| 129 | }, | ||
| 130 | .update => { | ||
| 131 | if (seen_update) { | ||
| 132 | std.debug.print("zig objcopy only supports 1 update for now\n", .{}); | ||
| 133 | std.process.exit(1); | ||
| 134 | } | ||
| 135 | seen_update = true; | ||
| 136 | |||
| 137 | try server.serveEmitBinPath(output, .{ | ||
| 138 | .flags = .{ .cache_hit = false }, | ||
| 139 | }); | ||
| 140 | }, | ||
| 141 | else => { | ||
| 142 | std.debug.print("unsupported message: {s}", .{@tagName(hdr.tag)}); | ||
| 143 | std.process.exit(1); | ||
| 144 | }, | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | return std.process.cleanExit(); | ||
| 109 | } | 149 | } |
| 110 | 150 | ||
| 111 | const usage = | 151 | const usage = |
| ... | @@ -417,7 +457,7 @@ const HexWriter = struct { | ... | @@ -417,7 +457,7 @@ const HexWriter = struct { |
| 417 | } | 457 | } |
| 418 | 458 | ||
| 419 | fn Address(address: u32) Record { | 459 | fn Address(address: u32) Record { |
| 420 | std.debug.assert(address > 0xFFFF); | 460 | assert(address > 0xFFFF); |
| 421 | const segment = @intCast(u16, address / 0x10000); | 461 | const segment = @intCast(u16, address / 0x10000); |
| 422 | if (address > 0xFFFFF) { | 462 | if (address > 0xFFFFF) { |
| 423 | return Record{ | 463 | return Record{ |
| ... | @@ -460,7 +500,7 @@ const HexWriter = struct { | ... | @@ -460,7 +500,7 @@ const HexWriter = struct { |
| 460 | const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; | 500 | const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; |
| 461 | var outbuf: [BUFSIZE]u8 = undefined; | 501 | var outbuf: [BUFSIZE]u8 = undefined; |
| 462 | const payload_bytes = self.getPayloadBytes(); | 502 | const payload_bytes = self.getPayloadBytes(); |
| 463 | std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN); | 503 | assert(payload_bytes.len <= MAX_PAYLOAD_LEN); |
| 464 | 504 | ||
| 465 | const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ | 505 | const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ |
| 466 | @intCast(u8, payload_bytes.len), | 506 | @intCast(u8, payload_bytes.len), |
src/print_air.zig+1| ... | @@ -194,6 +194,7 @@ const Writer = struct { | ... | @@ -194,6 +194,7 @@ const Writer = struct { |
| 194 | .c_va_end, | 194 | .c_va_end, |
| 195 | => try w.writeUnOp(s, inst), | 195 | => try w.writeUnOp(s, inst), |
| 196 | 196 | ||
| 197 | .trap, | ||
| 197 | .breakpoint, | 198 | .breakpoint, |
| 198 | .unreach, | 199 | .unreach, |
| 199 | .ret_addr, | 200 | .ret_addr, |
src/print_zir.zig+2-1| ... | @@ -196,7 +196,6 @@ const Writer = struct { | ... | @@ -196,7 +196,6 @@ const Writer = struct { |
| 196 | .error_name, | 196 | .error_name, |
| 197 | .panic, | 197 | .panic, |
| 198 | .panic_comptime, | 198 | .panic_comptime, |
| 199 | .set_cold, | ||
| 200 | .set_runtime_safety, | 199 | .set_runtime_safety, |
| 201 | .sqrt, | 200 | .sqrt, |
| 202 | .sin, | 201 | .sin, |
| ... | @@ -411,6 +410,7 @@ const Writer = struct { | ... | @@ -411,6 +410,7 @@ const Writer = struct { |
| 411 | .alloc_inferred_comptime_mut, | 410 | .alloc_inferred_comptime_mut, |
| 412 | .ret_ptr, | 411 | .ret_ptr, |
| 413 | .ret_type, | 412 | .ret_type, |
| 413 | .trap, | ||
| 414 | => try self.writeNode(stream, inst), | 414 | => try self.writeNode(stream, inst), |
| 415 | 415 | ||
| 416 | .error_value, | 416 | .error_value, |
| ... | @@ -503,6 +503,7 @@ const Writer = struct { | ... | @@ -503,6 +503,7 @@ const Writer = struct { |
| 503 | .fence, | 503 | .fence, |
| 504 | .set_float_mode, | 504 | .set_float_mode, |
| 505 | .set_align_stack, | 505 | .set_align_stack, |
| 506 | .set_cold, | ||
| 506 | .wasm_memory_size, | 507 | .wasm_memory_size, |
| 507 | .error_to_int, | 508 | .error_to_int, |
| 508 | .int_to_error, | 509 | .int_to_error, |
src/register_manager.zig+3| ... | @@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{ | ... | @@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{ |
| 19 | /// Can happen when spilling an instruction in codegen runs out of | 19 | /// Can happen when spilling an instruction in codegen runs out of |
| 20 | /// memory, so we propagate that error | 20 | /// memory, so we propagate that error |
| 21 | OutOfMemory, | 21 | OutOfMemory, |
| 22 | /// Can happen when spilling an instruction in codegen triggers integer | ||
| 23 | /// overflow, so we propagate that error | ||
| 24 | Overflow, | ||
| 22 | /// Can happen when spilling an instruction triggers a codegen | 25 | /// Can happen when spilling an instruction triggers a codegen |
| 23 | /// error, so we propagate that error | 26 | /// error, so we propagate that error |
| 24 | CodegenFail, | 27 | CodegenFail, |
src/test.zig deleted-1984| ... | @@ -1,1984 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Allocator = std.mem.Allocator; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | ||
| 5 | const print = std.debug.print; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | |||
| 8 | const link = @import("link.zig"); | ||
| 9 | const Compilation = @import("Compilation.zig"); | ||
| 10 | const Package = @import("Package.zig"); | ||
| 11 | const introspect = @import("introspect.zig"); | ||
| 12 | const build_options = @import("build_options"); | ||
| 13 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 14 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 15 | const zig_h = link.File.C.zig_h; | ||
| 16 | |||
| 17 | const enable_qemu: bool = build_options.enable_qemu; | ||
| 18 | const enable_wine: bool = build_options.enable_wine; | ||
| 19 | const enable_wasmtime: bool = build_options.enable_wasmtime; | ||
| 20 | const enable_darling: bool = build_options.enable_darling; | ||
| 21 | const enable_rosetta: bool = build_options.enable_rosetta; | ||
| 22 | const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; | ||
| 23 | const skip_stage1 = true; | ||
| 24 | |||
| 25 | const hr = "=" ** 80; | ||
| 26 | |||
| 27 | test { | ||
| 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 | |||
| 64 | const 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 | ||
| 157 | const 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 | ||
| 211 | const 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 | |||
| 403 | const 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. | ||
| 415 | const 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> | ||
| 485 | fn 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. | ||
| 511 | fn 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 | |||
| 546 | pub 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 | |||
| 1977 | fn dumpArgs(argv: []const []const u8) void { | ||
| 1978 | for (argv) |arg| { | ||
| 1979 | print("{s} ", .{arg}); | ||
| 1980 | } | ||
| 1981 | print("\n", .{}); | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | const tmp_src_path = "tmp.zig"; | ||
src/translate_c.zig+15-1| ... | @@ -784,9 +784,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co | ... | @@ -784,9 +784,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 784 | 784 | ||
| 785 | const qual_type = var_decl.getTypeSourceInfo_getType(); | 785 | const qual_type = var_decl.getTypeSourceInfo_getType(); |
| 786 | const storage_class = var_decl.getStorageClass(); | 786 | const storage_class = var_decl.getStorageClass(); |
| 787 | const is_const = qual_type.isConstQualified(); | ||
| 788 | const has_init = var_decl.hasInit(); | 787 | const has_init = var_decl.hasInit(); |
| 789 | const decl_init = var_decl.getInit(); | 788 | const decl_init = var_decl.getInit(); |
| 789 | var is_const = qual_type.isConstQualified(); | ||
| 790 | 790 | ||
| 791 | // In C extern variables with initializers behave like Zig exports. | 791 | // In C extern variables with initializers behave like Zig exports. |
| 792 | // extern int foo = 2; | 792 | // extern int foo = 2; |
| ... | @@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co | ... | @@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 843 | 843 | ||
| 844 | // std.mem.zeroes(T) | 844 | // std.mem.zeroes(T) |
| 845 | init_node = try Tag.std_mem_zeroes.create(c.arena, type_node); | 845 | init_node = try Tag.std_mem_zeroes.create(c.arena, type_node); |
| 846 | } else if (qual_type.getTypeClass() == .IncompleteArray) { | ||
| 847 | // Oh no, an extern array of unknown size! These are really fun because there's no | ||
| 848 | // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer | ||
| 849 | // to the data initialized via @extern. | ||
| 850 | |||
| 851 | const name_str = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{var_name}); | ||
| 852 | init_node = try Tag.builtin_extern.create(c.arena, .{ | ||
| 853 | .type = type_node, | ||
| 854 | .name = try Tag.string_literal.create(c.arena, name_str), | ||
| 855 | }); | ||
| 856 | |||
| 857 | // Since this is really a pointer to the underlying data, we tweak a few properties. | ||
| 858 | is_extern = false; | ||
| 859 | is_const = true; | ||
| 846 | } | 860 | } |
| 847 | 861 | ||
| 848 | const linksection_string = blk: { | 862 | const linksection_string = blk: { |
src/translate_c/ast.zig+28| ... | @@ -158,6 +158,8 @@ pub const Node = extern union { | ... | @@ -158,6 +158,8 @@ pub const Node = extern union { |
| 158 | vector_zero_init, | 158 | vector_zero_init, |
| 159 | /// @shuffle(type, a, b, mask) | 159 | /// @shuffle(type, a, b, mask) |
| 160 | shuffle, | 160 | shuffle, |
| 161 | /// @extern(ty, .{ .name = n }) | ||
| 162 | builtin_extern, | ||
| 161 | 163 | ||
| 162 | /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs) | 164 | /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs) |
| 163 | macro_arithmetic, | 165 | macro_arithmetic, |
| ... | @@ -373,6 +375,7 @@ pub const Node = extern union { | ... | @@ -373,6 +375,7 @@ pub const Node = extern union { |
| 373 | .field_access => Payload.FieldAccess, | 375 | .field_access => Payload.FieldAccess, |
| 374 | .string_slice => Payload.StringSlice, | 376 | .string_slice => Payload.StringSlice, |
| 375 | .shuffle => Payload.Shuffle, | 377 | .shuffle => Payload.Shuffle, |
| 378 | .builtin_extern => Payload.Extern, | ||
| 376 | .macro_arithmetic => Payload.MacroArithmetic, | 379 | .macro_arithmetic => Payload.MacroArithmetic, |
| 377 | }; | 380 | }; |
| 378 | } | 381 | } |
| ... | @@ -718,6 +721,14 @@ pub const Payload = struct { | ... | @@ -718,6 +721,14 @@ pub const Payload = struct { |
| 718 | }, | 721 | }, |
| 719 | }; | 722 | }; |
| 720 | 723 | ||
| 724 | pub const Extern = struct { | ||
| 725 | base: Payload, | ||
| 726 | data: struct { | ||
| 727 | type: Node, | ||
| 728 | name: Node, | ||
| 729 | }, | ||
| 730 | }; | ||
| 731 | |||
| 721 | pub const MacroArithmetic = struct { | 732 | pub const MacroArithmetic = struct { |
| 722 | base: Payload, | 733 | base: Payload, |
| 723 | data: struct { | 734 | data: struct { |
| ... | @@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { | ... | @@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1409 | payload.mask_vector, | 1420 | payload.mask_vector, |
| 1410 | }); | 1421 | }); |
| 1411 | }, | 1422 | }, |
| 1423 | .builtin_extern => { | ||
| 1424 | const payload = node.castTag(.builtin_extern).?.data; | ||
| 1425 | |||
| 1426 | var info_inits: [1]Payload.ContainerInitDot.Initializer = .{ | ||
| 1427 | .{ .name = "name", .value = payload.name }, | ||
| 1428 | }; | ||
| 1429 | var info_payload: Payload.ContainerInitDot = .{ | ||
| 1430 | .base = .{ .tag = .container_init_dot }, | ||
| 1431 | .data = &info_inits, | ||
| 1432 | }; | ||
| 1433 | |||
| 1434 | return renderBuiltinCall(c, "@extern", &.{ | ||
| 1435 | payload.type, | ||
| 1436 | .{ .ptr_otherwise = &info_payload.base }, | ||
| 1437 | }); | ||
| 1438 | }, | ||
| 1412 | .macro_arithmetic => { | 1439 | .macro_arithmetic => { |
| 1413 | const payload = node.castTag(.macro_arithmetic).?.data; | 1440 | const payload = node.castTag(.macro_arithmetic).?.data; |
| 1414 | const op = @tagName(payload.op); | 1441 | const op = @tagName(payload.op); |
| ... | @@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { | ... | @@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2348 | .div_exact, | 2375 | .div_exact, |
| 2349 | .offset_of, | 2376 | .offset_of, |
| 2350 | .shuffle, | 2377 | .shuffle, |
| 2378 | .builtin_extern, | ||
| 2351 | .static_local_var, | 2379 | .static_local_var, |
| 2352 | .mut_str, | 2380 | .mut_str, |
| 2353 | .macro_arithmetic, | 2381 | .macro_arithmetic, |
src/value.zig+60-7| ... | @@ -1365,6 +1365,17 @@ pub const Value = extern union { | ... | @@ -1365,6 +1365,17 @@ pub const Value = extern union { |
| 1365 | if (val.isDeclRef()) return error.ReinterpretDeclRef; | 1365 | if (val.isDeclRef()) return error.ReinterpretDeclRef; |
| 1366 | return val.writeToMemory(Type.usize, mod, buffer); | 1366 | return val.writeToMemory(Type.usize, mod, buffer); |
| 1367 | }, | 1367 | }, |
| 1368 | .Optional => { | ||
| 1369 | assert(ty.isPtrLikeOptional()); | ||
| 1370 | var buf: Type.Payload.ElemType = undefined; | ||
| 1371 | const child = ty.optionalChild(&buf); | ||
| 1372 | const opt_val = val.optionalValue(); | ||
| 1373 | if (opt_val) |some| { | ||
| 1374 | return some.writeToMemory(child, mod, buffer); | ||
| 1375 | } else { | ||
| 1376 | return writeToMemory(Value.zero, Type.usize, mod, buffer); | ||
| 1377 | } | ||
| 1378 | }, | ||
| 1368 | else => @panic("TODO implement writeToMemory for more types"), | 1379 | else => @panic("TODO implement writeToMemory for more types"), |
| 1369 | } | 1380 | } |
| 1370 | } | 1381 | } |
| ... | @@ -1471,6 +1482,17 @@ pub const Value = extern union { | ... | @@ -1471,6 +1482,17 @@ pub const Value = extern union { |
| 1471 | if (val.isDeclRef()) return error.ReinterpretDeclRef; | 1482 | if (val.isDeclRef()) return error.ReinterpretDeclRef; |
| 1472 | return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset); | 1483 | return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset); |
| 1473 | }, | 1484 | }, |
| 1485 | .Optional => { | ||
| 1486 | assert(ty.isPtrLikeOptional()); | ||
| 1487 | var buf: Type.Payload.ElemType = undefined; | ||
| 1488 | const child = ty.optionalChild(&buf); | ||
| 1489 | const opt_val = val.optionalValue(); | ||
| 1490 | if (opt_val) |some| { | ||
| 1491 | return some.writeToPackedMemory(child, mod, buffer, bit_offset); | ||
| 1492 | } else { | ||
| 1493 | return writeToPackedMemory(Value.zero, Type.usize, mod, buffer, bit_offset); | ||
| 1494 | } | ||
| 1495 | }, | ||
| 1474 | else => @panic("TODO implement writeToPackedMemory for more types"), | 1496 | else => @panic("TODO implement writeToPackedMemory for more types"), |
| 1475 | } | 1497 | } |
| 1476 | } | 1498 | } |
| ... | @@ -1579,6 +1601,12 @@ pub const Value = extern union { | ... | @@ -1579,6 +1601,12 @@ pub const Value = extern union { |
| 1579 | assert(!ty.isSlice()); // No well defined layout. | 1601 | assert(!ty.isSlice()); // No well defined layout. |
| 1580 | return readFromMemory(Type.usize, mod, buffer, arena); | 1602 | return readFromMemory(Type.usize, mod, buffer, arena); |
| 1581 | }, | 1603 | }, |
| 1604 | .Optional => { | ||
| 1605 | assert(ty.isPtrLikeOptional()); | ||
| 1606 | var buf: Type.Payload.ElemType = undefined; | ||
| 1607 | const child = ty.optionalChild(&buf); | ||
| 1608 | return readFromMemory(child, mod, buffer, arena); | ||
| 1609 | }, | ||
| 1582 | else => @panic("TODO implement readFromMemory for more types"), | 1610 | else => @panic("TODO implement readFromMemory for more types"), |
| 1583 | } | 1611 | } |
| 1584 | } | 1612 | } |
| ... | @@ -1670,6 +1698,12 @@ pub const Value = extern union { | ... | @@ -1670,6 +1698,12 @@ pub const Value = extern union { |
| 1670 | assert(!ty.isSlice()); // No well defined layout. | 1698 | assert(!ty.isSlice()); // No well defined layout. |
| 1671 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); | 1699 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); |
| 1672 | }, | 1700 | }, |
| 1701 | .Optional => { | ||
| 1702 | assert(ty.isPtrLikeOptional()); | ||
| 1703 | var buf: Type.Payload.ElemType = undefined; | ||
| 1704 | const child = ty.optionalChild(&buf); | ||
| 1705 | return readFromPackedMemory(child, mod, buffer, bit_offset, arena); | ||
| 1706 | }, | ||
| 1673 | else => @panic("TODO implement readFromPackedMemory for more types"), | 1707 | else => @panic("TODO implement readFromPackedMemory for more types"), |
| 1674 | } | 1708 | } |
| 1675 | } | 1709 | } |
| ... | @@ -3144,13 +3178,32 @@ pub const Value = extern union { | ... | @@ -3144,13 +3178,32 @@ pub const Value = extern union { |
| 3144 | /// TODO: check for cases such as array that is not marked undef but all the element | 3178 | /// TODO: check for cases such as array that is not marked undef but all the element |
| 3145 | /// values are marked undef, or struct that is not marked undef but all fields are marked | 3179 | /// values are marked undef, or struct that is not marked undef but all fields are marked |
| 3146 | /// undef, etc. | 3180 | /// undef, etc. |
| 3147 | pub fn anyUndef(self: Value) bool { | 3181 | pub fn anyUndef(self: Value, mod: *Module) bool { |
| 3148 | if (self.castTag(.aggregate)) |aggregate| { | 3182 | switch (self.tag()) { |
| 3149 | for (aggregate.data) |val| { | 3183 | .slice => { |
| 3150 | if (val.anyUndef()) return true; | 3184 | const payload = self.castTag(.slice).?; |
| 3151 | } | 3185 | const len = payload.data.len.toUnsignedInt(mod.getTarget()); |
| 3186 | |||
| 3187 | var elem_value_buf: ElemValueBuffer = undefined; | ||
| 3188 | var i: usize = 0; | ||
| 3189 | while (i < len) : (i += 1) { | ||
| 3190 | const elem_val = payload.data.ptr.elemValueBuffer(mod, i, &elem_value_buf); | ||
| 3191 | if (elem_val.anyUndef(mod)) return true; | ||
| 3192 | } | ||
| 3193 | }, | ||
| 3194 | |||
| 3195 | .aggregate => { | ||
| 3196 | const payload = self.castTag(.aggregate).?; | ||
| 3197 | for (payload.data) |val| { | ||
| 3198 | if (val.anyUndef(mod)) return true; | ||
| 3199 | } | ||
| 3200 | }, | ||
| 3201 | |||
| 3202 | .undef => return true, | ||
| 3203 | else => {}, | ||
| 3152 | } | 3204 | } |
| 3153 | return self.isUndef(); | 3205 | |
| 3206 | return false; | ||
| 3154 | } | 3207 | } |
| 3155 | 3208 | ||
| 3156 | /// Asserts the value is not undefined and not unreachable. | 3209 | /// Asserts the value is not undefined and not unreachable. |
| ... | @@ -3319,7 +3372,7 @@ pub const Value = extern union { | ... | @@ -3319,7 +3372,7 @@ pub const Value = extern union { |
| 3319 | } | 3372 | } |
| 3320 | } | 3373 | } |
| 3321 | 3374 | ||
| 3322 | fn floatToValue(float: f128, arena: Allocator, dest_ty: Type, target: Target) !Value { | 3375 | pub fn floatToValue(float: f128, arena: Allocator, dest_ty: Type, target: Target) !Value { |
| 3323 | switch (dest_ty.floatBits(target)) { | 3376 | switch (dest_ty.floatBits(target)) { |
| 3324 | 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)), | 3377 | 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)), |
| 3325 | 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)), | 3378 | 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)), |
src/wasi_libc.zig+8-8| ... | @@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co | ... | @@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co |
| 59 | }; | 59 | }; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 62 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 63 | if (!build_options.have_llvm) { | 63 | if (!build_options.have_llvm) { |
| 64 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 64 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 65 | } | 65 | } |
| ... | @@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 74 | var args = std.ArrayList([]const u8).init(arena); | 74 | var args = std.ArrayList([]const u8).init(arena); |
| 75 | try addCCArgs(comp, arena, &args, false); | 75 | try addCCArgs(comp, arena, &args, false); |
| 76 | try addLibcBottomHalfIncludes(comp, arena, &args); | 76 | try addLibcBottomHalfIncludes(comp, arena, &args); |
| 77 | return comp.build_crt_file("crt1-reactor", .Obj, &[1]Compilation.CSourceFile{ | 77 | return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &.{ |
| 78 | .{ | 78 | .{ |
| 79 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 79 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 80 | "libc", try sanitize(arena, crt1_reactor_src_file), | 80 | "libc", try sanitize(arena, crt1_reactor_src_file), |
| ... | @@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 87 | var args = std.ArrayList([]const u8).init(arena); | 87 | var args = std.ArrayList([]const u8).init(arena); |
| 88 | try addCCArgs(comp, arena, &args, false); | 88 | try addCCArgs(comp, arena, &args, false); |
| 89 | try addLibcBottomHalfIncludes(comp, arena, &args); | 89 | try addLibcBottomHalfIncludes(comp, arena, &args); |
| 90 | return comp.build_crt_file("crt1-command", .Obj, &[1]Compilation.CSourceFile{ | 90 | return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &.{ |
| 91 | .{ | 91 | .{ |
| 92 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 92 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 93 | "libc", try sanitize(arena, crt1_command_src_file), | 93 | "libc", try sanitize(arena, crt1_command_src_file), |
| ... | @@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 145 | } | 145 | } |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | try comp.build_crt_file("c", .Lib, libc_sources.items); | 148 | try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items); |
| 149 | }, | 149 | }, |
| 150 | .libwasi_emulated_process_clocks_a => { | 150 | .libwasi_emulated_process_clocks_a => { |
| 151 | var args = std.ArrayList([]const u8).init(arena); | 151 | var args = std.ArrayList([]const u8).init(arena); |
| ... | @@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 161 | .extra_flags = args.items, | 161 | .extra_flags = args.items, |
| 162 | }); | 162 | }); |
| 163 | } | 163 | } |
| 164 | try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, emu_clocks_sources.items); | 164 | try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items); |
| 165 | }, | 165 | }, |
| 166 | .libwasi_emulated_getpid_a => { | 166 | .libwasi_emulated_getpid_a => { |
| 167 | var args = std.ArrayList([]const u8).init(arena); | 167 | var args = std.ArrayList([]const u8).init(arena); |
| ... | @@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 177 | .extra_flags = args.items, | 177 | .extra_flags = args.items, |
| 178 | }); | 178 | }); |
| 179 | } | 179 | } |
| 180 | try comp.build_crt_file("wasi-emulated-getpid", .Lib, emu_getpid_sources.items); | 180 | try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items); |
| 181 | }, | 181 | }, |
| 182 | .libwasi_emulated_mman_a => { | 182 | .libwasi_emulated_mman_a => { |
| 183 | var args = std.ArrayList([]const u8).init(arena); | 183 | var args = std.ArrayList([]const u8).init(arena); |
| ... | @@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 193 | .extra_flags = args.items, | 193 | .extra_flags = args.items, |
| 194 | }); | 194 | }); |
| 195 | } | 195 | } |
| 196 | try comp.build_crt_file("wasi-emulated-mman", .Lib, emu_mman_sources.items); | 196 | try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items); |
| 197 | }, | 197 | }, |
| 198 | .libwasi_emulated_signal_a => { | 198 | .libwasi_emulated_signal_a => { |
| 199 | var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); | 199 | var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); |
| ... | @@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 228 | } | 228 | } |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | try comp.build_crt_file("wasi-emulated-signal", .Lib, emu_signal_sources.items); | 231 | try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", prog_node, emu_signal_sources.items); |
| 232 | }, | 232 | }, |
| 233 | } | 233 | } |
| 234 | } | 234 | } |
stage1/wasi.c-2| ... | @@ -497,8 +497,6 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov | ... | @@ -497,8 +497,6 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov |
| 497 | size_t read_size = 0; | 497 | size_t read_size = 0; |
| 498 | if (fds[fd].stream != NULL) | 498 | if (fds[fd].stream != NULL) |
| 499 | read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); | 499 | read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); |
| 500 | else | ||
| 501 | panic("unimplemented"); | ||
| 502 | size += read_size; | 500 | size += read_size; |
| 503 | if (read_size < iovs_ptr[i].len) break; | 501 | if (read_size < iovs_ptr[i].len) break; |
| 504 | } | 502 | } |
stage1/zig.h+1915-904| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | #undef linux | 1 | #undef linux |
| 2 | 2 | ||
| 3 | #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ | ||
| 3 | #define __STDC_WANT_IEC_60559_TYPES_EXT__ | 4 | #define __STDC_WANT_IEC_60559_TYPES_EXT__ |
| 5 | #endif | ||
| 4 | #include <float.h> | 6 | #include <float.h> |
| 5 | #include <limits.h> | 7 | #include <limits.h> |
| 8 | #include <stdarg.h> | ||
| 6 | #include <stddef.h> | 9 | #include <stddef.h> |
| 7 | #include <stdint.h> | 10 | #include <stdint.h> |
| 8 | 11 | ||
| ... | @@ -34,6 +37,14 @@ typedef char bool; | ... | @@ -34,6 +37,14 @@ typedef char bool; |
| 34 | #define zig_has_attribute(attribute) 0 | 37 | #define zig_has_attribute(attribute) 0 |
| 35 | #endif | 38 | #endif |
| 36 | 39 | ||
| 40 | #if __LITTLE_ENDIAN__ || _MSC_VER | ||
| 41 | #define zig_little_endian 1 | ||
| 42 | #define zig_big_endian 0 | ||
| 43 | #else | ||
| 44 | #define zig_little_endian 0 | ||
| 45 | #define zig_big_endian 1 | ||
| 46 | #endif | ||
| 47 | |||
| 37 | #if __STDC_VERSION__ >= 201112L | 48 | #if __STDC_VERSION__ >= 201112L |
| 38 | #define zig_threadlocal _Thread_local | 49 | #define zig_threadlocal _Thread_local |
| 39 | #elif defined(__GNUC__) | 50 | #elif defined(__GNUC__) |
| ... | @@ -75,6 +86,32 @@ typedef char bool; | ... | @@ -75,6 +86,32 @@ typedef char bool; |
| 75 | #define zig_cold | 86 | #define zig_cold |
| 76 | #endif | 87 | #endif |
| 77 | 88 | ||
| 89 | #if zig_has_attribute(flatten) | ||
| 90 | #define zig_maybe_flatten __attribute__((flatten)) | ||
| 91 | #else | ||
| 92 | #define zig_maybe_flatten | ||
| 93 | #endif | ||
| 94 | |||
| 95 | #if zig_has_attribute(noinline) | ||
| 96 | #define zig_never_inline __attribute__((noinline)) zig_maybe_flatten | ||
| 97 | #elif defined(_MSC_VER) | ||
| 98 | #define zig_never_inline __declspec(noinline) zig_maybe_flatten | ||
| 99 | #else | ||
| 100 | #define zig_never_inline zig_never_inline_unavailable | ||
| 101 | #endif | ||
| 102 | |||
| 103 | #if zig_has_attribute(not_tail_called) | ||
| 104 | #define zig_never_tail __attribute__((not_tail_called)) zig_never_inline | ||
| 105 | #else | ||
| 106 | #define zig_never_tail zig_never_tail_unavailable | ||
| 107 | #endif | ||
| 108 | |||
| 109 | #if zig_has_attribute(always_inline) | ||
| 110 | #define zig_always_tail __attribute__((musttail)) | ||
| 111 | #else | ||
| 112 | #define zig_always_tail zig_always_tail_unavailable | ||
| 113 | #endif | ||
| 114 | |||
| 78 | #if __STDC_VERSION__ >= 199901L | 115 | #if __STDC_VERSION__ >= 199901L |
| 79 | #define zig_restrict restrict | 116 | #define zig_restrict restrict |
| 80 | #elif defined(__GNUC__) | 117 | #elif defined(__GNUC__) |
| ... | @@ -151,16 +188,38 @@ typedef char bool; | ... | @@ -151,16 +188,38 @@ typedef char bool; |
| 151 | #define zig_export(sig, symbol, name) __asm(name " = " symbol) | 188 | #define zig_export(sig, symbol, name) __asm(name " = " symbol) |
| 152 | #endif | 189 | #endif |
| 153 | 190 | ||
| 191 | #if zig_has_builtin(trap) | ||
| 192 | #define zig_trap() __builtin_trap() | ||
| 193 | #elif _MSC_VER && (_M_IX86 || _M_X64) | ||
| 194 | #define zig_trap() __ud2() | ||
| 195 | #elif _MSC_VER | ||
| 196 | #define zig_trap() __fastfail(0) | ||
| 197 | #elif defined(__i386__) || defined(__x86_64__) | ||
| 198 | #define zig_trap() __asm__ volatile("ud2"); | ||
| 199 | #elif defined(__arm__) || defined(__aarch64__) | ||
| 200 | #define zig_breakpoint() __asm__ volatile("udf #0"); | ||
| 201 | #else | ||
| 202 | #include <stdlib.h> | ||
| 203 | #define zig_trap() abort() | ||
| 204 | #endif | ||
| 205 | |||
| 154 | #if zig_has_builtin(debugtrap) | 206 | #if zig_has_builtin(debugtrap) |
| 155 | #define zig_breakpoint() __builtin_debugtrap() | 207 | #define zig_breakpoint() __builtin_debugtrap() |
| 156 | #elif zig_has_builtin(trap) || defined(zig_gnuc) | ||
| 157 | #define zig_breakpoint() __builtin_trap() | ||
| 158 | #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) | 208 | #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) |
| 159 | #define zig_breakpoint() __debugbreak() | 209 | #define zig_breakpoint() __debugbreak() |
| 160 | #elif defined(__i386__) || defined(__x86_64__) | 210 | #elif defined(__i386__) || defined(__x86_64__) |
| 161 | #define zig_breakpoint() __asm__ volatile("int $0x03"); | 211 | #define zig_breakpoint() __asm__ volatile("int $0x03"); |
| 212 | #elif defined(__arm__) | ||
| 213 | #define zig_breakpoint() __asm__ volatile("bkpt #0"); | ||
| 214 | #elif defined(__aarch64__) | ||
| 215 | #define zig_breakpoint() __asm__ volatile("brk #0"); | ||
| 162 | #else | 216 | #else |
| 217 | #include <signal.h> | ||
| 218 | #if defined(SIGTRAP) | ||
| 163 | #define zig_breakpoint() raise(SIGTRAP) | 219 | #define zig_breakpoint() raise(SIGTRAP) |
| 220 | #else | ||
| 221 | #define zig_breakpoint() zig_breakpoint_unavailable | ||
| 222 | #endif | ||
| 164 | #endif | 223 | #endif |
| 165 | 224 | ||
| 166 | #if zig_has_builtin(return_address) || defined(zig_gnuc) | 225 | #if zig_has_builtin(return_address) || defined(zig_gnuc) |
| ... | @@ -286,701 +345,656 @@ typedef char bool; | ... | @@ -286,701 +345,656 @@ typedef char bool; |
| 286 | #endif | 345 | #endif |
| 287 | 346 | ||
| 288 | #if __STDC_VERSION__ >= 201112L | 347 | #if __STDC_VERSION__ >= 201112L |
| 289 | #define zig_noreturn _Noreturn void | 348 | #define zig_noreturn _Noreturn |
| 290 | #elif zig_has_attribute(noreturn) || defined(zig_gnuc) | 349 | #elif zig_has_attribute(noreturn) || defined(zig_gnuc) |
| 291 | #define zig_noreturn __attribute__((noreturn)) void | 350 | #define zig_noreturn __attribute__((noreturn)) |
| 292 | #elif _MSC_VER | 351 | #elif _MSC_VER |
| 293 | #define zig_noreturn __declspec(noreturn) void | 352 | #define zig_noreturn __declspec(noreturn) |
| 294 | #else | 353 | #else |
| 295 | #define zig_noreturn void | 354 | #define zig_noreturn |
| 296 | #endif | 355 | #endif |
| 297 | 356 | ||
| 298 | #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) | 357 | #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) |
| 299 | 358 | ||
| 300 | typedef uintptr_t zig_usize; | 359 | #define zig_compiler_rt_abbrev_uint32_t si |
| 301 | typedef intptr_t zig_isize; | 360 | #define zig_compiler_rt_abbrev_int32_t si |
| 302 | typedef signed short int zig_c_short; | 361 | #define zig_compiler_rt_abbrev_uint64_t di |
| 303 | typedef unsigned short int zig_c_ushort; | 362 | #define zig_compiler_rt_abbrev_int64_t di |
| 304 | typedef signed int zig_c_int; | 363 | #define zig_compiler_rt_abbrev_zig_u128 ti |
| 305 | typedef unsigned int zig_c_uint; | 364 | #define zig_compiler_rt_abbrev_zig_i128 ti |
| 306 | typedef signed long int zig_c_long; | 365 | #define zig_compiler_rt_abbrev_zig_f16 hf |
| 307 | typedef unsigned long int zig_c_ulong; | 366 | #define zig_compiler_rt_abbrev_zig_f32 sf |
| 308 | typedef signed long long int zig_c_longlong; | 367 | #define zig_compiler_rt_abbrev_zig_f64 df |
| 309 | typedef unsigned long long int zig_c_ulonglong; | 368 | #define zig_compiler_rt_abbrev_zig_f80 xf |
| 310 | 369 | #define zig_compiler_rt_abbrev_zig_f128 tf | |
| 311 | typedef uint8_t zig_u8; | 370 | |
| 312 | typedef int8_t zig_i8; | 371 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); |
| 313 | typedef uint16_t zig_u16; | 372 | zig_extern void *memset (void *, int, size_t); |
| 314 | typedef int16_t zig_i16; | 373 | |
| 315 | typedef uint32_t zig_u32; | 374 | /* ===================== 8/16/32/64-bit Integer Support ===================== */ |
| 316 | typedef int32_t zig_i32; | 375 | |
| 317 | typedef uint64_t zig_u64; | 376 | #if __STDC_VERSION__ >= 199901L || _MSC_VER |
| 318 | typedef int64_t zig_i64; | 377 | #include <stdint.h> |
| 319 | 378 | #else | |
| 320 | #define zig_as_u8(val) UINT8_C(val) | 379 | |
| 321 | #define zig_as_i8(val) INT8_C(val) | 380 | #if SCHAR_MIN == ~0x7F && SCHAR_MAX == 0x7F && UCHAR_MAX == 0xFF |
| 322 | #define zig_as_u16(val) UINT16_C(val) | 381 | typedef unsigned char uint8_t; |
| 323 | #define zig_as_i16(val) INT16_C(val) | 382 | typedef signed char int8_t; |
| 324 | #define zig_as_u32(val) UINT32_C(val) | 383 | #define INT8_C(c) c |
| 325 | #define zig_as_i32(val) INT32_C(val) | 384 | #define UINT8_C(c) c##U |
| 326 | #define zig_as_u64(val) UINT64_C(val) | 385 | #elif SHRT_MIN == ~0x7F && SHRT_MAX == 0x7F && USHRT_MAX == 0xFF |
| 327 | #define zig_as_i64(val) INT64_C(val) | 386 | typedef unsigned short uint8_t; |
| 328 | 387 | typedef signed short int8_t; | |
| 329 | #define zig_minInt_u8 zig_as_u8(0) | 388 | #define INT8_C(c) c |
| 330 | #define zig_maxInt_u8 UINT8_MAX | 389 | #define UINT8_C(c) c##U |
| 390 | #elif INT_MIN == ~0x7F && INT_MAX == 0x7F && UINT_MAX == 0xFF | ||
| 391 | typedef unsigned int uint8_t; | ||
| 392 | typedef 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 | ||
| 396 | typedef unsigned long uint8_t; | ||
| 397 | typedef 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 | ||
| 401 | typedef unsigned long long uint8_t; | ||
| 402 | typedef 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 | ||
| 411 | typedef unsigned char uint16_t; | ||
| 412 | typedef 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 | ||
| 416 | typedef unsigned short uint16_t; | ||
| 417 | typedef 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 | ||
| 421 | typedef unsigned int uint16_t; | ||
| 422 | typedef 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 | ||
| 426 | typedef unsigned long uint16_t; | ||
| 427 | typedef 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 | ||
| 431 | typedef unsigned long long uint16_t; | ||
| 432 | typedef 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 | ||
| 441 | typedef unsigned char uint32_t; | ||
| 442 | typedef 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 | ||
| 446 | typedef unsigned short uint32_t; | ||
| 447 | typedef 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 | ||
| 451 | typedef unsigned int uint32_t; | ||
| 452 | typedef 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 | ||
| 456 | typedef unsigned long uint32_t; | ||
| 457 | typedef 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 | ||
| 461 | typedef unsigned long long uint32_t; | ||
| 462 | typedef 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 | ||
| 471 | typedef unsigned char uint64_t; | ||
| 472 | typedef 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 | ||
| 476 | typedef unsigned short uint64_t; | ||
| 477 | typedef 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 | ||
| 481 | typedef unsigned int uint64_t; | ||
| 482 | typedef 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 | ||
| 486 | typedef unsigned long uint64_t; | ||
| 487 | typedef 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 | ||
| 491 | typedef unsigned long long uint64_t; | ||
| 492 | typedef 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 | |||
| 500 | typedef size_t uintptr_t; | ||
| 501 | typedef ptrdiff_t intptr_t; | ||
| 502 | |||
| 503 | #endif | ||
| 504 | |||
| 331 | #define zig_minInt_i8 INT8_MIN | 505 | #define zig_minInt_i8 INT8_MIN |
| 332 | #define zig_maxInt_i8 INT8_MAX | 506 | #define zig_maxInt_i8 INT8_MAX |
| 333 | #define zig_minInt_u16 zig_as_u16(0) | 507 | #define zig_minInt_u8 UINT8_C(0) |
| 334 | #define zig_maxInt_u16 UINT16_MAX | 508 | #define zig_maxInt_u8 UINT8_MAX |
| 335 | #define zig_minInt_i16 INT16_MIN | 509 | #define zig_minInt_i16 INT16_MIN |
| 336 | #define zig_maxInt_i16 INT16_MAX | 510 | #define zig_maxInt_i16 INT16_MAX |
| 337 | #define zig_minInt_u32 zig_as_u32(0) | 511 | #define zig_minInt_u16 UINT16_C(0) |
| 338 | #define zig_maxInt_u32 UINT32_MAX | 512 | #define zig_maxInt_u16 UINT16_MAX |
| 339 | #define zig_minInt_i32 INT32_MIN | 513 | #define zig_minInt_i32 INT32_MIN |
| 340 | #define zig_maxInt_i32 INT32_MAX | 514 | #define zig_maxInt_i32 INT32_MAX |
| 341 | #define zig_minInt_u64 zig_as_u64(0) | 515 | #define zig_minInt_u32 UINT32_C(0) |
| 342 | #define zig_maxInt_u64 UINT64_MAX | 516 | #define zig_maxInt_u32 UINT32_MAX |
| 343 | #define zig_minInt_i64 INT64_MIN | 517 | #define zig_minInt_i64 INT64_MIN |
| 344 | #define zig_maxInt_i64 INT64_MAX | 518 | #define zig_maxInt_i64 INT64_MAX |
| 519 | #define zig_minInt_u64 UINT64_C(0) | ||
| 520 | #define zig_maxInt_u64 UINT64_MAX | ||
| 345 | 521 | ||
| 346 | #define zig_compiler_rt_abbrev_u32 si | 522 | #define zig_intLimit(s, w, limit, bits) zig_shr_##s##w(zig_##limit##Int_##s##w, w - (bits)) |
| 347 | #define zig_compiler_rt_abbrev_i32 si | 523 | #define zig_minInt_i(w, bits) zig_intLimit(i, w, min, bits) |
| 348 | #define zig_compiler_rt_abbrev_u64 di | 524 | #define zig_maxInt_i(w, bits) zig_intLimit(i, w, max, bits) |
| 349 | #define zig_compiler_rt_abbrev_i64 di | 525 | #define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits) |
| 350 | #define zig_compiler_rt_abbrev_u128 ti | 526 | #define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits) |
| 351 | #define zig_compiler_rt_abbrev_i128 ti | ||
| 352 | #define zig_compiler_rt_abbrev_f16 hf | ||
| 353 | #define zig_compiler_rt_abbrev_f32 sf | ||
| 354 | #define zig_compiler_rt_abbrev_f64 df | ||
| 355 | #define zig_compiler_rt_abbrev_f80 xf | ||
| 356 | #define zig_compiler_rt_abbrev_f128 tf | ||
| 357 | |||
| 358 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); | ||
| 359 | zig_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) | ||
| 367 | 527 | ||
| 368 | #define zig_int_operator(Type, RhsType, operation, operator) \ | 528 | #define zig_int_operator(Type, RhsType, operation, operator) \ |
| 369 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ | 529 | static inline Type zig_##operation(Type lhs, RhsType rhs) { \ |
| 370 | return lhs operator rhs; \ | 530 | return lhs operator rhs; \ |
| 371 | } | 531 | } |
| 372 | #define zig_int_basic_operator(Type, operation, operator) \ | 532 | #define zig_int_basic_operator(Type, operation, operator) \ |
| 373 | zig_int_operator(Type, Type, operation, operator) | 533 | zig_int_operator(Type, Type, operation, operator) |
| 374 | #define zig_int_shift_operator(Type, operation, operator) \ | 534 | #define zig_int_shift_operator(Type, operation, operator) \ |
| 375 | zig_int_operator(Type, u8, operation, operator) | 535 | zig_int_operator(Type, uint8_t, operation, operator) |
| 376 | #define zig_int_helpers(w) \ | 536 | #define zig_int_helpers(w) \ |
| 377 | zig_int_basic_operator(u##w, and, &) \ | 537 | zig_int_basic_operator(uint##w##_t, and_u##w, &) \ |
| 378 | zig_int_basic_operator(i##w, and, &) \ | 538 | zig_int_basic_operator( int##w##_t, and_i##w, &) \ |
| 379 | zig_int_basic_operator(u##w, or, |) \ | 539 | zig_int_basic_operator(uint##w##_t, or_u##w, |) \ |
| 380 | zig_int_basic_operator(i##w, or, |) \ | 540 | zig_int_basic_operator( int##w##_t, or_i##w, |) \ |
| 381 | zig_int_basic_operator(u##w, xor, ^) \ | 541 | zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \ |
| 382 | zig_int_basic_operator(i##w, xor, ^) \ | 542 | zig_int_basic_operator( int##w##_t, xor_i##w, ^) \ |
| 383 | zig_int_shift_operator(u##w, shl, <<) \ | 543 | zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \ |
| 384 | zig_int_shift_operator(i##w, shl, <<) \ | 544 | zig_int_shift_operator( int##w##_t, shl_i##w, <<) \ |
| 385 | zig_int_shift_operator(u##w, shr, >>) \ | 545 | zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \ |
| 386 | \ | 546 | \ |
| 387 | static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ | 547 | static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \ |
| 388 | zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ | 548 | int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \ |
| 389 | return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ | 549 | return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ |
| 390 | } \ | 550 | } \ |
| 391 | \ | 551 | \ |
| 392 | static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ | 552 | static inline uint##w##_t zig_not_u##w(uint##w##_t val, uint8_t bits) { \ |
| 393 | return val ^ zig_maxInt(u##w, bits); \ | 553 | return val ^ zig_maxInt_u(w, bits); \ |
| 394 | } \ | 554 | } \ |
| 395 | \ | 555 | \ |
| 396 | static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ | 556 | static inline int##w##_t zig_not_i##w(int##w##_t val, uint8_t bits) { \ |
| 397 | (void)bits; \ | 557 | (void)bits; \ |
| 398 | return ~val; \ | 558 | return ~val; \ |
| 399 | } \ | 559 | } \ |
| 400 | \ | 560 | \ |
| 401 | static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ | 561 | static inline uint##w##_t zig_wrap_u##w(uint##w##_t val, uint8_t bits) { \ |
| 402 | return val & zig_maxInt(u##w, bits); \ | 562 | return val & zig_maxInt_u(w, bits); \ |
| 403 | } \ | 563 | } \ |
| 404 | \ | 564 | \ |
| 405 | static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ | 565 | static inline int##w##_t zig_wrap_i##w(int##w##_t val, uint8_t bits) { \ |
| 406 | return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ | 566 | return (val & UINT##w##_C(1) << (bits - UINT8_C(1))) != 0 \ |
| 407 | ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ | 567 | ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \ |
| 408 | } \ | 568 | } \ |
| 409 | \ | 569 | \ |
| 410 | zig_int_basic_operator(u##w, div_floor, /) \ | 570 | zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \ |
| 411 | \ | 571 | \ |
| 412 | static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ | 572 | static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 413 | return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ | 573 | return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \ |
| 414 | } \ | 574 | } \ |
| 415 | \ | 575 | \ |
| 416 | zig_int_basic_operator(u##w, mod, %) \ | 576 | zig_int_basic_operator(uint##w##_t, mod_u##w, %) \ |
| 417 | \ | 577 | \ |
| 418 | static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ | 578 | static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 419 | zig_i##w rem = lhs % rhs; \ | 579 | int##w##_t rem = lhs % rhs; \ |
| 420 | return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ | 580 | return rem + (((lhs ^ rhs) & rem) < INT##w##_C(0) ? rhs : INT##w##_C(0)); \ |
| 421 | } \ | 581 | } \ |
| 422 | \ | 582 | \ |
| 423 | static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 583 | static inline uint##w##_t zig_shlw_u##w(uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 424 | return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ | 584 | return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ |
| 425 | } \ | 585 | } \ |
| 426 | \ | 586 | \ |
| 427 | static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 587 | static inline int##w##_t zig_shlw_i##w(int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 428 | return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ | 588 | return zig_wrap_i##w((int##w##_t)zig_shl_u##w((uint##w##_t)lhs, (uint##w##_t)rhs), bits); \ |
| 429 | } \ | 589 | } \ |
| 430 | \ | 590 | \ |
| 431 | static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 591 | static inline uint##w##_t zig_addw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 432 | return zig_wrap_u##w(lhs + rhs, bits); \ | 592 | return zig_wrap_u##w(lhs + rhs, bits); \ |
| 433 | } \ | 593 | } \ |
| 434 | \ | 594 | \ |
| 435 | static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 595 | static inline int##w##_t zig_addw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 436 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ | 596 | return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs + (uint##w##_t)rhs), bits); \ |
| 437 | } \ | 597 | } \ |
| 438 | \ | 598 | \ |
| 439 | static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 599 | static inline uint##w##_t zig_subw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 440 | return zig_wrap_u##w(lhs - rhs, bits); \ | 600 | return zig_wrap_u##w(lhs - rhs, bits); \ |
| 441 | } \ | 601 | } \ |
| 442 | \ | 602 | \ |
| 443 | static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 603 | static inline int##w##_t zig_subw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 444 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ | 604 | return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs - (uint##w##_t)rhs), bits); \ |
| 445 | } \ | 605 | } \ |
| 446 | \ | 606 | \ |
| 447 | static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 607 | static inline uint##w##_t zig_mulw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 448 | return zig_wrap_u##w(lhs * rhs, bits); \ | 608 | return zig_wrap_u##w(lhs * rhs, bits); \ |
| 449 | } \ | 609 | } \ |
| 450 | \ | 610 | \ |
| 451 | static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 611 | static inline int##w##_t zig_mulw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 452 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ | 612 | return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs * (uint##w##_t)rhs), bits); \ |
| 453 | } | 613 | } |
| 454 | zig_int_helpers(8) | 614 | zig_int_helpers(8) |
| 455 | zig_int_helpers(16) | 615 | zig_int_helpers(16) |
| 456 | zig_int_helpers(32) | 616 | zig_int_helpers(32) |
| 457 | zig_int_helpers(64) | 617 | zig_int_helpers(64) |
| 458 | 618 | ||
| 459 | static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | 619 | static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 460 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 620 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 461 | zig_u32 full_res; | 621 | uint32_t full_res; |
| 462 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 622 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 463 | *res = zig_wrap_u32(full_res, bits); | 623 | *res = zig_wrap_u32(full_res, bits); |
| 464 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | 624 | return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); |
| 465 | #else | 625 | #else |
| 466 | *res = zig_addw_u32(lhs, rhs, bits); | 626 | *res = zig_addw_u32(lhs, rhs, bits); |
| 467 | return *res < lhs; | 627 | return *res < lhs; |
| 468 | #endif | 628 | #endif |
| 469 | } | 629 | } |
| 470 | 630 | ||
| 471 | static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, | 631 | zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 472 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | 632 | static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 473 | { | ||
| 474 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); | ||
| 475 | } | ||
| 476 | |||
| 477 | zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | ||
| 478 | static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | ||
| 479 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 633 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 480 | zig_i32 full_res; | 634 | int32_t full_res; |
| 481 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 635 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 482 | #else | 636 | #else |
| 483 | zig_c_int overflow_int; | 637 | int overflow_int; |
| 484 | zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int); | 638 | int32_t full_res = __addosi4(lhs, rhs, &overflow_int); |
| 485 | bool overflow = overflow_int != 0; | 639 | bool overflow = overflow_int != 0; |
| 486 | #endif | 640 | #endif |
| 487 | *res = zig_wrap_i32(full_res, bits); | 641 | *res = zig_wrap_i32(full_res, bits); |
| 488 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | 642 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 489 | } | 643 | } |
| 490 | 644 | ||
| 491 | static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, | 645 | static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 492 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | ||
| 493 | { | ||
| 494 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); | ||
| 495 | } | ||
| 496 | |||
| 497 | static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | ||
| 498 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 646 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 499 | zig_u64 full_res; | 647 | uint64_t full_res; |
| 500 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 648 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 501 | *res = zig_wrap_u64(full_res, bits); | 649 | *res = zig_wrap_u64(full_res, bits); |
| 502 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | 650 | return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); |
| 503 | #else | 651 | #else |
| 504 | *res = zig_addw_u64(lhs, rhs, bits); | 652 | *res = zig_addw_u64(lhs, rhs, bits); |
| 505 | return *res < lhs; | 653 | return *res < lhs; |
| 506 | #endif | 654 | #endif |
| 507 | } | 655 | } |
| 508 | 656 | ||
| 509 | static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, | 657 | zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 510 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | 658 | static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 511 | { | ||
| 512 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); | ||
| 513 | } | ||
| 514 | |||
| 515 | zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | ||
| 516 | static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | ||
| 517 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 659 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 518 | zig_i64 full_res; | 660 | int64_t full_res; |
| 519 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 661 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 520 | #else | 662 | #else |
| 521 | zig_c_int overflow_int; | 663 | int overflow_int; |
| 522 | zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int); | 664 | int64_t full_res = __addodi4(lhs, rhs, &overflow_int); |
| 523 | bool overflow = overflow_int != 0; | 665 | bool overflow = overflow_int != 0; |
| 524 | #endif | 666 | #endif |
| 525 | *res = zig_wrap_i64(full_res, bits); | 667 | *res = zig_wrap_i64(full_res, bits); |
| 526 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | 668 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 527 | } | 669 | } |
| 528 | 670 | ||
| 529 | static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, | 671 | static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 530 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | ||
| 531 | { | ||
| 532 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); | ||
| 533 | } | ||
| 534 | |||
| 535 | static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 536 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 672 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 537 | zig_u8 full_res; | 673 | uint8_t full_res; |
| 538 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 674 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 539 | *res = zig_wrap_u8(full_res, bits); | 675 | *res = zig_wrap_u8(full_res, bits); |
| 540 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | 676 | return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); |
| 541 | #else | 677 | #else |
| 542 | zig_u32 full_res; | 678 | uint32_t full_res; |
| 543 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); | 679 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); |
| 544 | *res = (zig_u8)full_res; | 680 | *res = (uint8_t)full_res; |
| 545 | return overflow; | 681 | return overflow; |
| 546 | #endif | 682 | #endif |
| 547 | } | 683 | } |
| 548 | 684 | ||
| 549 | static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, | 685 | static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 550 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | ||
| 551 | { | ||
| 552 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); | ||
| 553 | } | ||
| 554 | |||
| 555 | static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | ||
| 556 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 686 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 557 | zig_i8 full_res; | 687 | int8_t full_res; |
| 558 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 688 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 559 | *res = zig_wrap_i8(full_res, bits); | 689 | *res = zig_wrap_i8(full_res, bits); |
| 560 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | 690 | return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); |
| 561 | #else | 691 | #else |
| 562 | zig_i32 full_res; | 692 | int32_t full_res; |
| 563 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); | 693 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); |
| 564 | *res = (zig_i8)full_res; | 694 | *res = (int8_t)full_res; |
| 565 | return overflow; | 695 | return overflow; |
| 566 | #endif | 696 | #endif |
| 567 | } | 697 | } |
| 568 | 698 | ||
| 569 | static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, | 699 | static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 570 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | ||
| 571 | { | ||
| 572 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); | ||
| 573 | } | ||
| 574 | |||
| 575 | static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | ||
| 576 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 700 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 577 | zig_u16 full_res; | 701 | uint16_t full_res; |
| 578 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 702 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 579 | *res = zig_wrap_u16(full_res, bits); | 703 | *res = zig_wrap_u16(full_res, bits); |
| 580 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | 704 | return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); |
| 581 | #else | 705 | #else |
| 582 | zig_u32 full_res; | 706 | uint32_t full_res; |
| 583 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); | 707 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); |
| 584 | *res = (zig_u16)full_res; | 708 | *res = (uint16_t)full_res; |
| 585 | return overflow; | 709 | return overflow; |
| 586 | #endif | 710 | #endif |
| 587 | } | 711 | } |
| 588 | 712 | ||
| 589 | static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, | 713 | static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 590 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | ||
| 591 | { | ||
| 592 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); | ||
| 593 | } | ||
| 594 | |||
| 595 | static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | ||
| 596 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 714 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 597 | zig_i16 full_res; | 715 | int16_t full_res; |
| 598 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 716 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 599 | *res = zig_wrap_i16(full_res, bits); | 717 | *res = zig_wrap_i16(full_res, bits); |
| 600 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | 718 | return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); |
| 601 | #else | 719 | #else |
| 602 | zig_i32 full_res; | 720 | int32_t full_res; |
| 603 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); | 721 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); |
| 604 | *res = (zig_i16)full_res; | 722 | *res = (int16_t)full_res; |
| 605 | return overflow; | 723 | return overflow; |
| 606 | #endif | 724 | #endif |
| 607 | } | 725 | } |
| 608 | 726 | ||
| 609 | static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, | 727 | static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 610 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | ||
| 611 | { | ||
| 612 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 613 | } | ||
| 614 | |||
| 615 | static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | ||
| 616 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 728 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 617 | zig_u32 full_res; | 729 | uint32_t full_res; |
| 618 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 730 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 619 | *res = zig_wrap_u32(full_res, bits); | 731 | *res = zig_wrap_u32(full_res, bits); |
| 620 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | 732 | return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); |
| 621 | #else | 733 | #else |
| 622 | *res = zig_subw_u32(lhs, rhs, bits); | 734 | *res = zig_subw_u32(lhs, rhs, bits); |
| 623 | return *res > lhs; | 735 | return *res > lhs; |
| 624 | #endif | 736 | #endif |
| 625 | } | 737 | } |
| 626 | 738 | ||
| 627 | static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, | 739 | zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 628 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | 740 | static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 629 | { | ||
| 630 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); | ||
| 631 | } | ||
| 632 | |||
| 633 | zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | ||
| 634 | static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | ||
| 635 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 741 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 636 | zig_i32 full_res; | 742 | int32_t full_res; |
| 637 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 743 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 638 | #else | 744 | #else |
| 639 | zig_c_int overflow_int; | 745 | int overflow_int; |
| 640 | zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int); | 746 | int32_t full_res = __subosi4(lhs, rhs, &overflow_int); |
| 641 | bool overflow = overflow_int != 0; | 747 | bool overflow = overflow_int != 0; |
| 642 | #endif | 748 | #endif |
| 643 | *res = zig_wrap_i32(full_res, bits); | 749 | *res = zig_wrap_i32(full_res, bits); |
| 644 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | 750 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 645 | } | ||
| 646 | |||
| 647 | static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, | ||
| 648 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | ||
| 649 | { | ||
| 650 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); | ||
| 651 | } | 751 | } |
| 652 | 752 | ||
| 653 | static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | 753 | static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 654 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 754 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 655 | zig_u64 full_res; | 755 | uint64_t full_res; |
| 656 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 756 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 657 | *res = zig_wrap_u64(full_res, bits); | 757 | *res = zig_wrap_u64(full_res, bits); |
| 658 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | 758 | return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); |
| 659 | #else | 759 | #else |
| 660 | *res = zig_subw_u64(lhs, rhs, bits); | 760 | *res = zig_subw_u64(lhs, rhs, bits); |
| 661 | return *res > lhs; | 761 | return *res > lhs; |
| 662 | #endif | 762 | #endif |
| 663 | } | 763 | } |
| 664 | 764 | ||
| 665 | static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, | 765 | zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 666 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | 766 | static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 667 | { | ||
| 668 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); | ||
| 669 | } | ||
| 670 | |||
| 671 | zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | ||
| 672 | static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | ||
| 673 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 767 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 674 | zig_i64 full_res; | 768 | int64_t full_res; |
| 675 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 769 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 676 | #else | 770 | #else |
| 677 | zig_c_int overflow_int; | 771 | int overflow_int; |
| 678 | zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int); | 772 | int64_t full_res = __subodi4(lhs, rhs, &overflow_int); |
| 679 | bool overflow = overflow_int != 0; | 773 | bool overflow = overflow_int != 0; |
| 680 | #endif | 774 | #endif |
| 681 | *res = zig_wrap_i64(full_res, bits); | 775 | *res = zig_wrap_i64(full_res, bits); |
| 682 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | 776 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 683 | } | ||
| 684 | |||
| 685 | static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, | ||
| 686 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | ||
| 687 | { | ||
| 688 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); | ||
| 689 | } | 777 | } |
| 690 | 778 | ||
| 691 | static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | 779 | static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 692 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 780 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 693 | zig_u8 full_res; | 781 | uint8_t full_res; |
| 694 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 782 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 695 | *res = zig_wrap_u8(full_res, bits); | 783 | *res = zig_wrap_u8(full_res, bits); |
| 696 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | 784 | return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); |
| 697 | #else | 785 | #else |
| 698 | zig_u32 full_res; | 786 | uint32_t full_res; |
| 699 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); | 787 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); |
| 700 | *res = (zig_u8)full_res; | 788 | *res = (uint8_t)full_res; |
| 701 | return overflow; | 789 | return overflow; |
| 702 | #endif | 790 | #endif |
| 703 | } | 791 | } |
| 704 | 792 | ||
| 705 | static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, | 793 | static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 706 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | ||
| 707 | { | ||
| 708 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); | ||
| 709 | } | ||
| 710 | |||
| 711 | static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | ||
| 712 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 794 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 713 | zig_i8 full_res; | 795 | int8_t full_res; |
| 714 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 796 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 715 | *res = zig_wrap_i8(full_res, bits); | 797 | *res = zig_wrap_i8(full_res, bits); |
| 716 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | 798 | return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); |
| 717 | #else | 799 | #else |
| 718 | zig_i32 full_res; | 800 | int32_t full_res; |
| 719 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); | 801 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); |
| 720 | *res = (zig_i8)full_res; | 802 | *res = (int8_t)full_res; |
| 721 | return overflow; | 803 | return overflow; |
| 722 | #endif | 804 | #endif |
| 723 | } | 805 | } |
| 724 | 806 | ||
| 725 | static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, | 807 | static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 726 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | ||
| 727 | { | ||
| 728 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); | ||
| 729 | } | ||
| 730 | |||
| 731 | |||
| 732 | static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | ||
| 733 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 808 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 734 | zig_u16 full_res; | 809 | uint16_t full_res; |
| 735 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 810 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 736 | *res = zig_wrap_u16(full_res, bits); | 811 | *res = zig_wrap_u16(full_res, bits); |
| 737 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | 812 | return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); |
| 738 | #else | 813 | #else |
| 739 | zig_u32 full_res; | 814 | uint32_t full_res; |
| 740 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); | 815 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); |
| 741 | *res = (zig_u16)full_res; | 816 | *res = (uint16_t)full_res; |
| 742 | return overflow; | 817 | return overflow; |
| 743 | #endif | 818 | #endif |
| 744 | } | 819 | } |
| 745 | 820 | ||
| 746 | static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, | 821 | static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 747 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | ||
| 748 | { | ||
| 749 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); | ||
| 750 | } | ||
| 751 | |||
| 752 | |||
| 753 | static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | ||
| 754 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 822 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 755 | zig_i16 full_res; | 823 | int16_t full_res; |
| 756 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 824 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 757 | *res = zig_wrap_i16(full_res, bits); | 825 | *res = zig_wrap_i16(full_res, bits); |
| 758 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | 826 | return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); |
| 759 | #else | 827 | #else |
| 760 | zig_i32 full_res; | 828 | int32_t full_res; |
| 761 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); | 829 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); |
| 762 | *res = (zig_i16)full_res; | 830 | *res = (int16_t)full_res; |
| 763 | return overflow; | 831 | return overflow; |
| 764 | #endif | 832 | #endif |
| 765 | } | 833 | } |
| 766 | 834 | ||
| 767 | static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, | 835 | static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 768 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | ||
| 769 | { | ||
| 770 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 771 | } | ||
| 772 | |||
| 773 | static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | ||
| 774 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 836 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 775 | zig_u32 full_res; | 837 | uint32_t full_res; |
| 776 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 838 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 777 | *res = zig_wrap_u32(full_res, bits); | 839 | *res = zig_wrap_u32(full_res, bits); |
| 778 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | 840 | return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); |
| 779 | #else | 841 | #else |
| 780 | *res = zig_mulw_u32(lhs, rhs, bits); | 842 | *res = zig_mulw_u32(lhs, rhs, bits); |
| 781 | return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; | 843 | return rhs != UINT32_C(0) && lhs > zig_maxInt_u(32, bits) / rhs; |
| 782 | #endif | 844 | #endif |
| 783 | } | 845 | } |
| 784 | 846 | ||
| 785 | static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, | 847 | zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 786 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | 848 | static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 787 | { | ||
| 788 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); | ||
| 789 | } | ||
| 790 | |||
| 791 | zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | ||
| 792 | static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | ||
| 793 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 849 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 794 | zig_i32 full_res; | 850 | int32_t full_res; |
| 795 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 851 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 796 | #else | 852 | #else |
| 797 | zig_c_int overflow_int; | 853 | int overflow_int; |
| 798 | zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int); | 854 | int32_t full_res = __mulosi4(lhs, rhs, &overflow_int); |
| 799 | bool overflow = overflow_int != 0; | 855 | bool overflow = overflow_int != 0; |
| 800 | #endif | 856 | #endif |
| 801 | *res = zig_wrap_i32(full_res, bits); | 857 | *res = zig_wrap_i32(full_res, bits); |
| 802 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | 858 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 803 | } | ||
| 804 | |||
| 805 | static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, | ||
| 806 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | ||
| 807 | { | ||
| 808 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); | ||
| 809 | } | 859 | } |
| 810 | 860 | ||
| 811 | static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | 861 | static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 812 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 862 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 813 | zig_u64 full_res; | 863 | uint64_t full_res; |
| 814 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 864 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 815 | *res = zig_wrap_u64(full_res, bits); | 865 | *res = zig_wrap_u64(full_res, bits); |
| 816 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | 866 | return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); |
| 817 | #else | 867 | #else |
| 818 | *res = zig_mulw_u64(lhs, rhs, bits); | 868 | *res = zig_mulw_u64(lhs, rhs, bits); |
| 819 | return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; | 869 | return rhs != UINT64_C(0) && lhs > zig_maxInt_u(64, bits) / rhs; |
| 820 | #endif | 870 | #endif |
| 821 | } | 871 | } |
| 822 | 872 | ||
| 823 | static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, | 873 | zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 824 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | 874 | static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 825 | { | ||
| 826 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); | ||
| 827 | } | ||
| 828 | |||
| 829 | zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | ||
| 830 | static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | ||
| 831 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 875 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 832 | zig_i64 full_res; | 876 | int64_t full_res; |
| 833 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 877 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 834 | #else | 878 | #else |
| 835 | zig_c_int overflow_int; | 879 | int overflow_int; |
| 836 | zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int); | 880 | int64_t full_res = __mulodi4(lhs, rhs, &overflow_int); |
| 837 | bool overflow = overflow_int != 0; | 881 | bool overflow = overflow_int != 0; |
| 838 | #endif | 882 | #endif |
| 839 | *res = zig_wrap_i64(full_res, bits); | 883 | *res = zig_wrap_i64(full_res, bits); |
| 840 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | 884 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 841 | } | 885 | } |
| 842 | 886 | ||
| 843 | static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, | 887 | static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 844 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | ||
| 845 | { | ||
| 846 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); | ||
| 847 | } | ||
| 848 | |||
| 849 | static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 850 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 888 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 851 | zig_u8 full_res; | 889 | uint8_t full_res; |
| 852 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 890 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 853 | *res = zig_wrap_u8(full_res, bits); | 891 | *res = zig_wrap_u8(full_res, bits); |
| 854 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | 892 | return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); |
| 855 | #else | 893 | #else |
| 856 | zig_u32 full_res; | 894 | uint32_t full_res; |
| 857 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); | 895 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); |
| 858 | *res = (zig_u8)full_res; | 896 | *res = (uint8_t)full_res; |
| 859 | return overflow; | 897 | return overflow; |
| 860 | #endif | 898 | #endif |
| 861 | } | 899 | } |
| 862 | 900 | ||
| 863 | static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, | 901 | static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 864 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | ||
| 865 | { | ||
| 866 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); | ||
| 867 | } | ||
| 868 | |||
| 869 | static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | ||
| 870 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 902 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 871 | zig_i8 full_res; | 903 | int8_t full_res; |
| 872 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 904 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 873 | *res = zig_wrap_i8(full_res, bits); | 905 | *res = zig_wrap_i8(full_res, bits); |
| 874 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | 906 | return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); |
| 875 | #else | 907 | #else |
| 876 | zig_i32 full_res; | 908 | int32_t full_res; |
| 877 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); | 909 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); |
| 878 | *res = (zig_i8)full_res; | 910 | *res = (int8_t)full_res; |
| 879 | return overflow; | 911 | return overflow; |
| 880 | #endif | 912 | #endif |
| 881 | } | 913 | } |
| 882 | 914 | ||
| 883 | static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, | 915 | static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 884 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | ||
| 885 | { | ||
| 886 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); | ||
| 887 | } | ||
| 888 | |||
| 889 | static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | ||
| 890 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 916 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 891 | zig_u16 full_res; | 917 | uint16_t full_res; |
| 892 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 918 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 893 | *res = zig_wrap_u16(full_res, bits); | 919 | *res = zig_wrap_u16(full_res, bits); |
| 894 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | 920 | return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); |
| 895 | #else | 921 | #else |
| 896 | zig_u32 full_res; | 922 | uint32_t full_res; |
| 897 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); | 923 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); |
| 898 | *res = (zig_u16)full_res; | 924 | *res = (uint16_t)full_res; |
| 899 | return overflow; | 925 | return overflow; |
| 900 | #endif | 926 | #endif |
| 901 | } | 927 | } |
| 902 | 928 | ||
| 903 | static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, | 929 | static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 904 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | ||
| 905 | { | ||
| 906 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); | ||
| 907 | } | ||
| 908 | |||
| 909 | static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | ||
| 910 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 930 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 911 | zig_i16 full_res; | 931 | int16_t full_res; |
| 912 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 932 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 913 | *res = zig_wrap_i16(full_res, bits); | 933 | *res = zig_wrap_i16(full_res, bits); |
| 914 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | 934 | return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); |
| 915 | #else | 935 | #else |
| 916 | zig_i32 full_res; | 936 | int32_t full_res; |
| 917 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); | 937 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); |
| 918 | *res = (zig_i16)full_res; | 938 | *res = (int16_t)full_res; |
| 919 | return overflow; | 939 | return overflow; |
| 920 | #endif | 940 | #endif |
| 921 | } | 941 | } |
| 922 | 942 | ||
| 923 | static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, | ||
| 924 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | ||
| 925 | { | ||
| 926 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 927 | } | ||
| 928 | |||
| 929 | #define zig_int_builtins(w) \ | 943 | #define zig_int_builtins(w) \ |
| 930 | static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 944 | static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 931 | *res = zig_shlw_u##w(lhs, rhs, bits); \ | 945 | *res = zig_shlw_u##w(lhs, rhs, bits); \ |
| 932 | return lhs > zig_maxInt(u##w, bits) >> rhs; \ | 946 | return lhs > zig_maxInt_u(w, bits) >> rhs; \ |
| 933 | } \ | 947 | } \ |
| 934 | \ | 948 | \ |
| 935 | static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 949 | static inline bool zig_shlo_i##w(int##w##_t *res, int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 936 | *res = zig_shlw_i##w(lhs, rhs, bits); \ | 950 | *res = zig_shlw_i##w(lhs, rhs, bits); \ |
| 937 | zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ | 951 | int##w##_t mask = (int##w##_t)(UINT##w##_MAX << (bits - rhs - 1)); \ |
| 938 | return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ | 952 | return (lhs & mask) != INT##w##_C(0) && (lhs & mask) != mask; \ |
| 939 | } \ | 953 | } \ |
| 940 | \ | 954 | \ |
| 941 | static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 955 | static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 942 | zig_u##w res; \ | 956 | uint##w##_t res; \ |
| 943 | if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ | 957 | if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \ |
| 944 | return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | 958 | return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \ |
| 945 | } \ | 959 | } \ |
| 946 | \ | 960 | \ |
| 947 | static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 961 | static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 948 | zig_i##w res; \ | 962 | int##w##_t res; \ |
| 949 | if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ | 963 | if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \ |
| 950 | return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 964 | return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 951 | } \ | 965 | } \ |
| 952 | \ | 966 | \ |
| 953 | static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 967 | static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 954 | zig_u##w res; \ | 968 | uint##w##_t res; \ |
| 955 | return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | 969 | return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ |
| 956 | } \ | 970 | } \ |
| 957 | \ | 971 | \ |
| 958 | static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 972 | static inline int##w##_t zig_adds_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 959 | zig_i##w res; \ | 973 | int##w##_t res; \ |
| 960 | if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ | 974 | if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ |
| 961 | return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 975 | return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 962 | } \ | 976 | } \ |
| 963 | \ | 977 | \ |
| 964 | static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 978 | static inline uint##w##_t zig_subs_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 965 | zig_u##w res; \ | 979 | uint##w##_t res; \ |
| 966 | return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ | 980 | return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt_u(w, bits) : res; \ |
| 967 | } \ | 981 | } \ |
| 968 | \ | 982 | \ |
| 969 | static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 983 | static inline int##w##_t zig_subs_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 970 | zig_i##w res; \ | 984 | int##w##_t res; \ |
| 971 | if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ | 985 | if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ |
| 972 | return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 986 | return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 973 | } \ | 987 | } \ |
| 974 | \ | 988 | \ |
| 975 | static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 989 | static inline uint##w##_t zig_muls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 976 | zig_u##w res; \ | 990 | uint##w##_t res; \ |
| 977 | return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | 991 | return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ |
| 978 | } \ | 992 | } \ |
| 979 | \ | 993 | \ |
| 980 | static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 994 | static inline int##w##_t zig_muls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 981 | zig_i##w res; \ | 995 | int##w##_t res; \ |
| 982 | if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ | 996 | if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ |
| 983 | return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 997 | return (lhs ^ rhs) < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 984 | } | 998 | } |
| 985 | zig_int_builtins(8) | 999 | zig_int_builtins(8) |
| 986 | zig_int_builtins(16) | 1000 | zig_int_builtins(16) |
| ... | @@ -988,89 +1002,89 @@ zig_int_builtins(32) | ... | @@ -988,89 +1002,89 @@ zig_int_builtins(32) |
| 988 | zig_int_builtins(64) | 1002 | zig_int_builtins(64) |
| 989 | 1003 | ||
| 990 | #define zig_builtin8(name, val) __builtin_##name(val) | 1004 | #define zig_builtin8(name, val) __builtin_##name(val) |
| 991 | typedef zig_c_uint zig_Builtin8; | 1005 | typedef unsigned int zig_Builtin8; |
| 992 | 1006 | ||
| 993 | #define zig_builtin16(name, val) __builtin_##name(val) | 1007 | #define zig_builtin16(name, val) __builtin_##name(val) |
| 994 | typedef zig_c_uint zig_Builtin16; | 1008 | typedef unsigned int zig_Builtin16; |
| 995 | 1009 | ||
| 996 | #if INT_MIN <= INT32_MIN | 1010 | #if INT_MIN <= INT32_MIN |
| 997 | #define zig_builtin32(name, val) __builtin_##name(val) | 1011 | #define zig_builtin32(name, val) __builtin_##name(val) |
| 998 | typedef zig_c_uint zig_Builtin32; | 1012 | typedef unsigned int zig_Builtin32; |
| 999 | #elif LONG_MIN <= INT32_MIN | 1013 | #elif LONG_MIN <= INT32_MIN |
| 1000 | #define zig_builtin32(name, val) __builtin_##name##l(val) | 1014 | #define zig_builtin32(name, val) __builtin_##name##l(val) |
| 1001 | typedef zig_c_ulong zig_Builtin32; | 1015 | typedef unsigned long zig_Builtin32; |
| 1002 | #endif | 1016 | #endif |
| 1003 | 1017 | ||
| 1004 | #if INT_MIN <= INT64_MIN | 1018 | #if INT_MIN <= INT64_MIN |
| 1005 | #define zig_builtin64(name, val) __builtin_##name(val) | 1019 | #define zig_builtin64(name, val) __builtin_##name(val) |
| 1006 | typedef zig_c_uint zig_Builtin64; | 1020 | typedef unsigned int zig_Builtin64; |
| 1007 | #elif LONG_MIN <= INT64_MIN | 1021 | #elif LONG_MIN <= INT64_MIN |
| 1008 | #define zig_builtin64(name, val) __builtin_##name##l(val) | 1022 | #define zig_builtin64(name, val) __builtin_##name##l(val) |
| 1009 | typedef zig_c_ulong zig_Builtin64; | 1023 | typedef unsigned long zig_Builtin64; |
| 1010 | #elif LLONG_MIN <= INT64_MIN | 1024 | #elif LLONG_MIN <= INT64_MIN |
| 1011 | #define zig_builtin64(name, val) __builtin_##name##ll(val) | 1025 | #define zig_builtin64(name, val) __builtin_##name##ll(val) |
| 1012 | typedef zig_c_ulonglong zig_Builtin64; | 1026 | typedef unsigned long long zig_Builtin64; |
| 1013 | #endif | 1027 | #endif |
| 1014 | 1028 | ||
| 1015 | static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { | 1029 | static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) { |
| 1016 | return zig_wrap_u8(val >> (8 - bits), bits); | 1030 | return zig_wrap_u8(val >> (8 - bits), bits); |
| 1017 | } | 1031 | } |
| 1018 | 1032 | ||
| 1019 | static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { | 1033 | static inline int8_t zig_byte_swap_i8(int8_t val, uint8_t bits) { |
| 1020 | return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); | 1034 | return zig_wrap_i8((int8_t)zig_byte_swap_u8((uint8_t)val, bits), bits); |
| 1021 | } | 1035 | } |
| 1022 | 1036 | ||
| 1023 | static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { | 1037 | static inline uint16_t zig_byte_swap_u16(uint16_t val, uint8_t bits) { |
| 1024 | zig_u16 full_res; | 1038 | uint16_t full_res; |
| 1025 | #if zig_has_builtin(bswap16) || defined(zig_gnuc) | 1039 | #if zig_has_builtin(bswap16) || defined(zig_gnuc) |
| 1026 | full_res = __builtin_bswap16(val); | 1040 | full_res = __builtin_bswap16(val); |
| 1027 | #else | 1041 | #else |
| 1028 | full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 | | 1042 | full_res = (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 0), 8) << 8 | |
| 1029 | (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0; | 1043 | (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 8), 8) >> 0; |
| 1030 | #endif | 1044 | #endif |
| 1031 | return zig_wrap_u16(full_res >> (16 - bits), bits); | 1045 | return zig_wrap_u16(full_res >> (16 - bits), bits); |
| 1032 | } | 1046 | } |
| 1033 | 1047 | ||
| 1034 | static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { | 1048 | static inline int16_t zig_byte_swap_i16(int16_t val, uint8_t bits) { |
| 1035 | return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); | 1049 | return zig_wrap_i16((int16_t)zig_byte_swap_u16((uint16_t)val, bits), bits); |
| 1036 | } | 1050 | } |
| 1037 | 1051 | ||
| 1038 | static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { | 1052 | static inline uint32_t zig_byte_swap_u32(uint32_t val, uint8_t bits) { |
| 1039 | zig_u32 full_res; | 1053 | uint32_t full_res; |
| 1040 | #if zig_has_builtin(bswap32) || defined(zig_gnuc) | 1054 | #if zig_has_builtin(bswap32) || defined(zig_gnuc) |
| 1041 | full_res = __builtin_bswap32(val); | 1055 | full_res = __builtin_bswap32(val); |
| 1042 | #else | 1056 | #else |
| 1043 | full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 | | 1057 | full_res = (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 0), 16) << 16 | |
| 1044 | (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0; | 1058 | (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 16), 16) >> 0; |
| 1045 | #endif | 1059 | #endif |
| 1046 | return zig_wrap_u32(full_res >> (32 - bits), bits); | 1060 | return zig_wrap_u32(full_res >> (32 - bits), bits); |
| 1047 | } | 1061 | } |
| 1048 | 1062 | ||
| 1049 | static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { | 1063 | static inline int32_t zig_byte_swap_i32(int32_t val, uint8_t bits) { |
| 1050 | return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); | 1064 | return zig_wrap_i32((int32_t)zig_byte_swap_u32((uint32_t)val, bits), bits); |
| 1051 | } | 1065 | } |
| 1052 | 1066 | ||
| 1053 | static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { | 1067 | static inline uint64_t zig_byte_swap_u64(uint64_t val, uint8_t bits) { |
| 1054 | zig_u64 full_res; | 1068 | uint64_t full_res; |
| 1055 | #if zig_has_builtin(bswap64) || defined(zig_gnuc) | 1069 | #if zig_has_builtin(bswap64) || defined(zig_gnuc) |
| 1056 | full_res = __builtin_bswap64(val); | 1070 | full_res = __builtin_bswap64(val); |
| 1057 | #else | 1071 | #else |
| 1058 | full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 | | 1072 | full_res = (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 0), 32) << 32 | |
| 1059 | (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0; | 1073 | (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 32), 32) >> 0; |
| 1060 | #endif | 1074 | #endif |
| 1061 | return zig_wrap_u64(full_res >> (64 - bits), bits); | 1075 | return zig_wrap_u64(full_res >> (64 - bits), bits); |
| 1062 | } | 1076 | } |
| 1063 | 1077 | ||
| 1064 | static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { | 1078 | static inline int64_t zig_byte_swap_i64(int64_t val, uint8_t bits) { |
| 1065 | return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); | 1079 | return zig_wrap_i64((int64_t)zig_byte_swap_u64((uint64_t)val, bits), bits); |
| 1066 | } | 1080 | } |
| 1067 | 1081 | ||
| 1068 | static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { | 1082 | static inline uint8_t zig_bit_reverse_u8(uint8_t val, uint8_t bits) { |
| 1069 | zig_u8 full_res; | 1083 | uint8_t full_res; |
| 1070 | #if zig_has_builtin(bitreverse8) | 1084 | #if zig_has_builtin(bitreverse8) |
| 1071 | full_res = __builtin_bitreverse8(val); | 1085 | full_res = __builtin_bitreverse8(val); |
| 1072 | #else | 1086 | #else |
| 1073 | static zig_u8 const lut[0x10] = { | 1087 | static uint8_t const lut[0x10] = { |
| 1074 | 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, | 1088 | 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, |
| 1075 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf | 1089 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf |
| 1076 | }; | 1090 | }; |
| ... | @@ -1079,62 +1093,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { | ... | @@ -1079,62 +1093,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { |
| 1079 | return zig_wrap_u8(full_res >> (8 - bits), bits); | 1093 | return zig_wrap_u8(full_res >> (8 - bits), bits); |
| 1080 | } | 1094 | } |
| 1081 | 1095 | ||
| 1082 | static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { | 1096 | static inline int8_t zig_bit_reverse_i8(int8_t val, uint8_t bits) { |
| 1083 | return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); | 1097 | return zig_wrap_i8((int8_t)zig_bit_reverse_u8((uint8_t)val, bits), bits); |
| 1084 | } | 1098 | } |
| 1085 | 1099 | ||
| 1086 | static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { | 1100 | static inline uint16_t zig_bit_reverse_u16(uint16_t val, uint8_t bits) { |
| 1087 | zig_u16 full_res; | 1101 | uint16_t full_res; |
| 1088 | #if zig_has_builtin(bitreverse16) | 1102 | #if zig_has_builtin(bitreverse16) |
| 1089 | full_res = __builtin_bitreverse16(val); | 1103 | full_res = __builtin_bitreverse16(val); |
| 1090 | #else | 1104 | #else |
| 1091 | full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 | | 1105 | full_res = (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 0), 8) << 8 | |
| 1092 | (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0; | 1106 | (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 8), 8) >> 0; |
| 1093 | #endif | 1107 | #endif |
| 1094 | return zig_wrap_u16(full_res >> (16 - bits), bits); | 1108 | return zig_wrap_u16(full_res >> (16 - bits), bits); |
| 1095 | } | 1109 | } |
| 1096 | 1110 | ||
| 1097 | static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { | 1111 | static inline int16_t zig_bit_reverse_i16(int16_t val, uint8_t bits) { |
| 1098 | return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); | 1112 | return zig_wrap_i16((int16_t)zig_bit_reverse_u16((uint16_t)val, bits), bits); |
| 1099 | } | 1113 | } |
| 1100 | 1114 | ||
| 1101 | static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { | 1115 | static inline uint32_t zig_bit_reverse_u32(uint32_t val, uint8_t bits) { |
| 1102 | zig_u32 full_res; | 1116 | uint32_t full_res; |
| 1103 | #if zig_has_builtin(bitreverse32) | 1117 | #if zig_has_builtin(bitreverse32) |
| 1104 | full_res = __builtin_bitreverse32(val); | 1118 | full_res = __builtin_bitreverse32(val); |
| 1105 | #else | 1119 | #else |
| 1106 | full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 | | 1120 | full_res = (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 0), 16) << 16 | |
| 1107 | (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0; | 1121 | (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 16), 16) >> 0; |
| 1108 | #endif | 1122 | #endif |
| 1109 | return zig_wrap_u32(full_res >> (32 - bits), bits); | 1123 | return zig_wrap_u32(full_res >> (32 - bits), bits); |
| 1110 | } | 1124 | } |
| 1111 | 1125 | ||
| 1112 | static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { | 1126 | static inline int32_t zig_bit_reverse_i32(int32_t val, uint8_t bits) { |
| 1113 | return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); | 1127 | return zig_wrap_i32((int32_t)zig_bit_reverse_u32((uint32_t)val, bits), bits); |
| 1114 | } | 1128 | } |
| 1115 | 1129 | ||
| 1116 | static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { | 1130 | static inline uint64_t zig_bit_reverse_u64(uint64_t val, uint8_t bits) { |
| 1117 | zig_u64 full_res; | 1131 | uint64_t full_res; |
| 1118 | #if zig_has_builtin(bitreverse64) | 1132 | #if zig_has_builtin(bitreverse64) |
| 1119 | full_res = __builtin_bitreverse64(val); | 1133 | full_res = __builtin_bitreverse64(val); |
| 1120 | #else | 1134 | #else |
| 1121 | full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 | | 1135 | full_res = (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 0), 32) << 32 | |
| 1122 | (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0; | 1136 | (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 32), 32) >> 0; |
| 1123 | #endif | 1137 | #endif |
| 1124 | return zig_wrap_u64(full_res >> (64 - bits), bits); | 1138 | return zig_wrap_u64(full_res >> (64 - bits), bits); |
| 1125 | } | 1139 | } |
| 1126 | 1140 | ||
| 1127 | static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { | 1141 | static inline int64_t zig_bit_reverse_i64(int64_t val, uint8_t bits) { |
| 1128 | return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); | 1142 | return zig_wrap_i64((int64_t)zig_bit_reverse_u64((uint64_t)val, bits), bits); |
| 1129 | } | 1143 | } |
| 1130 | 1144 | ||
| 1131 | #define zig_builtin_popcount_common(w) \ | 1145 | #define zig_builtin_popcount_common(w) \ |
| 1132 | static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ | 1146 | static inline uint8_t zig_popcount_i##w(int##w##_t val, uint8_t bits) { \ |
| 1133 | return zig_popcount_u##w((zig_u##w)val, bits); \ | 1147 | return zig_popcount_u##w((uint##w##_t)val, bits); \ |
| 1134 | } | 1148 | } |
| 1135 | #if zig_has_builtin(popcount) || defined(zig_gnuc) | 1149 | #if zig_has_builtin(popcount) || defined(zig_gnuc) |
| 1136 | #define zig_builtin_popcount(w) \ | 1150 | #define zig_builtin_popcount(w) \ |
| 1137 | static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ | 1151 | static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1138 | (void)bits; \ | 1152 | (void)bits; \ |
| 1139 | return zig_builtin##w(popcount, val); \ | 1153 | return zig_builtin##w(popcount, val); \ |
| 1140 | } \ | 1154 | } \ |
| ... | @@ -1142,12 +1156,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { | ... | @@ -1142,12 +1156,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { |
| 1142 | zig_builtin_popcount_common(w) | 1156 | zig_builtin_popcount_common(w) |
| 1143 | #else | 1157 | #else |
| 1144 | #define zig_builtin_popcount(w) \ | 1158 | #define zig_builtin_popcount(w) \ |
| 1145 | static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ | 1159 | static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1146 | (void)bits; \ | 1160 | (void)bits; \ |
| 1147 | zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \ | 1161 | uint##w##_t temp = val - ((val >> 1) & (UINT##w##_MAX / 3)); \ |
| 1148 | temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \ | 1162 | temp = (temp & (UINT##w##_MAX / 5)) + ((temp >> 2) & (UINT##w##_MAX / 5)); \ |
| 1149 | temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \ | 1163 | temp = (temp + (temp >> 4)) & (UINT##w##_MAX / 17); \ |
| 1150 | return temp * (zig_maxInt_u##w / 255) >> (w - 8); \ | 1164 | return temp * (UINT##w##_MAX / 255) >> (w - 8); \ |
| 1151 | } \ | 1165 | } \ |
| 1152 | \ | 1166 | \ |
| 1153 | zig_builtin_popcount_common(w) | 1167 | zig_builtin_popcount_common(w) |
| ... | @@ -1158,12 +1172,12 @@ zig_builtin_popcount(32) | ... | @@ -1158,12 +1172,12 @@ zig_builtin_popcount(32) |
| 1158 | zig_builtin_popcount(64) | 1172 | zig_builtin_popcount(64) |
| 1159 | 1173 | ||
| 1160 | #define zig_builtin_ctz_common(w) \ | 1174 | #define zig_builtin_ctz_common(w) \ |
| 1161 | static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ | 1175 | static inline uint8_t zig_ctz_i##w(int##w##_t val, uint8_t bits) { \ |
| 1162 | return zig_ctz_u##w((zig_u##w)val, bits); \ | 1176 | return zig_ctz_u##w((uint##w##_t)val, bits); \ |
| 1163 | } | 1177 | } |
| 1164 | #if zig_has_builtin(ctz) || defined(zig_gnuc) | 1178 | #if zig_has_builtin(ctz) || defined(zig_gnuc) |
| 1165 | #define zig_builtin_ctz(w) \ | 1179 | #define zig_builtin_ctz(w) \ |
| 1166 | static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ | 1180 | static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1167 | if (val == 0) return bits; \ | 1181 | if (val == 0) return bits; \ |
| 1168 | return zig_builtin##w(ctz, val); \ | 1182 | return zig_builtin##w(ctz, val); \ |
| 1169 | } \ | 1183 | } \ |
| ... | @@ -1171,7 +1185,7 @@ zig_builtin_popcount(64) | ... | @@ -1171,7 +1185,7 @@ zig_builtin_popcount(64) |
| 1171 | zig_builtin_ctz_common(w) | 1185 | zig_builtin_ctz_common(w) |
| 1172 | #else | 1186 | #else |
| 1173 | #define zig_builtin_ctz(w) \ | 1187 | #define zig_builtin_ctz(w) \ |
| 1174 | static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ | 1188 | static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1175 | return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ | 1189 | return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ |
| 1176 | } \ | 1190 | } \ |
| 1177 | \ | 1191 | \ |
| ... | @@ -1183,12 +1197,12 @@ zig_builtin_ctz(32) | ... | @@ -1183,12 +1197,12 @@ zig_builtin_ctz(32) |
| 1183 | zig_builtin_ctz(64) | 1197 | zig_builtin_ctz(64) |
| 1184 | 1198 | ||
| 1185 | #define zig_builtin_clz_common(w) \ | 1199 | #define zig_builtin_clz_common(w) \ |
| 1186 | static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ | 1200 | static inline uint8_t zig_clz_i##w(int##w##_t val, uint8_t bits) { \ |
| 1187 | return zig_clz_u##w((zig_u##w)val, bits); \ | 1201 | return zig_clz_u##w((uint##w##_t)val, bits); \ |
| 1188 | } | 1202 | } |
| 1189 | #if zig_has_builtin(clz) || defined(zig_gnuc) | 1203 | #if zig_has_builtin(clz) || defined(zig_gnuc) |
| 1190 | #define zig_builtin_clz(w) \ | 1204 | #define zig_builtin_clz(w) \ |
| 1191 | static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ | 1205 | static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1192 | if (val == 0) return bits; \ | 1206 | if (val == 0) return bits; \ |
| 1193 | return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ | 1207 | return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ |
| 1194 | } \ | 1208 | } \ |
| ... | @@ -1196,7 +1210,7 @@ zig_builtin_ctz(64) | ... | @@ -1196,7 +1210,7 @@ zig_builtin_ctz(64) |
| 1196 | zig_builtin_clz_common(w) | 1210 | zig_builtin_clz_common(w) |
| 1197 | #else | 1211 | #else |
| 1198 | #define zig_builtin_clz(w) \ | 1212 | #define zig_builtin_clz(w) \ |
| 1199 | static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ | 1213 | static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1200 | return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ | 1214 | return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ |
| 1201 | } \ | 1215 | } \ |
| 1202 | \ | 1216 | \ |
| ... | @@ -1207,7 +1221,7 @@ zig_builtin_clz(16) | ... | @@ -1207,7 +1221,7 @@ zig_builtin_clz(16) |
| 1207 | zig_builtin_clz(32) | 1221 | zig_builtin_clz(32) |
| 1208 | zig_builtin_clz(64) | 1222 | zig_builtin_clz(64) |
| 1209 | 1223 | ||
| 1210 | /* ======================== 128-bit Integer Routines ======================== */ | 1224 | /* ======================== 128-bit Integer Support ========================= */ |
| 1211 | 1225 | ||
| 1212 | #if !defined(zig_has_int128) | 1226 | #if !defined(zig_has_int128) |
| 1213 | # if defined(__SIZEOF_INT128__) | 1227 | # if defined(__SIZEOF_INT128__) |
| ... | @@ -1222,18 +1236,18 @@ zig_builtin_clz(64) | ... | @@ -1222,18 +1236,18 @@ zig_builtin_clz(64) |
| 1222 | typedef unsigned __int128 zig_u128; | 1236 | typedef unsigned __int128 zig_u128; |
| 1223 | typedef signed __int128 zig_i128; | 1237 | typedef signed __int128 zig_i128; |
| 1224 | 1238 | ||
| 1225 | #define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) | 1239 | #define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) |
| 1226 | #define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) | 1240 | #define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) |
| 1227 | #define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) | 1241 | #define zig_init_u128(hi, lo) zig_make_u128(hi, lo) |
| 1228 | #define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) | 1242 | #define zig_init_i128(hi, lo) zig_make_i128(hi, lo) |
| 1229 | #define zig_hi_u128(val) ((zig_u64)((val) >> 64)) | 1243 | #define zig_hi_u128(val) ((uint64_t)((val) >> 64)) |
| 1230 | #define zig_lo_u128(val) ((zig_u64)((val) >> 0)) | 1244 | #define zig_lo_u128(val) ((uint64_t)((val) >> 0)) |
| 1231 | #define zig_hi_i128(val) ((zig_i64)((val) >> 64)) | 1245 | #define zig_hi_i128(val) (( int64_t)((val) >> 64)) |
| 1232 | #define zig_lo_i128(val) ((zig_u64)((val) >> 0)) | 1246 | #define zig_lo_i128(val) ((uint64_t)((val) >> 0)) |
| 1233 | #define zig_bitcast_u128(val) ((zig_u128)(val)) | 1247 | #define zig_bitcast_u128(val) ((zig_u128)(val)) |
| 1234 | #define zig_bitcast_i128(val) ((zig_i128)(val)) | 1248 | #define zig_bitcast_i128(val) ((zig_i128)(val)) |
| 1235 | #define zig_cmp_int128(Type) \ | 1249 | #define zig_cmp_int128(Type) \ |
| 1236 | static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 1250 | static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 1237 | return (lhs > rhs) - (lhs < rhs); \ | 1251 | return (lhs > rhs) - (lhs < rhs); \ |
| 1238 | } | 1252 | } |
| 1239 | #define zig_bit_int128(Type, operation, operator) \ | 1253 | #define zig_bit_int128(Type, operation, operator) \ |
| ... | @@ -1243,32 +1257,32 @@ typedef signed __int128 zig_i128; | ... | @@ -1243,32 +1257,32 @@ typedef signed __int128 zig_i128; |
| 1243 | 1257 | ||
| 1244 | #else /* zig_has_int128 */ | 1258 | #else /* zig_has_int128 */ |
| 1245 | 1259 | ||
| 1246 | #if __LITTLE_ENDIAN__ || _MSC_VER | 1260 | #if zig_little_endian |
| 1247 | typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; | 1261 | typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; |
| 1248 | typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; | 1262 | typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; |
| 1249 | #else | 1263 | #else |
| 1250 | typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; | 1264 | typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128; |
| 1251 | typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; | 1265 | typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; |
| 1252 | #endif | 1266 | #endif |
| 1253 | 1267 | ||
| 1254 | #define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) | 1268 | #define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) |
| 1255 | #define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) | 1269 | #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) |
| 1256 | 1270 | ||
| 1257 | #if _MSC_VER | 1271 | #if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ |
| 1258 | #define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } | 1272 | #define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } |
| 1259 | #define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } | 1273 | #define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } |
| 1260 | #else | 1274 | #else /* But non-MSVC doesn't like the unprotected commas */ |
| 1261 | #define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) | 1275 | #define zig_init_u128(hi, lo) zig_make_u128(hi, lo) |
| 1262 | #define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) | 1276 | #define zig_init_i128(hi, lo) zig_make_i128(hi, lo) |
| 1263 | #endif | 1277 | #endif |
| 1264 | #define zig_hi_u128(val) ((val).hi) | 1278 | #define zig_hi_u128(val) ((val).hi) |
| 1265 | #define zig_lo_u128(val) ((val).lo) | 1279 | #define zig_lo_u128(val) ((val).lo) |
| 1266 | #define zig_hi_i128(val) ((val).hi) | 1280 | #define zig_hi_i128(val) ((val).hi) |
| 1267 | #define zig_lo_i128(val) ((val).lo) | 1281 | #define zig_lo_i128(val) ((val).lo) |
| 1268 | #define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) | 1282 | #define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo) |
| 1269 | #define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) | 1283 | #define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo) |
| 1270 | #define zig_cmp_int128(Type) \ | 1284 | #define zig_cmp_int128(Type) \ |
| 1271 | static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 1285 | static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 1272 | return (lhs.hi == rhs.hi) \ | 1286 | return (lhs.hi == rhs.hi) \ |
| 1273 | ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ | 1287 | ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ |
| 1274 | : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ | 1288 | : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ |
| ... | @@ -1280,10 +1294,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; | ... | @@ -1280,10 +1294,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; |
| 1280 | 1294 | ||
| 1281 | #endif /* zig_has_int128 */ | 1295 | #endif /* zig_has_int128 */ |
| 1282 | 1296 | ||
| 1283 | #define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) | 1297 | #define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64) |
| 1284 | #define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) | 1298 | #define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64) |
| 1285 | #define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) | 1299 | #define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64) |
| 1286 | #define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) | 1300 | #define zig_maxInt_i128 zig_make_i128(zig_maxInt_i64, zig_maxInt_u64) |
| 1287 | 1301 | ||
| 1288 | zig_cmp_int128(u128) | 1302 | zig_cmp_int128(u128) |
| 1289 | zig_cmp_int128(i128) | 1303 | zig_cmp_int128(i128) |
| ... | @@ -1297,28 +1311,33 @@ zig_bit_int128(i128, or, |) | ... | @@ -1297,28 +1311,33 @@ zig_bit_int128(i128, or, |) |
| 1297 | zig_bit_int128(u128, xor, ^) | 1311 | zig_bit_int128(u128, xor, ^) |
| 1298 | zig_bit_int128(i128, xor, ^) | 1312 | zig_bit_int128(i128, xor, ^) |
| 1299 | 1313 | ||
| 1300 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); | 1314 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs); |
| 1301 | 1315 | ||
| 1302 | #if zig_has_int128 | 1316 | #if zig_has_int128 |
| 1303 | 1317 | ||
| 1304 | static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { | 1318 | static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { |
| 1305 | return val ^ zig_maxInt(u128, bits); | 1319 | return val ^ zig_maxInt_u(128, bits); |
| 1306 | } | 1320 | } |
| 1307 | 1321 | ||
| 1308 | static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { | 1322 | static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { |
| 1309 | (void)bits; | 1323 | (void)bits; |
| 1310 | return ~val; | 1324 | return ~val; |
| 1311 | } | 1325 | } |
| 1312 | 1326 | ||
| 1313 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { | 1327 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { |
| 1314 | return lhs >> rhs; | 1328 | return lhs >> rhs; |
| 1315 | } | 1329 | } |
| 1316 | 1330 | ||
| 1317 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { | 1331 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { |
| 1318 | return lhs << rhs; | 1332 | return lhs << rhs; |
| 1319 | } | 1333 | } |
| 1320 | 1334 | ||
| 1321 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { | 1335 | static 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 | |||
| 1340 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { | ||
| 1322 | return lhs << rhs; | 1341 | return lhs << rhs; |
| 1323 | } | 1342 | } |
| 1324 | 1343 | ||
| ... | @@ -1363,40 +1382,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1363,40 +1382,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1363 | } | 1382 | } |
| 1364 | 1383 | ||
| 1365 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | 1384 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1366 | return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); | 1385 | return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_make_i128(0, 0)); |
| 1367 | } | 1386 | } |
| 1368 | 1387 | ||
| 1369 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | 1388 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1370 | zig_i128 rem = zig_rem_i128(lhs, rhs); | 1389 | zig_i128 rem = zig_rem_i128(lhs, rhs); |
| 1371 | return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); | 1390 | return rem + (((lhs ^ rhs) & rem) < zig_make_i128(0, 0) ? rhs : zig_make_i128(0, 0)); |
| 1372 | } | 1391 | } |
| 1373 | 1392 | ||
| 1374 | #else /* zig_has_int128 */ | 1393 | #else /* zig_has_int128 */ |
| 1375 | 1394 | ||
| 1376 | static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { | 1395 | static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { |
| 1377 | return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; | 1396 | return (zig_u128){ .hi = zig_not_u64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; |
| 1397 | } | ||
| 1398 | |||
| 1399 | static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { | ||
| 1400 | return (zig_i128){ .hi = zig_not_i64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; | ||
| 1378 | } | 1401 | } |
| 1379 | 1402 | ||
| 1380 | static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { | 1403 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { |
| 1381 | return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; | 1404 | if (rhs == UINT8_C(0)) return lhs; |
| 1405 | if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - UINT8_C(64)) }; | ||
| 1406 | return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (UINT8_C(64) - rhs) | lhs.lo >> rhs }; | ||
| 1382 | } | 1407 | } |
| 1383 | 1408 | ||
| 1384 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { | 1409 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { |
| 1385 | if (rhs == zig_as_u8(0)) return lhs; | 1410 | if (rhs == UINT8_C(0)) return lhs; |
| 1386 | if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) }; | 1411 | if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; |
| 1387 | return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs }; | 1412 | return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; |
| 1388 | } | 1413 | } |
| 1389 | 1414 | ||
| 1390 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { | 1415 | static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { |
| 1391 | if (rhs == zig_as_u8(0)) return lhs; | 1416 | if (rhs == UINT8_C(0)) return lhs; |
| 1392 | if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; | 1417 | if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = zig_shr_i64(lhs.hi, 63), .lo = zig_shr_i64(lhs.hi, (rhs - UINT8_C(64))) }; |
| 1393 | return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; | 1418 | return (zig_i128){ .hi = zig_shr_i64(lhs.hi, rhs), .lo = lhs.lo >> rhs | (uint64_t)lhs.hi << (UINT8_C(64) - rhs) }; |
| 1394 | } | 1419 | } |
| 1395 | 1420 | ||
| 1396 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { | 1421 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { |
| 1397 | if (rhs == zig_as_u8(0)) return lhs; | 1422 | if (rhs == UINT8_C(0)) return lhs; |
| 1398 | if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; | 1423 | if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; |
| 1399 | return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; | 1424 | return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; |
| 1400 | } | 1425 | } |
| 1401 | 1426 | ||
| 1402 | static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { | 1427 | static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { |
| ... | @@ -1424,14 +1449,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1424,14 +1449,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1424 | } | 1449 | } |
| 1425 | 1450 | ||
| 1426 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); | 1451 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); |
| 1427 | static 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 | |||
| 1431 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { | 1452 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1432 | return __multi3(lhs, rhs); | 1453 | return __multi3(lhs, rhs); |
| 1433 | } | 1454 | } |
| 1434 | 1455 | ||
| 1456 | static 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 | |||
| 1435 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); | 1460 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); |
| 1436 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { | 1461 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1437 | return __udivti3(lhs, rhs); | 1462 | return __udivti3(lhs, rhs); |
| ... | @@ -1454,11 +1479,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1454,11 +1479,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1454 | 1479 | ||
| 1455 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | 1480 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1456 | zig_i128 rem = zig_rem_i128(lhs, rhs); | 1481 | zig_i128 rem = zig_rem_i128(lhs, rhs); |
| 1457 | return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); | 1482 | return zig_add_i128(rem, ((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0)); |
| 1458 | } | 1483 | } |
| 1459 | 1484 | ||
| 1460 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | 1485 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1461 | return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); | 1486 | return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_make_i128(0, 0)) < INT32_C(0))); |
| 1462 | } | 1487 | } |
| 1463 | 1488 | ||
| 1464 | #endif /* zig_has_int128 */ | 1489 | #endif /* zig_has_int128 */ |
| ... | @@ -1471,326 +1496,1303 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { | ... | @@ -1471,326 +1496,1303 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1471 | } | 1496 | } |
| 1472 | 1497 | ||
| 1473 | static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { | 1498 | static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1474 | return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; | 1499 | return zig_cmp_u128(lhs, rhs) < INT32_C(0) ? lhs : rhs; |
| 1475 | } | 1500 | } |
| 1476 | 1501 | ||
| 1477 | static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { | 1502 | static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1478 | return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; | 1503 | return zig_cmp_i128(lhs, rhs) < INT32_C(0) ? lhs : rhs; |
| 1479 | } | 1504 | } |
| 1480 | 1505 | ||
| 1481 | static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { | 1506 | static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1482 | return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; | 1507 | return zig_cmp_u128(lhs, rhs) > INT32_C(0) ? lhs : rhs; |
| 1483 | } | 1508 | } |
| 1484 | 1509 | ||
| 1485 | static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { | 1510 | static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1486 | return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; | 1511 | return zig_cmp_i128(lhs, rhs) > INT32_C(0) ? lhs : rhs; |
| 1487 | } | 1512 | } |
| 1488 | 1513 | ||
| 1489 | static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { | 1514 | static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { |
| 1490 | zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0); | 1515 | return zig_and_u128(val, zig_maxInt_u(128, bits)); |
| 1491 | return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); | ||
| 1492 | } | 1516 | } |
| 1493 | 1517 | ||
| 1494 | static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { | 1518 | static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { |
| 1495 | return zig_and_u128(val, zig_maxInt(u128, bits)); | 1519 | if (bits > UINT8_C(64)) return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); |
| 1520 | int64_t lo = zig_wrap_i64((int64_t)zig_lo_i128(val), bits); | ||
| 1521 | return zig_make_i128(zig_shr_i64(lo, 63), (uint64_t)lo); | ||
| 1496 | } | 1522 | } |
| 1497 | 1523 | ||
| 1498 | static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { | 1524 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { |
| 1499 | return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 1503 | return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); | 1525 | return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); |
| 1504 | } | 1526 | } |
| 1505 | 1527 | ||
| 1506 | static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { | 1528 | static inline zig_i128 zig_shlw_i128(zig_i128 lhs, uint8_t rhs, uint8_t bits) { |
| 1507 | return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); | 1529 | return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); |
| 1508 | } | 1530 | } |
| 1509 | 1531 | ||
| 1510 | static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1532 | static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1511 | return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); | 1533 | return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); |
| 1512 | } | 1534 | } |
| 1513 | 1535 | ||
| 1514 | static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1536 | static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1515 | return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | 1537 | return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); |
| 1516 | } | 1538 | } |
| 1517 | 1539 | ||
| 1518 | static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1540 | static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1519 | return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); | 1541 | return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); |
| 1520 | } | 1542 | } |
| 1521 | 1543 | ||
| 1522 | static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1544 | static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1523 | return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | 1545 | return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); |
| 1524 | } | 1546 | } |
| 1525 | 1547 | ||
| 1526 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1548 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1527 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); | 1549 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); |
| 1528 | } | 1550 | } |
| 1529 | 1551 | ||
| 1530 | static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1552 | static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1531 | return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | 1553 | return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); |
| 1532 | } | 1554 | } |
| 1533 | 1555 | ||
| 1534 | #if zig_has_int128 | 1556 | #if zig_has_int128 |
| 1535 | 1557 | ||
| 1536 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1558 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1537 | #if zig_has_builtin(add_overflow) | 1559 | #if zig_has_builtin(add_overflow) |
| 1538 | zig_u128 full_res; | 1560 | zig_u128 full_res; |
| 1539 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 1561 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 1540 | *res = zig_wrap_u128(full_res, bits); | 1562 | *res = zig_wrap_u128(full_res, bits); |
| 1541 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | 1563 | return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); |
| 1542 | #else | 1564 | #else |
| 1543 | *res = zig_addw_u128(lhs, rhs, bits); | 1565 | *res = zig_addw_u128(lhs, rhs, bits); |
| 1544 | return *res < lhs; | 1566 | return *res < lhs; |
| 1545 | #endif | 1567 | #endif |
| 1546 | } | 1568 | } |
| 1547 | 1569 | ||
| 1548 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1570 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1549 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1571 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1550 | #if zig_has_builtin(add_overflow) | 1572 | #if zig_has_builtin(add_overflow) |
| 1551 | zig_i128 full_res; | 1573 | zig_i128 full_res; |
| 1552 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 1574 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 1553 | #else | 1575 | #else |
| 1554 | zig_c_int overflow_int; | 1576 | int overflow_int; |
| 1555 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); | 1577 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); |
| 1556 | bool overflow = overflow_int != 0; | 1578 | bool overflow = overflow_int != 0; |
| 1557 | #endif | 1579 | #endif |
| 1558 | *res = zig_wrap_i128(full_res, bits); | 1580 | *res = zig_wrap_i128(full_res, bits); |
| 1559 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | 1581 | return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); |
| 1560 | } | 1582 | } |
| 1561 | 1583 | ||
| 1562 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1584 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1563 | #if zig_has_builtin(sub_overflow) | 1585 | #if zig_has_builtin(sub_overflow) |
| 1564 | zig_u128 full_res; | 1586 | zig_u128 full_res; |
| 1565 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 1587 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 1566 | *res = zig_wrap_u128(full_res, bits); | 1588 | *res = zig_wrap_u128(full_res, bits); |
| 1567 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | 1589 | return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); |
| 1568 | #else | 1590 | #else |
| 1569 | *res = zig_subw_u128(lhs, rhs, bits); | 1591 | *res = zig_subw_u128(lhs, rhs, bits); |
| 1570 | return *res > lhs; | 1592 | return *res > lhs; |
| 1571 | #endif | 1593 | #endif |
| 1572 | } | 1594 | } |
| 1573 | 1595 | ||
| 1574 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1596 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1575 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1597 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1576 | #if zig_has_builtin(sub_overflow) | 1598 | #if zig_has_builtin(sub_overflow) |
| 1577 | zig_i128 full_res; | 1599 | zig_i128 full_res; |
| 1578 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 1600 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 1579 | #else | 1601 | #else |
| 1580 | zig_c_int overflow_int; | 1602 | int overflow_int; |
| 1581 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); | 1603 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); |
| 1582 | bool overflow = overflow_int != 0; | 1604 | bool overflow = overflow_int != 0; |
| 1583 | #endif | 1605 | #endif |
| 1584 | *res = zig_wrap_i128(full_res, bits); | 1606 | *res = zig_wrap_i128(full_res, bits); |
| 1585 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | 1607 | return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); |
| 1586 | } | 1608 | } |
| 1587 | 1609 | ||
| 1588 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1610 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1589 | #if zig_has_builtin(mul_overflow) | 1611 | #if zig_has_builtin(mul_overflow) |
| 1590 | zig_u128 full_res; | 1612 | zig_u128 full_res; |
| 1591 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1613 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 1592 | *res = zig_wrap_u128(full_res, bits); | 1614 | *res = zig_wrap_u128(full_res, bits); |
| 1593 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | 1615 | return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); |
| 1594 | #else | 1616 | #else |
| 1595 | *res = zig_mulw_u128(lhs, rhs, bits); | 1617 | *res = zig_mulw_u128(lhs, rhs, bits); |
| 1596 | return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; | 1618 | return rhs != zig_make_u128(0, 0) && lhs > zig_maxInt_u(128, bits) / rhs; |
| 1597 | #endif | 1619 | #endif |
| 1598 | } | 1620 | } |
| 1599 | 1621 | ||
| 1600 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1622 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1601 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1623 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1602 | #if zig_has_builtin(mul_overflow) | 1624 | #if zig_has_builtin(mul_overflow) |
| 1603 | zig_i128 full_res; | 1625 | zig_i128 full_res; |
| 1604 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1626 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 1605 | #else | 1627 | #else |
| 1606 | zig_c_int overflow_int; | 1628 | int overflow_int; |
| 1607 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); | 1629 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); |
| 1608 | bool overflow = overflow_int != 0; | 1630 | bool overflow = overflow_int != 0; |
| 1609 | #endif | 1631 | #endif |
| 1610 | *res = zig_wrap_i128(full_res, bits); | 1632 | *res = zig_wrap_i128(full_res, bits); |
| 1611 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | 1633 | return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); |
| 1612 | } | 1634 | } |
| 1613 | 1635 | ||
| 1614 | #else /* zig_has_int128 */ | 1636 | #else /* zig_has_int128 */ |
| 1615 | 1637 | ||
| 1616 | static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) { | 1638 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1617 | return overflow || | 1639 | uint64_t hi; |
| 1618 | zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) || | 1640 | bool overflow = zig_addo_u64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1619 | zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0); | 1641 | return overflow ^ zig_addo_u64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1620 | } | ||
| 1621 | |||
| 1622 | static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) { | ||
| 1623 | return overflow || | ||
| 1624 | zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) || | ||
| 1625 | zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0); | ||
| 1626 | } | 1642 | } |
| 1627 | 1643 | ||
| 1628 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1644 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1629 | zig_u128 full_res; | 1645 | int64_t hi; |
| 1630 | bool overflow = | 1646 | bool overflow = zig_addo_i64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1631 | zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | | 1647 | return overflow ^ zig_addo_i64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1632 | zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); | ||
| 1633 | *res = zig_wrap_u128(full_res, bits); | ||
| 1634 | return zig_overflow_u128(overflow, full_res, bits); | ||
| 1635 | } | 1648 | } |
| 1636 | 1649 | ||
| 1637 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1650 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1638 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1651 | uint64_t hi; |
| 1639 | zig_c_int overflow_int; | 1652 | bool overflow = zig_subo_u64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1640 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); | 1653 | return overflow ^ zig_subo_u64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1641 | *res = zig_wrap_i128(full_res, bits); | ||
| 1642 | return zig_overflow_i128(overflow_int, full_res, bits); | ||
| 1643 | } | 1654 | } |
| 1644 | 1655 | ||
| 1645 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1656 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1646 | zig_u128 full_res; | 1657 | int64_t hi; |
| 1647 | bool overflow = | 1658 | bool overflow = zig_subo_i64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1648 | zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | | 1659 | return overflow ^ zig_subo_i64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1649 | zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); | ||
| 1650 | *res = zig_wrap_u128(full_res, bits); | ||
| 1651 | return zig_overflow_u128(overflow, full_res, bits); | ||
| 1652 | } | 1660 | } |
| 1653 | 1661 | ||
| 1654 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1662 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1655 | static 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 | |||
| 1662 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1663 | *res = zig_mulw_u128(lhs, rhs, bits); | 1663 | *res = zig_mulw_u128(lhs, rhs, bits); |
| 1664 | return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) && | 1664 | return zig_cmp_u128(*res, zig_make_u128(0, 0)) != INT32_C(0) && |
| 1665 | zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); | 1665 | zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); |
| 1666 | } | 1666 | } |
| 1667 | 1667 | ||
| 1668 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1668 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1669 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1669 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1670 | zig_c_int overflow_int; | 1670 | int overflow_int; |
| 1671 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); | 1671 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); |
| 1672 | bool overflow = overflow_int != 0 || | ||
| 1673 | zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) || | ||
| 1674 | zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0); | ||
| 1672 | *res = zig_wrap_i128(full_res, bits); | 1675 | *res = zig_wrap_i128(full_res, bits); |
| 1673 | return zig_overflow_i128(overflow_int, full_res, bits); | 1676 | return overflow; |
| 1674 | } | 1677 | } |
| 1675 | 1678 | ||
| 1676 | #endif /* zig_has_int128 */ | 1679 | #endif /* zig_has_int128 */ |
| 1677 | 1680 | ||
| 1678 | static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { | 1681 | static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, uint8_t rhs, uint8_t bits) { |
| 1679 | *res = zig_shlw_u128(lhs, rhs, bits); | 1682 | *res = zig_shlw_u128(lhs, rhs, bits); |
| 1680 | return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); | 1683 | return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); |
| 1681 | } | 1684 | } |
| 1682 | 1685 | ||
| 1683 | static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { | 1686 | static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8_t bits) { |
| 1684 | *res = zig_shlw_i128(lhs, rhs, bits); | 1687 | *res = zig_shlw_i128(lhs, rhs, bits); |
| 1685 | zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); | 1688 | zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1))); |
| 1686 | return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) && | 1689 | return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) && |
| 1687 | zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0); | 1690 | zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0); |
| 1688 | } | 1691 | } |
| 1689 | 1692 | ||
| 1690 | static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1693 | static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1691 | zig_u128 res; | 1694 | zig_u128 res; |
| 1692 | if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0)) | 1695 | if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0)) |
| 1693 | return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs; | 1696 | return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs; |
| 1694 | 1697 | return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res; | |
| 1695 | #if zig_has_int128 | ||
| 1696 | return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1697 | #else | ||
| 1698 | return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1699 | #endif | ||
| 1700 | } | 1698 | } |
| 1701 | 1699 | ||
| 1702 | static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1700 | static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1703 | zig_i128 res; | 1701 | zig_i128 res; |
| 1704 | if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; | 1702 | if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res; |
| 1705 | return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1703 | return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1706 | } | 1704 | } |
| 1707 | 1705 | ||
| 1708 | static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1706 | static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1709 | zig_u128 res; | 1707 | zig_u128 res; |
| 1710 | return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; | 1708 | return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; |
| 1711 | } | 1709 | } |
| 1712 | 1710 | ||
| 1713 | static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1711 | static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1714 | zig_i128 res; | 1712 | zig_i128 res; |
| 1715 | if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; | 1713 | if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; |
| 1716 | return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1714 | return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1717 | } | 1715 | } |
| 1718 | 1716 | ||
| 1719 | static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1717 | static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1720 | zig_u128 res; | 1718 | zig_u128 res; |
| 1721 | return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; | 1719 | return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt_u(128, bits) : res; |
| 1722 | } | 1720 | } |
| 1723 | 1721 | ||
| 1724 | static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1722 | static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1725 | zig_i128 res; | 1723 | zig_i128 res; |
| 1726 | if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; | 1724 | if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; |
| 1727 | return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1725 | return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1728 | } | 1726 | } |
| 1729 | 1727 | ||
| 1730 | static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1728 | static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1731 | zig_u128 res; | 1729 | zig_u128 res; |
| 1732 | return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; | 1730 | return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; |
| 1733 | } | 1731 | } |
| 1734 | 1732 | ||
| 1735 | static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1733 | static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1736 | zig_i128 res; | 1734 | zig_i128 res; |
| 1737 | if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; | 1735 | if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; |
| 1738 | return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1736 | return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1739 | } | 1737 | } |
| 1740 | 1738 | ||
| 1741 | static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { | 1739 | static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) { |
| 1742 | if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits); | 1740 | if (bits <= UINT8_C(64)) return zig_clz_u64(zig_lo_u128(val), bits); |
| 1743 | if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); | 1741 | if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - UINT8_C(64)); |
| 1744 | return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64)); | 1742 | return zig_clz_u64(zig_lo_u128(val), UINT8_C(64)) + (bits - UINT8_C(64)); |
| 1745 | } | 1743 | } |
| 1746 | 1744 | ||
| 1747 | static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { | 1745 | static inline uint8_t zig_clz_i128(zig_i128 val, uint8_t bits) { |
| 1748 | return zig_clz_u128(zig_bitcast_u128(val), bits); | 1746 | return zig_clz_u128(zig_bitcast_u128(val), bits); |
| 1749 | } | 1747 | } |
| 1750 | 1748 | ||
| 1751 | static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { | 1749 | static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) { |
| 1752 | if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); | 1750 | if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), UINT8_C(64)); |
| 1753 | return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); | 1751 | return zig_ctz_u64(zig_hi_u128(val), bits - UINT8_C(64)) + UINT8_C(64); |
| 1754 | } | 1752 | } |
| 1755 | 1753 | ||
| 1756 | static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { | 1754 | static inline uint8_t zig_ctz_i128(zig_i128 val, uint8_t bits) { |
| 1757 | return zig_ctz_u128(zig_bitcast_u128(val), bits); | 1755 | return zig_ctz_u128(zig_bitcast_u128(val), bits); |
| 1758 | } | 1756 | } |
| 1759 | 1757 | ||
| 1760 | static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { | 1758 | static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) { |
| 1761 | return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + | 1759 | return zig_popcount_u64(zig_hi_u128(val), bits - UINT8_C(64)) + |
| 1762 | zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); | 1760 | zig_popcount_u64(zig_lo_u128(val), UINT8_C(64)); |
| 1763 | } | 1761 | } |
| 1764 | 1762 | ||
| 1765 | static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { | 1763 | static inline uint8_t zig_popcount_i128(zig_i128 val, uint8_t bits) { |
| 1766 | return zig_popcount_u128(zig_bitcast_u128(val), bits); | 1764 | return zig_popcount_u128(zig_bitcast_u128(val), bits); |
| 1767 | } | 1765 | } |
| 1768 | 1766 | ||
| 1769 | static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { | 1767 | static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) { |
| 1770 | zig_u128 full_res; | 1768 | zig_u128 full_res; |
| 1771 | #if zig_has_builtin(bswap128) | 1769 | #if zig_has_builtin(bswap128) |
| 1772 | full_res = __builtin_bswap128(val); | 1770 | full_res = __builtin_bswap128(val); |
| 1773 | #else | 1771 | #else |
| 1774 | full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), | 1772 | full_res = zig_make_u128(zig_byte_swap_u64(zig_lo_u128(val), UINT8_C(64)), |
| 1775 | zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); | 1773 | zig_byte_swap_u64(zig_hi_u128(val), UINT8_C(64))); |
| 1776 | #endif | 1774 | #endif |
| 1777 | return zig_shr_u128(full_res, zig_as_u8(128) - bits); | 1775 | return zig_shr_u128(full_res, UINT8_C(128) - bits); |
| 1778 | } | 1776 | } |
| 1779 | 1777 | ||
| 1780 | static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { | 1778 | static inline zig_i128 zig_byte_swap_i128(zig_i128 val, uint8_t bits) { |
| 1781 | return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); | 1779 | return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); |
| 1782 | } | 1780 | } |
| 1783 | 1781 | ||
| 1784 | static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { | 1782 | static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) { |
| 1785 | return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), | 1783 | return zig_shr_u128(zig_make_u128(zig_bit_reverse_u64(zig_lo_u128(val), UINT8_C(64)), |
| 1786 | zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), | 1784 | zig_bit_reverse_u64(zig_hi_u128(val), UINT8_C(64))), |
| 1787 | zig_as_u8(128) - bits); | 1785 | UINT8_C(128) - bits); |
| 1788 | } | 1786 | } |
| 1789 | 1787 | ||
| 1790 | static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { | 1788 | static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { |
| 1791 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); | 1789 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); |
| 1792 | } | 1790 | } |
| 1793 | 1791 | ||
| 1792 | /* ========================== Big Integer Support =========================== */ | ||
| 1793 | |||
| 1794 | static 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 | |||
| 1801 | static 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 | |||
| 1963 | static 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 | |||
| 2179 | static 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 | |||
| 2395 | static 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 | |||
| 2399 | static 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 | |||
| 2403 | zig_extern void __udivei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); | ||
| 2404 | static 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 | |||
| 2413 | static 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 | |||
| 2422 | zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits); | ||
| 2423 | static 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 | |||
| 2432 | static 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 | |||
| 2441 | static 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 | |||
| 2567 | static 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 | |||
| 2687 | static inline uint16_t zig_popcount_big(const void *val, bool is_signed, uint16_t bits) { | ||
| 2688 | const uint8_t *val_bytes = val; | ||
| 2689 | uint16_t byte_offset = 0; | ||
| 2690 | uint16_t remaining_bytes = zig_int_bytes(bits); | ||
| 2691 | uint16_t total_pc = 0; | ||
| 2692 | (void)is_signed; | ||
| 2693 | |||
| 2694 | #if zig_big_endian | ||
| 2695 | byte_offset = remaining_bytes; | ||
| 2696 | #endif | ||
| 2697 | |||
| 2698 | while (remaining_bytes >= 128 / CHAR_BIT) { | ||
| 2699 | #if zig_big_endian | ||
| 2700 | byte_offset -= 128 / CHAR_BIT; | ||
| 2701 | #endif | ||
| 2702 | |||
| 2703 | { | ||
| 2704 | zig_u128 val_limb; | ||
| 2705 | |||
| 2706 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2707 | total_pc += zig_popcount_u128(val_limb, 128); | ||
| 2708 | } | ||
| 2709 | |||
| 2710 | remaining_bytes -= 128 / CHAR_BIT; | ||
| 2711 | |||
| 2712 | #if zig_little_endian | ||
| 2713 | byte_offset += 128 / CHAR_BIT; | ||
| 2714 | #endif | ||
| 2715 | } | ||
| 2716 | |||
| 2717 | while (remaining_bytes >= 64 / CHAR_BIT) { | ||
| 2718 | #if zig_big_endian | ||
| 2719 | byte_offset -= 64 / CHAR_BIT; | ||
| 2720 | #endif | ||
| 2721 | |||
| 2722 | { | ||
| 2723 | uint64_t val_limb; | ||
| 2724 | |||
| 2725 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2726 | total_pc += zig_popcount_u64(val_limb, 64); | ||
| 2727 | } | ||
| 2728 | |||
| 2729 | remaining_bytes -= 64 / CHAR_BIT; | ||
| 2730 | |||
| 2731 | #if zig_little_endian | ||
| 2732 | byte_offset += 64 / CHAR_BIT; | ||
| 2733 | #endif | ||
| 2734 | } | ||
| 2735 | |||
| 2736 | while (remaining_bytes >= 32 / CHAR_BIT) { | ||
| 2737 | #if zig_big_endian | ||
| 2738 | byte_offset -= 32 / CHAR_BIT; | ||
| 2739 | #endif | ||
| 2740 | |||
| 2741 | { | ||
| 2742 | uint32_t val_limb; | ||
| 2743 | |||
| 2744 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2745 | total_pc += zig_popcount_u32(val_limb, 32); | ||
| 2746 | } | ||
| 2747 | |||
| 2748 | remaining_bytes -= 32 / CHAR_BIT; | ||
| 2749 | |||
| 2750 | #if zig_little_endian | ||
| 2751 | byte_offset += 32 / CHAR_BIT; | ||
| 2752 | #endif | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | while (remaining_bytes >= 16 / CHAR_BIT) { | ||
| 2756 | #if zig_big_endian | ||
| 2757 | byte_offset -= 16 / CHAR_BIT; | ||
| 2758 | #endif | ||
| 2759 | |||
| 2760 | { | ||
| 2761 | uint16_t val_limb; | ||
| 2762 | |||
| 2763 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2764 | total_pc = zig_popcount_u16(val_limb, 16); | ||
| 2765 | } | ||
| 2766 | |||
| 2767 | remaining_bytes -= 16 / CHAR_BIT; | ||
| 2768 | |||
| 2769 | #if zig_little_endian | ||
| 2770 | byte_offset += 16 / CHAR_BIT; | ||
| 2771 | #endif | ||
| 2772 | } | ||
| 2773 | |||
| 2774 | while (remaining_bytes >= 8 / CHAR_BIT) { | ||
| 2775 | #if zig_big_endian | ||
| 2776 | byte_offset -= 8 / CHAR_BIT; | ||
| 2777 | #endif | ||
| 2778 | |||
| 2779 | { | ||
| 2780 | uint8_t val_limb; | ||
| 2781 | |||
| 2782 | memcpy(&val_limb, &val_bytes[byte_offset], sizeof(val_limb)); | ||
| 2783 | total_pc = zig_popcount_u8(val_limb, 8); | ||
| 2784 | } | ||
| 2785 | |||
| 2786 | remaining_bytes -= 8 / CHAR_BIT; | ||
| 2787 | |||
| 2788 | #if zig_little_endian | ||
| 2789 | byte_offset += 8 / CHAR_BIT; | ||
| 2790 | #endif | ||
| 2791 | } | ||
| 2792 | |||
| 2793 | return total_pc; | ||
| 2794 | } | ||
| 2795 | |||
| 1794 | /* ========================= Floating Point Support ========================= */ | 2796 | /* ========================= Floating Point Support ========================= */ |
| 1795 | 2797 | ||
| 1796 | #if _MSC_VER | 2798 | #if _MSC_VER |
| ... | @@ -1810,252 +2812,253 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { | ... | @@ -1810,252 +2812,253 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { |
| 1810 | 2812 | ||
| 1811 | #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) | 2813 | #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) |
| 1812 | #define zig_has_float_builtins 1 | 2814 | #define zig_has_float_builtins 1 |
| 1813 | #define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) | 2815 | #define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg) |
| 1814 | #define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) | 2816 | #define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg) |
| 1815 | #define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) | 2817 | #define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) |
| 1816 | #define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) | 2818 | #define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) |
| 1817 | #define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) | 2819 | #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) |
| 1818 | #define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg) | ||
| 1819 | #else | 2820 | #else |
| 1820 | #define zig_has_float_builtins 0 | 2821 | #define zig_has_float_builtins 0 |
| 1821 | #define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) | 2822 | #define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) |
| 1822 | #define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) | 2823 | #define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) |
| 1823 | #define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) | 2824 | #define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) |
| 1824 | #define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) | 2825 | #define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) |
| 1825 | #define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) | 2826 | #define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) |
| 1826 | #define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) | ||
| 1827 | #endif | 2827 | #endif |
| 1828 | 2828 | ||
| 1829 | #define zig_has_f16 1 | 2829 | #define zig_has_f16 1 |
| 1830 | #define zig_bitSizeOf_f16 16 | 2830 | #define zig_bitSizeOf_f16 16 |
| 2831 | typedef int16_t zig_repr_f16; | ||
| 1831 | #define zig_libc_name_f16(name) __##name##h | 2832 | #define zig_libc_name_f16(name) __##name##h |
| 1832 | #define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) | 2833 | #define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) |
| 1833 | #if FLT_MANT_DIG == 11 | 2834 | #if FLT_MANT_DIG == 11 |
| 1834 | typedef float zig_f16; | 2835 | typedef float zig_f16; |
| 1835 | #define zig_as_f16(fp, repr) fp##f | 2836 | #define zig_make_f16(fp, repr) fp##f |
| 1836 | #elif DBL_MANT_DIG == 11 | 2837 | #elif DBL_MANT_DIG == 11 |
| 1837 | typedef double zig_f16; | 2838 | typedef double zig_f16; |
| 1838 | #define zig_as_f16(fp, repr) fp | 2839 | #define zig_make_f16(fp, repr) fp |
| 1839 | #elif LDBL_MANT_DIG == 11 | 2840 | #elif LDBL_MANT_DIG == 11 |
| 1840 | #define zig_bitSizeOf_c_longdouble 16 | 2841 | #define zig_bitSizeOf_c_longdouble 16 |
| 2842 | #ifndef ZIG_TARGET_ABI_MSVC | ||
| 2843 | typedef zig_repr_f16 zig_repr_c_longdouble; | ||
| 2844 | #endif | ||
| 1841 | typedef long double zig_f16; | 2845 | typedef long double zig_f16; |
| 1842 | #define zig_as_f16(fp, repr) fp##l | 2846 | #define zig_make_f16(fp, repr) fp##l |
| 1843 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) | 2847 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) |
| 1844 | typedef _Float16 zig_f16; | 2848 | typedef _Float16 zig_f16; |
| 1845 | #define zig_as_f16(fp, repr) fp##f16 | 2849 | #define zig_make_f16(fp, repr) fp##f16 |
| 1846 | #elif defined(__SIZEOF_FP16__) | 2850 | #elif defined(__SIZEOF_FP16__) |
| 1847 | typedef __fp16 zig_f16; | 2851 | typedef __fp16 zig_f16; |
| 1848 | #define zig_as_f16(fp, repr) fp##f16 | 2852 | #define zig_make_f16(fp, repr) fp##f16 |
| 1849 | #else | 2853 | #else |
| 1850 | #undef zig_has_f16 | 2854 | #undef zig_has_f16 |
| 1851 | #define zig_has_f16 0 | 2855 | #define zig_has_f16 0 |
| 1852 | #define zig_repr_f16 i16 | 2856 | #define zig_bitSizeOf_repr_f16 16 |
| 1853 | typedef zig_i16 zig_f16; | 2857 | typedef int16_t zig_f16; |
| 1854 | #define zig_as_f16(fp, repr) repr | 2858 | #define zig_make_f16(fp, repr) repr |
| 1855 | #undef zig_as_special_f16 | 2859 | #undef zig_make_special_f16 |
| 1856 | #define zig_as_special_f16(sign, name, arg, repr) repr | 2860 | #define zig_make_special_f16(sign, name, arg, repr) repr |
| 1857 | #undef zig_as_special_constant_f16 | 2861 | #undef zig_init_special_f16 |
| 1858 | #define zig_as_special_constant_f16(sign, name, arg, repr) repr | 2862 | #define zig_init_special_f16(sign, name, arg, repr) repr |
| 1859 | #endif | 2863 | #endif |
| 1860 | 2864 | ||
| 1861 | #define zig_has_f32 1 | 2865 | #define zig_has_f32 1 |
| 1862 | #define zig_bitSizeOf_f32 32 | 2866 | #define zig_bitSizeOf_f32 32 |
| 2867 | typedef int32_t zig_repr_f32; | ||
| 1863 | #define zig_libc_name_f32(name) name##f | 2868 | #define zig_libc_name_f32(name) name##f |
| 1864 | #if _MSC_VER | 2869 | #if _MSC_VER |
| 1865 | #define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) | 2870 | #define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) |
| 1866 | #else | 2871 | #else |
| 1867 | #define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) | 2872 | #define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) |
| 1868 | #endif | 2873 | #endif |
| 1869 | #if FLT_MANT_DIG == 24 | 2874 | #if FLT_MANT_DIG == 24 |
| 1870 | typedef float zig_f32; | 2875 | typedef float zig_f32; |
| 1871 | #define zig_as_f32(fp, repr) fp##f | 2876 | #define zig_make_f32(fp, repr) fp##f |
| 1872 | #elif DBL_MANT_DIG == 24 | 2877 | #elif DBL_MANT_DIG == 24 |
| 1873 | typedef double zig_f32; | 2878 | typedef double zig_f32; |
| 1874 | #define zig_as_f32(fp, repr) fp | 2879 | #define zig_make_f32(fp, repr) fp |
| 1875 | #elif LDBL_MANT_DIG == 24 | 2880 | #elif LDBL_MANT_DIG == 24 |
| 1876 | #define zig_bitSizeOf_c_longdouble 32 | 2881 | #define zig_bitSizeOf_c_longdouble 32 |
| 2882 | #ifndef ZIG_TARGET_ABI_MSVC | ||
| 2883 | typedef zig_repr_f32 zig_repr_c_longdouble; | ||
| 2884 | #endif | ||
| 1877 | typedef long double zig_f32; | 2885 | typedef long double zig_f32; |
| 1878 | #define zig_as_f32(fp, repr) fp##l | 2886 | #define zig_make_f32(fp, repr) fp##l |
| 1879 | #elif FLT32_MANT_DIG == 24 | 2887 | #elif FLT32_MANT_DIG == 24 |
| 1880 | typedef _Float32 zig_f32; | 2888 | typedef _Float32 zig_f32; |
| 1881 | #define zig_as_f32(fp, repr) fp##f32 | 2889 | #define zig_make_f32(fp, repr) fp##f32 |
| 1882 | #else | 2890 | #else |
| 1883 | #undef zig_has_f32 | 2891 | #undef zig_has_f32 |
| 1884 | #define zig_has_f32 0 | 2892 | #define zig_has_f32 0 |
| 1885 | #define zig_repr_f32 i32 | 2893 | #define zig_bitSizeOf_repr_f32 32 |
| 1886 | typedef zig_i32 zig_f32; | 2894 | typedef int32_t zig_f32; |
| 1887 | #define zig_as_f32(fp, repr) repr | 2895 | #define zig_make_f32(fp, repr) repr |
| 1888 | #undef zig_as_special_f32 | 2896 | #undef zig_make_special_f32 |
| 1889 | #define zig_as_special_f32(sign, name, arg, repr) repr | 2897 | #define zig_make_special_f32(sign, name, arg, repr) repr |
| 1890 | #undef zig_as_special_constant_f32 | 2898 | #undef zig_init_special_f32 |
| 1891 | #define zig_as_special_constant_f32(sign, name, arg, repr) repr | 2899 | #define zig_init_special_f32(sign, name, arg, repr) repr |
| 1892 | #endif | 2900 | #endif |
| 1893 | 2901 | ||
| 1894 | #define zig_has_f64 1 | 2902 | #define zig_has_f64 1 |
| 1895 | #define zig_bitSizeOf_f64 64 | 2903 | #define zig_bitSizeOf_f64 64 |
| 2904 | typedef int64_t zig_repr_f64; | ||
| 1896 | #define zig_libc_name_f64(name) name | 2905 | #define zig_libc_name_f64(name) name |
| 1897 | #if _MSC_VER | 2906 | #if _MSC_VER |
| 1898 | #ifdef ZIG_TARGET_ABI_MSVC | 2907 | #ifdef ZIG_TARGET_ABI_MSVC |
| 1899 | #define zig_bitSizeOf_c_longdouble 64 | 2908 | #define zig_bitSizeOf_c_longdouble 64 |
| 2909 | #ifndef ZIG_TARGET_ABI_MSVC | ||
| 2910 | typedef zig_repr_f64 zig_repr_c_longdouble; | ||
| 2911 | #endif | ||
| 1900 | #endif | 2912 | #endif |
| 1901 | #define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) | 2913 | #define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) |
| 1902 | #else /* _MSC_VER */ | 2914 | #else /* _MSC_VER */ |
| 1903 | #define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) | 2915 | #define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) |
| 1904 | #endif /* _MSC_VER */ | 2916 | #endif /* _MSC_VER */ |
| 1905 | #if FLT_MANT_DIG == 53 | 2917 | #if FLT_MANT_DIG == 53 |
| 1906 | typedef float zig_f64; | 2918 | typedef float zig_f64; |
| 1907 | #define zig_as_f64(fp, repr) fp##f | 2919 | #define zig_make_f64(fp, repr) fp##f |
| 1908 | #elif DBL_MANT_DIG == 53 | 2920 | #elif DBL_MANT_DIG == 53 |
| 1909 | typedef double zig_f64; | 2921 | typedef double zig_f64; |
| 1910 | #define zig_as_f64(fp, repr) fp | 2922 | #define zig_make_f64(fp, repr) fp |
| 1911 | #elif LDBL_MANT_DIG == 53 | 2923 | #elif LDBL_MANT_DIG == 53 |
| 1912 | #define zig_bitSizeOf_c_longdouble 64 | 2924 | #define zig_bitSizeOf_c_longdouble 64 |
| 2925 | #ifndef ZIG_TARGET_ABI_MSVC | ||
| 2926 | typedef zig_repr_f64 zig_repr_c_longdouble; | ||
| 2927 | #endif | ||
| 1913 | typedef long double zig_f64; | 2928 | typedef long double zig_f64; |
| 1914 | #define zig_as_f64(fp, repr) fp##l | 2929 | #define zig_make_f64(fp, repr) fp##l |
| 1915 | #elif FLT64_MANT_DIG == 53 | 2930 | #elif FLT64_MANT_DIG == 53 |
| 1916 | typedef _Float64 zig_f64; | 2931 | typedef _Float64 zig_f64; |
| 1917 | #define zig_as_f64(fp, repr) fp##f64 | 2932 | #define zig_make_f64(fp, repr) fp##f64 |
| 1918 | #elif FLT32X_MANT_DIG == 53 | 2933 | #elif FLT32X_MANT_DIG == 53 |
| 1919 | typedef _Float32x zig_f64; | 2934 | typedef _Float32x zig_f64; |
| 1920 | #define zig_as_f64(fp, repr) fp##f32x | 2935 | #define zig_make_f64(fp, repr) fp##f32x |
| 1921 | #else | 2936 | #else |
| 1922 | #undef zig_has_f64 | 2937 | #undef zig_has_f64 |
| 1923 | #define zig_has_f64 0 | 2938 | #define zig_has_f64 0 |
| 1924 | #define zig_repr_f64 i64 | 2939 | #define zig_bitSizeOf_repr_f64 64 |
| 1925 | typedef zig_i64 zig_f64; | 2940 | typedef int64_t zig_f64; |
| 1926 | #define zig_as_f64(fp, repr) repr | 2941 | #define zig_make_f64(fp, repr) repr |
| 1927 | #undef zig_as_special_f64 | 2942 | #undef zig_make_special_f64 |
| 1928 | #define zig_as_special_f64(sign, name, arg, repr) repr | 2943 | #define zig_make_special_f64(sign, name, arg, repr) repr |
| 1929 | #undef zig_as_special_constant_f64 | 2944 | #undef zig_init_special_f64 |
| 1930 | #define zig_as_special_constant_f64(sign, name, arg, repr) repr | 2945 | #define zig_init_special_f64(sign, name, arg, repr) repr |
| 1931 | #endif | 2946 | #endif |
| 1932 | 2947 | ||
| 1933 | #define zig_has_f80 1 | 2948 | #define zig_has_f80 1 |
| 1934 | #define zig_bitSizeOf_f80 80 | 2949 | #define zig_bitSizeOf_f80 80 |
| 2950 | typedef zig_i128 zig_repr_f80; | ||
| 1935 | #define zig_libc_name_f80(name) __##name##x | 2951 | #define zig_libc_name_f80(name) __##name##x |
| 1936 | #define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) | 2952 | #define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) |
| 1937 | #if FLT_MANT_DIG == 64 | 2953 | #if FLT_MANT_DIG == 64 |
| 1938 | typedef float zig_f80; | 2954 | typedef float zig_f80; |
| 1939 | #define zig_as_f80(fp, repr) fp##f | 2955 | #define zig_make_f80(fp, repr) fp##f |
| 1940 | #elif DBL_MANT_DIG == 64 | 2956 | #elif DBL_MANT_DIG == 64 |
| 1941 | typedef double zig_f80; | 2957 | typedef double zig_f80; |
| 1942 | #define zig_as_f80(fp, repr) fp | 2958 | #define zig_make_f80(fp, repr) fp |
| 1943 | #elif LDBL_MANT_DIG == 64 | 2959 | #elif LDBL_MANT_DIG == 64 |
| 1944 | #define zig_bitSizeOf_c_longdouble 80 | 2960 | #define zig_bitSizeOf_c_longdouble 80 |
| 2961 | #ifndef ZIG_TARGET_ABI_MSVC | ||
| 2962 | typedef zig_repr_f80 zig_repr_c_longdouble; | ||
| 2963 | #endif | ||
| 1945 | typedef long double zig_f80; | 2964 | typedef long double zig_f80; |
| 1946 | #define zig_as_f80(fp, repr) fp##l | 2965 | #define zig_make_f80(fp, repr) fp##l |
| 1947 | #elif FLT80_MANT_DIG == 64 | 2966 | #elif FLT80_MANT_DIG == 64 |
| 1948 | typedef _Float80 zig_f80; | 2967 | typedef _Float80 zig_f80; |
| 1949 | #define zig_as_f80(fp, repr) fp##f80 | 2968 | #define zig_make_f80(fp, repr) fp##f80 |
| 1950 | #elif FLT64X_MANT_DIG == 64 | 2969 | #elif FLT64X_MANT_DIG == 64 |
| 1951 | typedef _Float64x zig_f80; | 2970 | typedef _Float64x zig_f80; |
| 1952 | #define zig_as_f80(fp, repr) fp##f64x | 2971 | #define zig_make_f80(fp, repr) fp##f64x |
| 1953 | #elif defined(__SIZEOF_FLOAT80__) | 2972 | #elif defined(__SIZEOF_FLOAT80__) |
| 1954 | typedef __float80 zig_f80; | 2973 | typedef __float80 zig_f80; |
| 1955 | #define zig_as_f80(fp, repr) fp##l | 2974 | #define zig_make_f80(fp, repr) fp##l |
| 1956 | #else | 2975 | #else |
| 1957 | #undef zig_has_f80 | 2976 | #undef zig_has_f80 |
| 1958 | #define zig_has_f80 0 | 2977 | #define zig_has_f80 0 |
| 1959 | #define zig_repr_f80 i128 | 2978 | #define zig_bitSizeOf_repr_f80 128 |
| 1960 | typedef zig_i128 zig_f80; | 2979 | typedef zig_i128 zig_f80; |
| 1961 | #define zig_as_f80(fp, repr) repr | 2980 | #define zig_make_f80(fp, repr) repr |
| 1962 | #undef zig_as_special_f80 | 2981 | #undef zig_make_special_f80 |
| 1963 | #define zig_as_special_f80(sign, name, arg, repr) repr | 2982 | #define zig_make_special_f80(sign, name, arg, repr) repr |
| 1964 | #undef zig_as_special_constant_f80 | 2983 | #undef zig_init_special_f80 |
| 1965 | #define zig_as_special_constant_f80(sign, name, arg, repr) repr | 2984 | #define zig_init_special_f80(sign, name, arg, repr) repr |
| 1966 | #endif | 2985 | #endif |
| 1967 | 2986 | ||
| 1968 | #define zig_has_f128 1 | 2987 | #define zig_has_f128 1 |
| 1969 | #define zig_bitSizeOf_f128 128 | 2988 | #define zig_bitSizeOf_f128 128 |
| 2989 | typedef zig_i128 zig_repr_f128; | ||
| 1970 | #define zig_libc_name_f128(name) name##q | 2990 | #define zig_libc_name_f128(name) name##q |
| 1971 | #define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) | 2991 | #define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) |
| 1972 | #if FLT_MANT_DIG == 113 | 2992 | #if FLT_MANT_DIG == 113 |
| 1973 | typedef float zig_f128; | 2993 | typedef float zig_f128; |
| 1974 | #define zig_as_f128(fp, repr) fp##f | 2994 | #define zig_make_f128(fp, repr) fp##f |
| 1975 | #elif DBL_MANT_DIG == 113 | 2995 | #elif DBL_MANT_DIG == 113 |
| 1976 | typedef double zig_f128; | 2996 | typedef double zig_f128; |
| 1977 | #define zig_as_f128(fp, repr) fp | 2997 | #define zig_make_f128(fp, repr) fp |
| 1978 | #elif LDBL_MANT_DIG == 113 | 2998 | #elif LDBL_MANT_DIG == 113 |
| 1979 | #define zig_bitSizeOf_c_longdouble 128 | 2999 | #define zig_bitSizeOf_c_longdouble 128 |
| 3000 | #ifndef ZIG_TARGET_ABI_MSVC | ||
| 3001 | typedef zig_repr_f128 zig_repr_c_longdouble; | ||
| 3002 | #endif | ||
| 1980 | typedef long double zig_f128; | 3003 | typedef long double zig_f128; |
| 1981 | #define zig_as_f128(fp, repr) fp##l | 3004 | #define zig_make_f128(fp, repr) fp##l |
| 1982 | #elif FLT128_MANT_DIG == 113 | 3005 | #elif FLT128_MANT_DIG == 113 |
| 1983 | typedef _Float128 zig_f128; | 3006 | typedef _Float128 zig_f128; |
| 1984 | #define zig_as_f128(fp, repr) fp##f128 | 3007 | #define zig_make_f128(fp, repr) fp##f128 |
| 1985 | #elif FLT64X_MANT_DIG == 113 | 3008 | #elif FLT64X_MANT_DIG == 113 |
| 1986 | typedef _Float64x zig_f128; | 3009 | typedef _Float64x zig_f128; |
| 1987 | #define zig_as_f128(fp, repr) fp##f64x | 3010 | #define zig_make_f128(fp, repr) fp##f64x |
| 1988 | #elif defined(__SIZEOF_FLOAT128__) | 3011 | #elif defined(__SIZEOF_FLOAT128__) |
| 1989 | typedef __float128 zig_f128; | 3012 | typedef __float128 zig_f128; |
| 1990 | #define zig_as_f128(fp, repr) fp##q | 3013 | #define zig_make_f128(fp, repr) fp##q |
| 1991 | #undef zig_as_special_f128 | 3014 | #undef zig_make_special_f128 |
| 1992 | #define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) | 3015 | #define zig_make_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) |
| 1993 | #else | 3016 | #else |
| 1994 | #undef zig_has_f128 | 3017 | #undef zig_has_f128 |
| 1995 | #define zig_has_f128 0 | 3018 | #define zig_has_f128 0 |
| 1996 | #define zig_repr_f128 i128 | 3019 | #define zig_bitSizeOf_repr_f128 128 |
| 1997 | typedef zig_i128 zig_f128; | 3020 | typedef zig_i128 zig_f128; |
| 1998 | #define zig_as_f128(fp, repr) repr | 3021 | #define zig_make_f128(fp, repr) repr |
| 1999 | #undef zig_as_special_f128 | 3022 | #undef zig_make_special_f128 |
| 2000 | #define zig_as_special_f128(sign, name, arg, repr) repr | 3023 | #define zig_make_special_f128(sign, name, arg, repr) repr |
| 2001 | #undef zig_as_special_constant_f128 | 3024 | #undef zig_init_special_f128 |
| 2002 | #define zig_as_special_constant_f128(sign, name, arg, repr) repr | 3025 | #define zig_init_special_f128(sign, name, arg, repr) repr |
| 2003 | #endif | 3026 | #endif |
| 2004 | 3027 | ||
| 2005 | #define zig_has_c_longdouble 1 | ||
| 2006 | |||
| 2007 | #ifdef ZIG_TARGET_ABI_MSVC | ||
| 2008 | #define zig_libc_name_c_longdouble(name) name | ||
| 2009 | #else | ||
| 2010 | #define zig_libc_name_c_longdouble(name) name##l | ||
| 2011 | #endif | ||
| 2012 | |||
| 2013 | #define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) | ||
| 2014 | #ifdef zig_bitSizeOf_c_longdouble | 3028 | #ifdef zig_bitSizeOf_c_longdouble |
| 2015 | 3029 | ||
| 3030 | #define zig_has_c_longdouble 1 | ||
| 2016 | #ifdef ZIG_TARGET_ABI_MSVC | 3031 | #ifdef ZIG_TARGET_ABI_MSVC |
| 2017 | typedef double zig_c_longdouble; | ||
| 2018 | #undef zig_bitSizeOf_c_longdouble | 3032 | #undef zig_bitSizeOf_c_longdouble |
| 2019 | #define zig_bitSizeOf_c_longdouble 64 | 3033 | #define zig_bitSizeOf_c_longdouble 64 |
| 2020 | #define zig_as_c_longdouble(fp, repr) fp | 3034 | typedef zig_f64 zig_c_longdouble; |
| 3035 | typedef zig_repr_f64 zig_repr_c_longdouble; | ||
| 2021 | #else | 3036 | #else |
| 2022 | typedef long double zig_c_longdouble; | 3037 | typedef long double zig_c_longdouble; |
| 2023 | #define zig_as_c_longdouble(fp, repr) fp##l | ||
| 2024 | #endif | 3038 | #endif |
| 2025 | 3039 | ||
| 2026 | #else /* zig_bitSizeOf_c_longdouble */ | 3040 | #else /* zig_bitSizeOf_c_longdouble */ |
| 2027 | 3041 | ||
| 2028 | #undef zig_has_c_longdouble | ||
| 2029 | #define zig_has_c_longdouble 0 | 3042 | #define zig_has_c_longdouble 0 |
| 2030 | #define zig_bitSizeOf_c_longdouble 80 | 3043 | #define zig_bitSizeOf_repr_c_longdouble 128 |
| 2031 | #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 | 3044 | typedef zig_f128 zig_c_longdouble; |
| 2032 | #define zig_repr_c_longdouble i128 | 3045 | typedef zig_repr_f128 zig_repr_c_longdouble; |
| 2033 | typedef 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 | ||
| 2039 | 3046 | ||
| 2040 | #endif /* zig_bitSizeOf_c_longdouble */ | 3047 | #endif /* zig_bitSizeOf_c_longdouble */ |
| 2041 | 3048 | ||
| 2042 | #if !zig_has_float_builtins | 3049 | #if !zig_has_float_builtins |
| 2043 | #define zig_float_from_repr(Type, ReprType) \ | 3050 | #define zig_float_from_repr(Type) \ |
| 2044 | static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \ | 3051 | static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \ |
| 2045 | return *((zig_##Type*)&repr); \ | 3052 | zig_##Type result; \ |
| 3053 | memcpy(&result, &repr, sizeof(result)); \ | ||
| 3054 | return result; \ | ||
| 2046 | } | 3055 | } |
| 2047 | 3056 | ||
| 2048 | zig_float_from_repr(f16, u16) | 3057 | zig_float_from_repr(f16) |
| 2049 | zig_float_from_repr(f32, u32) | 3058 | zig_float_from_repr(f32) |
| 2050 | zig_float_from_repr(f64, u64) | 3059 | zig_float_from_repr(f64) |
| 2051 | zig_float_from_repr(f80, u128) | 3060 | zig_float_from_repr(f80) |
| 2052 | zig_float_from_repr(f128, u128) | 3061 | zig_float_from_repr(f128) |
| 2053 | #if zig_bitSizeOf_c_longdouble == 80 | ||
| 2054 | zig_float_from_repr(c_longdouble, u128) | ||
| 2055 | #else | ||
| 2056 | #define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType) | ||
| 2057 | zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble)) | ||
| 2058 | #endif | ||
| 2059 | #endif | 3062 | #endif |
| 2060 | 3063 | ||
| 2061 | #define zig_cast_f16 (zig_f16) | 3064 | #define zig_cast_f16 (zig_f16) |
| ... | @@ -2064,41 +3067,42 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo | ... | @@ -2064,41 +3067,42 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo |
| 2064 | 3067 | ||
| 2065 | #if _MSC_VER && !zig_has_f128 | 3068 | #if _MSC_VER && !zig_has_f128 |
| 2066 | #define zig_cast_f80 | 3069 | #define zig_cast_f80 |
| 2067 | #define zig_cast_c_longdouble | ||
| 2068 | #define zig_cast_f128 | 3070 | #define zig_cast_f128 |
| 2069 | #else | 3071 | #else |
| 2070 | #define zig_cast_f80 (zig_f80) | 3072 | #define zig_cast_f80 (zig_f80) |
| 2071 | #define zig_cast_c_longdouble (zig_c_longdouble) | ||
| 2072 | #define zig_cast_f128 (zig_f128) | 3073 | #define zig_cast_f128 (zig_f128) |
| 2073 | #endif | 3074 | #endif |
| 2074 | 3075 | ||
| 2075 | #define zig_convert_builtin(ResType, operation, ArgType, version) \ | 3076 | #define zig_convert_builtin(ResType, operation, ArgType, version) \ |
| 2076 | zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ | 3077 | zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ |
| 2077 | zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType); | 3078 | zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType); |
| 2078 | zig_convert_builtin(f16, trunc, f32, 2) | 3079 | zig_convert_builtin(zig_f16, trunc, zig_f32, 2) |
| 2079 | zig_convert_builtin(f16, trunc, f64, 2) | 3080 | zig_convert_builtin(zig_f16, trunc, zig_f64, 2) |
| 2080 | zig_convert_builtin(f16, trunc, f80, 2) | 3081 | zig_convert_builtin(zig_f16, trunc, zig_f80, 2) |
| 2081 | zig_convert_builtin(f16, trunc, f128, 2) | 3082 | zig_convert_builtin(zig_f16, trunc, zig_f128, 2) |
| 2082 | zig_convert_builtin(f32, extend, f16, 2) | 3083 | zig_convert_builtin(zig_f32, extend, zig_f16, 2) |
| 2083 | zig_convert_builtin(f32, trunc, f64, 2) | 3084 | zig_convert_builtin(zig_f32, trunc, zig_f64, 2) |
| 2084 | zig_convert_builtin(f32, trunc, f80, 2) | 3085 | zig_convert_builtin(zig_f32, trunc, zig_f80, 2) |
| 2085 | zig_convert_builtin(f32, trunc, f128, 2) | 3086 | zig_convert_builtin(zig_f32, trunc, zig_f128, 2) |
| 2086 | zig_convert_builtin(f64, extend, f16, 2) | 3087 | zig_convert_builtin(zig_f64, extend, zig_f16, 2) |
| 2087 | zig_convert_builtin(f64, extend, f32, 2) | 3088 | zig_convert_builtin(zig_f64, extend, zig_f32, 2) |
| 2088 | zig_convert_builtin(f64, trunc, f80, 2) | 3089 | zig_convert_builtin(zig_f64, trunc, zig_f80, 2) |
| 2089 | zig_convert_builtin(f64, trunc, f128, 2) | 3090 | zig_convert_builtin(zig_f64, trunc, zig_f128, 2) |
| 2090 | zig_convert_builtin(f80, extend, f16, 2) | 3091 | zig_convert_builtin(zig_f80, extend, zig_f16, 2) |
| 2091 | zig_convert_builtin(f80, extend, f32, 2) | 3092 | zig_convert_builtin(zig_f80, extend, zig_f32, 2) |
| 2092 | zig_convert_builtin(f80, extend, f64, 2) | 3093 | zig_convert_builtin(zig_f80, extend, zig_f64, 2) |
| 2093 | zig_convert_builtin(f80, trunc, f128, 2) | 3094 | zig_convert_builtin(zig_f80, trunc, zig_f128, 2) |
| 2094 | zig_convert_builtin(f128, extend, f16, 2) | 3095 | zig_convert_builtin(zig_f128, extend, zig_f16, 2) |
| 2095 | zig_convert_builtin(f128, extend, f32, 2) | 3096 | zig_convert_builtin(zig_f128, extend, zig_f32, 2) |
| 2096 | zig_convert_builtin(f128, extend, f64, 2) | 3097 | zig_convert_builtin(zig_f128, extend, zig_f64, 2) |
| 2097 | zig_convert_builtin(f128, extend, f80, 2) | 3098 | zig_convert_builtin(zig_f128, extend, zig_f80, 2) |
| 2098 | 3099 | ||
| 2099 | #define zig_float_negate_builtin_0(Type) \ | 3100 | #define zig_float_negate_builtin_0(Type) \ |
| 2100 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ | 3101 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ |
| 2101 | return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \ | 3102 | return zig_expand_concat(zig_xor_i, zig_bitSizeOf_repr_##Type)( \ |
| 3103 | arg, \ | ||
| 3104 | zig_minInt_i(zig_bitSizeOf_repr_##Type, zig_bitSizeOf_##Type) \ | ||
| 3105 | ); \ | ||
| 2102 | } | 3106 | } |
| 2103 | #define zig_float_negate_builtin_1(Type) \ | 3107 | #define zig_float_negate_builtin_1(Type) \ |
| 2104 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ | 3108 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ |
| ... | @@ -2106,28 +3110,28 @@ zig_convert_builtin(f128, extend, f80, 2) | ... | @@ -2106,28 +3110,28 @@ zig_convert_builtin(f128, extend, f80, 2) |
| 2106 | } | 3110 | } |
| 2107 | 3111 | ||
| 2108 | #define zig_float_less_builtin_0(Type, operation) \ | 3112 | #define zig_float_less_builtin_0(Type, operation) \ |
| 2109 | zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \ | 3113 | zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \ |
| 2110 | zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \ | 3114 | zig_compiler_rt_abbrev_zig_##Type), 2)(zig_##Type, zig_##Type); \ |
| 2111 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 3115 | static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2112 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \ | 3116 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 2)(lhs, rhs); \ |
| 2113 | } | 3117 | } |
| 2114 | #define zig_float_less_builtin_1(Type, operation) \ | 3118 | #define zig_float_less_builtin_1(Type, operation) \ |
| 2115 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 3119 | static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2116 | return (!(lhs <= rhs) - (lhs < rhs)); \ | 3120 | return (!(lhs <= rhs) - (lhs < rhs)); \ |
| 2117 | } | 3121 | } |
| 2118 | 3122 | ||
| 2119 | #define zig_float_greater_builtin_0(Type, operation) \ | 3123 | #define zig_float_greater_builtin_0(Type, operation) \ |
| 2120 | zig_float_less_builtin_0(Type, operation) | 3124 | zig_float_less_builtin_0(Type, operation) |
| 2121 | #define zig_float_greater_builtin_1(Type, operation) \ | 3125 | #define zig_float_greater_builtin_1(Type, operation) \ |
| 2122 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 3126 | static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2123 | return ((lhs > rhs) - !(lhs >= rhs)); \ | 3127 | return ((lhs > rhs) - !(lhs >= rhs)); \ |
| 2124 | } | 3128 | } |
| 2125 | 3129 | ||
| 2126 | #define zig_float_binary_builtin_0(Type, operation, operator) \ | 3130 | #define zig_float_binary_builtin_0(Type, operation, operator) \ |
| 2127 | zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ | 3131 | zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ |
| 2128 | zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \ | 3132 | zig_compiler_rt_abbrev_zig_##Type), 3)(zig_##Type, zig_##Type); \ |
| 2129 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 3133 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2130 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \ | 3134 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 3)(lhs, rhs); \ |
| 2131 | } | 3135 | } |
| 2132 | #define zig_float_binary_builtin_1(Type, operation, operator) \ | 3136 | #define zig_float_binary_builtin_1(Type, operation, operator) \ |
| 2133 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 3137 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| ... | @@ -2135,18 +3139,18 @@ zig_convert_builtin(f128, extend, f80, 2) | ... | @@ -2135,18 +3139,18 @@ zig_convert_builtin(f128, extend, f80, 2) |
| 2135 | } | 3139 | } |
| 2136 | 3140 | ||
| 2137 | #define zig_float_builtins(Type) \ | 3141 | #define zig_float_builtins(Type) \ |
| 2138 | zig_convert_builtin(i32, fix, Type, ) \ | 3142 | zig_convert_builtin( int32_t, fix, zig_##Type, ) \ |
| 2139 | zig_convert_builtin(u32, fixuns, Type, ) \ | 3143 | zig_convert_builtin(uint32_t, fixuns, zig_##Type, ) \ |
| 2140 | zig_convert_builtin(i64, fix, Type, ) \ | 3144 | zig_convert_builtin( int64_t, fix, zig_##Type, ) \ |
| 2141 | zig_convert_builtin(u64, fixuns, Type, ) \ | 3145 | zig_convert_builtin(uint64_t, fixuns, zig_##Type, ) \ |
| 2142 | zig_convert_builtin(i128, fix, Type, ) \ | 3146 | zig_convert_builtin(zig_i128, fix, zig_##Type, ) \ |
| 2143 | zig_convert_builtin(u128, fixuns, Type, ) \ | 3147 | zig_convert_builtin(zig_u128, fixuns, zig_##Type, ) \ |
| 2144 | zig_convert_builtin(Type, float, i32, ) \ | 3148 | zig_convert_builtin(zig_##Type, float, int32_t, ) \ |
| 2145 | zig_convert_builtin(Type, floatun, u32, ) \ | 3149 | zig_convert_builtin(zig_##Type, floatun, uint32_t, ) \ |
| 2146 | zig_convert_builtin(Type, float, i64, ) \ | 3150 | zig_convert_builtin(zig_##Type, float, int64_t, ) \ |
| 2147 | zig_convert_builtin(Type, floatun, u64, ) \ | 3151 | zig_convert_builtin(zig_##Type, floatun, uint64_t, ) \ |
| 2148 | zig_convert_builtin(Type, float, i128, ) \ | 3152 | zig_convert_builtin(zig_##Type, float, zig_i128, ) \ |
| 2149 | zig_convert_builtin(Type, floatun, u128, ) \ | 3153 | zig_convert_builtin(zig_##Type, floatun, zig_u128, ) \ |
| 2150 | zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ | 3154 | zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ |
| 2151 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ | 3155 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ |
| 2152 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ | 3156 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ |
| ... | @@ -2194,155 +3198,162 @@ zig_float_builtins(f32) | ... | @@ -2194,155 +3198,162 @@ zig_float_builtins(f32) |
| 2194 | zig_float_builtins(f64) | 3198 | zig_float_builtins(f64) |
| 2195 | zig_float_builtins(f80) | 3199 | zig_float_builtins(f80) |
| 2196 | zig_float_builtins(f128) | 3200 | zig_float_builtins(f128) |
| 2197 | zig_float_builtins(c_longdouble) | ||
| 2198 | 3201 | ||
| 2199 | #if _MSC_VER && (_M_IX86 || _M_X64) | 3202 | #if _MSC_VER && (_M_IX86 || _M_X64) |
| 2200 | 3203 | ||
| 2201 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 | 3204 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 |
| 2202 | 3205 | ||
| 2203 | #define zig_msvc_atomics(Type, suffix) \ | 3206 | #define zig_msvc_atomics(ZigType, Type, suffix) \ |
| 2204 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ | 3207 | static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \ |
| 2205 | zig_##Type comparand = *expected; \ | 3208 | Type comparand = *expected; \ |
| 2206 | zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ | 3209 | Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ |
| 2207 | bool exchanged = initial == comparand; \ | 3210 | bool exchanged = initial == comparand; \ |
| 2208 | if (!exchanged) { \ | 3211 | if (!exchanged) { \ |
| 2209 | *expected = initial; \ | 3212 | *expected = initial; \ |
| 2210 | } \ | 3213 | } \ |
| 2211 | return exchanged; \ | 3214 | return exchanged; \ |
| 2212 | } \ | 3215 | } \ |
| 2213 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3216 | static inline Type zig_msvc_atomicrmw_xchg_##ZigType(Type volatile* obj, Type value) { \ |
| 2214 | return _InterlockedExchange##suffix(obj, value); \ | 3217 | return _InterlockedExchange##suffix(obj, value); \ |
| 2215 | } \ | 3218 | } \ |
| 2216 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3219 | static inline Type zig_msvc_atomicrmw_add_##ZigType(Type volatile* obj, Type value) { \ |
| 2217 | return _InterlockedExchangeAdd##suffix(obj, value); \ | 3220 | return _InterlockedExchangeAdd##suffix(obj, value); \ |
| 2218 | } \ | 3221 | } \ |
| 2219 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3222 | static inline Type zig_msvc_atomicrmw_sub_##ZigType(Type volatile* obj, Type value) { \ |
| 2220 | bool success = false; \ | 3223 | bool success = false; \ |
| 2221 | zig_##Type new; \ | 3224 | Type new; \ |
| 2222 | zig_##Type prev; \ | 3225 | Type prev; \ |
| 2223 | while (!success) { \ | 3226 | while (!success) { \ |
| 2224 | prev = *obj; \ | 3227 | prev = *obj; \ |
| 2225 | new = prev - value; \ | 3228 | new = prev - value; \ |
| 2226 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 3229 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2227 | } \ | 3230 | } \ |
| 2228 | return prev; \ | 3231 | return prev; \ |
| 2229 | } \ | 3232 | } \ |
| 2230 | static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3233 | static inline Type zig_msvc_atomicrmw_or_##ZigType(Type volatile* obj, Type value) { \ |
| 2231 | return _InterlockedOr##suffix(obj, value); \ | 3234 | return _InterlockedOr##suffix(obj, value); \ |
| 2232 | } \ | 3235 | } \ |
| 2233 | static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3236 | static inline Type zig_msvc_atomicrmw_xor_##ZigType(Type volatile* obj, Type value) { \ |
| 2234 | return _InterlockedXor##suffix(obj, value); \ | 3237 | return _InterlockedXor##suffix(obj, value); \ |
| 2235 | } \ | 3238 | } \ |
| 2236 | static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3239 | static inline Type zig_msvc_atomicrmw_and_##ZigType(Type volatile* obj, Type value) { \ |
| 2237 | return _InterlockedAnd##suffix(obj, value); \ | 3240 | return _InterlockedAnd##suffix(obj, value); \ |
| 2238 | } \ | 3241 | } \ |
| 2239 | static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3242 | static inline Type zig_msvc_atomicrmw_nand_##ZigType(Type volatile* obj, Type value) { \ |
| 2240 | bool success = false; \ | 3243 | bool success = false; \ |
| 2241 | zig_##Type new; \ | 3244 | Type new; \ |
| 2242 | zig_##Type prev; \ | 3245 | Type prev; \ |
| 2243 | while (!success) { \ | 3246 | while (!success) { \ |
| 2244 | prev = *obj; \ | 3247 | prev = *obj; \ |
| 2245 | new = ~(prev & value); \ | 3248 | new = ~(prev & value); \ |
| 2246 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 3249 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2247 | } \ | 3250 | } \ |
| 2248 | return prev; \ | 3251 | return prev; \ |
| 2249 | } \ | 3252 | } \ |
| 2250 | static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3253 | static inline Type zig_msvc_atomicrmw_min_##ZigType(Type volatile* obj, Type value) { \ |
| 2251 | bool success = false; \ | 3254 | bool success = false; \ |
| 2252 | zig_##Type new; \ | 3255 | Type new; \ |
| 2253 | zig_##Type prev; \ | 3256 | Type prev; \ |
| 2254 | while (!success) { \ | 3257 | while (!success) { \ |
| 2255 | prev = *obj; \ | 3258 | prev = *obj; \ |
| 2256 | new = value < prev ? value : prev; \ | 3259 | new = value < prev ? value : prev; \ |
| 2257 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 3260 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2258 | } \ | 3261 | } \ |
| 2259 | return prev; \ | 3262 | return prev; \ |
| 2260 | } \ | 3263 | } \ |
| 2261 | static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3264 | static inline Type zig_msvc_atomicrmw_max_##ZigType(Type volatile* obj, Type value) { \ |
| 2262 | bool success = false; \ | 3265 | bool success = false; \ |
| 2263 | zig_##Type new; \ | 3266 | Type new; \ |
| 2264 | zig_##Type prev; \ | 3267 | Type prev; \ |
| 2265 | while (!success) { \ | 3268 | while (!success) { \ |
| 2266 | prev = *obj; \ | 3269 | prev = *obj; \ |
| 2267 | new = value > prev ? value : prev; \ | 3270 | new = value > prev ? value : prev; \ |
| 2268 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 3271 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2269 | } \ | 3272 | } \ |
| 2270 | return prev; \ | 3273 | return prev; \ |
| 2271 | } \ | 3274 | } \ |
| 2272 | static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3275 | static inline void zig_msvc_atomic_store_##ZigType(Type volatile* obj, Type value) { \ |
| 2273 | _InterlockedExchange##suffix(obj, value); \ | 3276 | _InterlockedExchange##suffix(obj, value); \ |
| 2274 | } \ | 3277 | } \ |
| 2275 | static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ | 3278 | static inline Type zig_msvc_atomic_load_##ZigType(Type volatile* obj) { \ |
| 2276 | return _InterlockedOr##suffix(obj, 0); \ | 3279 | return _InterlockedOr##suffix(obj, 0); \ |
| 2277 | } | 3280 | } |
| 2278 | 3281 | ||
| 2279 | zig_msvc_atomics(u8, 8) | 3282 | zig_msvc_atomics( u8, uint8_t, 8) |
| 2280 | zig_msvc_atomics(i8, 8) | 3283 | zig_msvc_atomics( i8, int8_t, 8) |
| 2281 | zig_msvc_atomics(u16, 16) | 3284 | zig_msvc_atomics(u16, uint16_t, 16) |
| 2282 | zig_msvc_atomics(i16, 16) | 3285 | zig_msvc_atomics(i16, int16_t, 16) |
| 2283 | zig_msvc_atomics(u32, ) | 3286 | zig_msvc_atomics(u32, uint32_t, ) |
| 2284 | zig_msvc_atomics(i32, ) | 3287 | zig_msvc_atomics(i32, int32_t, ) |
| 2285 | 3288 | ||
| 2286 | #if _M_X64 | 3289 | #if _M_X64 |
| 2287 | zig_msvc_atomics(u64, 64) | 3290 | zig_msvc_atomics(u64, uint64_t, 64) |
| 2288 | zig_msvc_atomics(i64, 64) | 3291 | zig_msvc_atomics(i64, int64_t, 64) |
| 2289 | #endif | 3292 | #endif |
| 2290 | 3293 | ||
| 2291 | #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ | 3294 | #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ |
| 2292 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ | 3295 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ |
| 2293 | zig_##ReprType comparand = *((zig_##ReprType*)expected); \ | 3296 | ReprType exchange; \ |
| 2294 | zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \ | 3297 | ReprType comparand; \ |
| 2295 | bool exchanged = initial == comparand; \ | 3298 | ReprType initial; \ |
| 2296 | if (!exchanged) { \ | 3299 | bool success; \ |
| 2297 | *expected = *((zig_##Type*)&initial); \ | 3300 | memcpy(&comparand, expected, sizeof(comparand)); \ |
| 2298 | } \ | 3301 | memcpy(&exchange, &desired, sizeof(exchange)); \ |
| 2299 | return exchanged; \ | 3302 | initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \ |
| 3303 | success = initial == comparand; \ | ||
| 3304 | if (!success) memcpy(expected, &initial, sizeof(*expected)); \ | ||
| 3305 | return success; \ | ||
| 2300 | } \ | 3306 | } \ |
| 2301 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3307 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ |
| 2302 | zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \ | 3308 | ReprType repr; \ |
| 2303 | return *((zig_##Type*)&initial); \ | 3309 | ReprType initial; \ |
| 3310 | zig_##Type result; \ | ||
| 3311 | memcpy(&repr, &value, sizeof(repr)); \ | ||
| 3312 | initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \ | ||
| 3313 | memcpy(&result, &initial, sizeof(result)); \ | ||
| 3314 | return result; \ | ||
| 2304 | } \ | 3315 | } \ |
| 2305 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3316 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ |
| 2306 | bool success = false; \ | 3317 | ReprType repr; \ |
| 2307 | zig_##ReprType new; \ | 3318 | zig_##Type expected; \ |
| 2308 | zig_##Type prev; \ | 3319 | zig_##Type desired; \ |
| 2309 | while (!success) { \ | 3320 | repr = *(ReprType volatile*)obj; \ |
| 2310 | prev = *obj; \ | 3321 | memcpy(&expected, &repr, sizeof(expected)); \ |
| 2311 | new = prev + value; \ | 3322 | do { \ |
| 2312 | success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ | 3323 | desired = expected + value; \ |
| 2313 | } \ | 3324 | } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ |
| 2314 | return prev; \ | 3325 | return expected; \ |
| 2315 | } \ | 3326 | } \ |
| 2316 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 3327 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ |
| 2317 | bool success = false; \ | 3328 | ReprType repr; \ |
| 2318 | zig_##ReprType new; \ | 3329 | zig_##Type expected; \ |
| 2319 | zig_##Type prev; \ | 3330 | zig_##Type desired; \ |
| 2320 | while (!success) { \ | 3331 | repr = *(ReprType volatile*)obj; \ |
| 2321 | prev = *obj; \ | 3332 | memcpy(&expected, &repr, sizeof(expected)); \ |
| 2322 | new = prev - value; \ | 3333 | do { \ |
| 2323 | success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ | 3334 | desired = expected - value; \ |
| 2324 | } \ | 3335 | } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ |
| 2325 | return prev; \ | 3336 | return expected; \ |
| 2326 | } | 3337 | } |
| 2327 | 3338 | ||
| 2328 | zig_msvc_flt_atomics(f32, u32, ) | 3339 | zig_msvc_flt_atomics(f32, uint32_t, ) |
| 2329 | #if _M_X64 | 3340 | #if _M_X64 |
| 2330 | zig_msvc_flt_atomics(f64, u64, 64) | 3341 | zig_msvc_flt_atomics(f64, uint64_t, 64) |
| 2331 | #endif | 3342 | #endif |
| 2332 | 3343 | ||
| 2333 | #if _M_IX86 | 3344 | #if _M_IX86 |
| 2334 | static inline void zig_msvc_atomic_barrier() { | 3345 | static inline void zig_msvc_atomic_barrier() { |
| 2335 | zig_i32 barrier; | 3346 | int32_t barrier; |
| 2336 | __asm { | 3347 | __asm { |
| 2337 | xchg barrier, eax | 3348 | xchg barrier, eax |
| 2338 | } | 3349 | } |
| 2339 | } | 3350 | } |
| 2340 | 3351 | ||
| 2341 | static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) { | 3352 | static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, void* arg) { |
| 2342 | return _InterlockedExchangePointer(obj, arg); | 3353 | return _InterlockedExchangePointer(obj, arg); |
| 2343 | } | 3354 | } |
| 2344 | 3355 | ||
| 2345 | static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) { | 3356 | static inline void zig_msvc_atomic_store_p32(void** obj, void* arg) { |
| 2346 | _InterlockedExchangePointer(obj, arg); | 3357 | _InterlockedExchangePointer(obj, arg); |
| 2347 | } | 3358 | } |
| 2348 | 3359 | ||
| ... | @@ -2360,11 +3371,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir | ... | @@ -2360,11 +3371,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir |
| 2360 | return exchanged; | 3371 | return exchanged; |
| 2361 | } | 3372 | } |
| 2362 | #else /* _M_IX86 */ | 3373 | #else /* _M_IX86 */ |
| 2363 | static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) { | 3374 | static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, void* arg) { |
| 2364 | return _InterlockedExchangePointer(obj, arg); | 3375 | return _InterlockedExchangePointer(obj, arg); |
| 2365 | } | 3376 | } |
| 2366 | 3377 | ||
| 2367 | static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) { | 3378 | static inline void zig_msvc_atomic_store_p64(void** obj, void* arg) { |
| 2368 | _InterlockedExchangePointer(obj, arg); | 3379 | _InterlockedExchangePointer(obj, arg); |
| 2369 | } | 3380 | } |
| 2370 | 3381 | ||
| ... | @@ -2383,11 +3394,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir | ... | @@ -2383,11 +3394,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir |
| 2383 | } | 3394 | } |
| 2384 | 3395 | ||
| 2385 | static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { | 3396 | static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { |
| 2386 | return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected); | 3397 | return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (int64_t*)expected); |
| 2387 | } | 3398 | } |
| 2388 | 3399 | ||
| 2389 | static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { | 3400 | static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { |
| 2390 | return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected); | 3401 | return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (uint64_t*)expected); |
| 2391 | } | 3402 | } |
| 2392 | 3403 | ||
| 2393 | #define zig_msvc_atomics_128xchg(Type) \ | 3404 | #define zig_msvc_atomics_128xchg(Type) \ |
| ... | @@ -2429,7 +3440,7 @@ zig_msvc_atomics_128op(u128, max) | ... | @@ -2429,7 +3440,7 @@ zig_msvc_atomics_128op(u128, max) |
| 2429 | 3440 | ||
| 2430 | #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ | 3441 | #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ |
| 2431 | 3442 | ||
| 2432 | /* ========================= Special Case Intrinsics ========================= */ | 3443 | /* ======================== Special Case Intrinsics ========================= */ |
| 2433 | 3444 | ||
| 2434 | #if (_MSC_VER && _M_X64) || defined(__x86_64__) | 3445 | #if (_MSC_VER && _M_X64) || defined(__x86_64__) |
| 2435 | 3446 | ||
| ... | @@ -2459,8 +3470,8 @@ static inline void* zig_x86_windows_teb(void) { | ... | @@ -2459,8 +3470,8 @@ static inline void* zig_x86_windows_teb(void) { |
| 2459 | 3470 | ||
| 2460 | #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) | 3471 | #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) |
| 2461 | 3472 | ||
| 2462 | static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { | 3473 | static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) { |
| 2463 | zig_u32 cpu_info[4]; | 3474 | uint32_t cpu_info[4]; |
| 2464 | #if _MSC_VER | 3475 | #if _MSC_VER |
| 2465 | __cpuidex(cpu_info, leaf_id, subid); | 3476 | __cpuidex(cpu_info, leaf_id, subid); |
| 2466 | #else | 3477 | #else |
| ... | @@ -2472,12 +3483,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z | ... | @@ -2472,12 +3483,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z |
| 2472 | *edx = cpu_info[3]; | 3483 | *edx = cpu_info[3]; |
| 2473 | } | 3484 | } |
| 2474 | 3485 | ||
| 2475 | static inline zig_u32 zig_x86_get_xcr0(void) { | 3486 | static inline uint32_t zig_x86_get_xcr0(void) { |
| 2476 | #if _MSC_VER | 3487 | #if _MSC_VER |
| 2477 | return (zig_u32)_xgetbv(0); | 3488 | return (uint32_t)_xgetbv(0); |
| 2478 | #else | 3489 | #else |
| 2479 | zig_u32 eax; | 3490 | uint32_t eax; |
| 2480 | zig_u32 edx; | 3491 | uint32_t edx; |
| 2481 | __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); | 3492 | __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); |
| 2482 | return eax; | 3493 | return eax; |
| 2483 | #endif | 3494 | #endif |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ | |||
test/behavior.zig+1| ... | @@ -141,6 +141,7 @@ test { | ... | @@ -141,6 +141,7 @@ test { |
| 141 | _ = @import("behavior/bugs/13664.zig"); | 141 | _ = @import("behavior/bugs/13664.zig"); |
| 142 | _ = @import("behavior/bugs/13714.zig"); | 142 | _ = @import("behavior/bugs/13714.zig"); |
| 143 | _ = @import("behavior/bugs/13785.zig"); | 143 | _ = @import("behavior/bugs/13785.zig"); |
| 144 | _ = @import("behavior/bugs/14854.zig"); | ||
| 144 | _ = @import("behavior/byteswap.zig"); | 145 | _ = @import("behavior/byteswap.zig"); |
| 145 | _ = @import("behavior/byval_arg_var.zig"); | 146 | _ = @import("behavior/byval_arg_var.zig"); |
| 146 | _ = @import("behavior/call.zig"); | 147 | _ = @import("behavior/call.zig"); |
test/behavior/align.zig-1| ... | @@ -568,7 +568,6 @@ fn overaligned_fn() align(0x1000) i32 { | ... | @@ -568,7 +568,6 @@ fn overaligned_fn() align(0x1000) i32 { |
| 568 | } | 568 | } |
| 569 | 569 | ||
| 570 | test "comptime alloc alignment" { | 570 | test "comptime alloc alignment" { |
| 571 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 572 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 571 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 573 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 572 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 574 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 573 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
test/behavior/array.zig+18-11| ... | @@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize { | ... | @@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize { |
| 48 | test "array concat with undefined" { | 48 | test "array concat with undefined" { |
| 49 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 49 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 50 | 50 | ||
| 51 | { | 51 | const S = struct { |
| 52 | var array = "hello".* ++ @as([5]u8, undefined); | 52 | fn doTheTest() !void { |
| 53 | array[5..10].* = "world".*; | 53 | { |
| 54 | try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); | 54 | var array = "hello".* ++ @as([5]u8, undefined); |
| 55 | } | 55 | array[5..10].* = "world".*; |
| 56 | { | 56 | try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); |
| 57 | var array = @as([5]u8, undefined) ++ "world".*; | 57 | } |
| 58 | array[0..5].* = "hello".*; | 58 | { |
| 59 | try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); | 59 | var array = @as([5]u8, undefined) ++ "world".*; |
| 60 | } | 60 | array[0..5].* = "hello".*; |
| 61 | try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | }; | ||
| 65 | |||
| 66 | try S.doTheTest(); | ||
| 67 | comptime try S.doTheTest(); | ||
| 61 | } | 68 | } |
| 62 | 69 | ||
| 63 | test "array concat with tuple" { | 70 | test "array concat with tuple" { |
| ... | @@ -77,6 +84,7 @@ test "array concat with tuple" { | ... | @@ -77,6 +84,7 @@ test "array concat with tuple" { |
| 77 | } | 84 | } |
| 78 | 85 | ||
| 79 | test "array init with concat" { | 86 | test "array init with concat" { |
| 87 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 80 | const a = 'a'; | 88 | const a = 'a'; |
| 81 | var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; | 89 | var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; |
| 82 | try expect(std.mem.eql(u8, &i, "abcd")); | 90 | try expect(std.mem.eql(u8, &i, "abcd")); |
| ... | @@ -584,7 +592,6 @@ test "type coercion of anon struct literal to array" { | ... | @@ -584,7 +592,6 @@ test "type coercion of anon struct literal to array" { |
| 584 | test "type coercion of pointer to anon struct literal to pointer to array" { | 592 | test "type coercion of pointer to anon struct literal to pointer to array" { |
| 585 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 593 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 586 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 594 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 587 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 588 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 595 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 589 | 596 | ||
| 590 | const S = struct { | 597 | const S = struct { |
test/behavior/bit_shifting.zig-1| ... | @@ -63,7 +63,6 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt | ... | @@ -63,7 +63,6 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt |
| 63 | test "sharded table" { | 63 | test "sharded table" { |
| 64 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 64 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 65 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 65 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 66 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 67 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 66 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 68 | 67 | ||
| 69 | // realistic 16-way sharding | 68 | // realistic 16-way sharding |
test/behavior/bitcast.zig-3| ... | @@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" { | ... | @@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" { |
| 34 | 34 | ||
| 35 | test "@bitCast iX -> uX exotic integers" { | 35 | test "@bitCast iX -> uX exotic integers" { |
| 36 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 36 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 37 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 38 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 37 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 39 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 38 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 40 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 39 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | @@ -81,7 +80,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe | ... | @@ -81,7 +80,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe |
| 81 | 80 | ||
| 82 | test "bitcast uX to bytes" { | 81 | test "bitcast uX to bytes" { |
| 83 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 82 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 84 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 85 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 83 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 86 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 84 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 87 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 85 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | @@ -368,7 +366,6 @@ test "comptime @bitCast packed struct to int and back" { | ... | @@ -368,7 +366,6 @@ test "comptime @bitCast packed struct to int and back" { |
| 368 | } | 366 | } |
| 369 | 367 | ||
| 370 | test "comptime bitcast with fields following f80" { | 368 | test "comptime bitcast with fields following f80" { |
| 371 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 372 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 369 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 373 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 370 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 374 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 371 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
test/behavior/bitreverse.zig-4| ... | @@ -96,7 +96,6 @@ fn vector8() !void { | ... | @@ -96,7 +96,6 @@ fn vector8() !void { |
| 96 | 96 | ||
| 97 | test "bitReverse vectors u8" { | 97 | test "bitReverse vectors u8" { |
| 98 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 98 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 99 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 99 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 101 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 100 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 102 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 101 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -115,7 +114,6 @@ fn vector16() !void { | ... | @@ -115,7 +114,6 @@ fn vector16() !void { |
| 115 | 114 | ||
| 116 | test "bitReverse vectors u16" { | 115 | test "bitReverse vectors u16" { |
| 117 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 116 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 118 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 119 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 117 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 120 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 118 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 121 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 119 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -134,7 +132,6 @@ fn vector24() !void { | ... | @@ -134,7 +132,6 @@ fn vector24() !void { |
| 134 | 132 | ||
| 135 | test "bitReverse vectors u24" { | 133 | test "bitReverse vectors u24" { |
| 136 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 134 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 137 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 138 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 135 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 139 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 136 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 140 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 137 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -153,7 +150,6 @@ fn vector0() !void { | ... | @@ -153,7 +150,6 @@ fn vector0() !void { |
| 153 | 150 | ||
| 154 | test "bitReverse vectors u0" { | 151 | test "bitReverse vectors u0" { |
| 155 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 152 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 156 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 157 | 153 | ||
| 158 | comptime try vector0(); | 154 | comptime try vector0(); |
| 159 | try vector0(); | 155 | try vector0(); |
test/behavior/bugs/10147.zig-1| ... | @@ -6,7 +6,6 @@ test "test calling @clz on both vector and scalar inputs" { | ... | @@ -6,7 +6,6 @@ test "test calling @clz on both vector and scalar inputs" { |
| 6 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 6 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 10 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 11 | 10 | ||
| 12 | var x: u32 = 0x1; | 11 | var x: u32 = 0x1; |
test/behavior/bugs/1076.zig-1| ... | @@ -4,7 +4,6 @@ const mem = std.mem; | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
| 5 | 5 | ||
| 6 | test "comptime code should not modify constant data" { | 6 | test "comptime code should not modify constant data" { |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 9 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 10 | 9 |
test/behavior/bugs/11046.zig-1| ... | @@ -10,7 +10,6 @@ fn bar() !void { | ... | @@ -10,7 +10,6 @@ fn bar() !void { |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | test "fixed" { | 12 | test "fixed" { |
| 13 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 13 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 15 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 16 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 15 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/bugs/11787.zig-1| ... | @@ -4,7 +4,6 @@ const testing = std.testing; | ... | @@ -4,7 +4,6 @@ const testing = std.testing; |
| 4 | 4 | ||
| 5 | test "slicing zero length array field of struct" { | 5 | test "slicing zero length array field of struct" { |
| 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 8 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/12000.zig-1| ... | @@ -7,7 +7,6 @@ const T = struct { | ... | @@ -7,7 +7,6 @@ const T = struct { |
| 7 | 7 | ||
| 8 | test { | 8 | test { |
| 9 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 11 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 12 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 13 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/12119.zig-1| ... | @@ -6,7 +6,6 @@ const u32x8 = @Vector(8, u32); | ... | @@ -6,7 +6,6 @@ const u32x8 = @Vector(8, u32); |
| 6 | 6 | ||
| 7 | test { | 7 | test { |
| 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 10 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/12169.zig-1| ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | test { | 4 | test { |
| 5 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 5 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 6 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 7 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 6 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/12890.zig-1| ... | @@ -10,7 +10,6 @@ fn a(b: []u3, c: u3) void { | ... | @@ -10,7 +10,6 @@ fn a(b: []u3, c: u3) void { |
| 10 | } | 10 | } |
| 11 | test { | 11 | test { |
| 12 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 12 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 13 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 14 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 13 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 15 | 14 | ||
| 16 | var arr: [8]u3 = undefined; | 15 | var arr: [8]u3 = undefined; |
test/behavior/bugs/13113.zig-1| ... | @@ -8,7 +8,6 @@ const Foo = extern struct { | ... | @@ -8,7 +8,6 @@ const Foo = extern struct { |
| 8 | 8 | ||
| 9 | test { | 9 | test { |
| 10 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 12 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 13 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 12 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 13 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/1421.zig-1| ... | @@ -9,7 +9,6 @@ const S = struct { | ... | @@ -9,7 +9,6 @@ const S = struct { |
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | test "functions with return type required to be comptime are generic" { | 11 | test "functions with return type required to be comptime are generic" { |
| 12 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 13 | const ti = S.method(); | 12 | const ti = S.method(); |
| 14 | try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct); | 13 | try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct); |
| 15 | } | 14 | } |
test/behavior/bugs/14854.zig created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | const testing = @import("std").testing; | ||
| 2 | |||
| 3 | test { | ||
| 4 | try testing.expect(getGeneric(u8, getU8) == 123); | ||
| 5 | } | ||
| 6 | |||
| 7 | fn getU8() callconv(.C) u8 { | ||
| 8 | return 123; | ||
| 9 | } | ||
| 10 | |||
| 11 | fn getGeneric(comptime T: type, supplier: fn () callconv(.C) T) T { | ||
| 12 | return supplier(); | ||
| 13 | } | ||
test/behavior/bugs/1607.zig-1| ... | @@ -13,7 +13,6 @@ fn checkAddress(s: []const u8) !void { | ... | @@ -13,7 +13,6 @@ fn checkAddress(s: []const u8) !void { |
| 13 | test "slices pointing at the same address as global array." { | 13 | test "slices pointing at the same address as global array." { |
| 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 16 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 17 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 16 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 18 | try checkAddress(&a); | 17 | try checkAddress(&a); |
| 19 | comptime try checkAddress(&a); | 18 | comptime try checkAddress(&a); |
test/behavior/bugs/2622.zig-1| ... | @@ -4,7 +4,6 @@ var buf: []u8 = undefined; | ... | @@ -4,7 +4,6 @@ var buf: []u8 = undefined; |
| 4 | 4 | ||
| 5 | test "reslice of undefined global var slice" { | 5 | test "reslice of undefined global var slice" { |
| 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 8 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/2727.zig-1| ... | @@ -6,7 +6,6 @@ fn t() bool { | ... | @@ -6,7 +6,6 @@ fn t() bool { |
| 6 | 6 | ||
| 7 | test { | 7 | test { |
| 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 10 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/bugs/3742.zig-1| ... | @@ -37,7 +37,6 @@ pub const ArgSerializer = struct { | ... | @@ -37,7 +37,6 @@ pub const ArgSerializer = struct { |
| 37 | test "fixed" { | 37 | test "fixed" { |
| 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 39 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 39 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 40 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 41 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 40 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 42 | if (builtin.zig_backend == .stage2_llvm and | 41 | if (builtin.zig_backend == .stage2_llvm and |
| 43 | builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest; | 42 | builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest; |
test/behavior/bugs/394.zig-1| ... | @@ -11,7 +11,6 @@ const expect = @import("std").testing.expect; | ... | @@ -11,7 +11,6 @@ const expect = @import("std").testing.expect; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
| 12 | 12 | ||
| 13 | test "fixed" { | 13 | test "fixed" { |
| 14 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 15 | const x = S{ | 14 | const x = S{ |
| 16 | .x = 3, | 15 | .x = 3, |
| 17 | .y = E{ .B = 1 }, | 16 | .y = E{ .B = 1 }, |
test/behavior/bugs/421.zig-1| ... | @@ -2,7 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,7 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const expect = @import("std").testing.expect; | 2 | const expect = @import("std").testing.expect; |
| 3 | 3 | ||
| 4 | test "bitCast to array" { | 4 | test "bitCast to array" { |
| 5 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 5 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 7 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 6 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 8 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/bugs/5398.zig-1| ... | @@ -21,7 +21,6 @@ var renderable: Renderable = undefined; | ... | @@ -21,7 +21,6 @@ var renderable: Renderable = undefined; |
| 21 | test "assignment of field with padding" { | 21 | test "assignment of field with padding" { |
| 22 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 22 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 23 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 23 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 24 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 25 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 24 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 26 | 25 | ||
| 27 | renderable = Renderable{ | 26 | renderable = Renderable{ |
test/behavior/bugs/5487.zig-1| ... | @@ -12,7 +12,6 @@ pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_ty | ... | @@ -12,7 +12,6 @@ pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_ty |
| 12 | test "crash" { | 12 | test "crash" { |
| 13 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 13 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 15 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 16 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 15 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 17 | _ = io.multiWriter(.{writer()}); | 16 | _ = io.multiWriter(.{writer()}); |
| 18 | } | 17 | } |
test/behavior/bugs/656.zig-1| ... | @@ -13,7 +13,6 @@ const Value = struct { | ... | @@ -13,7 +13,6 @@ const Value = struct { |
| 13 | test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { | 13 | test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { |
| 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 15 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 15 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 16 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 17 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 16 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 18 | try foo(false, true); | 17 | try foo(false, true); |
| 19 | } | 18 | } |
test/behavior/bugs/6947.zig-1| ... | @@ -6,7 +6,6 @@ fn destroy(ptr: *void) void { | ... | @@ -6,7 +6,6 @@ fn destroy(ptr: *void) void { |
| 6 | 6 | ||
| 7 | test { | 7 | test { |
| 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 10 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 11 | 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | var x: u8 = 1; | ||
| 6 | |||
| 7 | // This excludes builtin functions that return void or noreturn that cannot be tested. | ||
| 8 | test { | ||
| 9 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 10 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 11 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 12 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 13 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 14 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 15 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO | ||
| 16 | |||
| 17 | var val: u8 = undefined; | ||
| 18 | try testing.expectEqual({}, @atomicStore(u8, &val, 0, .Unordered)); | ||
| 19 | try testing.expectEqual(void, @TypeOf(@breakpoint())); | ||
| 20 | try testing.expectEqual({}, @export(x, .{ .name = "x" })); | ||
| 21 | try testing.expectEqual({}, @fence(.Acquire)); | ||
| 22 | try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1), @intToPtr([*]u8, 1), 0)); | ||
| 23 | try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1), undefined, 0)); | ||
| 24 | try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); | ||
| 25 | try testing.expectEqual({}, @prefetch(&val, .{})); | ||
| 26 | try testing.expectEqual({}, @setAlignStack(16)); | ||
| 27 | try testing.expectEqual({}, @setCold(true)); | ||
| 28 | try testing.expectEqual({}, @setEvalBranchQuota(0)); | ||
| 29 | try testing.expectEqual({}, @setFloatMode(.Optimized)); | ||
| 30 | try testing.expectEqual({}, @setRuntimeSafety(true)); | ||
| 31 | try testing.expectEqual(noreturn, @TypeOf(if (true) @trap() else {})); | ||
| 32 | } | ||
test/behavior/byteswap.zig-4| ... | @@ -62,7 +62,6 @@ fn vector8() !void { | ... | @@ -62,7 +62,6 @@ fn vector8() !void { |
| 62 | 62 | ||
| 63 | test "@byteSwap vectors u8" { | 63 | test "@byteSwap vectors u8" { |
| 64 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 64 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 65 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 66 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 65 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 67 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 66 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 68 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 67 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -81,7 +80,6 @@ fn vector16() !void { | ... | @@ -81,7 +80,6 @@ fn vector16() !void { |
| 81 | 80 | ||
| 82 | test "@byteSwap vectors u16" { | 81 | test "@byteSwap vectors u16" { |
| 83 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 82 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 84 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 85 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 83 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 86 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 84 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 87 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 85 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -100,7 +98,6 @@ fn vector24() !void { | ... | @@ -100,7 +98,6 @@ fn vector24() !void { |
| 100 | 98 | ||
| 101 | test "@byteSwap vectors u24" { | 99 | test "@byteSwap vectors u24" { |
| 102 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 100 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 103 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 104 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 105 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 102 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 106 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 103 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -119,7 +116,6 @@ fn vector0() !void { | ... | @@ -119,7 +116,6 @@ fn vector0() !void { |
| 119 | 116 | ||
| 120 | test "@byteSwap vectors u0" { | 117 | test "@byteSwap vectors u0" { |
| 121 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 118 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 122 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 123 | 119 | ||
| 124 | comptime try vector0(); | 120 | comptime try vector0(); |
| 125 | try vector0(); | 121 | try vector0(); |
test/behavior/byval_arg_var.zig-1| ... | @@ -4,7 +4,6 @@ const builtin = @import("builtin"); | ... | @@ -4,7 +4,6 @@ const builtin = @import("builtin"); |
| 4 | var result: []const u8 = "wrong"; | 4 | var result: []const u8 = "wrong"; |
| 5 | 5 | ||
| 6 | test "pass string literal byvalue to a generic var param" { | 6 | test "pass string literal byvalue to a generic var param" { |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 9 | 8 | ||
| 10 | start(); | 9 | start(); |
test/behavior/call.zig-2| ... | @@ -21,7 +21,6 @@ test "super basic invocations" { | ... | @@ -21,7 +21,6 @@ test "super basic invocations" { |
| 21 | test "basic invocations" { | 21 | test "basic invocations" { |
| 22 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 22 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 23 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 23 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 25 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 24 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 26 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 25 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 27 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 26 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -56,7 +55,6 @@ test "basic invocations" { | ... | @@ -56,7 +55,6 @@ test "basic invocations" { |
| 56 | test "tuple parameters" { | 55 | test "tuple parameters" { |
| 57 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 56 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 58 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 57 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 59 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 60 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 58 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 61 | 59 | ||
| 62 | const add = struct { | 60 | const add = struct { |
test/behavior/cast.zig-10| ... | @@ -176,7 +176,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | ... | @@ -176,7 +176,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { |
| 176 | 176 | ||
| 177 | test "implicitly cast indirect pointer to maybe-indirect pointer" { | 177 | test "implicitly cast indirect pointer to maybe-indirect pointer" { |
| 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 179 | if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 180 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 179 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 181 | 180 | ||
| 182 | const S = struct { | 181 | const S = struct { |
| ... | @@ -318,7 +317,6 @@ test "peer result null and comptime_int" { | ... | @@ -318,7 +317,6 @@ test "peer result null and comptime_int" { |
| 318 | 317 | ||
| 319 | test "*const ?[*]const T to [*c]const [*c]const T" { | 318 | test "*const ?[*]const T to [*c]const [*c]const T" { |
| 320 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 319 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 321 | if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 322 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 320 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 323 | 321 | ||
| 324 | var array = [_]u8{ 'o', 'k' }; | 322 | var array = [_]u8{ 'o', 'k' }; |
| ... | @@ -588,7 +586,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void { | ... | @@ -588,7 +586,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void { |
| 588 | 586 | ||
| 589 | test "cast *[1][*]const u8 to [*]const ?[*]const u8" { | 587 | test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 590 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 588 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 591 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 592 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 589 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 593 | 590 | ||
| 594 | const window_name = [1][*]const u8{"window name"}; | 591 | const window_name = [1][*]const u8{"window name"}; |
| ... | @@ -598,7 +595,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { | ... | @@ -598,7 +595,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 598 | 595 | ||
| 599 | test "vector casts" { | 596 | test "vector casts" { |
| 600 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 597 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 601 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 602 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 598 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 603 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 599 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 604 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 600 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -928,7 +924,6 @@ test "peer cast *[N:x]T to *[N]T" { | ... | @@ -928,7 +924,6 @@ test "peer cast *[N:x]T to *[N]T" { |
| 928 | 924 | ||
| 929 | test "peer cast [*:x]T to [*]T" { | 925 | test "peer cast [*:x]T to [*]T" { |
| 930 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 926 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 931 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 932 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 927 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 933 | 928 | ||
| 934 | const S = struct { | 929 | const S = struct { |
| ... | @@ -949,7 +944,6 @@ test "peer cast [*:x]T to [*]T" { | ... | @@ -949,7 +944,6 @@ test "peer cast [*:x]T to [*]T" { |
| 949 | test "peer cast [:x]T to [*:x]T" { | 944 | test "peer cast [:x]T to [*:x]T" { |
| 950 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 945 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 951 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 946 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 952 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 953 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 947 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 954 | 948 | ||
| 955 | const S = struct { | 949 | const S = struct { |
| ... | @@ -990,7 +984,6 @@ test "peer type resolution implicit cast to return type" { | ... | @@ -990,7 +984,6 @@ test "peer type resolution implicit cast to return type" { |
| 990 | 984 | ||
| 991 | test "peer type resolution implicit cast to variable type" { | 985 | test "peer type resolution implicit cast to variable type" { |
| 992 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 986 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 993 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 994 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 987 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 995 | 988 | ||
| 996 | const S = struct { | 989 | const S = struct { |
| ... | @@ -1113,8 +1106,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { | ... | @@ -1113,8 +1106,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { |
| 1113 | } | 1106 | } |
| 1114 | 1107 | ||
| 1115 | test "compile time int to ptr of function" { | 1108 | test "compile time int to ptr of function" { |
| 1116 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1117 | |||
| 1118 | try foobar(FUNCTION_CONSTANT); | 1109 | try foobar(FUNCTION_CONSTANT); |
| 1119 | } | 1110 | } |
| 1120 | 1111 | ||
| ... | @@ -1506,7 +1497,6 @@ test "implicit cast from [:0]T to [*c]T" { | ... | @@ -1506,7 +1497,6 @@ test "implicit cast from [:0]T to [*c]T" { |
| 1506 | test "bitcast packed struct with u0" { | 1497 | test "bitcast packed struct with u0" { |
| 1507 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1498 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1508 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1499 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1509 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1510 | 1500 | ||
| 1511 | const S = packed struct(u2) { a: u0, b: u2 }; | 1501 | const S = packed struct(u2) { a: u0, b: u2 }; |
| 1512 | const s = @bitCast(S, @as(u2, 2)); | 1502 | const s = @bitCast(S, @as(u2, 2)); |
test/behavior/comptime_memory.zig+8| ... | @@ -412,3 +412,11 @@ test "bitcast packed union to integer" { | ... | @@ -412,3 +412,11 @@ test "bitcast packed union to integer" { |
| 412 | try testing.expectEqual(@as(u2, 2), cast_b); | 412 | try testing.expectEqual(@as(u2, 2), cast_b); |
| 413 | } | 413 | } |
| 414 | } | 414 | } |
| 415 | |||
| 416 | test "mutate entire slice at comptime" { | ||
| 417 | comptime { | ||
| 418 | var buf: [3]u8 = undefined; | ||
| 419 | const x: [2]u8 = .{ 1, 2 }; // Avoid RLS | ||
| 420 | buf[1..3].* = x; | ||
| 421 | } | ||
| 422 | } |
test/behavior/defer.zig-1| ... | @@ -50,7 +50,6 @@ fn testNestedFnErrDefer() anyerror!void { | ... | @@ -50,7 +50,6 @@ fn testNestedFnErrDefer() anyerror!void { |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | test "return variable while defer expression in scope to modify it" { | 52 | test "return variable while defer expression in scope to modify it" { |
| 53 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 54 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 53 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 55 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 54 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 56 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 55 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/enum.zig-2| ... | @@ -937,7 +937,6 @@ test "enum literal casting to error union with payload enum" { | ... | @@ -937,7 +937,6 @@ test "enum literal casting to error union with payload enum" { |
| 937 | } | 937 | } |
| 938 | 938 | ||
| 939 | test "constant enum initialization with differing sizes" { | 939 | test "constant enum initialization with differing sizes" { |
| 940 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 941 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 940 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 942 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 941 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 943 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 942 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -1137,7 +1136,6 @@ test "tag name functions are unique" { | ... | @@ -1137,7 +1136,6 @@ test "tag name functions are unique" { |
| 1137 | } | 1136 | } |
| 1138 | 1137 | ||
| 1139 | test "size of enum with only one tag which has explicit integer tag type" { | 1138 | test "size of enum with only one tag which has explicit integer tag type" { |
| 1140 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 1141 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 1139 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1142 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1140 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1143 | 1141 |
test/behavior/error.zig-10| ... | @@ -144,15 +144,11 @@ test "implicit cast to optional to error union to return result loc" { | ... | @@ -144,15 +144,11 @@ test "implicit cast to optional to error union to return result loc" { |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | test "fn returning empty error set can be passed as fn returning any error" { | 146 | test "fn returning empty error set can be passed as fn returning any error" { |
| 147 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 148 | |||
| 149 | entry(); | 147 | entry(); |
| 150 | comptime entry(); | 148 | comptime entry(); |
| 151 | } | 149 | } |
| 152 | 150 | ||
| 153 | test "fn returning empty error set can be passed as fn returning any error - pointer" { | 151 | test "fn returning empty error set can be passed as fn returning any error - pointer" { |
| 154 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 155 | |||
| 156 | entryPtr(); | 152 | entryPtr(); |
| 157 | comptime entryPtr(); | 153 | comptime entryPtr(); |
| 158 | } | 154 | } |
| ... | @@ -219,7 +215,6 @@ fn testErrorSetType() !void { | ... | @@ -219,7 +215,6 @@ fn testErrorSetType() !void { |
| 219 | } | 215 | } |
| 220 | 216 | ||
| 221 | test "explicit error set cast" { | 217 | test "explicit error set cast" { |
| 222 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 223 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 218 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 224 | 219 | ||
| 225 | try testExplicitErrorSetCast(Set1.A); | 220 | try testExplicitErrorSetCast(Set1.A); |
| ... | @@ -238,7 +233,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void { | ... | @@ -238,7 +233,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void { |
| 238 | } | 233 | } |
| 239 | 234 | ||
| 240 | test "comptime test error for empty error set" { | 235 | test "comptime test error for empty error set" { |
| 241 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 242 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 236 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 243 | 237 | ||
| 244 | try testComptimeTestErrorEmptySet(1234); | 238 | try testComptimeTestErrorEmptySet(1234); |
| ... | @@ -255,8 +249,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { | ... | @@ -255,8 +249,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { |
| 255 | } | 249 | } |
| 256 | 250 | ||
| 257 | test "comptime err to int of error set with only 1 possible value" { | 251 | test "comptime err to int of error set with only 1 possible value" { |
| 258 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 259 | |||
| 260 | testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); | 252 | testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); |
| 261 | comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); | 253 | comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); |
| 262 | } | 254 | } |
| ... | @@ -426,7 +418,6 @@ test "nested error union function call in optional unwrap" { | ... | @@ -426,7 +418,6 @@ test "nested error union function call in optional unwrap" { |
| 426 | } | 418 | } |
| 427 | 419 | ||
| 428 | test "return function call to error set from error union function" { | 420 | test "return function call to error set from error union function" { |
| 429 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 430 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 421 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 431 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 422 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 432 | 423 | ||
| ... | @@ -486,7 +477,6 @@ test "nested catch" { | ... | @@ -486,7 +477,6 @@ test "nested catch" { |
| 486 | } | 477 | } |
| 487 | 478 | ||
| 488 | test "function pointer with return type that is error union with payload which is pointer of parent struct" { | 479 | test "function pointer with return type that is error union with payload which is pointer of parent struct" { |
| 489 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 490 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 480 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 491 | 481 | ||
| 492 | const S = struct { | 482 | const S = struct { |
test/behavior/eval.zig-2| ... | @@ -138,7 +138,6 @@ test "pointer to type" { | ... | @@ -138,7 +138,6 @@ test "pointer to type" { |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | test "a type constructed in a global expression" { | 140 | test "a type constructed in a global expression" { |
| 141 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 142 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 141 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 143 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 142 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 144 | 143 | ||
| ... | @@ -1515,7 +1514,6 @@ test "x or true is comptime-known true" { | ... | @@ -1515,7 +1514,6 @@ test "x or true is comptime-known true" { |
| 1515 | } | 1514 | } |
| 1516 | 1515 | ||
| 1517 | test "non-optional and optional array elements concatenated" { | 1516 | test "non-optional and optional array elements concatenated" { |
| 1518 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1519 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1517 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1520 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1518 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1521 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1519 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
test/behavior/field_parent_ptr.zig+5| ... | @@ -3,6 +3,7 @@ const builtin = @import("builtin"); | ... | @@ -3,6 +3,7 @@ const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | test "@fieldParentPtr non-first field" { | 4 | test "@fieldParentPtr non-first field" { |
| 5 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 5 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 6 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 6 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 7 | try testParentFieldPtr(&foo.c); | 8 | try testParentFieldPtr(&foo.c); |
| 8 | comptime try testParentFieldPtr(&foo.c); | 9 | comptime try testParentFieldPtr(&foo.c); |
| ... | @@ -10,6 +11,7 @@ test "@fieldParentPtr non-first field" { | ... | @@ -10,6 +11,7 @@ test "@fieldParentPtr non-first field" { |
| 10 | 11 | ||
| 11 | test "@fieldParentPtr first field" { | 12 | test "@fieldParentPtr first field" { |
| 12 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 13 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 13 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 15 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 14 | try testParentFieldPtrFirst(&foo.a); | 16 | try testParentFieldPtrFirst(&foo.a); |
| 15 | comptime try testParentFieldPtrFirst(&foo.a); | 17 | comptime try testParentFieldPtrFirst(&foo.a); |
| ... | @@ -47,6 +49,7 @@ fn testParentFieldPtrFirst(a: *const bool) !void { | ... | @@ -47,6 +49,7 @@ fn testParentFieldPtrFirst(a: *const bool) !void { |
| 47 | 49 | ||
| 48 | test "@fieldParentPtr untagged union" { | 50 | test "@fieldParentPtr untagged union" { |
| 49 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 51 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 52 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 53 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 54 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 52 | 55 | ||
| ... | @@ -73,6 +76,7 @@ fn testFieldParentPtrUnion(c: *const i32) !void { | ... | @@ -73,6 +76,7 @@ fn testFieldParentPtrUnion(c: *const i32) !void { |
| 73 | 76 | ||
| 74 | test "@fieldParentPtr tagged union" { | 77 | test "@fieldParentPtr tagged union" { |
| 75 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 78 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 79 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 76 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 80 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 77 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 81 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 78 | 82 | ||
| ... | @@ -99,6 +103,7 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void { | ... | @@ -99,6 +103,7 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void { |
| 99 | 103 | ||
| 100 | test "@fieldParentPtr extern union" { | 104 | test "@fieldParentPtr extern union" { |
| 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 105 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 106 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 102 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 107 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 103 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 108 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 104 | 109 |
test/behavior/floatop.zig-19| ... | @@ -21,7 +21,6 @@ fn epsForType(comptime T: type) T { | ... | @@ -21,7 +21,6 @@ fn epsForType(comptime T: type) T { |
| 21 | 21 | ||
| 22 | test "floating point comparisons" { | 22 | test "floating point comparisons" { |
| 23 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 23 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 25 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 24 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 26 | 25 | ||
| 27 | try testFloatComparisons(); | 26 | try testFloatComparisons(); |
| ... | @@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void { | ... | @@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void { |
| 91 | 90 | ||
| 92 | test "negative f128 floatToInt at compile-time" { | 91 | test "negative f128 floatToInt at compile-time" { |
| 93 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 92 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 94 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 95 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 93 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 96 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 94 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 97 | 95 | ||
| ... | @@ -141,7 +139,6 @@ fn testSqrt() !void { | ... | @@ -141,7 +139,6 @@ fn testSqrt() !void { |
| 141 | 139 | ||
| 142 | test "@sqrt with vectors" { | 140 | test "@sqrt with vectors" { |
| 143 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 141 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 144 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 145 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 142 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 146 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 143 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 147 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 144 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -234,7 +231,6 @@ fn testSin() !void { | ... | @@ -234,7 +231,6 @@ fn testSin() !void { |
| 234 | 231 | ||
| 235 | test "@sin with vectors" { | 232 | test "@sin with vectors" { |
| 236 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 233 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 237 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 238 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 234 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 239 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 235 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 240 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 236 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -275,7 +271,6 @@ fn testCos() !void { | ... | @@ -275,7 +271,6 @@ fn testCos() !void { |
| 275 | 271 | ||
| 276 | test "@cos with vectors" { | 272 | test "@cos with vectors" { |
| 277 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 273 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 278 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 279 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 274 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 280 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 275 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 281 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 276 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -315,7 +310,6 @@ fn testExp() !void { | ... | @@ -315,7 +310,6 @@ fn testExp() !void { |
| 315 | 310 | ||
| 316 | test "@exp with vectors" { | 311 | test "@exp with vectors" { |
| 317 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 312 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 318 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 319 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 313 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 320 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 314 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 321 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 315 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -335,7 +329,6 @@ fn testExpWithVectors() !void { | ... | @@ -335,7 +329,6 @@ fn testExpWithVectors() !void { |
| 335 | 329 | ||
| 336 | test "@exp2" { | 330 | test "@exp2" { |
| 337 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 331 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 338 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 339 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 332 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 340 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 333 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 341 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 334 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -355,7 +348,6 @@ fn testExp2() !void { | ... | @@ -355,7 +348,6 @@ fn testExp2() !void { |
| 355 | 348 | ||
| 356 | test "@exp2" { | 349 | test "@exp2" { |
| 357 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 350 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 358 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 359 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 351 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 360 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 352 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 361 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 353 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -409,7 +401,6 @@ test "@log with @vectors" { | ... | @@ -409,7 +401,6 @@ test "@log with @vectors" { |
| 409 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 401 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 410 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 402 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 411 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 403 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 412 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 413 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 404 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 414 | 405 | ||
| 415 | { | 406 | { |
| ... | @@ -447,7 +438,6 @@ test "@log2 with vectors" { | ... | @@ -447,7 +438,6 @@ test "@log2 with vectors" { |
| 447 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 438 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 448 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 439 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 449 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 440 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 450 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 451 | // https://github.com/ziglang/zig/issues/13681 | 441 | // https://github.com/ziglang/zig/issues/13681 |
| 452 | if (builtin.zig_backend == .stage2_llvm and | 442 | if (builtin.zig_backend == .stage2_llvm and |
| 453 | builtin.cpu.arch == .aarch64 and | 443 | builtin.cpu.arch == .aarch64 and |
| ... | @@ -491,7 +481,6 @@ test "@log10 with vectors" { | ... | @@ -491,7 +481,6 @@ test "@log10 with vectors" { |
| 491 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 481 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 492 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 482 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 493 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 483 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 494 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 495 | 484 | ||
| 496 | comptime try testLog10WithVectors(); | 485 | comptime try testLog10WithVectors(); |
| 497 | try testLog10WithVectors(); | 486 | try testLog10WithVectors(); |
| ... | @@ -508,7 +497,6 @@ fn testLog10WithVectors() !void { | ... | @@ -508,7 +497,6 @@ fn testLog10WithVectors() !void { |
| 508 | 497 | ||
| 509 | test "@fabs" { | 498 | test "@fabs" { |
| 510 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 499 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 511 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 512 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 500 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 513 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 501 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 514 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 502 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -537,7 +525,6 @@ fn testFabs() !void { | ... | @@ -537,7 +525,6 @@ fn testFabs() !void { |
| 537 | 525 | ||
| 538 | test "@fabs with vectors" { | 526 | test "@fabs with vectors" { |
| 539 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 527 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 540 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 541 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 528 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 542 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 529 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 543 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 530 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -636,7 +623,6 @@ test "a third @fabs test, surely there should not be three fabs tests" { | ... | @@ -636,7 +623,6 @@ test "a third @fabs test, surely there should not be three fabs tests" { |
| 636 | 623 | ||
| 637 | test "@floor" { | 624 | test "@floor" { |
| 638 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 625 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 639 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 640 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 626 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 641 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 627 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 642 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 628 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -660,7 +646,6 @@ fn testFloor() !void { | ... | @@ -660,7 +646,6 @@ fn testFloor() !void { |
| 660 | 646 | ||
| 661 | test "@floor with vectors" { | 647 | test "@floor with vectors" { |
| 662 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 648 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 663 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 664 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 649 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 665 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 650 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 666 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 651 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -730,7 +715,6 @@ fn testFloorLegacy(comptime T: type, x: T) !void { | ... | @@ -730,7 +715,6 @@ fn testFloorLegacy(comptime T: type, x: T) !void { |
| 730 | 715 | ||
| 731 | test "@ceil" { | 716 | test "@ceil" { |
| 732 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 717 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 733 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 734 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 718 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 735 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 719 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 736 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 720 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -754,7 +738,6 @@ fn testCeil() !void { | ... | @@ -754,7 +738,6 @@ fn testCeil() !void { |
| 754 | 738 | ||
| 755 | test "@ceil with vectors" { | 739 | test "@ceil with vectors" { |
| 756 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 740 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 757 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 758 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 741 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 759 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 742 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 760 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 743 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -824,7 +807,6 @@ fn testCeilLegacy(comptime T: type, x: T) !void { | ... | @@ -824,7 +807,6 @@ fn testCeilLegacy(comptime T: type, x: T) !void { |
| 824 | 807 | ||
| 825 | test "@trunc" { | 808 | test "@trunc" { |
| 826 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 809 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 827 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 828 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 810 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 829 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 811 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 830 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 812 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -848,7 +830,6 @@ fn testTrunc() !void { | ... | @@ -848,7 +830,6 @@ fn testTrunc() !void { |
| 848 | 830 | ||
| 849 | test "@trunc with vectors" { | 831 | test "@trunc with vectors" { |
| 850 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 832 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 851 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 852 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 833 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 853 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 834 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 854 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 835 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
test/behavior/fn.zig-2| ... | @@ -275,7 +275,6 @@ test "implicit cast fn call result to optional in field result" { | ... | @@ -275,7 +275,6 @@ test "implicit cast fn call result to optional in field result" { |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | test "void parameters" { | 277 | test "void parameters" { |
| 278 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 279 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 278 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 280 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 279 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 281 | 280 | ||
| ... | @@ -300,7 +299,6 @@ fn acceptsString(foo: []u8) void { | ... | @@ -300,7 +299,6 @@ fn acceptsString(foo: []u8) void { |
| 300 | } | 299 | } |
| 301 | 300 | ||
| 302 | test "function pointers" { | 301 | test "function pointers" { |
| 303 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 304 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 302 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 305 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 303 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 306 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 304 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/for.zig-8| ... | @@ -110,7 +110,6 @@ test "basic for loop" { | ... | @@ -110,7 +110,6 @@ test "basic for loop" { |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | test "for with null and T peer types and inferred result location type" { | 112 | test "for with null and T peer types and inferred result location type" { |
| 113 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 114 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 113 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 115 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 114 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 116 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 115 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -152,7 +151,6 @@ test "2 break statements and an else" { | ... | @@ -152,7 +151,6 @@ test "2 break statements and an else" { |
| 152 | } | 151 | } |
| 153 | 152 | ||
| 154 | test "for loop with pointer elem var" { | 153 | test "for loop with pointer elem var" { |
| 155 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 156 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 154 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 157 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 155 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 158 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 156 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -198,7 +196,6 @@ test "for copies its payload" { | ... | @@ -198,7 +196,6 @@ test "for copies its payload" { |
| 198 | } | 196 | } |
| 199 | 197 | ||
| 200 | test "for on slice with allowzero ptr" { | 198 | test "for on slice with allowzero ptr" { |
| 201 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 202 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 199 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 203 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 200 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 204 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 201 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -253,7 +250,6 @@ test "for loop with else branch" { | ... | @@ -253,7 +250,6 @@ test "for loop with else branch" { |
| 253 | test "count over fixed range" { | 250 | test "count over fixed range" { |
| 254 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 251 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 255 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 252 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 256 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 257 | 253 | ||
| 258 | var sum: usize = 0; | 254 | var sum: usize = 0; |
| 259 | for (0..6) |i| { | 255 | for (0..6) |i| { |
| ... | @@ -266,7 +262,6 @@ test "count over fixed range" { | ... | @@ -266,7 +262,6 @@ test "count over fixed range" { |
| 266 | test "two counters" { | 262 | test "two counters" { |
| 267 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 263 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 268 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 264 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 269 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 270 | 265 | ||
| 271 | var sum: usize = 0; | 266 | var sum: usize = 0; |
| 272 | for (0..10, 10..20) |i, j| { | 267 | for (0..10, 10..20) |i, j| { |
| ... | @@ -280,7 +275,6 @@ test "two counters" { | ... | @@ -280,7 +275,6 @@ test "two counters" { |
| 280 | test "1-based counter and ptr to array" { | 275 | test "1-based counter and ptr to array" { |
| 281 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 276 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 282 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 277 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 283 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 284 | 278 | ||
| 285 | var ok: usize = 0; | 279 | var ok: usize = 0; |
| 286 | 280 | ||
| ... | @@ -401,7 +395,6 @@ test "raw pointer and counter" { | ... | @@ -401,7 +395,6 @@ test "raw pointer and counter" { |
| 401 | test "inline for with slice as the comptime-known" { | 395 | test "inline for with slice as the comptime-known" { |
| 402 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 396 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 403 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 397 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 404 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 405 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 398 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 406 | 399 | ||
| 407 | const comptime_slice = "hello"; | 400 | const comptime_slice = "hello"; |
| ... | @@ -432,7 +425,6 @@ test "inline for with slice as the comptime-known" { | ... | @@ -432,7 +425,6 @@ test "inline for with slice as the comptime-known" { |
| 432 | test "inline for with counter as the comptime-known" { | 425 | test "inline for with counter as the comptime-known" { |
| 433 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 426 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 434 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 427 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 435 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 436 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 428 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 437 | 429 | ||
| 438 | var runtime_slice = "hello"; | 430 | var runtime_slice = "hello"; |
test/behavior/generics.zig-3| ... | @@ -56,7 +56,6 @@ fn sameButWithFloats(a: f64, b: f64) f64 { | ... | @@ -56,7 +56,6 @@ fn sameButWithFloats(a: f64, b: f64) f64 { |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | test "fn with comptime args" { | 58 | test "fn with comptime args" { |
| 59 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 60 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 59 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 61 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 60 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 62 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 61 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -67,7 +66,6 @@ test "fn with comptime args" { | ... | @@ -67,7 +66,6 @@ test "fn with comptime args" { |
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | test "anytype params" { | 68 | test "anytype params" { |
| 70 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 71 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 69 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 72 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 70 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 73 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 71 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -93,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 { | ... | @@ -93,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 { |
| 93 | } | 91 | } |
| 94 | 92 | ||
| 95 | test "type constructed by comptime function call" { | 93 | test "type constructed by comptime function call" { |
| 96 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 97 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 94 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 98 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 95 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 99 | 96 |
test/behavior/if.zig-1| ... | @@ -78,7 +78,6 @@ test "const result loc, runtime if cond, else unreachable" { | ... | @@ -78,7 +78,6 @@ test "const result loc, runtime if cond, else unreachable" { |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | test "if copies its payload" { | 80 | test "if copies its payload" { |
| 81 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 82 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 81 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 83 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 82 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 84 | 83 |
test/behavior/inline_switch.zig-1| ... | @@ -46,7 +46,6 @@ const U = union(E) { a: void, b: u2, c: u3, d: u4 }; | ... | @@ -46,7 +46,6 @@ const U = union(E) { a: void, b: u2, c: u3, d: u4 }; |
| 46 | test "inline switch unions" { | 46 | test "inline switch unions" { |
| 47 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 47 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 48 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 48 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 49 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 49 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 51 | 50 | ||
| 52 | var x: U = .a; | 51 | var x: U = .a; |
test/behavior/int_div.zig+19| ... | @@ -91,3 +91,22 @@ fn mod(comptime T: type, a: T, b: T) T { | ... | @@ -91,3 +91,22 @@ fn mod(comptime T: type, a: T, b: T) T { |
| 91 | fn rem(comptime T: type, a: T, b: T) T { | 91 | fn rem(comptime T: type, a: T, b: T) T { |
| 92 | return @rem(a, b); | 92 | return @rem(a, b); |
| 93 | } | 93 | } |
| 94 | |||
| 95 | test "large integer division" { | ||
| 96 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 97 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 98 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 99 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 101 | |||
| 102 | { | ||
| 103 | var numerator: u256 = 99999999999999999997315645440; | ||
| 104 | var divisor: u256 = 10000000000000000000000000000; | ||
| 105 | try expect(numerator / divisor == 9); | ||
| 106 | } | ||
| 107 | { | ||
| 108 | var numerator: u256 = 99999999999999999999000000000000000000000; | ||
| 109 | var divisor: u256 = 10000000000000000000000000000000000000000; | ||
| 110 | try expect(numerator / divisor == 9); | ||
| 111 | } | ||
| 112 | } |
test/behavior/math.zig+6-3| ... | @@ -100,7 +100,6 @@ test "@clz vectors" { | ... | @@ -100,7 +100,6 @@ test "@clz vectors" { |
| 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 101 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 101 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 102 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 102 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 103 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 104 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 103 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 105 | 104 | ||
| 106 | try testClzVectors(); | 105 | try testClzVectors(); |
| ... | @@ -163,7 +162,6 @@ test "@ctz vectors" { | ... | @@ -163,7 +162,6 @@ test "@ctz vectors" { |
| 163 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 162 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 164 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 163 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 165 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 164 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 166 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 167 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 165 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 168 | 166 | ||
| 169 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { | 167 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { |
| ... | @@ -1526,7 +1524,6 @@ fn testNanEqNan(comptime F: type) !void { | ... | @@ -1526,7 +1524,6 @@ fn testNanEqNan(comptime F: type) !void { |
| 1526 | } | 1524 | } |
| 1527 | 1525 | ||
| 1528 | test "vector comparison" { | 1526 | test "vector comparison" { |
| 1529 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1530 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1527 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1531 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1528 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1532 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1529 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -1563,6 +1560,12 @@ test "signed zeros are represented properly" { | ... | @@ -1563,6 +1560,12 @@ test "signed zeros are represented properly" { |
| 1563 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1560 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1564 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1561 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1565 | 1562 | ||
| 1563 | if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and | ||
| 1564 | builtin.zig_backend == .stage2_c) | ||
| 1565 | { | ||
| 1566 | return error.SkipZigTest; | ||
| 1567 | } | ||
| 1568 | |||
| 1566 | const S = struct { | 1569 | const S = struct { |
| 1567 | fn doTheTest() !void { | 1570 | fn doTheTest() !void { |
| 1568 | try testOne(f16); | 1571 | try testOne(f16); |
test/behavior/maximum_minimum.zig-2| ... | @@ -25,7 +25,6 @@ test "@max" { | ... | @@ -25,7 +25,6 @@ test "@max" { |
| 25 | test "@max on vectors" { | 25 | test "@max on vectors" { |
| 26 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 26 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 27 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 27 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 28 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 29 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 28 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 30 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 29 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 31 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 30 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -75,7 +74,6 @@ test "@min for vectors" { | ... | @@ -75,7 +74,6 @@ test "@min for vectors" { |
| 75 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 74 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 76 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 75 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 77 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 76 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 78 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 79 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 77 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 80 | 78 | ||
| 81 | const S = struct { | 79 | const S = struct { |
test/behavior/member_func.zig-2| ... | @@ -27,7 +27,6 @@ const HasFuncs = struct { | ... | @@ -27,7 +27,6 @@ const HasFuncs = struct { |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | test "standard field calls" { | 29 | test "standard field calls" { |
| 30 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 31 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 30 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 32 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 31 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 33 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 32 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -71,7 +70,6 @@ test "standard field calls" { | ... | @@ -71,7 +70,6 @@ test "standard field calls" { |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | test "@field field calls" { | 72 | test "@field field calls" { |
| 74 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 75 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 73 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 76 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 74 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 77 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 75 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/muladd.zig+7-5| ... | @@ -100,7 +100,6 @@ fn vector16() !void { | ... | @@ -100,7 +100,6 @@ fn vector16() !void { |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | test "vector f16" { | 102 | test "vector f16" { |
| 103 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 104 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 103 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 105 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 104 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 106 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 105 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -124,7 +123,6 @@ fn vector32() !void { | ... | @@ -124,7 +123,6 @@ fn vector32() !void { |
| 124 | } | 123 | } |
| 125 | 124 | ||
| 126 | test "vector f32" { | 125 | test "vector f32" { |
| 127 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 128 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 126 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 129 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 127 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 130 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 128 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -148,7 +146,6 @@ fn vector64() !void { | ... | @@ -148,7 +146,6 @@ fn vector64() !void { |
| 148 | } | 146 | } |
| 149 | 147 | ||
| 150 | test "vector f64" { | 148 | test "vector f64" { |
| 151 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 152 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 149 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 153 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 150 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 154 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 151 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -171,7 +168,6 @@ fn vector80() !void { | ... | @@ -171,7 +168,6 @@ fn vector80() !void { |
| 171 | } | 168 | } |
| 172 | 169 | ||
| 173 | test "vector f80" { | 170 | test "vector f80" { |
| 174 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 175 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 171 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 176 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 172 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 177 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 173 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -195,13 +191,19 @@ fn vector128() !void { | ... | @@ -195,13 +191,19 @@ fn vector128() !void { |
| 195 | } | 191 | } |
| 196 | 192 | ||
| 197 | test "vector f128" { | 193 | test "vector f128" { |
| 198 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 199 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 194 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 200 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 195 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 201 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 196 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 202 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 197 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 203 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 198 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 204 | 199 | ||
| 200 | if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and | ||
| 201 | builtin.zig_backend == .stage2_c) | ||
| 202 | { | ||
| 203 | // https://github.com/ziglang/zig/issues/13876 | ||
| 204 | return error.SkipZigTest; | ||
| 205 | } | ||
| 206 | |||
| 205 | comptime try vector128(); | 207 | comptime try vector128(); |
| 206 | try vector128(); | 208 | try vector128(); |
| 207 | } | 209 | } |
test/behavior/null.zig-3| ... | @@ -50,7 +50,6 @@ test "rhs maybe unwrap return" { | ... | @@ -50,7 +50,6 @@ test "rhs maybe unwrap return" { |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | test "maybe return" { | 52 | test "maybe return" { |
| 53 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 54 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 53 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 55 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 54 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 56 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 55 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -71,7 +70,6 @@ fn foo(x: ?i32) ?bool { | ... | @@ -71,7 +70,6 @@ fn foo(x: ?i32) ?bool { |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | test "test null runtime" { | 72 | test "test null runtime" { |
| 74 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 75 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 73 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 76 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 74 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 77 | 75 | ||
| ... | @@ -184,7 +182,6 @@ const SillyStruct = struct { | ... | @@ -184,7 +182,6 @@ const SillyStruct = struct { |
| 184 | const here_is_a_null_literal = SillyStruct{ .context = null }; | 182 | const here_is_a_null_literal = SillyStruct{ .context = null }; |
| 185 | 183 | ||
| 186 | test "unwrap optional which is field of global var" { | 184 | test "unwrap optional which is field of global var" { |
| 187 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 188 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 185 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 189 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 186 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 190 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 187 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/optional.zig-1| ... | @@ -274,7 +274,6 @@ test "0-bit child type coerced to optional return ptr result location" { | ... | @@ -274,7 +274,6 @@ test "0-bit child type coerced to optional return ptr result location" { |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | test "0-bit child type coerced to optional" { | 276 | test "0-bit child type coerced to optional" { |
| 277 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 278 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 277 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 279 | 278 | ||
| 280 | const S = struct { | 279 | const S = struct { |
test/behavior/pointers.zig-2| ... | @@ -190,7 +190,6 @@ test "compare equality of optional and non-optional pointer" { | ... | @@ -190,7 +190,6 @@ test "compare equality of optional and non-optional pointer" { |
| 190 | test "allowzero pointer and slice" { | 190 | test "allowzero pointer and slice" { |
| 191 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 191 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 192 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 192 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 193 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 194 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 193 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 195 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 194 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 196 | 195 | ||
| ... | @@ -510,7 +509,6 @@ test "ptrToInt on a generic function" { | ... | @@ -510,7 +509,6 @@ test "ptrToInt on a generic function" { |
| 510 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 509 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 511 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 510 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 512 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 511 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 513 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 514 | 512 | ||
| 515 | const S = struct { | 513 | const S = struct { |
| 516 | fn generic(i: anytype) @TypeOf(i) { | 514 | fn generic(i: anytype) @TypeOf(i) { |
test/behavior/popcount.zig-1| ... | @@ -67,7 +67,6 @@ fn testPopCountIntegers() !void { | ... | @@ -67,7 +67,6 @@ fn testPopCountIntegers() !void { |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | test "@popCount vectors" { | 69 | test "@popCount vectors" { |
| 70 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 71 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 70 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 72 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 71 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 73 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 72 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
test/behavior/ptrcast.zig+2-1| ... | @@ -53,7 +53,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void { | ... | @@ -53,7 +53,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void { |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | test "reinterpret bytes of an array into an extern struct" { | 55 | test "reinterpret bytes of an array into an extern struct" { |
| 56 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 57 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 56 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 58 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 57 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 59 | 58 | ||
| ... | @@ -171,6 +170,7 @@ test "lower reinterpreted comptime field ptr" { | ... | @@ -171,6 +170,7 @@ test "lower reinterpreted comptime field ptr" { |
| 171 | 170 | ||
| 172 | test "reinterpret struct field at comptime" { | 171 | test "reinterpret struct field at comptime" { |
| 173 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 172 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 173 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 174 | 174 | ||
| 175 | const numNative = comptime Bytes.init(0x12345678); | 175 | const numNative = comptime Bytes.init(0x12345678); |
| 176 | if (native_endian != .Little) { | 176 | if (native_endian != .Little) { |
| ... | @@ -233,6 +233,7 @@ test "ptrcast of const integer has the correct object size" { | ... | @@ -233,6 +233,7 @@ test "ptrcast of const integer has the correct object size" { |
| 233 | test "implicit optional pointer to optional anyopaque pointer" { | 233 | test "implicit optional pointer to optional anyopaque pointer" { |
| 234 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 234 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 235 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 235 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 236 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 236 | 237 | ||
| 237 | var buf: [4]u8 = "aoeu".*; | 238 | var buf: [4]u8 = "aoeu".*; |
| 238 | var x: ?[*]u8 = &buf; | 239 | var x: ?[*]u8 = &buf; |
test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig-1| ... | @@ -5,7 +5,6 @@ const mem = std.mem; | ... | @@ -5,7 +5,6 @@ const mem = std.mem; |
| 5 | 5 | ||
| 6 | var ok: bool = false; | 6 | var ok: bool = false; |
| 7 | test "reference a variable in an if after an if in the 2nd switch prong" { | 7 | test "reference a variable in an if after an if in the 2nd switch prong" { |
| 8 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 9 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 11 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/reflection.zig-1| ... | @@ -27,7 +27,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 { | ... | @@ -27,7 +27,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 { |
| 27 | 27 | ||
| 28 | test "reflection: @field" { | 28 | test "reflection: @field" { |
| 29 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 29 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 30 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 31 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 30 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 32 | 31 | ||
| 33 | var f = Foo{ | 32 | var f = Foo{ |
test/behavior/select.zig-2| ... | @@ -4,7 +4,6 @@ const mem = std.mem; | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
| 5 | 5 | ||
| 6 | test "@select vectors" { | 6 | test "@select vectors" { |
| 7 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 8 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -33,7 +32,6 @@ fn selectVectors() !void { | ... | @@ -33,7 +32,6 @@ fn selectVectors() !void { |
| 33 | } | 32 | } |
| 34 | 33 | ||
| 35 | test "@select arrays" { | 34 | test "@select arrays" { |
| 36 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 37 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 35 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 38 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 36 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 39 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 37 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/shuffle.zig+1-4| ... | @@ -8,7 +8,6 @@ test "@shuffle int" { | ... | @@ -8,7 +8,6 @@ test "@shuffle int" { |
| 8 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 12 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 13 | 12 | ||
| 14 | const S = struct { | 13 | const S = struct { |
| ... | @@ -50,7 +49,6 @@ test "@shuffle bool 1" { | ... | @@ -50,7 +49,6 @@ test "@shuffle bool 1" { |
| 50 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 49 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 50 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 52 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 51 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 53 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 54 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 52 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 55 | 53 | ||
| 56 | const S = struct { | 54 | const S = struct { |
| ... | @@ -71,7 +69,6 @@ test "@shuffle bool 2" { | ... | @@ -71,7 +69,6 @@ test "@shuffle bool 2" { |
| 71 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 69 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 72 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 70 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 73 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 71 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 74 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 75 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 72 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 76 | 73 | ||
| 77 | if (builtin.zig_backend == .stage2_llvm) { | 74 | if (builtin.zig_backend == .stage2_llvm) { |
| ... | @@ -83,7 +80,7 @@ test "@shuffle bool 2" { | ... | @@ -83,7 +80,7 @@ test "@shuffle bool 2" { |
| 83 | fn doTheTest() !void { | 80 | fn doTheTest() !void { |
| 84 | var x: @Vector(3, bool) = [3]bool{ false, true, false }; | 81 | var x: @Vector(3, bool) = [3]bool{ false, true, false }; |
| 85 | var v: @Vector(2, bool) = [2]bool{ true, false }; | 82 | var v: @Vector(2, bool) = [2]bool{ true, false }; |
| 86 | const mask: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; | 83 | const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; |
| 87 | var res = @shuffle(bool, x, v, mask); | 84 | var res = @shuffle(bool, x, v, mask); |
| 88 | try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); | 85 | try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); |
| 89 | } | 86 | } |
test/behavior/sizeof_and_typeof.zig-3| ... | @@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" { | ... | @@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" { |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | test "@TypeOf() with multiple arguments" { | 20 | test "@TypeOf() with multiple arguments" { |
| 21 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 22 | { | 21 | { |
| 23 | var var_1: u32 = undefined; | 22 | var var_1: u32 = undefined; |
| 24 | var var_2: u8 = undefined; | 23 | var var_2: u8 = undefined; |
| ... | @@ -138,7 +137,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { | ... | @@ -138,7 +137,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { |
| 138 | } | 137 | } |
| 139 | 138 | ||
| 140 | test "@TypeOf() has no runtime side effects" { | 139 | test "@TypeOf() has no runtime side effects" { |
| 141 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 142 | const S = struct { | 140 | const S = struct { |
| 143 | fn foo(comptime T: type, ptr: *T) T { | 141 | fn foo(comptime T: type, ptr: *T) T { |
| 144 | ptr.* += 1; | 142 | ptr.* += 1; |
| ... | @@ -153,7 +151,6 @@ test "@TypeOf() has no runtime side effects" { | ... | @@ -153,7 +151,6 @@ test "@TypeOf() has no runtime side effects" { |
| 153 | 151 | ||
| 154 | test "branching logic inside @TypeOf" { | 152 | test "branching logic inside @TypeOf" { |
| 155 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 153 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 156 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 157 | const S = struct { | 154 | const S = struct { |
| 158 | var data: i32 = 0; | 155 | var data: i32 = 0; |
| 159 | fn foo() anyerror!i32 { | 156 | fn foo() anyerror!i32 { |
test/behavior/slice.zig+21-9| ... | @@ -119,7 +119,6 @@ test "slice of type" { | ... | @@ -119,7 +119,6 @@ test "slice of type" { |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | test "generic malloc free" { | 121 | test "generic malloc free" { |
| 122 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 123 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 122 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 124 | 123 | ||
| 125 | const a = memAlloc(u8, 10) catch unreachable; | 124 | const a = memAlloc(u8, 10) catch unreachable; |
| ... | @@ -171,7 +170,6 @@ test "comptime pointer cast array and then slice" { | ... | @@ -171,7 +170,6 @@ test "comptime pointer cast array and then slice" { |
| 171 | 170 | ||
| 172 | test "slicing zero length array" { | 171 | test "slicing zero length array" { |
| 173 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 172 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 174 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 175 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 173 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 176 | 174 | ||
| 177 | const s1 = ""[0..]; | 175 | const s1 = ""[0..]; |
| ... | @@ -185,8 +183,6 @@ test "slicing zero length array" { | ... | @@ -185,8 +183,6 @@ test "slicing zero length array" { |
| 185 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; | 183 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; |
| 186 | const y = x[0x100..]; | 184 | const y = x[0x100..]; |
| 187 | test "compile time slice of pointer to hard coded address" { | 185 | test "compile time slice of pointer to hard coded address" { |
| 188 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 189 | |||
| 190 | try expect(@ptrToInt(x) == 0x1000); | 186 | try expect(@ptrToInt(x) == 0x1000); |
| 191 | try expect(x.len == 0x500); | 187 | try expect(x.len == 0x500); |
| 192 | 188 | ||
| ... | @@ -231,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { | ... | @@ -231,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { |
| 231 | 227 | ||
| 232 | test "C pointer" { | 228 | test "C pointer" { |
| 233 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 229 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 230 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 234 | 231 | ||
| 235 | var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; | 232 | var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; |
| 236 | var len: u32 = 10; | 233 | var len: u32 = 10; |
| ... | @@ -342,7 +339,6 @@ test "@ptrCast slice to pointer" { | ... | @@ -342,7 +339,6 @@ test "@ptrCast slice to pointer" { |
| 342 | } | 339 | } |
| 343 | 340 | ||
| 344 | test "slice syntax resulting in pointer-to-array" { | 341 | test "slice syntax resulting in pointer-to-array" { |
| 345 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 346 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 342 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 347 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 343 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 348 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 344 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -477,7 +473,6 @@ test "slice syntax resulting in pointer-to-array" { | ... | @@ -477,7 +473,6 @@ test "slice syntax resulting in pointer-to-array" { |
| 477 | } | 473 | } |
| 478 | 474 | ||
| 479 | test "slice pointer-to-array null terminated" { | 475 | test "slice pointer-to-array null terminated" { |
| 480 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 481 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 476 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 482 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 477 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 483 | 478 | ||
| ... | @@ -497,7 +492,6 @@ test "slice pointer-to-array null terminated" { | ... | @@ -497,7 +492,6 @@ test "slice pointer-to-array null terminated" { |
| 497 | } | 492 | } |
| 498 | 493 | ||
| 499 | test "slice pointer-to-array zero length" { | 494 | test "slice pointer-to-array zero length" { |
| 500 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 501 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 495 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 502 | 496 | ||
| 503 | comptime { | 497 | comptime { |
| ... | @@ -530,7 +524,6 @@ test "slice pointer-to-array zero length" { | ... | @@ -530,7 +524,6 @@ test "slice pointer-to-array zero length" { |
| 530 | } | 524 | } |
| 531 | 525 | ||
| 532 | test "type coercion of pointer to anon struct literal to pointer to slice" { | 526 | test "type coercion of pointer to anon struct literal to pointer to slice" { |
| 533 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 534 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 527 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 535 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 528 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 536 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 529 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -730,7 +723,6 @@ test "slice with dereferenced value" { | ... | @@ -730,7 +723,6 @@ test "slice with dereferenced value" { |
| 730 | 723 | ||
| 731 | test "empty slice ptr is non null" { | 724 | test "empty slice ptr is non null" { |
| 732 | if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest; // TODO | 725 | 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 | ||
| 734 | 726 | ||
| 735 | const empty_slice: []u8 = &[_]u8{}; | 727 | const empty_slice: []u8 = &[_]u8{}; |
| 736 | const p: [*]u8 = empty_slice.ptr + 0; | 728 | const p: [*]u8 = empty_slice.ptr + 0; |
| ... | @@ -747,3 +739,23 @@ test "slice decays to many pointer" { | ... | @@ -747,3 +739,23 @@ test "slice decays to many pointer" { |
| 747 | const p: [*:0]const u8 = buf[0..7 :0]; | 739 | const p: [*:0]const u8 = buf[0..7 :0]; |
| 748 | try expectEqualStrings(buf[0..7], std.mem.span(p)); | 740 | try expectEqualStrings(buf[0..7], std.mem.span(p)); |
| 749 | } | 741 | } |
| 742 | |||
| 743 | test "write through pointer to optional slice arg" { | ||
| 744 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 745 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 746 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 747 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 748 | |||
| 749 | const S = struct { | ||
| 750 | fn bar(foo: *?[]const u8) !void { | ||
| 751 | foo.* = try baz(); | ||
| 752 | } | ||
| 753 | |||
| 754 | fn baz() ![]const u8 { | ||
| 755 | return "ok"; | ||
| 756 | } | ||
| 757 | }; | ||
| 758 | var foo: ?[]const u8 = null; | ||
| 759 | try S.bar(&foo); | ||
| 760 | try expectEqualStrings(foo.?, "ok"); | ||
| 761 | } |
test/behavior/struct.zig-5| ... | @@ -342,7 +342,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { | ... | @@ -342,7 +342,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { |
| 342 | test "self-referencing struct via array member" { | 342 | test "self-referencing struct via array member" { |
| 343 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 343 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 344 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 344 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 345 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 346 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 345 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 347 | 346 | ||
| 348 | const T = struct { | 347 | const T = struct { |
| ... | @@ -536,7 +535,6 @@ test "implicit cast packed struct field to const ptr" { | ... | @@ -536,7 +535,6 @@ test "implicit cast packed struct field to const ptr" { |
| 536 | } | 535 | } |
| 537 | 536 | ||
| 538 | test "zero-bit field in packed struct" { | 537 | test "zero-bit field in packed struct" { |
| 539 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 540 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 538 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 541 | 539 | ||
| 542 | const S = packed struct { | 540 | const S = packed struct { |
| ... | @@ -719,7 +717,6 @@ test "pointer to packed struct member in a stack variable" { | ... | @@ -719,7 +717,6 @@ test "pointer to packed struct member in a stack variable" { |
| 719 | } | 717 | } |
| 720 | 718 | ||
| 721 | test "packed struct with u0 field access" { | 719 | test "packed struct with u0 field access" { |
| 722 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 723 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 720 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 724 | 721 | ||
| 725 | const S = packed struct { | 722 | const S = packed struct { |
| ... | @@ -1018,7 +1015,6 @@ test "type coercion of anon struct literal to struct" { | ... | @@ -1018,7 +1015,6 @@ test "type coercion of anon struct literal to struct" { |
| 1018 | } | 1015 | } |
| 1019 | 1016 | ||
| 1020 | test "type coercion of pointer to anon struct literal to pointer to struct" { | 1017 | test "type coercion of pointer to anon struct literal to pointer to struct" { |
| 1021 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1022 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1018 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1023 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1019 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1024 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1020 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -1088,7 +1084,6 @@ test "packed struct with undefined initializers" { | ... | @@ -1088,7 +1084,6 @@ test "packed struct with undefined initializers" { |
| 1088 | } | 1084 | } |
| 1089 | 1085 | ||
| 1090 | test "for loop over pointers to struct, getting field from struct pointer" { | 1086 | test "for loop over pointers to struct, getting field from struct pointer" { |
| 1091 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1092 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1087 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1093 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1088 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1094 | 1089 |
test/behavior/struct_contains_null_ptr_itself.zig-1| ... | @@ -4,7 +4,6 @@ const builtin = @import("builtin"); | ... | @@ -4,7 +4,6 @@ const builtin = @import("builtin"); |
| 4 | 4 | ||
| 5 | test "struct contains null pointer which contains original struct" { | 5 | test "struct contains null pointer which contains original struct" { |
| 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 9 | 8 | ||
| 10 | var x: ?*NodeLineComment = null; | 9 | var x: ?*NodeLineComment = null; |
test/behavior/switch.zig-2| ... | @@ -403,7 +403,6 @@ fn return_a_number() anyerror!i32 { | ... | @@ -403,7 +403,6 @@ fn return_a_number() anyerror!i32 { |
| 403 | } | 403 | } |
| 404 | 404 | ||
| 405 | test "switch on integer with else capturing expr" { | 405 | test "switch on integer with else capturing expr" { |
| 406 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 407 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 406 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 408 | 407 | ||
| 409 | const S = struct { | 408 | const S = struct { |
| ... | @@ -577,7 +576,6 @@ test "switch prongs with cases with identical payload types" { | ... | @@ -577,7 +576,6 @@ test "switch prongs with cases with identical payload types" { |
| 577 | } | 576 | } |
| 578 | 577 | ||
| 579 | test "switch on pointer type" { | 578 | test "switch on pointer type" { |
| 580 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 581 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 579 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 582 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 580 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 583 | 581 |
test/behavior/threadlocal.zig-2| ... | @@ -4,7 +4,6 @@ const expect = std.testing.expect; | ... | @@ -4,7 +4,6 @@ const expect = std.testing.expect; |
| 4 | 4 | ||
| 5 | test "thread local variable" { | 5 | test "thread local variable" { |
| 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { | 9 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { |
| ... | @@ -40,7 +39,6 @@ threadlocal var buffer: [11]u8 = undefined; | ... | @@ -40,7 +39,6 @@ threadlocal var buffer: [11]u8 = undefined; |
| 40 | 39 | ||
| 41 | test "reference a global threadlocal variable" { | 40 | test "reference a global threadlocal variable" { |
| 42 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 41 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 43 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 44 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 42 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 45 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 43 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 46 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { | 44 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { |
test/behavior/translate_c_macros.zig-1| ... | @@ -79,7 +79,6 @@ test "casting to union with a macro" { | ... | @@ -79,7 +79,6 @@ test "casting to union with a macro" { |
| 79 | 79 | ||
| 80 | test "casting or calling a value with a paren-surrounded macro" { | 80 | test "casting or calling a value with a paren-surrounded macro" { |
| 81 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 81 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 82 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 83 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 82 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 84 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 83 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 85 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 84 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/truncate.zig-1| ... | @@ -60,7 +60,6 @@ test "truncate on comptime integer" { | ... | @@ -60,7 +60,6 @@ test "truncate on comptime integer" { |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | test "truncate on vectors" { | 62 | test "truncate on vectors" { |
| 63 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 64 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 63 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 65 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 64 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 66 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 65 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
test/behavior/tuple.zig+19-1| ... | @@ -126,7 +126,6 @@ test "tuple initializer for var" { | ... | @@ -126,7 +126,6 @@ test "tuple initializer for var" { |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | test "array-like initializer for tuple types" { | 128 | test "array-like initializer for tuple types" { |
| 129 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 130 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 129 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 131 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 130 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 132 | 131 | ||
| ... | @@ -397,3 +396,22 @@ test "nested runtime conditionals in tuple initializer" { | ... | @@ -397,3 +396,22 @@ test "nested runtime conditionals in tuple initializer" { |
| 397 | }; | 396 | }; |
| 398 | try expectEqualStrings("up", x[0]); | 397 | try expectEqualStrings("up", x[0]); |
| 399 | } | 398 | } |
| 399 | |||
| 400 | test "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 | |||
| 413 | test "sentinel slice in tuple" { | ||
| 414 | const S = struct { [:0]const u8 }; | ||
| 415 | |||
| 416 | _ = S; | ||
| 417 | } |
test/behavior/type.zig-4| ... | @@ -449,8 +449,6 @@ test "Type.Union" { | ... | @@ -449,8 +449,6 @@ test "Type.Union" { |
| 449 | } | 449 | } |
| 450 | 450 | ||
| 451 | test "Type.Union from Type.Enum" { | 451 | test "Type.Union from Type.Enum" { |
| 452 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 453 | |||
| 454 | const Tag = @Type(.{ | 452 | const Tag = @Type(.{ |
| 455 | .Enum = .{ | 453 | .Enum = .{ |
| 456 | .tag_type = u0, | 454 | .tag_type = u0, |
| ... | @@ -475,8 +473,6 @@ test "Type.Union from Type.Enum" { | ... | @@ -475,8 +473,6 @@ test "Type.Union from Type.Enum" { |
| 475 | } | 473 | } |
| 476 | 474 | ||
| 477 | test "Type.Union from regular enum" { | 475 | test "Type.Union from regular enum" { |
| 478 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 479 | |||
| 480 | const E = enum { working_as_expected }; | 476 | const E = enum { working_as_expected }; |
| 481 | const T = @Type(.{ | 477 | const T = @Type(.{ |
| 482 | .Union = .{ | 478 | .Union = .{ |
test/behavior/type_info.zig-2| ... | @@ -509,7 +509,6 @@ test "type info for async frames" { | ... | @@ -509,7 +509,6 @@ test "type info for async frames" { |
| 509 | } | 509 | } |
| 510 | 510 | ||
| 511 | test "Declarations are returned in declaration order" { | 511 | test "Declarations are returned in declaration order" { |
| 512 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 513 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 512 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 514 | 513 | ||
| 515 | const S = struct { | 514 | const S = struct { |
| ... | @@ -532,7 +531,6 @@ test "Struct.is_tuple for anon list literal" { | ... | @@ -532,7 +531,6 @@ test "Struct.is_tuple for anon list literal" { |
| 532 | } | 531 | } |
| 533 | 532 | ||
| 534 | test "Struct.is_tuple for anon struct literal" { | 533 | test "Struct.is_tuple for anon struct literal" { |
| 535 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 536 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 534 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 537 | 535 | ||
| 538 | const info = @typeInfo(@TypeOf(.{ .a = 0 })); | 536 | const info = @typeInfo(@TypeOf(.{ .a = 0 })); |
test/behavior/union.zig-11| ... | @@ -11,7 +11,6 @@ const FooWithFloats = union { | ... | @@ -11,7 +11,6 @@ const FooWithFloats = union { |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | test "basic unions with floats" { | 13 | test "basic unions with floats" { |
| 14 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 15 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 16 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 17 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 16 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -215,7 +214,6 @@ const Payload = union(Letter) { | ... | @@ -215,7 +214,6 @@ const Payload = union(Letter) { |
| 215 | }; | 214 | }; |
| 216 | 215 | ||
| 217 | test "union with specified enum tag" { | 216 | test "union with specified enum tag" { |
| 218 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 219 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 217 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 220 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 218 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 221 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 219 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -225,7 +223,6 @@ test "union with specified enum tag" { | ... | @@ -225,7 +223,6 @@ test "union with specified enum tag" { |
| 225 | } | 223 | } |
| 226 | 224 | ||
| 227 | test "packed union generates correctly aligned type" { | 225 | test "packed union generates correctly aligned type" { |
| 228 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 229 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 226 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 230 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 227 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 231 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 228 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -379,7 +376,6 @@ test "union with only 1 field which is void should be zero bits" { | ... | @@ -379,7 +376,6 @@ test "union with only 1 field which is void should be zero bits" { |
| 379 | } | 376 | } |
| 380 | 377 | ||
| 381 | test "tagged union initialization with runtime void" { | 378 | test "tagged union initialization with runtime void" { |
| 382 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 383 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 379 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 384 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 380 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 385 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 381 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -400,7 +396,6 @@ fn testTaggedUnionInit(x: anytype) bool { | ... | @@ -400,7 +396,6 @@ fn testTaggedUnionInit(x: anytype) bool { |
| 400 | pub const UnionEnumNoPayloads = union(enum) { A, B }; | 396 | pub const UnionEnumNoPayloads = union(enum) { A, B }; |
| 401 | 397 | ||
| 402 | test "tagged union with no payloads" { | 398 | test "tagged union with no payloads" { |
| 403 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 404 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 399 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 405 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 400 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 406 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 401 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -466,7 +461,6 @@ pub const FooUnion = union(enum) { | ... | @@ -466,7 +461,6 @@ pub const FooUnion = union(enum) { |
| 466 | var glbl_array: [2]FooUnion = undefined; | 461 | var glbl_array: [2]FooUnion = undefined; |
| 467 | 462 | ||
| 468 | test "initialize global array of union" { | 463 | test "initialize global array of union" { |
| 469 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 470 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 464 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 471 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 465 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 472 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 466 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -494,7 +488,6 @@ test "update the tag value for zero-sized unions" { | ... | @@ -494,7 +488,6 @@ test "update the tag value for zero-sized unions" { |
| 494 | 488 | ||
| 495 | test "union initializer generates padding only if needed" { | 489 | test "union initializer generates padding only if needed" { |
| 496 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 490 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 497 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 498 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 491 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 499 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 492 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 500 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 493 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -561,7 +554,6 @@ const FooNoVoid = union(enum) { | ... | @@ -561,7 +554,6 @@ const FooNoVoid = union(enum) { |
| 561 | const Baz = enum { A, B, C, D }; | 554 | const Baz = enum { A, B, C, D }; |
| 562 | 555 | ||
| 563 | test "tagged union type" { | 556 | test "tagged union type" { |
| 564 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 565 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 557 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 566 | 558 | ||
| 567 | const foo1 = TaggedFoo{ .One = 13 }; | 559 | const foo1 = TaggedFoo{ .One = 13 }; |
| ... | @@ -598,7 +590,6 @@ fn returnAnInt(x: i32) TaggedFoo { | ... | @@ -598,7 +590,6 @@ fn returnAnInt(x: i32) TaggedFoo { |
| 598 | } | 590 | } |
| 599 | 591 | ||
| 600 | test "tagged union with all void fields but a meaningful tag" { | 592 | test "tagged union with all void fields but a meaningful tag" { |
| 601 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 602 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 593 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 603 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 594 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 604 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 595 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -777,7 +768,6 @@ fn Setter(comptime attr: Attribute) type { | ... | @@ -777,7 +768,6 @@ fn Setter(comptime attr: Attribute) type { |
| 777 | } | 768 | } |
| 778 | 769 | ||
| 779 | test "return union init with void payload" { | 770 | test "return union init with void payload" { |
| 780 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 781 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 771 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 782 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 772 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 783 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 773 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| ... | @@ -913,7 +903,6 @@ test "extern union doesn't trigger field check at comptime" { | ... | @@ -913,7 +903,6 @@ test "extern union doesn't trigger field check at comptime" { |
| 913 | test "anonymous union literal syntax" { | 903 | test "anonymous union literal syntax" { |
| 914 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 904 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 915 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 905 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 916 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 917 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 906 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 918 | 907 | ||
| 919 | const S = struct { | 908 | const S = struct { |
test/behavior/union_with_members.zig-1| ... | @@ -18,7 +18,6 @@ const ET = union(enum) { | ... | @@ -18,7 +18,6 @@ const ET = union(enum) { |
| 18 | 18 | ||
| 19 | test "enum with members" { | 19 | test "enum with members" { |
| 20 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 20 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 21 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 22 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 21 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 23 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 22 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 23 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/vector.zig+17-37| ... | @@ -25,7 +25,6 @@ test "implicit cast vector to array - bool" { | ... | @@ -25,7 +25,6 @@ test "implicit cast vector to array - bool" { |
| 25 | 25 | ||
| 26 | test "vector wrap operators" { | 26 | test "vector wrap operators" { |
| 27 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 27 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 28 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 29 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 28 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 30 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 29 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 31 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 30 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -48,7 +47,6 @@ test "vector wrap operators" { | ... | @@ -48,7 +47,6 @@ test "vector wrap operators" { |
| 48 | 47 | ||
| 49 | test "vector bin compares with mem.eql" { | 48 | test "vector bin compares with mem.eql" { |
| 50 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 49 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 51 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 52 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 50 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 53 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 54 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 52 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -93,12 +91,18 @@ test "vector int operators" { | ... | @@ -93,12 +91,18 @@ test "vector int operators" { |
| 93 | 91 | ||
| 94 | test "vector float operators" { | 92 | test "vector float operators" { |
| 95 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 93 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 96 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 97 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 94 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 98 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 95 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 99 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 96 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 100 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 97 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 101 | 98 | ||
| 99 | if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64 and | ||
| 100 | builtin.zig_backend == .stage2_c) | ||
| 101 | { | ||
| 102 | // https://github.com/ziglang/zig/issues/13876 | ||
| 103 | return error.SkipZigTest; | ||
| 104 | } | ||
| 105 | |||
| 102 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { | 106 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { |
| 103 | const S = struct { | 107 | const S = struct { |
| 104 | fn doTheTest() !void { | 108 | fn doTheTest() !void { |
| ... | @@ -117,7 +121,6 @@ test "vector float operators" { | ... | @@ -117,7 +121,6 @@ test "vector float operators" { |
| 117 | 121 | ||
| 118 | test "vector bit operators" { | 122 | test "vector bit operators" { |
| 119 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 123 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 120 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 121 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 124 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 122 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 125 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 123 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 126 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -237,7 +240,6 @@ test "vector casts of sizes not divisible by 8" { | ... | @@ -237,7 +240,6 @@ test "vector casts of sizes not divisible by 8" { |
| 237 | } | 240 | } |
| 238 | 241 | ||
| 239 | test "vector @splat" { | 242 | test "vector @splat" { |
| 240 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 241 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 243 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 242 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 244 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 243 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 245 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -380,7 +382,6 @@ test "store vector elements via runtime index" { | ... | @@ -380,7 +382,6 @@ test "store vector elements via runtime index" { |
| 380 | } | 382 | } |
| 381 | 383 | ||
| 382 | test "initialize vector which is a struct field" { | 384 | test "initialize vector which is a struct field" { |
| 383 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 384 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 385 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 385 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 386 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 386 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 387 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -403,7 +404,6 @@ test "initialize vector which is a struct field" { | ... | @@ -403,7 +404,6 @@ test "initialize vector which is a struct field" { |
| 403 | 404 | ||
| 404 | test "vector comparison operators" { | 405 | test "vector comparison operators" { |
| 405 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 406 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 406 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 407 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 407 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 408 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 408 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 409 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 409 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -444,7 +444,6 @@ test "vector comparison operators" { | ... | @@ -444,7 +444,6 @@ test "vector comparison operators" { |
| 444 | 444 | ||
| 445 | test "vector division operators" { | 445 | test "vector division operators" { |
| 446 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 446 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 447 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 448 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 447 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 449 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 448 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 450 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 449 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -527,7 +526,6 @@ test "vector division operators" { | ... | @@ -527,7 +526,6 @@ test "vector division operators" { |
| 527 | 526 | ||
| 528 | test "vector bitwise not operator" { | 527 | test "vector bitwise not operator" { |
| 529 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 528 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 530 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 531 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 529 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 532 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 530 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 533 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 531 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -559,7 +557,6 @@ test "vector bitwise not operator" { | ... | @@ -559,7 +557,6 @@ test "vector bitwise not operator" { |
| 559 | 557 | ||
| 560 | test "vector shift operators" { | 558 | test "vector shift operators" { |
| 561 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 559 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 562 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 563 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 560 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 564 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 561 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 565 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 562 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -653,7 +650,6 @@ test "vector shift operators" { | ... | @@ -653,7 +650,6 @@ test "vector shift operators" { |
| 653 | 650 | ||
| 654 | test "vector reduce operation" { | 651 | test "vector reduce operation" { |
| 655 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 652 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 656 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 657 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 653 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 658 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 654 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 659 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 655 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -709,7 +705,7 @@ test "vector reduce operation" { | ... | @@ -709,7 +705,7 @@ test "vector reduce operation" { |
| 709 | 705 | ||
| 710 | // LLVM 11 ERROR: Cannot select type | 706 | // LLVM 11 ERROR: Cannot select type |
| 711 | // https://github.com/ziglang/zig/issues/7138 | 707 | // https://github.com/ziglang/zig/issues/7138 |
| 712 | if (builtin.target.cpu.arch != .aarch64) { | 708 | if (builtin.zig_backend != .stage2_llvm or builtin.target.cpu.arch != .aarch64) { |
| 713 | try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); | 709 | try testReduce(.Min, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, -386)); |
| 714 | try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9)); | 710 | try testReduce(.Min, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 9)); |
| 715 | } | 711 | } |
| ... | @@ -727,7 +723,7 @@ test "vector reduce operation" { | ... | @@ -727,7 +723,7 @@ test "vector reduce operation" { |
| 727 | 723 | ||
| 728 | // LLVM 11 ERROR: Cannot select type | 724 | // LLVM 11 ERROR: Cannot select type |
| 729 | // https://github.com/ziglang/zig/issues/7138 | 725 | // https://github.com/ziglang/zig/issues/7138 |
| 730 | if (builtin.target.cpu.arch != .aarch64) { | 726 | if (builtin.zig_backend != .stage2_llvm or builtin.target.cpu.arch != .aarch64) { |
| 731 | try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); | 727 | try testReduce(.Max, [4]i64{ 1234567, -386, 0, 3 }, @as(i64, 1234567)); |
| 732 | try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999)); | 728 | try testReduce(.Max, [4]u64{ 99, 9999, 9, 99999 }, @as(u64, 99999)); |
| 733 | } | 729 | } |
| ... | @@ -775,14 +771,14 @@ test "vector reduce operation" { | ... | @@ -775,14 +771,14 @@ test "vector reduce operation" { |
| 775 | 771 | ||
| 776 | // LLVM 11 ERROR: Cannot select type | 772 | // LLVM 11 ERROR: Cannot select type |
| 777 | // https://github.com/ziglang/zig/issues/7138 | 773 | // https://github.com/ziglang/zig/issues/7138 |
| 778 | if (false) { | 774 | if (builtin.zig_backend != .stage2_llvm) { |
| 779 | try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 775 | try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, -1.9)); |
| 780 | try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | 776 | try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, -1.9)); |
| 781 | try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | 777 | try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, -1.9)); |
| 782 | 778 | ||
| 783 | try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 779 | try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, 100.0)); |
| 784 | try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | 780 | try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, 100.0)); |
| 785 | try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | 781 | try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, 100.0)); |
| 786 | } | 782 | } |
| 787 | 783 | ||
| 788 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | 784 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| ... | @@ -797,7 +793,6 @@ test "vector reduce operation" { | ... | @@ -797,7 +793,6 @@ test "vector reduce operation" { |
| 797 | 793 | ||
| 798 | test "vector @reduce comptime" { | 794 | test "vector @reduce comptime" { |
| 799 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 795 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 800 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 801 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 796 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 802 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 797 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 803 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 798 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -813,7 +808,6 @@ test "vector @reduce comptime" { | ... | @@ -813,7 +808,6 @@ test "vector @reduce comptime" { |
| 813 | 808 | ||
| 814 | test "mask parameter of @shuffle is comptime scope" { | 809 | test "mask parameter of @shuffle is comptime scope" { |
| 815 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 810 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 816 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 817 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 811 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 818 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 812 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 819 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 813 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -833,7 +827,6 @@ test "mask parameter of @shuffle is comptime scope" { | ... | @@ -833,7 +827,6 @@ test "mask parameter of @shuffle is comptime scope" { |
| 833 | 827 | ||
| 834 | test "saturating add" { | 828 | test "saturating add" { |
| 835 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 829 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 836 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 837 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 830 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 838 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 831 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 839 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 832 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -865,7 +858,6 @@ test "saturating add" { | ... | @@ -865,7 +858,6 @@ test "saturating add" { |
| 865 | 858 | ||
| 866 | test "saturating subtraction" { | 859 | test "saturating subtraction" { |
| 867 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 860 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 868 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 869 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 861 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 870 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 862 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 871 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 863 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -888,7 +880,6 @@ test "saturating subtraction" { | ... | @@ -888,7 +880,6 @@ test "saturating subtraction" { |
| 888 | 880 | ||
| 889 | test "saturating multiplication" { | 881 | test "saturating multiplication" { |
| 890 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 882 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 891 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 892 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 883 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 893 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 884 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 894 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 885 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -915,7 +906,6 @@ test "saturating multiplication" { | ... | @@ -915,7 +906,6 @@ test "saturating multiplication" { |
| 915 | 906 | ||
| 916 | test "saturating shift-left" { | 907 | test "saturating shift-left" { |
| 917 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 908 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 918 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 919 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 909 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 920 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 910 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 921 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 911 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -1049,7 +1039,6 @@ test "@mulWithOverflow" { | ... | @@ -1049,7 +1039,6 @@ test "@mulWithOverflow" { |
| 1049 | } | 1039 | } |
| 1050 | 1040 | ||
| 1051 | test "@shlWithOverflow" { | 1041 | test "@shlWithOverflow" { |
| 1052 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1053 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1042 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1054 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1043 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1055 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1044 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -1078,7 +1067,6 @@ test "alignment of vectors" { | ... | @@ -1078,7 +1067,6 @@ test "alignment of vectors" { |
| 1078 | 1067 | ||
| 1079 | test "loading the second vector from a slice of vectors" { | 1068 | test "loading the second vector from a slice of vectors" { |
| 1080 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1069 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1081 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1082 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1070 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1083 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1071 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1084 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1072 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -1131,7 +1119,6 @@ test "byte vector initialized in inline function" { | ... | @@ -1131,7 +1119,6 @@ test "byte vector initialized in inline function" { |
| 1131 | } | 1119 | } |
| 1132 | 1120 | ||
| 1133 | test "byte vector initialized in inline function" { | 1121 | test "byte vector initialized in inline function" { |
| 1134 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1135 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1122 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1136 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1123 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1137 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1124 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -1160,7 +1147,6 @@ test "byte vector initialized in inline function" { | ... | @@ -1160,7 +1147,6 @@ test "byte vector initialized in inline function" { |
| 1160 | 1147 | ||
| 1161 | test "zero divisor" { | 1148 | test "zero divisor" { |
| 1162 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1149 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1163 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1164 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1150 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1165 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1151 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1166 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1152 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -1181,7 +1167,6 @@ test "zero divisor" { | ... | @@ -1181,7 +1167,6 @@ test "zero divisor" { |
| 1181 | 1167 | ||
| 1182 | test "zero multiplicand" { | 1168 | test "zero multiplicand" { |
| 1183 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1169 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1184 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1185 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1170 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1186 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1171 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1187 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1172 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -1204,7 +1189,6 @@ test "zero multiplicand" { | ... | @@ -1204,7 +1189,6 @@ test "zero multiplicand" { |
| 1204 | 1189 | ||
| 1205 | test "@intCast to u0" { | 1190 | test "@intCast to u0" { |
| 1206 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1191 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1207 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1208 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1192 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1209 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1193 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1210 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1194 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -1228,7 +1212,6 @@ test "modRem with zero divisor" { | ... | @@ -1228,7 +1212,6 @@ test "modRem with zero divisor" { |
| 1228 | 1212 | ||
| 1229 | test "array operands to shuffle are coerced to vectors" { | 1213 | test "array operands to shuffle are coerced to vectors" { |
| 1230 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1214 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1231 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1232 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1215 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1233 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1216 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1234 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1217 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -1246,8 +1229,6 @@ test "load packed vector element" { | ... | @@ -1246,8 +1229,6 @@ test "load packed vector element" { |
| 1246 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1229 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1247 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1230 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1248 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1231 | 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 | ||
| 1251 | 1232 | ||
| 1252 | var x: @Vector(2, u15) = .{ 1, 4 }; | 1233 | var x: @Vector(2, u15) = .{ 1, 4 }; |
| 1253 | try expect((&x[0]).* == 1); | 1234 | try expect((&x[0]).* == 1); |
| ... | @@ -1260,7 +1241,6 @@ test "store packed vector element" { | ... | @@ -1260,7 +1241,6 @@ test "store packed vector element" { |
| 1260 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1241 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1261 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1242 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1262 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1243 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1263 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1264 | 1244 | ||
| 1265 | var v = @Vector(4, u1){ 1, 1, 1, 1 }; | 1245 | var v = @Vector(4, u1){ 1, 1, 1, 1 }; |
| 1266 | try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v); | 1246 | try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v); |
test/behavior/void.zig-1| ... | @@ -33,7 +33,6 @@ fn times(n: usize) []const void { | ... | @@ -33,7 +33,6 @@ fn times(n: usize) []const void { |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | test "void optional" { | 35 | test "void optional" { |
| 36 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 37 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 36 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 38 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 37 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 39 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 38 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/while.zig-8| ... | @@ -104,7 +104,6 @@ fn testBreakOuter() void { | ... | @@ -104,7 +104,6 @@ fn testBreakOuter() void { |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | test "while copies its payload" { | 106 | test "while copies its payload" { |
| 107 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 108 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 107 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 109 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 108 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 110 | 109 | ||
| ... | @@ -143,7 +142,6 @@ fn runContinueAndBreakTest() !void { | ... | @@ -143,7 +142,6 @@ fn runContinueAndBreakTest() !void { |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | test "while with optional as condition" { | 144 | test "while with optional as condition" { |
| 146 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 147 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 145 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 148 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 146 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 149 | 147 | ||
| ... | @@ -156,7 +154,6 @@ test "while with optional as condition" { | ... | @@ -156,7 +154,6 @@ test "while with optional as condition" { |
| 156 | } | 154 | } |
| 157 | 155 | ||
| 158 | test "while with optional as condition with else" { | 156 | test "while with optional as condition with else" { |
| 159 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 160 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 157 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 161 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 158 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 162 | 159 | ||
| ... | @@ -174,7 +171,6 @@ test "while with optional as condition with else" { | ... | @@ -174,7 +171,6 @@ test "while with optional as condition with else" { |
| 174 | } | 171 | } |
| 175 | 172 | ||
| 176 | test "while with error union condition" { | 173 | test "while with error union condition" { |
| 177 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 178 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 174 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 179 | 175 | ||
| 180 | numbers_left = 10; | 176 | numbers_left = 10; |
| ... | @@ -205,7 +201,6 @@ test "while on bool with else result follow break prong" { | ... | @@ -205,7 +201,6 @@ test "while on bool with else result follow break prong" { |
| 205 | } | 201 | } |
| 206 | 202 | ||
| 207 | test "while on optional with else result follow else prong" { | 203 | test "while on optional with else result follow else prong" { |
| 208 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 209 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 204 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 210 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 205 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 211 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 206 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -217,7 +212,6 @@ test "while on optional with else result follow else prong" { | ... | @@ -217,7 +212,6 @@ test "while on optional with else result follow else prong" { |
| 217 | } | 212 | } |
| 218 | 213 | ||
| 219 | test "while on optional with else result follow break prong" { | 214 | test "while on optional with else result follow break prong" { |
| 220 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 221 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 215 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 222 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 216 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 223 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 217 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -290,7 +284,6 @@ test "while bool 2 break statements and an else" { | ... | @@ -290,7 +284,6 @@ test "while bool 2 break statements and an else" { |
| 290 | } | 284 | } |
| 291 | 285 | ||
| 292 | test "while optional 2 break statements and an else" { | 286 | test "while optional 2 break statements and an else" { |
| 293 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 294 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 287 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 295 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 288 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 296 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 289 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -310,7 +303,6 @@ test "while optional 2 break statements and an else" { | ... | @@ -310,7 +303,6 @@ test "while optional 2 break statements and an else" { |
| 310 | } | 303 | } |
| 311 | 304 | ||
| 312 | test "while error 2 break statements and an else" { | 305 | test "while error 2 break statements and an else" { |
| 313 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 314 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 306 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 315 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 307 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 316 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 308 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/widening.zig-1| ... | @@ -28,7 +28,6 @@ test "integer widening u0 to u8" { | ... | @@ -28,7 +28,6 @@ test "integer widening u0 to u8" { |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | test "implicit unsigned integer to signed integer" { | 30 | test "implicit unsigned integer to signed integer" { |
| 31 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 32 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 31 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 33 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 32 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 34 | 33 |
test/cases.zig+5-5| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const TestContext = @import("../src/test.zig").TestContext; | 2 | const Cases = @import("src/Cases.zig"); |
| 3 | 3 | ||
| 4 | pub fn addCases(ctx: *TestContext) !void { | 4 | pub fn addCases(cases: *Cases) !void { |
| 5 | try @import("compile_errors.zig").addCases(ctx); | 5 | try @import("compile_errors.zig").addCases(cases); |
| 6 | try @import("stage2/cbe.zig").addCases(ctx); | 6 | try @import("cbe.zig").addCases(cases); |
| 7 | try @import("stage2/nvptx.zig").addCases(ctx); | 7 | try @import("nvptx.zig").addCases(cases); |
| 8 | } | 8 | } |
test/cases/compile_errors/access_inactive_union_field_comptime.zig+1| ... | @@ -21,3 +21,4 @@ pub export fn entry1() void { | ... | @@ -21,3 +21,4 @@ pub export fn entry1() void { |
| 21 | // :9:15: error: access of union field 'a' while field 'b' is active | 21 | // :9:15: error: access of union field 'a' while field 'b' is active |
| 22 | // :2:21: note: union declared here | 22 | // :2:21: note: union declared here |
| 23 | // :14:16: error: access of union field 'a' while field 'b' is active | 23 | // :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 @@ | ||
| 1 | export fn entry0(single: *u32) void { | ||
| 2 | _ = @as(*const [0]u32, single); | ||
| 3 | } | ||
| 4 | export fn entry1(single: *u32) void { | ||
| 5 | _ = @as(*const [1]u32, single); | ||
| 6 | } | ||
| 7 | export fn entry2(single: *u32) void { | ||
| 8 | _ = @as(*const [2]u32, single); | ||
| 9 | } | ||
| 10 | |||
| 11 | // error | ||
| 12 | // backend=stage2 | ||
| 13 | // target=native | ||
| 14 | // | ||
| 15 | // :2:28: error: expected type '*const [0]u32', found '*u32' | ||
| 16 | // :2:28: note: pointer type child 'u32' cannot cast into pointer type child '[0]u32' | ||
| 17 | // :8:28: error: expected type '*const [2]u32', found '*u32' | ||
| 18 | // :8:28: note: pointer type child 'u32' cannot cast into pointer type child '[2]u32' | ||
test/cases/compile_errors/bad_import.zig+1-1| ... | @@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",); | ... | @@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",); |
| 4 | // backend=stage2 | 4 | // backend=stage2 |
| 5 | // target=native | 5 | // target=native |
| 6 | // | 6 | // |
| 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 @@ | ||
| 1 | export fn f1() void { | ||
| 2 | const x: usize = for ("hello") |_| {}; | ||
| 3 | _ = x; | ||
| 4 | } | ||
| 5 | export fn f2() void { | ||
| 6 | const x: usize = for ("hello") |_| { | ||
| 7 | break; | ||
| 8 | }; | ||
| 9 | _ = x; | ||
| 10 | } | ||
| 11 | export fn f3() void { | ||
| 12 | var t: bool = true; | ||
| 13 | const x: usize = while (t) { | ||
| 14 | break; | ||
| 15 | }; | ||
| 16 | _ = x; | ||
| 17 | } | ||
| 18 | export fn f4() void { | ||
| 19 | const x: usize = blk: { | ||
| 20 | break :blk; | ||
| 21 | }; | ||
| 22 | _ = x; | ||
| 23 | } | ||
| 24 | |||
| 25 | // error | ||
| 26 | // backend=stage2 | ||
| 27 | // target=native | ||
| 28 | // | ||
| 29 | // :2:22: error: expected type 'usize', found 'void' | ||
| 30 | // :7:9: error: expected type 'usize', found 'void' | ||
| 31 | // :14:9: error: expected type 'usize', found 'void' | ||
| 32 | // :18:1: error: expected type 'usize', found 'void' | ||
test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig+4| ... | @@ -15,3 +15,7 @@ pub export fn entry() void { | ... | @@ -15,3 +15,7 @@ pub export fn entry() void { |
| 15 | // target=native | 15 | // target=native |
| 16 | // | 16 | // |
| 17 | // :6:5: error: found compile log statement | 17 | // :6:5: error: found compile log statement |
| 18 | // | ||
| 19 | // Compile Log Output: | ||
| 20 | // @as(tmp.Bar, .{ .X = 123 }) | ||
| 21 | // @as(tmp.Bar, [runtime value]) |
test/cases/compile_errors/compile_log.zig+9| ... | @@ -17,3 +17,12 @@ export fn baz() void { | ... | @@ -17,3 +17,12 @@ export fn baz() void { |
| 17 | // | 17 | // |
| 18 | // :5:5: error: found compile log statement | 18 | // :5:5: error: found compile log statement |
| 19 | // :11:5: note: also here | 19 | // :11:5: note: also here |
| 20 | // | ||
| 21 | // Compile Log Output: | ||
| 22 | // @as(*const [5:0]u8, "begin") | ||
| 23 | // @as(*const [1:0]u8, "a"), @as(i32, 12), @as(*const [1:0]u8, "b"), @as([]const u8, "hi") | ||
| 24 | // @as(*const [3:0]u8, "end") | ||
| 25 | // @as(comptime_int, 4) | ||
| 26 | // @as(*const [5:0]u8, "begin") | ||
| 27 | // @as(*const [1:0]u8, "a"), @as(i32, [runtime value]), @as(*const [1:0]u8, "b"), @as([]const u8, [runtime value]) | ||
| 28 | // @as(*const [3:0]u8, "end") |
test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig+4-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | export fn entry() void { | 1 | export fn entry() void { |
| 2 | @compileLog(@ptrCast(*const anyopaque, &entry)); | 2 | @compileLog(@as(*align(1) const anyopaque, @ptrCast(*const anyopaque, &entry))); |
| 3 | } | 3 | } |
| 4 | 4 | ||
| 5 | // error | 5 | // error |
| ... | @@ -7,3 +7,6 @@ export fn entry() void { | ... | @@ -7,3 +7,6 @@ export fn entry() void { |
| 7 | // target=native | 7 | // target=native |
| 8 | // | 8 | // |
| 9 | // :2:5: error: found compile log statement | 9 | // :2:5: error: found compile log statement |
| 10 | // | ||
| 11 | // Compile Log Output: | ||
| 12 | // @as(*const anyopaque, (function 'entry')) |
test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig+3| ... | @@ -12,3 +12,6 @@ export fn entry() void { | ... | @@ -12,3 +12,6 @@ export fn entry() void { |
| 12 | // target=native | 12 | // target=native |
| 13 | // | 13 | // |
| 14 | // :2:5: error: found compile log statement | 14 | // :2:5: error: found compile log statement |
| 15 | // | ||
| 16 | // Compile Log Output: | ||
| 17 | // @as(*const [3:0]u8, "i32\x00") |
test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig+5| ... | @@ -13,3 +13,8 @@ fn inner(comptime n: usize) void { | ... | @@ -13,3 +13,8 @@ fn inner(comptime n: usize) void { |
| 13 | // | 13 | // |
| 14 | // :7:39: error: found compile log statement | 14 | // :7:39: error: found compile log statement |
| 15 | // :7:39: note: also here | 15 | // :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 @@ | ||
| 1 | comptime { | ||
| 2 | foo(); | ||
| 3 | } | ||
| 4 | |||
| 5 | fn foo() void { | ||
| 6 | try bar(); | ||
| 7 | } | ||
| 8 | |||
| 9 | pub fn bar() u8 { | ||
| 10 | return 0; | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // backend=stage2 | ||
| 15 | // target=native | ||
| 16 | // | ||
| 17 | // :6:12: error: expected error union type, found 'u8' | ||
| 18 | // :2:8: note: called from here | ||
test/cases/compile_errors/condition_comptime_reason_explained.zig+2| ... | @@ -45,4 +45,6 @@ pub export fn entry2() void { | ... | @@ -45,4 +45,6 @@ pub export fn entry2() void { |
| 45 | // :22:13: error: unable to resolve comptime value | 45 | // :22:13: error: unable to resolve comptime value |
| 46 | // :22:13: note: condition in comptime switch must be comptime-known | 46 | // :22:13: note: condition in comptime switch must be comptime-known |
| 47 | // :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S' | 47 | // :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S' |
| 48 | // :2:12: note: struct requires comptime because of this field | ||
| 49 | // :2:12: note: use '*const fn() void' for a function pointer type | ||
| 48 | // :32:19: note: called from here | 50 | // :32:19: note: called from here |
test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig+1| ... | @@ -32,6 +32,7 @@ export fn d() void { | ... | @@ -32,6 +32,7 @@ export fn d() void { |
| 32 | // :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 32 | // :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
| 33 | // :1:11: note: opaque declared here | 33 | // :1:11: note: opaque declared here |
| 34 | // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions | 34 | // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions |
| 35 | // :1:11: note: opaque declared here | ||
| 35 | // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 36 | // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
| 36 | // :18:22: note: opaque declared here | 37 | // :18:22: note: opaque declared here |
| 37 | // :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 38 | // :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 @@ | ||
| 1 | pub export fn entry1() void { | ||
| 2 | @export(u100, .{ .name = "a" }); | ||
| 3 | } | ||
| 4 | pub export fn entry3() void { | ||
| 5 | @export(undefined, .{ .name = "b" }); | ||
| 6 | } | ||
| 7 | pub export fn entry4() void { | ||
| 8 | @export(null, .{ .name = "c" }); | ||
| 9 | } | ||
| 10 | pub export fn entry5() void { | ||
| 11 | @export(false, .{ .name = "d" }); | ||
| 12 | } | ||
| 13 | pub export fn entry6() void { | ||
| 14 | @export(u8, .{ .name = "e" }); | ||
| 15 | } | ||
| 16 | pub export fn entry7() void { | ||
| 17 | @export(u65535, .{ .name = "f" }); | ||
| 18 | } | ||
| 19 | |||
| 20 | // error | ||
| 21 | // backend=llvm | ||
| 22 | // target=native | ||
| 23 | // | ||
| 24 | // :2:13: error: unable to export primitive value | ||
| 25 | // :5:13: error: unable to export primitive value | ||
| 26 | // :8:13: error: unable to export primitive value | ||
| 27 | // :11:13: error: unable to export primitive value | ||
| 28 | // :14:13: error: unable to export primitive value | ||
| 29 | // :17:13: error: unable to export primitive value | ||
test/cases/compile_errors/extern_function_with_comptime_parameter.zig+1-1| ... | @@ -12,6 +12,6 @@ comptime { _ = entry2; } | ... | @@ -12,6 +12,6 @@ comptime { _ = entry2; } |
| 12 | // backend=stage2 | 12 | // backend=stage2 |
| 13 | // target=native | 13 | // target=native |
| 14 | // | 14 | // |
| 15 | // :1:15: error: comptime parameters not allowed in function with calling convention 'C' | ||
| 16 | // :5:30: error: comptime parameters not allowed in function with calling convention 'C' | 15 | // :5:30: error: comptime parameters not allowed in function with calling convention 'C' |
| 17 | // :6:30: error: generic parameters not allowed in function with calling convention 'C' | 16 | // :6:30: error: generic parameters not allowed in function with calling convention 'C' |
| 17 | // :1:15: error: comptime parameters not allowed in function with calling convention 'C' |
test/cases/compile_errors/function_parameter_is_opaque.zig+1| ... | @@ -27,4 +27,5 @@ export fn entry4() void { | ... | @@ -27,4 +27,5 @@ export fn entry4() void { |
| 27 | // :1:17: note: opaque declared here | 27 | // :1:17: note: opaque declared here |
| 28 | // :8:28: error: parameter of type '@TypeOf(null)' not allowed | 28 | // :8:28: error: parameter of type '@TypeOf(null)' not allowed |
| 29 | // :12:8: error: parameter of opaque type 'tmp.FooType' not allowed | 29 | // :12:8: error: parameter of opaque type 'tmp.FooType' not allowed |
| 30 | // :1:17: note: opaque declared here | ||
| 30 | // :17:8: error: parameter of type '@TypeOf(null)' not allowed | 31 | // :17:8: error: parameter of type '@TypeOf(null)' not allowed |
test/cases/compile_errors/helpful_return_type_error_message.zig+1-1| ... | @@ -24,9 +24,9 @@ export fn quux() u32 { | ... | @@ -24,9 +24,9 @@ export fn quux() u32 { |
| 24 | // :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set' | 24 | // :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set' |
| 25 | // :7:17: note: function cannot return an error | 25 | // :7:17: note: function cannot return an error |
| 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' | 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 27 | // :10:17: note: function cannot return an error | ||
| 28 | // :11:15: note: cannot convert error union to payload type | 27 | // :11:15: note: cannot convert error union to payload type |
| 29 | // :11:15: note: consider using 'try', 'catch', or 'if' | 28 | // :11:15: note: consider using 'try', 'catch', or 'if' |
| 29 | // :10:17: note: function cannot return an error | ||
| 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' | 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 31 | // :15:14: note: cannot convert error union to payload type | 31 | // :15:14: note: cannot convert error union to payload type |
| 32 | // :15:14: note: consider using 'try', 'catch', or 'if' | 32 | // :15:14: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/implicit_semicolon-block_expr.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = {} | 4 | _ = {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-block_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | ({}) | 4 | ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-comptime_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = comptime {} | 4 | _ = comptime {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-comptime_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | comptime ({}) | 4 | comptime ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-defer.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | defer ({}) | 4 | defer ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-for_expression.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = for(foo()) |_| {} | 4 | _ = for(foo()) |_| {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-for_statement.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | for(foo()) |_| ({}) | 4 | for(foo()) |_| ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} else if(true) {} else {} | 4 | _ = if(true) {} else if(true) {} else {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) else if(true) ({}) else ({}) | 4 | if(true) ({}) else if(true) ({}) else ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} else if(true) {} | 4 | _ = if(true) {} else if(true) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) else if(true) ({}) | 4 | if(true) ({}) else if(true) ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} else {} | 4 | _ = if(true) {} else {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) else ({}) | 4 | if(true) ({}) else ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} | 4 | _ = if(true) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) | 4 | if(true) ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-test_expression.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if (foo()) |_| {} | 4 | _ = if (foo()) |_| {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-test_statement.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if (foo()) |_| ({}) | 4 | if (foo()) |_| ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = while(true):({}) {} | 4 | _ = while(true):({}) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | while(true):({}) ({}) | 4 | while(true):({}) ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-while_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = while(true) {} | 4 | _ = while(true) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-while_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | while(true) 1 | 4 | while(true) 1 |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/invalid_member_of_builtin_enum.zig+1-1| ... | @@ -9,4 +9,4 @@ export fn entry() void { | ... | @@ -9,4 +9,4 @@ export fn entry() void { |
| 9 | // target=native | 9 | // target=native |
| 10 | // | 10 | // |
| 11 | // :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86' | 11 | // :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86' |
| 12 | // :?:18: note: enum declared 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 { | ... | @@ -73,11 +73,11 @@ pub export fn entry8() void { |
| 73 | // | 73 | // |
| 74 | // :6:19: error: value stored in comptime field does not match the default value of the field | 74 | // :6:19: error: value stored in comptime field does not match the default value of the field |
| 75 | // :14:19: error: value stored in comptime field does not match the default value of the field | 75 | // :14:19: error: value stored in comptime field does not match the default value of the field |
| 76 | // :53:16: error: value stored in comptime field does not match the default value of the field | ||
| 77 | // :19:38: error: value stored in comptime field does not match the default value of the field | 76 | // :19:38: error: value stored in comptime field does not match the default value of the field |
| 78 | // :31:19: error: value stored in comptime field does not match the default value of the field | 77 | // :31:19: error: value stored in comptime field does not match the default value of the field |
| 79 | // :25:29: note: default value set here | 78 | // :25:29: note: default value set here |
| 80 | // :41:16: error: value stored in comptime field does not match the default value of the field | 79 | // :41:16: error: value stored in comptime field does not match the default value of the field |
| 81 | // :45:12: error: value stored in comptime field does not match the default value of the field | 80 | // :45:12: error: value stored in comptime field does not match the default value of the field |
| 81 | // :53:16: error: value stored in comptime field does not match the default value of the field | ||
| 82 | // :66:43: error: value stored in comptime field does not match the default value of the field | 82 | // :66:43: error: value stored in comptime field does not match the default value of the field |
| 83 | // :59:35: error: value stored in comptime field does not match the default value of the field | 83 | // :59:35: error: value stored in comptime field does not match the default value of the field |
test/cases/compile_errors/invalid_struct_field.zig+1| ... | @@ -25,5 +25,6 @@ export fn e() void { | ... | @@ -25,5 +25,6 @@ export fn e() void { |
| 25 | // :4:7: error: no field named 'foo' in struct 'tmp.A' | 25 | // :4:7: error: no field named 'foo' in struct 'tmp.A' |
| 26 | // :1:11: note: struct declared here | 26 | // :1:11: note: struct declared here |
| 27 | // :10:17: error: no field named 'bar' in struct 'tmp.A' | 27 | // :10:17: error: no field named 'bar' in struct 'tmp.A' |
| 28 | // :1:11: note: struct declared here | ||
| 28 | // :18:45: error: no field named 'f' in struct 'tmp.e.B' | 29 | // :18:45: error: no field named 'f' in struct 'tmp.e.B' |
| 29 | // :14:15: note: struct declared here | 30 | // :14:15: note: struct declared here |
test/cases/compile_errors/method_call_with_first_arg_type_primitive.zig+2-1| ... | @@ -2,7 +2,7 @@ const Foo = struct { | ... | @@ -2,7 +2,7 @@ const Foo = struct { |
| 2 | x: i32, | 2 | x: i32, |
| 3 | 3 | ||
| 4 | fn init(x: i32) Foo { | 4 | fn init(x: i32) Foo { |
| 5 | return Foo { | 5 | return Foo{ |
| 6 | .x = x, | 6 | .x = x, |
| 7 | }; | 7 | }; |
| 8 | } | 8 | } |
| ... | @@ -20,3 +20,4 @@ export fn f() void { | ... | @@ -20,3 +20,4 @@ export fn f() void { |
| 20 | // | 20 | // |
| 21 | // :14:9: error: no field or member function named 'init' in 'tmp.Foo' | 21 | // :14:9: error: no field or member function named 'init' in 'tmp.Foo' |
| 22 | // :1:13: note: struct declared here | 22 | // :1:13: note: struct declared here |
| 23 | // :4:5: note: 'init' is not a member function |
test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig+1| ... | @@ -29,3 +29,4 @@ export fn foo() void { | ... | @@ -29,3 +29,4 @@ export fn foo() void { |
| 29 | // | 29 | // |
| 30 | // :23:6: error: no field or member function named 'init' in 'tmp.List' | 30 | // :23:6: error: no field or member function named 'init' in 'tmp.List' |
| 31 | // :1:18: note: struct declared here | 31 | // :1:18: note: struct declared here |
| 32 | // :5:9: note: 'init' is not a member function |
test/cases/compile_errors/missing_main_fn_in_executable.zig+4-2| ... | @@ -5,5 +5,7 @@ | ... | @@ -5,5 +5,7 @@ |
| 5 | // target=x86_64-linux | 5 | // target=x86_64-linux |
| 6 | // output_mode=Exe | 6 | // output_mode=Exe |
| 7 | // | 7 | // |
| 8 | // :?:?: error: root struct of file 'tmp' has no member named 'main' | 8 | // : error: root struct of file 'tmp' has no member named 'main' |
| 9 | // :?:?: note: called from here | 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 {} | ... | @@ -5,6 +5,8 @@ fn main() void {} |
| 5 | // target=x86_64-linux | 5 | // target=x86_64-linux |
| 6 | // output_mode=Exe | 6 | // output_mode=Exe |
| 7 | // | 7 | // |
| 8 | // :?:?: error: 'main' is not marked 'pub' | 8 | // : error: 'main' is not marked 'pub' |
| 9 | // :1:1: note: declared here | 9 | // :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 { | ... | @@ -11,6 +11,18 @@ comptime { |
| 11 | }, | 11 | }, |
| 12 | }); | 12 | }); |
| 13 | } | 13 | } |
| 14 | comptime { | ||
| 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 | } | ||
| 14 | 26 | ||
| 15 | // error | 27 | // error |
| 16 | // backend=stage2 | 28 | // backend=stage2 |
| ... | @@ -18,3 +30,4 @@ comptime { | ... | @@ -18,3 +30,4 @@ comptime { |
| 18 | // | 30 | // |
| 19 | // :2:9: error: use of undefined value here causes undefined behavior | 31 | // :2:9: error: use of undefined value here causes undefined behavior |
| 20 | // :5:9: error: use of undefined value here causes undefined behavior | 32 | // :5:9: error: use of undefined value here causes undefined behavior |
| 33 | // :17:9: error: use of undefined value here causes undefined behavior |
test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig+3-2| ... | @@ -15,5 +15,6 @@ export fn entry() void { | ... | @@ -15,5 +15,6 @@ export fn entry() void { |
| 15 | // target=native | 15 | // target=native |
| 16 | // | 16 | // |
| 17 | // :9:51: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known | 17 | // :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 | 18 | // : note: struct requires comptime because of this field |
| 19 | // :?:21: note: types are not available at runtime | 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 { | ... | @@ -13,6 +13,6 @@ comptime { |
| 13 | // target=native | 13 | // target=native |
| 14 | // | 14 | // |
| 15 | // :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' | 15 | // :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' |
| 16 | // :1:13: note: struct declared here | ||
| 17 | // :2:13: note: struct declared here | 16 | // :2:13: note: struct declared here |
| 17 | // :1:13: note: struct declared here | ||
| 18 | // :4:18: note: parameter type declared here | 18 | // :4:18: note: parameter type declared here |
test/cases/compile_errors/undefined_as_field_type_is_rejected.zig+8-4| ... | @@ -1,9 +1,13 @@ | ... | @@ -1,9 +1,13 @@ |
| 1 | export fn a() void { | 1 | const Foo = struct { |
| 2 | b(); | 2 | a: undefined, |
| 3 | }; | ||
| 4 | export fn entry1() void { | ||
| 5 | const foo: Foo = undefined; | ||
| 6 | _ = foo; | ||
| 3 | } | 7 | } |
| 4 | 8 | ||
| 5 | // error | 9 | // error |
| 6 | // backend=stage2 | 10 | // backend=stage1 |
| 7 | // target=native | 11 | // target=native |
| 8 | // | 12 | // |
| 9 | // :2:5: error: use of undeclared identifier 'b' | 13 | // tmp.zig:2:8: error: use of undefined value here causes undefined behavior |
test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig+2-1| ... | @@ -28,10 +28,11 @@ export fn u2m() void { | ... | @@ -28,10 +28,11 @@ export fn u2m() void { |
| 28 | // target=native | 28 | // target=native |
| 29 | // | 29 | // |
| 30 | // :9:1: error: union initializer must initialize one field | 30 | // :9:1: error: union initializer must initialize one field |
| 31 | // :1:12: note: union declared here | ||
| 31 | // :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field | 32 | // :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field |
| 32 | // :14:31: note: additional initializer here | 33 | // :14:31: note: additional initializer here |
| 34 | // :1:12: note: union declared here | ||
| 33 | // :18:21: error: union initializer must initialize one field | 35 | // :18:21: error: union initializer must initialize one field |
| 34 | // :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field | 36 | // :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field |
| 35 | // :22:31: note: additional initializer here | 37 | // :22:31: note: additional initializer here |
| 36 | // :1:12: note: union declared here | ||
| 37 | // :5:12: note: union declared here | 38 | // :5:12: note: union declared here |
test/cases/compile_log.0.zig+5| ... | @@ -15,3 +15,8 @@ fn x() void {} | ... | @@ -15,3 +15,8 @@ fn x() void {} |
| 15 | // error | 15 | // error |
| 16 | // | 16 | // |
| 17 | // :6:23: error: expected type 'usize', found 'bool' | 17 | // :6:23: error: expected type 'usize', found 'bool' |
| 18 | // | ||
| 19 | // Compile Log Output: | ||
| 20 | // @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x')) | ||
| 21 | // @as(comptime_int, 1000) | ||
| 22 | // @as(comptime_int, 1234) |
test/cases/compile_log.1.zig+4| ... | @@ -14,3 +14,7 @@ fn x() void {} | ... | @@ -14,3 +14,7 @@ fn x() void {} |
| 14 | // | 14 | // |
| 15 | // :9:5: error: found compile log statement | 15 | // :9:5: error: found compile log statement |
| 16 | // :4:5: note: also here | 16 | // :4:5: note: also here |
| 17 | // | ||
| 18 | // Compile Log Output: | ||
| 19 | // @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x')) | ||
| 20 | // @as(comptime_int, 1000) |
test/cases/f32_passed_to_variadic_fn.zig+2-1| ... | @@ -9,7 +9,8 @@ pub fn main() void { | ... | @@ -9,7 +9,8 @@ pub fn main() void { |
| 9 | // run | 9 | // run |
| 10 | // backend=llvm | 10 | // backend=llvm |
| 11 | // target=x86_64-linux-gnu | 11 | // target=x86_64-linux-gnu |
| 12 | // link_libc=1 | ||
| 12 | // | 13 | // |
| 13 | // f64: 2.000000 | 14 | // f64: 2.000000 |
| 14 | // f32: 10.000000 | 15 | // f32: 10.000000 |
| 15 | // | ||
| \ No newline at end of file | |||
| 16 | // | ||
test/cases/fn_typeinfo_passed_to_comptime_fn.zig+1| ... | @@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void { | ... | @@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void { |
| 14 | 14 | ||
| 15 | // run | 15 | // run |
| 16 | // is_test=1 | 16 | // is_test=1 |
| 17 | // backend=llvm | ||
| 17 | // | 18 | // |
test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=llvm | 10 | // backend=llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig+1-1| ... | @@ -6,7 +6,7 @@ pub fn main() void { | ... | @@ -6,7 +6,7 @@ pub fn main() void { |
| 6 | _ = entry; | 6 | _ = entry; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | // error | 9 | // compile |
| 10 | // output_mode=Exe | 10 | // output_mode=Exe |
| 11 | // backend=llvm | 11 | // backend=llvm |
| 12 | // target=x86_64-linux,x86_64-macos | 12 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig+1-1| ... | @@ -6,7 +6,7 @@ pub fn main() void { | ... | @@ -6,7 +6,7 @@ pub fn main() void { |
| 6 | _ = entry; | 6 | _ = entry; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | // error | 9 | // compile |
| 10 | // output_mode=Exe | 10 | // output_mode=Exe |
| 11 | // backend=stage2,llvm | 11 | // backend=stage2,llvm |
| 12 | // target=x86_64-linux,x86_64-macos | 12 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/hello_world.zig+1| ... | @@ -7,6 +7,7 @@ pub fn main() void { | ... | @@ -7,6 +7,7 @@ pub fn main() void { |
| 7 | // run | 7 | // run |
| 8 | // backend=llvm | 8 | // backend=llvm |
| 9 | // target=x86_64-linux,x86_64-macos | 9 | // target=x86_64-linux,x86_64-macos |
| 10 | // link_libc=1 | ||
| 10 | // | 11 | // |
| 11 | // hello world! | 12 | // hello world! |
| 12 | // | 13 | // |
test/cases/llvm/pointer_keeps_address_space.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cbe.zig created+950| ... | @@ -0,0 +1,950 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const 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. | ||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const testing = std.testing; | ||
| 4 | const process = std.process; | ||
| 5 | const fs = std.fs; | ||
| 6 | const ChildProcess = std.ChildProcess; | ||
| 7 | |||
| 8 | var a: std.mem.Allocator = undefined; | ||
| 9 | |||
| 10 | pub 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 | |||
| 59 | fn 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 | |||
| 67 | fn 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 | |||
| 98 | fn 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 | |||
| 104 | fn 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 | |||
| 111 | fn 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 | |||
| 151 | fn 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 | |||
| 162 | fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { | ||
| 163 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); | ||
| 164 | |||
| 165 | const unformatted_code = " // no reason for indent"; | ||
| 166 | |||
| 167 | const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" }); | ||
| 168 | try fs.cwd().writeFile(fmt1_zig_path, unformatted_code); | ||
| 169 | |||
| 170 | const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); | ||
| 171 | // stderr should be file path + \n | ||
| 172 | try testing.expect(std.mem.startsWith(u8, run_result1.stdout, fmt1_zig_path)); | ||
| 173 | try testing.expect(run_result1.stdout.len == fmt1_zig_path.len + 1 and run_result1.stdout[run_result1.stdout.len - 1] == '\n'); | ||
| 174 | |||
| 175 | const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" }); | ||
| 176 | try fs.cwd().writeFile(fmt2_zig_path, unformatted_code); | ||
| 177 | |||
| 178 | const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | ||
| 179 | // running it on the dir, only the new file should be changed | ||
| 180 | try testing.expect(std.mem.startsWith(u8, run_result2.stdout, fmt2_zig_path)); | ||
| 181 | try testing.expect(run_result2.stdout.len == fmt2_zig_path.len + 1 and run_result2.stdout[run_result2.stdout.len - 1] == '\n'); | ||
| 182 | |||
| 183 | const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | ||
| 184 | // both files have been formatted, nothing should change now | ||
| 185 | try testing.expect(run_result3.stdout.len == 0); | ||
| 186 | |||
| 187 | // Check UTF-16 decoding | ||
| 188 | const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" }); | ||
| 189 | var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; | ||
| 190 | try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16); | ||
| 191 | |||
| 192 | const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | ||
| 193 | try testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path)); | ||
| 194 | try testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n'); | ||
| 195 | } | ||
test/compile_errors.zig+25-199| ... | @@ -1,146 +1,10 @@ | ... | @@ -1,146 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const TestContext = @import("../src/test.zig").TestContext; | 3 | const Cases = @import("src/Cases.zig"); |
| 4 | |||
| 5 | pub 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 | }); | ||
| 140 | 4 | ||
| 5 | pub fn addCases(ctx: *Cases) !void { | ||
| 141 | { | 6 | { |
| 142 | const case = ctx.obj("multiline error messages", .{}); | 7 | const case = ctx.obj("multiline error messages", .{}); |
| 143 | case.backend = .stage2; | ||
| 144 | 8 | ||
| 145 | case.addError( | 9 | case.addError( |
| 146 | \\comptime { | 10 | \\comptime { |
| ... | @@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 176 | 40 | ||
| 177 | { | 41 | { |
| 178 | const case = ctx.obj("isolated carriage return in multiline string literal", .{}); | 42 | const case = ctx.obj("isolated carriage return in multiline string literal", .{}); |
| 179 | case.backend = .stage2; | ||
| 180 | 43 | ||
| 181 | case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{ | 44 | case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{ |
| 182 | ":1:19: error: expected ';' after declaration", | 45 | ":1:19: error: expected ';' after declaration", |
| ... | @@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 195 | 58 | ||
| 196 | { | 59 | { |
| 197 | const case = ctx.obj("argument causes error", .{}); | 60 | const case = ctx.obj("argument causes error", .{}); |
| 198 | case.backend = .stage2; | ||
| 199 | |||
| 200 | case.addSourceFile("b.zig", | ||
| 201 | \\pub const ElfDynLib = struct { | ||
| 202 | \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T { | ||
| 203 | \\ _ = self; | ||
| 204 | \\ return undefined; | ||
| 205 | \\ } | ||
| 206 | \\}; | ||
| 207 | ); | ||
| 208 | 61 | ||
| 209 | case.addError( | 62 | case.addError( |
| 210 | \\pub export fn entry() void { | 63 | \\pub export fn entry() void { |
| ... | @@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void { |
| 216 | ":3:12: note: argument to function being called at comptime must be comptime-known", | 69 | ":3:12: note: argument to function being called at comptime must be comptime-known", |
| 217 | ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type", | 70 | ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type", |
| 218 | }); | 71 | }); |
| 72 | case.addSourceFile("b.zig", | ||
| 73 | \\pub const ElfDynLib = struct { | ||
| 74 | \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T { | ||
| 75 | \\ _ = self; | ||
| 76 | \\ return undefined; | ||
| 77 | \\ } | ||
| 78 | \\}; | ||
| 79 | ); | ||
| 219 | } | 80 | } |
| 220 | 81 | ||
| 221 | { | 82 | { |
| 222 | const case = ctx.obj("astgen failure in file struct", .{}); | 83 | const case = ctx.obj("astgen failure in file struct", .{}); |
| 223 | case.backend = .stage2; | ||
| 224 | |||
| 225 | case.addSourceFile("b.zig", | ||
| 226 | \\+ | ||
| 227 | ); | ||
| 228 | 84 | ||
| 229 | case.addError( | 85 | case.addError( |
| 230 | \\pub export fn entry() void { | 86 | \\pub export fn entry() void { |
| ... | @@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void { |
| 233 | , &[_][]const u8{ | 89 | , &[_][]const u8{ |
| 234 | ":1:1: error: expected type expression, found '+'", | 90 | ":1:1: error: expected type expression, found '+'", |
| 235 | }); | 91 | }); |
| 92 | case.addSourceFile("b.zig", | ||
| 93 | \\+ | ||
| 94 | ); | ||
| 236 | } | 95 | } |
| 237 | 96 | ||
| 238 | { | 97 | { |
| 239 | const case = ctx.obj("invalid store to comptime field", .{}); | 98 | const case = ctx.obj("invalid store to comptime field", .{}); |
| 240 | case.backend = .stage2; | ||
| 241 | |||
| 242 | case.addSourceFile("a.zig", | ||
| 243 | \\pub const S = struct { | ||
| 244 | \\ comptime foo: u32 = 1, | ||
| 245 | \\ bar: u32, | ||
| 246 | \\ pub fn foo(x: @This()) void { | ||
| 247 | \\ _ = x; | ||
| 248 | \\ } | ||
| 249 | \\}; | ||
| 250 | ); | ||
| 251 | 99 | ||
| 252 | case.addError( | 100 | case.addError( |
| 253 | \\const a = @import("a.zig"); | 101 | \\const a = @import("a.zig"); |
| ... | @@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void { |
| 259 | ":4:23: error: value stored in comptime field does not match the default value of the field", | 107 | ":4:23: error: value stored in comptime field does not match the default value of the field", |
| 260 | ":2:25: note: default value set here", | 108 | ":2:25: note: default value set here", |
| 261 | }); | 109 | }); |
| 110 | case.addSourceFile("a.zig", | ||
| 111 | \\pub const S = struct { | ||
| 112 | \\ comptime foo: u32 = 1, | ||
| 113 | \\ bar: u32, | ||
| 114 | \\ pub fn foo(x: @This()) void { | ||
| 115 | \\ _ = x; | ||
| 116 | \\ } | ||
| 117 | \\}; | ||
| 118 | ); | ||
| 262 | } | 119 | } |
| 263 | 120 | ||
| 264 | // TODO test this in stage2, but we won't even try in stage1 | ||
| 265 | //ctx.objErrStage1("inline fn calls itself indirectly", | ||
| 266 | // \\export fn foo() void { | ||
| 267 | // \\ bar(); | ||
| 268 | // \\} | ||
| 269 | // \\fn bar() callconv(.Inline) void { | ||
| 270 | // \\ baz(); | ||
| 271 | // \\ quux(); | ||
| 272 | // \\} | ||
| 273 | // \\fn baz() callconv(.Inline) void { | ||
| 274 | // \\ bar(); | ||
| 275 | // \\ quux(); | ||
| 276 | // \\} | ||
| 277 | // \\extern fn quux() void; | ||
| 278 | //, &[_][]const u8{ | ||
| 279 | // "tmp.zig:4:1: error: unable to inline function", | ||
| 280 | //}); | ||
| 281 | |||
| 282 | //ctx.objErrStage1("save reference to inline function", | ||
| 283 | // \\export fn foo() void { | ||
| 284 | // \\ quux(@ptrToInt(bar)); | ||
| 285 | // \\} | ||
| 286 | // \\fn bar() callconv(.Inline) void { } | ||
| 287 | // \\extern fn quux(usize) void; | ||
| 288 | //, &[_][]const u8{ | ||
| 289 | // "tmp.zig:4:1: error: unable to inline function", | ||
| 290 | //}); | ||
| 291 | |||
| 292 | { | 121 | { |
| 293 | const case = ctx.obj("file in multiple modules", .{}); | 122 | const case = ctx.obj("file in multiple modules", .{}); |
| 294 | case.backend = .stage2; | ||
| 295 | |||
| 296 | case.addSourceFile("foo.zig", | ||
| 297 | \\const dummy = 0; | ||
| 298 | ); | ||
| 299 | |||
| 300 | case.addDepModule("foo", "foo.zig"); | 123 | case.addDepModule("foo", "foo.zig"); |
| 301 | 124 | ||
| 302 | case.addError( | 125 | case.addError( |
| ... | @@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 309 | ":1:1: note: root of module root.foo", | 132 | ":1:1: note: root of module root.foo", |
| 310 | ":3:17: note: imported from module root", | 133 | ":3:17: note: imported from module root", |
| 311 | }); | 134 | }); |
| 135 | case.addSourceFile("foo.zig", | ||
| 136 | \\const dummy = 0; | ||
| 137 | ); | ||
| 312 | } | 138 | } |
| 313 | } | 139 | } |
test/link.zig+172-213| ... | @@ -1,213 +1,172 @@ | ... | @@ -1,213 +1,172 @@ |
| 1 | const std = @import("std"); | 1 | pub const Case = struct { |
| 2 | const builtin = @import("builtin"); | 2 | build_root: []const u8, |
| 3 | const tests = @import("tests.zig"); | 3 | import: type, |
| 4 | 4 | }; | |
| 5 | pub fn addCases(cases: *tests.StandaloneContext) void { | 5 | |
| 6 | cases.addBuildFile("test/link/bss/build.zig", .{ | 6 | pub const cases = [_]Case{ |
| 7 | .build_modes = false, // we only guarantee zerofill for undefined in Debug | 7 | .{ |
| 8 | }); | 8 | .build_root = "test/link/bss", |
| 9 | 9 | .import = @import("link/bss/build.zig"), | |
| 10 | cases.addBuildFile("test/link/common_symbols/build.zig", .{ | 10 | }, |
| 11 | .build_modes = true, | 11 | .{ |
| 12 | }); | 12 | .build_root = "test/link/common_symbols", |
| 13 | 13 | .import = @import("link/common_symbols/build.zig"), | |
| 14 | cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{ | 14 | }, |
| 15 | .build_modes = true, | 15 | .{ |
| 16 | }); | 16 | .build_root = "test/link/common_symbols_alignment", |
| 17 | 17 | .import = @import("link/common_symbols_alignment/build.zig"), | |
| 18 | cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{ | 18 | }, |
| 19 | .build_modes = true, | 19 | .{ |
| 20 | }); | 20 | .build_root = "test/link/interdependent_static_c_libs", |
| 21 | 21 | .import = @import("link/interdependent_static_c_libs/build.zig"), | |
| 22 | cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{ | 22 | }, |
| 23 | .build_modes = true, | 23 | |
| 24 | }); | 24 | // WASM Cases |
| 25 | 25 | .{ | |
| 26 | addWasmCases(cases); | 26 | .build_root = "test/link/wasm/archive", |
| 27 | addMachOCases(cases); | 27 | .import = @import("link/wasm/archive/build.zig"), |
| 28 | } | 28 | }, |
| 29 | 29 | .{ | |
| 30 | fn addWasmCases(cases: *tests.StandaloneContext) void { | 30 | .build_root = "test/link/wasm/basic-features", |
| 31 | cases.addBuildFile("test/link/wasm/archive/build.zig", .{ | 31 | .import = @import("link/wasm/basic-features/build.zig"), |
| 32 | .build_modes = true, | 32 | }, |
| 33 | .requires_stage2 = true, | 33 | .{ |
| 34 | }); | 34 | .build_root = "test/link/wasm/bss", |
| 35 | 35 | .import = @import("link/wasm/bss/build.zig"), | |
| 36 | cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{ | 36 | }, |
| 37 | .requires_stage2 = true, | 37 | .{ |
| 38 | }); | 38 | .build_root = "test/link/wasm/export", |
| 39 | 39 | .import = @import("link/wasm/export/build.zig"), | |
| 40 | cases.addBuildFile("test/link/wasm/bss/build.zig", .{ | 40 | }, |
| 41 | .build_modes = false, | 41 | .{ |
| 42 | .requires_stage2 = true, | 42 | .build_root = "test/link/wasm/export-data", |
| 43 | }); | 43 | .import = @import("link/wasm/export-data/build.zig"), |
| 44 | 44 | }, | |
| 45 | cases.addBuildFile("test/link/wasm/export/build.zig", .{ | 45 | .{ |
| 46 | .build_modes = true, | 46 | .build_root = "test/link/wasm/extern", |
| 47 | .requires_stage2 = true, | 47 | .import = @import("link/wasm/extern/build.zig"), |
| 48 | }); | 48 | }, |
| 49 | 49 | .{ | |
| 50 | // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. | 50 | .build_root = "test/link/wasm/extern-mangle", |
| 51 | if (builtin.os.tag != .windows) { | 51 | .import = @import("link/wasm/extern-mangle/build.zig"), |
| 52 | cases.addBuildFile("test/link/wasm/export-data/build.zig", .{}); | 52 | }, |
| 53 | } | 53 | .{ |
| 54 | 54 | .build_root = "test/link/wasm/function-table", | |
| 55 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ | 55 | .import = @import("link/wasm/function-table/build.zig"), |
| 56 | .build_modes = true, | 56 | }, |
| 57 | .requires_stage2 = true, | 57 | .{ |
| 58 | .use_emulation = true, | 58 | .build_root = "test/link/wasm/infer-features", |
| 59 | }); | 59 | .import = @import("link/wasm/infer-features/build.zig"), |
| 60 | 60 | }, | |
| 61 | cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{ | 61 | .{ |
| 62 | .build_modes = true, | 62 | .build_root = "test/link/wasm/producers", |
| 63 | .requires_stage2 = true, | 63 | .import = @import("link/wasm/producers/build.zig"), |
| 64 | }); | 64 | }, |
| 65 | 65 | .{ | |
| 66 | cases.addBuildFile("test/link/wasm/function-table/build.zig", .{ | 66 | .build_root = "test/link/wasm/segments", |
| 67 | .build_modes = true, | 67 | .import = @import("link/wasm/segments/build.zig"), |
| 68 | .requires_stage2 = true, | 68 | }, |
| 69 | }); | 69 | .{ |
| 70 | 70 | .build_root = "test/link/wasm/stack_pointer", | |
| 71 | cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{ | 71 | .import = @import("link/wasm/stack_pointer/build.zig"), |
| 72 | .requires_stage2 = true, | 72 | }, |
| 73 | }); | 73 | .{ |
| 74 | 74 | .build_root = "test/link/wasm/type", | |
| 75 | cases.addBuildFile("test/link/wasm/producers/build.zig", .{ | 75 | .import = @import("link/wasm/type/build.zig"), |
| 76 | .build_modes = true, | 76 | }, |
| 77 | .requires_stage2 = true, | 77 | |
| 78 | }); | 78 | // Mach-O Cases |
| 79 | 79 | .{ | |
| 80 | cases.addBuildFile("test/link/wasm/segments/build.zig", .{ | 80 | .build_root = "test/link/macho/bugs/13056", |
| 81 | .build_modes = true, | 81 | .import = @import("link/macho/bugs/13056/build.zig"), |
| 82 | .requires_stage2 = true, | 82 | }, |
| 83 | }); | 83 | .{ |
| 84 | 84 | .build_root = "test/link/macho/bugs/13457", | |
| 85 | cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ | 85 | .import = @import("link/macho/bugs/13457/build.zig"), |
| 86 | .build_modes = true, | 86 | }, |
| 87 | .requires_stage2 = true, | 87 | .{ |
| 88 | }); | 88 | .build_root = "test/link/macho/dead_strip", |
| 89 | 89 | .import = @import("link/macho/dead_strip/build.zig"), | |
| 90 | cases.addBuildFile("test/link/wasm/type/build.zig", .{ | 90 | }, |
| 91 | .build_modes = true, | 91 | .{ |
| 92 | .requires_stage2 = true, | 92 | .build_root = "test/link/macho/dead_strip_dylibs", |
| 93 | }); | 93 | .import = @import("link/macho/dead_strip_dylibs/build.zig"), |
| 94 | } | 94 | }, |
| 95 | 95 | .{ | |
| 96 | fn addMachOCases(cases: *tests.StandaloneContext) void { | 96 | .build_root = "test/link/macho/dylib", |
| 97 | cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ | 97 | .import = @import("link/macho/dylib/build.zig"), |
| 98 | .build_modes = true, | 98 | }, |
| 99 | .requires_macos_sdk = true, | 99 | .{ |
| 100 | .requires_symlinks = true, | 100 | .build_root = "test/link/macho/empty", |
| 101 | }); | 101 | .import = @import("link/macho/empty/build.zig"), |
| 102 | 102 | }, | |
| 103 | cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ | 103 | .{ |
| 104 | .build_modes = true, | 104 | .build_root = "test/link/macho/entry", |
| 105 | .requires_symlinks = true, | 105 | .import = @import("link/macho/entry/build.zig"), |
| 106 | }); | 106 | }, |
| 107 | 107 | .{ | |
| 108 | cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ | 108 | .build_root = "test/link/macho/headerpad", |
| 109 | .build_modes = false, | 109 | .import = @import("link/macho/headerpad/build.zig"), |
| 110 | .requires_symlinks = true, | 110 | }, |
| 111 | }); | 111 | .{ |
| 112 | 112 | .build_root = "test/link/macho/linksection", | |
| 113 | cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ | 113 | .import = @import("link/macho/linksection/build.zig"), |
| 114 | .build_modes = true, | 114 | }, |
| 115 | .requires_macos_sdk = true, | 115 | .{ |
| 116 | .requires_symlinks = true, | 116 | .build_root = "test/link/macho/needed_framework", |
| 117 | }); | 117 | .import = @import("link/macho/needed_framework/build.zig"), |
| 118 | 118 | }, | |
| 119 | cases.addBuildFile("test/link/macho/dylib/build.zig", .{ | 119 | .{ |
| 120 | .build_modes = true, | 120 | .build_root = "test/link/macho/needed_library", |
| 121 | .requires_symlinks = true, | 121 | .import = @import("link/macho/needed_library/build.zig"), |
| 122 | }); | 122 | }, |
| 123 | 123 | .{ | |
| 124 | cases.addBuildFile("test/link/macho/empty/build.zig", .{ | 124 | .build_root = "test/link/macho/objc", |
| 125 | .build_modes = true, | 125 | .import = @import("link/macho/objc/build.zig"), |
| 126 | .requires_symlinks = true, | 126 | }, |
| 127 | }); | 127 | .{ |
| 128 | 128 | .build_root = "test/link/macho/objcpp", | |
| 129 | cases.addBuildFile("test/link/macho/entry/build.zig", .{ | 129 | .import = @import("link/macho/objcpp/build.zig"), |
| 130 | .build_modes = true, | 130 | }, |
| 131 | .requires_symlinks = true, | 131 | .{ |
| 132 | }); | 132 | .build_root = "test/link/macho/pagezero", |
| 133 | 133 | .import = @import("link/macho/pagezero/build.zig"), | |
| 134 | cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ | 134 | }, |
| 135 | .build_modes = true, | 135 | .{ |
| 136 | .requires_macos_sdk = true, | 136 | .build_root = "test/link/macho/search_strategy", |
| 137 | .requires_symlinks = true, | 137 | .import = @import("link/macho/search_strategy/build.zig"), |
| 138 | }); | 138 | }, |
| 139 | 139 | .{ | |
| 140 | cases.addBuildFile("test/link/macho/linksection/build.zig", .{ | 140 | .build_root = "test/link/macho/stack_size", |
| 141 | .build_modes = true, | 141 | .import = @import("link/macho/stack_size/build.zig"), |
| 142 | .requires_symlinks = true, | 142 | }, |
| 143 | }); | 143 | .{ |
| 144 | 144 | .build_root = "test/link/macho/strict_validation", | |
| 145 | cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ | 145 | .import = @import("link/macho/strict_validation/build.zig"), |
| 146 | .build_modes = true, | 146 | }, |
| 147 | .requires_macos_sdk = true, | 147 | .{ |
| 148 | .requires_symlinks = true, | 148 | .build_root = "test/link/macho/tls", |
| 149 | }); | 149 | .import = @import("link/macho/tls/build.zig"), |
| 150 | 150 | }, | |
| 151 | cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ | 151 | .{ |
| 152 | .build_modes = true, | 152 | .build_root = "test/link/macho/unwind_info", |
| 153 | .requires_symlinks = true, | 153 | .import = @import("link/macho/unwind_info/build.zig"), |
| 154 | }); | 154 | }, |
| 155 | 155 | // TODO: re-enable this test. It currently has some incompatibilities with | |
| 156 | cases.addBuildFile("test/link/macho/objc/build.zig", .{ | 156 | // the new build system API. In particular, it depends on installing the build |
| 157 | .build_modes = true, | 157 | // artifacts, which should be unnecessary, and it has a custom build step that |
| 158 | .requires_macos_sdk = true, | 158 | // prints directly to stderr instead of failing the step with an error message. |
| 159 | .requires_symlinks = true, | 159 | //.{ |
| 160 | }); | 160 | // .build_root = "test/link/macho/uuid", |
| 161 | 161 | // .import = @import("link/macho/uuid/build.zig"), | |
| 162 | cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ | 162 | //}, |
| 163 | .build_modes = true, | 163 | |
| 164 | .requires_macos_sdk = true, | 164 | .{ |
| 165 | .requires_symlinks = true, | 165 | .build_root = "test/link/macho/weak_library", |
| 166 | }); | 166 | .import = @import("link/macho/weak_library/build.zig"), |
| 167 | 167 | }, | |
| 168 | cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ | 168 | .{ |
| 169 | .build_modes = false, | 169 | .build_root = "test/link/macho/weak_framework", |
| 170 | .requires_symlinks = true, | 170 | .import = @import("link/macho/weak_framework/build.zig"), |
| 171 | }); | 171 | }, |
| 172 | 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 | } |
test/link/bss/build.zig+3-3| ... | @@ -1,17 +1,17 @@ | ... | @@ -1,17 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 5 | b.default_step = test_step; | ||
| 6 | 6 | ||
| 7 | const exe = b.addExecutable(.{ | 7 | const exe = b.addExecutable(.{ |
| 8 | .name = "bss", | 8 | .name = "bss", |
| 9 | .root_source_file = .{ .path = "main.zig" }, | 9 | .root_source_file = .{ .path = "main.zig" }, |
| 10 | .optimize = optimize, | 10 | .optimize = .Debug, |
| 11 | }); | 11 | }); |
| 12 | b.default_step.dependOn(&exe.step); | ||
| 13 | 12 | ||
| 14 | const run = exe.run(); | 13 | const run = exe.run(); |
| 15 | run.expectStdOutEqual("0, 1, 0\n"); | 14 | run.expectStdOutEqual("0, 1, 0\n"); |
| 15 | |||
| 16 | test_step.dependOn(&run.step); | 16 | test_step.dependOn(&run.step); |
| 17 | } | 17 | } |
test/link/bss/main.zig+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | // Stress test zerofill layout | 3 | // Stress test zerofill layout |
| 4 | var buffer: [0x1000000]u64 = undefined; | 4 | var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; |
| 5 | 5 | ||
| 6 | pub fn main() anyerror!void { | 6 | pub fn main() anyerror!void { |
| 7 | buffer[0x10] = 1; | 7 | buffer[0x10] = 1; |
test/link/common_symbols/build.zig+10-3| ... | @@ -1,8 +1,16 @@ | ... | @@ -1,8 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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 | } | ||
| 5 | 12 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 6 | const lib_a = b.addStaticLibrary(.{ | 14 | const lib_a = b.addStaticLibrary(.{ |
| 7 | .name = "a", | 15 | .name = "a", |
| 8 | .optimize = optimize, | 16 | .optimize = optimize, |
| ... | @@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void { |
| 16 | }); | 24 | }); |
| 17 | test_exe.linkLibrary(lib_a); | 25 | test_exe.linkLibrary(lib_a); |
| 18 | 26 | ||
| 19 | const test_step = b.step("test", "Test it"); | 27 | test_step.dependOn(&test_exe.run().step); |
| 20 | test_step.dependOn(&test_exe.step); | ||
| 21 | } | 28 | } |
test/link/common_symbols_alignment/build.zig+11-6| ... | @@ -1,23 +1,28 @@ | ... | @@ -1,23 +1,28 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | add(b, test_step, .Debug); | ||
| 8 | add(b, test_step, .ReleaseFast); | ||
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | ||
| 6 | 12 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib_a = b.addStaticLibrary(.{ | 14 | const lib_a = b.addStaticLibrary(.{ |
| 8 | .name = "a", | 15 | .name = "a", |
| 9 | .optimize = optimize, | 16 | .optimize = optimize, |
| 10 | .target = target, | 17 | .target = .{}, |
| 11 | }); | 18 | }); |
| 12 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | 19 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); |
| 13 | 20 | ||
| 14 | const test_exe = b.addTest(.{ | 21 | const test_exe = b.addTest(.{ |
| 15 | .root_source_file = .{ .path = "main.zig" }, | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 16 | .optimize = optimize, | 23 | .optimize = optimize, |
| 17 | .target = target, | ||
| 18 | }); | 24 | }); |
| 19 | test_exe.linkLibrary(lib_a); | 25 | test_exe.linkLibrary(lib_a); |
| 20 | 26 | ||
| 21 | const test_step = b.step("test", "Test it"); | 27 | test_step.dependOn(&test_exe.run().step); |
| 22 | test_step.dependOn(&test_exe.step); | ||
| 23 | } | 28 | } |
test/link/interdependent_static_c_libs/build.zig+12-7| ... | @@ -1,13 +1,20 @@ | ... | @@ -1,13 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | add(b, test_step, .Debug); | ||
| 8 | add(b, test_step, .ReleaseFast); | ||
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | ||
| 6 | 12 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib_a = b.addStaticLibrary(.{ | 14 | const lib_a = b.addStaticLibrary(.{ |
| 8 | .name = "a", | 15 | .name = "a", |
| 9 | .optimize = optimize, | 16 | .optimize = optimize, |
| 10 | .target = target, | 17 | .target = .{}, |
| 11 | }); | 18 | }); |
| 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | 19 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); |
| 13 | lib_a.addIncludePath("."); | 20 | lib_a.addIncludePath("."); |
| ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 15 | const lib_b = b.addStaticLibrary(.{ | 22 | const lib_b = b.addStaticLibrary(.{ |
| 16 | .name = "b", | 23 | .name = "b", |
| 17 | .optimize = optimize, | 24 | .optimize = optimize, |
| 18 | .target = target, | 25 | .target = .{}, |
| 19 | }); | 26 | }); |
| 20 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | 27 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); |
| 21 | lib_b.addIncludePath("."); | 28 | lib_b.addIncludePath("."); |
| ... | @@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void { |
| 23 | const test_exe = b.addTest(.{ | 30 | const test_exe = b.addTest(.{ |
| 24 | .root_source_file = .{ .path = "main.zig" }, | 31 | .root_source_file = .{ .path = "main.zig" }, |
| 25 | .optimize = optimize, | 32 | .optimize = optimize, |
| 26 | .target = target, | ||
| 27 | }); | 33 | }); |
| 28 | test_exe.linkLibrary(lib_a); | 34 | test_exe.linkLibrary(lib_a); |
| 29 | test_exe.linkLibrary(lib_b); | 35 | test_exe.linkLibrary(lib_b); |
| 30 | test_exe.addIncludePath("."); | 36 | test_exe.addIncludePath("."); |
| 31 | 37 | ||
| 32 | const test_step = b.step("test", "Test it"); | 38 | test_step.dependOn(&test_exe.run().step); |
| 33 | test_step.dependOn(&test_exe.step); | ||
| 34 | } | 39 | } |
test/link/macho/bugs/13056/build.zig+12-4| ... | @@ -1,20 +1,28 @@ | ... | @@ -1,20 +1,28 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_macos_sdk = true; | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub 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 | } | ||
| 5 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 17 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; | 18 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; |
| 8 | const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse | 19 | const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse |
| 9 | @panic("macOS SDK is required to run the test"); | 20 | @panic("macOS SDK is required to run the test"); |
| 10 | 21 | ||
| 11 | const test_step = b.step("test", "Test the program"); | ||
| 12 | |||
| 13 | const exe = b.addExecutable(.{ | 22 | const exe = b.addExecutable(.{ |
| 14 | .name = "test", | 23 | .name = "test", |
| 15 | .optimize = optimize, | 24 | .optimize = optimize, |
| 16 | }); | 25 | }); |
| 17 | b.default_step.dependOn(&exe.step); | ||
| 18 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); | 26 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); |
| 19 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); | 27 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); |
| 20 | exe.addCSourceFile("test.cpp", &.{ | 28 | exe.addCSourceFile("test.cpp", &.{ |
test/link/macho/bugs/13457/build.zig+16-4| ... | @@ -1,10 +1,19 @@ | ... | @@ -1,10 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 8 | 17 | ||
| 9 | const exe = b.addExecutable(.{ | 18 | const exe = b.addExecutable(.{ |
| 10 | .name = "test", | 19 | .name = "test", |
| ... | @@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void { |
| 13 | .target = target, | 22 | .target = target, |
| 14 | }); | 23 | }); |
| 15 | 24 | ||
| 16 | const run = exe.runEmulatable(); | 25 | const run = b.addRunArtifact(exe); |
| 26 | run.skip_foreign_checks = true; | ||
| 27 | run.expectStdOutEqual(""); | ||
| 28 | |||
| 17 | test_step.dependOn(&run.step); | 29 | test_step.dependOn(&run.step); |
| 18 | } | 30 | } |
test/link/macho/dead_strip/build.zig+10-7| ... | @@ -1,17 +1,19 @@ | ... | @@ -1,17 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | const test_step = b.step("test", "Test the program"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | b.default_step = test_step; |
| 9 | 11 | ||
| 10 | { | 12 | { |
| 11 | // Without -dead_strip, we expect `iAmUnused` symbol present | 13 | // Without -dead_strip, we expect `iAmUnused` symbol present |
| 12 | const exe = createScenario(b, optimize, target); | 14 | const exe = createScenario(b, optimize, target, "no-gc"); |
| 13 | 15 | ||
| 14 | const check = exe.checkObject(.macho); | 16 | const check = exe.checkObject(); |
| 15 | check.checkInSymtab(); | 17 | check.checkInSymtab(); |
| 16 | check.checkNext("{*} (__TEXT,__text) external _iAmUnused"); | 18 | check.checkNext("{*} (__TEXT,__text) external _iAmUnused"); |
| 17 | 19 | ||
| ... | @@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void { |
| 22 | 24 | ||
| 23 | { | 25 | { |
| 24 | // With -dead_strip, no `iAmUnused` symbol should be present | 26 | // With -dead_strip, no `iAmUnused` symbol should be present |
| 25 | const exe = createScenario(b, optimize, target); | 27 | const exe = createScenario(b, optimize, target, "yes-gc"); |
| 26 | exe.link_gc_sections = true; | 28 | exe.link_gc_sections = true; |
| 27 | 29 | ||
| 28 | const check = exe.checkObject(.macho); | 30 | const check = exe.checkObject(); |
| 29 | check.checkInSymtab(); | 31 | check.checkInSymtab(); |
| 30 | check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused"); | 32 | check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused"); |
| 31 | 33 | ||
| ... | @@ -39,9 +41,10 @@ fn createScenario( | ... | @@ -39,9 +41,10 @@ fn createScenario( |
| 39 | b: *std.Build, | 41 | b: *std.Build, |
| 40 | optimize: std.builtin.OptimizeMode, | 42 | optimize: std.builtin.OptimizeMode, |
| 41 | target: std.zig.CrossTarget, | 43 | target: std.zig.CrossTarget, |
| 44 | name: []const u8, | ||
| 42 | ) *std.Build.CompileStep { | 45 | ) *std.Build.CompileStep { |
| 43 | const exe = b.addExecutable(.{ | 46 | const exe = b.addExecutable(.{ |
| 44 | .name = "test", | 47 | .name = name, |
| 45 | .optimize = optimize, | 48 | .optimize = optimize, |
| 46 | .target = target, | 49 | .target = target, |
| 47 | }); | 50 | }); |
test/link/macho/dead_strip_dylibs/build.zig+22-10| ... | @@ -1,16 +1,24 @@ | ... | @@ -1,16 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_macos_sdk = true; | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub 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; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 8 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | { | 17 | { |
| 10 | // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable | 18 | // 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"); |
| 12 | 20 | ||
| 13 | const check = exe.checkObject(.macho); | 21 | const check = exe.checkObject(); |
| 14 | check.checkStart("cmd LOAD_DYLIB"); | 22 | check.checkStart("cmd LOAD_DYLIB"); |
| 15 | check.checkNext("name {*}Cocoa"); | 23 | check.checkNext("name {*}Cocoa"); |
| 16 | 24 | ||
| ... | @@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void { | ... | @@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void { |
| 25 | 33 | ||
| 26 | { | 34 | { |
| 27 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable | 35 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable |
| 28 | const exe = createScenario(b, optimize); | 36 | const exe = createScenario(b, optimize, "yes-dead-strip"); |
| 29 | exe.dead_strip_dylibs = true; | 37 | exe.dead_strip_dylibs = true; |
| 30 | 38 | ||
| 31 | const run_cmd = exe.run(); | 39 | const run_cmd = b.addRunArtifact(exe); |
| 32 | run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail | 40 | run_cmd.expectExitCode(@bitCast(u8, @as(i8, -2))); // should fail |
| 33 | test_step.dependOn(&run_cmd.step); | 41 | test_step.dependOn(&run_cmd.step); |
| 34 | } | 42 | } |
| 35 | } | 43 | } |
| 36 | 44 | ||
| 37 | fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { | 45 | fn createScenario( |
| 46 | b: *std.Build, | ||
| 47 | optimize: std.builtin.OptimizeMode, | ||
| 48 | name: []const u8, | ||
| 49 | ) *std.Build.CompileStep { | ||
| 38 | const exe = b.addExecutable(.{ | 50 | const exe = b.addExecutable(.{ |
| 39 | .name = "test", | 51 | .name = name, |
| 40 | .optimize = optimize, | 52 | .optimize = optimize, |
| 41 | }); | 53 | }); |
| 42 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 54 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
test/link/macho/dylib/build.zig+20-11| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const dylib = b.addSharedLibrary(.{ | 18 | const dylib = b.addSharedLibrary(.{ |
| 11 | .name = "a", | 19 | .name = "a", |
| ... | @@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void { |
| 15 | }); | 23 | }); |
| 16 | dylib.addCSourceFile("a.c", &.{}); | 24 | dylib.addCSourceFile("a.c", &.{}); |
| 17 | dylib.linkLibC(); | 25 | dylib.linkLibC(); |
| 18 | dylib.install(); | ||
| 19 | 26 | ||
| 20 | const check_dylib = dylib.checkObject(.macho); | 27 | const check_dylib = dylib.checkObject(); |
| 21 | check_dylib.checkStart("cmd ID_DYLIB"); | 28 | check_dylib.checkStart("cmd ID_DYLIB"); |
| 22 | check_dylib.checkNext("name @rpath/liba.dylib"); | 29 | check_dylib.checkNext("name @rpath/liba.dylib"); |
| 23 | check_dylib.checkNext("timestamp 2"); | 30 | check_dylib.checkNext("timestamp 2"); |
| ... | @@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void { |
| 33 | }); | 40 | }); |
| 34 | exe.addCSourceFile("main.c", &.{}); | 41 | exe.addCSourceFile("main.c", &.{}); |
| 35 | exe.linkSystemLibrary("a"); | 42 | exe.linkSystemLibrary("a"); |
| 43 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); | ||
| 44 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); | ||
| 36 | exe.linkLibC(); | 45 | exe.linkLibC(); |
| 37 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); | ||
| 38 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | ||
| 39 | 46 | ||
| 40 | const check_exe = exe.checkObject(.macho); | 47 | const check_exe = exe.checkObject(); |
| 41 | check_exe.checkStart("cmd LOAD_DYLIB"); | 48 | check_exe.checkStart("cmd LOAD_DYLIB"); |
| 42 | check_exe.checkNext("name @rpath/liba.dylib"); | 49 | check_exe.checkNext("name @rpath/liba.dylib"); |
| 43 | check_exe.checkNext("timestamp 2"); | 50 | check_exe.checkNext("timestamp 2"); |
| ... | @@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void { | ... | @@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void { |
| 45 | check_exe.checkNext("compatibility version 10000"); | 52 | check_exe.checkNext("compatibility version 10000"); |
| 46 | 53 | ||
| 47 | check_exe.checkStart("cmd RPATH"); | 54 | check_exe.checkStart("cmd RPATH"); |
| 48 | check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); | 55 | // TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`) |
| 56 | //check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{ | ||
| 57 | // b.pathFromRoot("zig-out/lib"), | ||
| 58 | //}) catch unreachable); | ||
| 49 | 59 | ||
| 50 | const run = check_exe.runAndCompare(); | 60 | const run = check_exe.runAndCompare(); |
| 51 | run.cwd = b.pathFromRoot("."); | ||
| 52 | run.expectStdOutEqual("Hello world"); | 61 | run.expectStdOutEqual("Hello world"); |
| 53 | test_step.dependOn(&run.step); | 62 | test_step.dependOn(&run.step); |
| 54 | } | 63 | } |
test/link/macho/empty/build.zig+14-5| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const exe = b.addExecutable(.{ | 18 | const exe = b.addExecutable(.{ |
| 11 | .name = "test", | 19 | .name = "test", |
| ... | @@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void { |
| 16 | exe.addCSourceFile("empty.c", &[0][]const u8{}); | 24 | exe.addCSourceFile("empty.c", &[0][]const u8{}); |
| 17 | exe.linkLibC(); | 25 | exe.linkLibC(); |
| 18 | 26 | ||
| 19 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | 27 | const run_cmd = b.addRunArtifact(exe); |
| 28 | run_cmd.skip_foreign_checks = true; | ||
| 20 | run_cmd.expectStdOutEqual("Hello!\n"); | 29 | run_cmd.expectStdOutEqual("Hello!\n"); |
| 21 | test_step.dependOn(&run_cmd.step); | 30 | test_step.dependOn(&run_cmd.step); |
| 22 | } | 31 | } |
test/link/macho/entry/build.zig+11-4| ... | @@ -1,11 +1,18 @@ | ... | @@ -1,11 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub 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; | ||
| 5 | 8 | ||
| 6 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 8 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const exe = b.addExecutable(.{ | 16 | const exe = b.addExecutable(.{ |
| 10 | .name = "main", | 17 | .name = "main", |
| 11 | .optimize = optimize, | 18 | .optimize = optimize, |
| ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 15 | exe.linkLibC(); | 22 | exe.linkLibC(); |
| 16 | exe.entry_symbol_name = "_non_main"; | 23 | exe.entry_symbol_name = "_non_main"; |
| 17 | 24 | ||
| 18 | const check_exe = exe.checkObject(.macho); | 25 | const check_exe = exe.checkObject(); |
| 19 | 26 | ||
| 20 | check_exe.checkStart("segname __TEXT"); | 27 | check_exe.checkStart("segname __TEXT"); |
| 21 | check_exe.checkNext("vmaddr {vmaddr}"); | 28 | check_exe.checkNext("vmaddr {vmaddr}"); |
test/link/macho/headerpad/build.zig+25-13| ... | @@ -1,18 +1,26 @@ | ... | @@ -1,18 +1,26 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | pub const requires_macos_sdk = true; | ||
| 6 | |||
| 4 | pub fn build(b: *std.Build) void { | 7 | pub 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; | ||
| 6 | 10 | ||
| 7 | const test_step = b.step("test", "Test"); | 11 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 12 | add(b, test_step, .ReleaseFast); |
| 13 | add(b, test_step, .ReleaseSmall); | ||
| 14 | add(b, test_step, .ReleaseSafe); | ||
| 15 | } | ||
| 9 | 16 | ||
| 17 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 10 | { | 18 | { |
| 11 | // Test -headerpad_max_install_names | 19 | // Test -headerpad_max_install_names |
| 12 | const exe = simpleExe(b, optimize); | 20 | const exe = simpleExe(b, optimize, "headerpad_max_install_names"); |
| 13 | exe.headerpad_max_install_names = true; | 21 | exe.headerpad_max_install_names = true; |
| 14 | 22 | ||
| 15 | const check = exe.checkObject(.macho); | 23 | const check = exe.checkObject(); |
| 16 | check.checkStart("sectname __text"); | 24 | check.checkStart("sectname __text"); |
| 17 | check.checkNext("offset {offset}"); | 25 | check.checkNext("offset {offset}"); |
| 18 | 26 | ||
| ... | @@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void { |
| 34 | 42 | ||
| 35 | { | 43 | { |
| 36 | // Test -headerpad | 44 | // Test -headerpad |
| 37 | const exe = simpleExe(b, optimize); | 45 | const exe = simpleExe(b, optimize, "headerpad"); |
| 38 | exe.headerpad_size = 0x10000; | 46 | exe.headerpad_size = 0x10000; |
| 39 | 47 | ||
| 40 | const check = exe.checkObject(.macho); | 48 | const check = exe.checkObject(); |
| 41 | check.checkStart("sectname __text"); | 49 | check.checkStart("sectname __text"); |
| 42 | check.checkNext("offset {offset}"); | 50 | check.checkNext("offset {offset}"); |
| 43 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); | 51 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); |
| ... | @@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void { |
| 50 | 58 | ||
| 51 | { | 59 | { |
| 52 | // Test both flags with -headerpad overriding -headerpad_max_install_names | 60 | // Test both flags with -headerpad overriding -headerpad_max_install_names |
| 53 | const exe = simpleExe(b, optimize); | 61 | const exe = simpleExe(b, optimize, "headerpad_overriding"); |
| 54 | exe.headerpad_max_install_names = true; | 62 | exe.headerpad_max_install_names = true; |
| 55 | exe.headerpad_size = 0x10000; | 63 | exe.headerpad_size = 0x10000; |
| 56 | 64 | ||
| 57 | const check = exe.checkObject(.macho); | 65 | const check = exe.checkObject(); |
| 58 | check.checkStart("sectname __text"); | 66 | check.checkStart("sectname __text"); |
| 59 | check.checkNext("offset {offset}"); | 67 | check.checkNext("offset {offset}"); |
| 60 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); | 68 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); |
| ... | @@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void { |
| 67 | 75 | ||
| 68 | { | 76 | { |
| 69 | // Test both flags with -headerpad_max_install_names overriding -headerpad | 77 | // Test both flags with -headerpad_max_install_names overriding -headerpad |
| 70 | const exe = simpleExe(b, optimize); | 78 | const exe = simpleExe(b, optimize, "headerpad_max_install_names_overriding"); |
| 71 | exe.headerpad_size = 0x1000; | 79 | exe.headerpad_size = 0x1000; |
| 72 | exe.headerpad_max_install_names = true; | 80 | exe.headerpad_max_install_names = true; |
| 73 | 81 | ||
| 74 | const check = exe.checkObject(.macho); | 82 | const check = exe.checkObject(); |
| 75 | check.checkStart("sectname __text"); | 83 | check.checkStart("sectname __text"); |
| 76 | check.checkNext("offset {offset}"); | 84 | check.checkNext("offset {offset}"); |
| 77 | 85 | ||
| ... | @@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void { | ... | @@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void { |
| 92 | } | 100 | } |
| 93 | } | 101 | } |
| 94 | 102 | ||
| 95 | fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { | 103 | fn simpleExe( |
| 104 | b: *std.Build, | ||
| 105 | optimize: std.builtin.OptimizeMode, | ||
| 106 | name: []const u8, | ||
| 107 | ) *std.Build.CompileStep { | ||
| 96 | const exe = b.addExecutable(.{ | 108 | const exe = b.addExecutable(.{ |
| 97 | .name = "main", | 109 | .name = name, |
| 98 | .optimize = optimize, | 110 | .optimize = optimize, |
| 99 | }); | 111 | }); |
| 100 | exe.addCSourceFile("main.c", &.{}); | 112 | exe.addCSourceFile("main.c", &.{}); |
test/link/macho/linksection/build.zig+13-5| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target = std.zig.CrossTarget{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target = std.zig.CrossTarget{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const obj = b.addObject(.{ | 18 | const obj = b.addObject(.{ |
| 11 | .name = "test", | 19 | .name = "test", |
| ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 14 | .target = target, | 22 | .target = target, |
| 15 | }); | 23 | }); |
| 16 | 24 | ||
| 17 | const check = obj.checkObject(.macho); | 25 | const check = obj.checkObject(); |
| 18 | 26 | ||
| 19 | check.checkInSymtab(); | 27 | check.checkInSymtab(); |
| 20 | check.checkNext("{*} (__DATA,__TestGlobal) external _test_global"); | 28 | check.checkNext("{*} (__DATA,__TestGlobal) external _test_global"); |
test/link/macho/needed_framework/build.zig+12-4| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub 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; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 8 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | // -dead_strip_dylibs | 17 | // -dead_strip_dylibs |
| 10 | // -needed_framework Cocoa | 18 | // -needed_framework Cocoa |
| 11 | const exe = b.addExecutable(.{ | 19 | const exe = b.addExecutable(.{ |
| ... | @@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void { |
| 17 | exe.linkFrameworkNeeded("Cocoa"); | 25 | exe.linkFrameworkNeeded("Cocoa"); |
| 18 | exe.dead_strip_dylibs = true; | 26 | exe.dead_strip_dylibs = true; |
| 19 | 27 | ||
| 20 | const check = exe.checkObject(.macho); | 28 | const check = exe.checkObject(); |
| 21 | check.checkStart("cmd LOAD_DYLIB"); | 29 | check.checkStart("cmd LOAD_DYLIB"); |
| 22 | check.checkNext("name {*}Cocoa"); | 30 | check.checkNext("name {*}Cocoa"); |
| 23 | test_step.dependOn(&check.step); | 31 | test_step.dependOn(&check.step); |
test/link/macho/needed_library/build.zig+16-8| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const dylib = b.addSharedLibrary(.{ | 18 | const dylib = b.addSharedLibrary(.{ |
| 11 | .name = "a", | 19 | .name = "a", |
| ... | @@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void { |
| 15 | }); | 23 | }); |
| 16 | dylib.addCSourceFile("a.c", &.{}); | 24 | dylib.addCSourceFile("a.c", &.{}); |
| 17 | dylib.linkLibC(); | 25 | dylib.linkLibC(); |
| 18 | dylib.install(); | ||
| 19 | 26 | ||
| 20 | // -dead_strip_dylibs | 27 | // -dead_strip_dylibs |
| 21 | // -needed-la | 28 | // -needed-la |
| ... | @@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void { | ... | @@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void { |
| 27 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 34 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 28 | exe.linkLibC(); | 35 | exe.linkLibC(); |
| 29 | exe.linkSystemLibraryNeeded("a"); | 36 | exe.linkSystemLibraryNeeded("a"); |
| 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); | 37 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); |
| 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | 38 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); |
| 32 | exe.dead_strip_dylibs = true; | 39 | exe.dead_strip_dylibs = true; |
| 33 | 40 | ||
| 34 | const check = exe.checkObject(.macho); | 41 | const check = exe.checkObject(); |
| 35 | check.checkStart("cmd LOAD_DYLIB"); | 42 | check.checkStart("cmd LOAD_DYLIB"); |
| 36 | check.checkNext("name @rpath/liba.dylib"); | 43 | check.checkNext("name @rpath/liba.dylib"); |
| 37 | 44 | ||
| 38 | const run_cmd = check.runAndCompare(); | 45 | const run_cmd = check.runAndCompare(); |
| 46 | run_cmd.expectStdOutEqual(""); | ||
| 39 | test_step.dependOn(&run_cmd.step); | 47 | test_step.dependOn(&run_cmd.step); |
| 40 | } | 48 | } |
test/link/macho/objc/build.zig+14-3| ... | @@ -1,10 +1,19 @@ | ... | @@ -1,10 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub 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; | ||
| 5 | 9 | ||
| 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 | } | ||
| 7 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 8 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 9 | .name = "test", | 18 | .name = "test", |
| 10 | .optimize = optimize, | 19 | .optimize = optimize, |
| ... | @@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void { |
| 17 | // populate paths to the sysroot here. | 26 | // populate paths to the sysroot here. |
| 18 | exe.linkFramework("Foundation"); | 27 | exe.linkFramework("Foundation"); |
| 19 | 28 | ||
| 20 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | 29 | const run_cmd = b.addRunArtifact(exe); |
| 30 | run_cmd.skip_foreign_checks = true; | ||
| 31 | run_cmd.expectStdOutEqual(""); | ||
| 21 | test_step.dependOn(&run_cmd.step); | 32 | test_step.dependOn(&run_cmd.step); |
| 22 | } | 33 | } |
test/link/macho/objcpp/build.zig+11-2| ... | @@ -1,10 +1,19 @@ | ... | @@ -1,10 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub 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; | ||
| 5 | 9 | ||
| 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 | } | ||
| 7 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 8 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 9 | .name = "test", | 18 | .name = "test", |
| 10 | .optimize = optimize, | 19 | .optimize = optimize, |
test/link/macho/pagezero/build.zig+8-6| ... | @@ -1,11 +1,13 @@ | ... | @@ -1,11 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 9 | 11 | ||
| 10 | { | 12 | { |
| 11 | const exe = b.addExecutable(.{ | 13 | const exe = b.addExecutable(.{ |
| ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { |
| 17 | exe.linkLibC(); | 19 | exe.linkLibC(); |
| 18 | exe.pagezero_size = 0x4000; | 20 | exe.pagezero_size = 0x4000; |
| 19 | 21 | ||
| 20 | const check = exe.checkObject(.macho); | 22 | const check = exe.checkObject(); |
| 21 | check.checkStart("LC 0"); | 23 | check.checkStart("LC 0"); |
| 22 | check.checkNext("segname __PAGEZERO"); | 24 | check.checkNext("segname __PAGEZERO"); |
| 23 | check.checkNext("vmaddr 0"); | 25 | check.checkNext("vmaddr 0"); |
| ... | @@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void { |
| 39 | exe.linkLibC(); | 41 | exe.linkLibC(); |
| 40 | exe.pagezero_size = 0; | 42 | exe.pagezero_size = 0; |
| 41 | 43 | ||
| 42 | const check = exe.checkObject(.macho); | 44 | const check = exe.checkObject(); |
| 43 | check.checkStart("LC 0"); | 45 | check.checkStart("LC 0"); |
| 44 | check.checkNext("segname __TEXT"); | 46 | check.checkNext("segname __TEXT"); |
| 45 | check.checkNext("vmaddr 0"); | 47 | check.checkNext("vmaddr 0"); |
test/link/macho/search_strategy/build.zig+26-20| ... | @@ -1,34 +1,41 @@ | ... | @@ -1,34 +1,41 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | { | 18 | { |
| 11 | // -search_dylibs_first | 19 | // -search_dylibs_first |
| 12 | const exe = createScenario(b, optimize, target); | 20 | const exe = createScenario(b, optimize, target, "search_dylibs_first"); |
| 13 | exe.search_strategy = .dylibs_first; | 21 | exe.search_strategy = .dylibs_first; |
| 14 | 22 | ||
| 15 | const check = exe.checkObject(.macho); | 23 | const check = exe.checkObject(); |
| 16 | check.checkStart("cmd LOAD_DYLIB"); | 24 | check.checkStart("cmd LOAD_DYLIB"); |
| 17 | check.checkNext("name @rpath/liba.dylib"); | 25 | check.checkNext("name @rpath/libsearch_dylibs_first.dylib"); |
| 18 | 26 | ||
| 19 | const run = check.runAndCompare(); | 27 | const run = check.runAndCompare(); |
| 20 | run.cwd = b.pathFromRoot("."); | ||
| 21 | run.expectStdOutEqual("Hello world"); | 28 | run.expectStdOutEqual("Hello world"); |
| 22 | test_step.dependOn(&run.step); | 29 | test_step.dependOn(&run.step); |
| 23 | } | 30 | } |
| 24 | 31 | ||
| 25 | { | 32 | { |
| 26 | // -search_paths_first | 33 | // -search_paths_first |
| 27 | const exe = createScenario(b, optimize, target); | 34 | const exe = createScenario(b, optimize, target, "search_paths_first"); |
| 28 | exe.search_strategy = .paths_first; | 35 | exe.search_strategy = .paths_first; |
| 29 | 36 | ||
| 30 | const run = std.Build.EmulatableRunStep.create(b, "run", exe); | 37 | const run = b.addRunArtifact(exe); |
| 31 | run.cwd = b.pathFromRoot("."); | 38 | run.skip_foreign_checks = true; |
| 32 | run.expectStdOutEqual("Hello world"); | 39 | run.expectStdOutEqual("Hello world"); |
| 33 | test_step.dependOn(&run.step); | 40 | test_step.dependOn(&run.step); |
| 34 | } | 41 | } |
| ... | @@ -38,9 +45,10 @@ fn createScenario( | ... | @@ -38,9 +45,10 @@ fn createScenario( |
| 38 | b: *std.Build, | 45 | b: *std.Build, |
| 39 | optimize: std.builtin.OptimizeMode, | 46 | optimize: std.builtin.OptimizeMode, |
| 40 | target: std.zig.CrossTarget, | 47 | target: std.zig.CrossTarget, |
| 48 | name: []const u8, | ||
| 41 | ) *std.Build.CompileStep { | 49 | ) *std.Build.CompileStep { |
| 42 | const static = b.addStaticLibrary(.{ | 50 | const static = b.addStaticLibrary(.{ |
| 43 | .name = "a", | 51 | .name = name, |
| 44 | .optimize = optimize, | 52 | .optimize = optimize, |
| 45 | .target = target, | 53 | .target = target, |
| 46 | }); | 54 | }); |
| ... | @@ -49,10 +57,9 @@ fn createScenario( | ... | @@ -49,10 +57,9 @@ fn createScenario( |
| 49 | static.override_dest_dir = std.Build.InstallDir{ | 57 | static.override_dest_dir = std.Build.InstallDir{ |
| 50 | .custom = "static", | 58 | .custom = "static", |
| 51 | }; | 59 | }; |
| 52 | static.install(); | ||
| 53 | 60 | ||
| 54 | const dylib = b.addSharedLibrary(.{ | 61 | const dylib = b.addSharedLibrary(.{ |
| 55 | .name = "a", | 62 | .name = name, |
| 56 | .version = .{ .major = 1, .minor = 0 }, | 63 | .version = .{ .major = 1, .minor = 0 }, |
| 57 | .optimize = optimize, | 64 | .optimize = optimize, |
| 58 | .target = target, | 65 | .target = target, |
| ... | @@ -62,18 +69,17 @@ fn createScenario( | ... | @@ -62,18 +69,17 @@ fn createScenario( |
| 62 | dylib.override_dest_dir = std.Build.InstallDir{ | 69 | dylib.override_dest_dir = std.Build.InstallDir{ |
| 63 | .custom = "dynamic", | 70 | .custom = "dynamic", |
| 64 | }; | 71 | }; |
| 65 | dylib.install(); | ||
| 66 | 72 | ||
| 67 | const exe = b.addExecutable(.{ | 73 | const exe = b.addExecutable(.{ |
| 68 | .name = "main", | 74 | .name = name, |
| 69 | .optimize = optimize, | 75 | .optimize = optimize, |
| 70 | .target = target, | 76 | .target = target, |
| 71 | }); | 77 | }); |
| 72 | exe.addCSourceFile("main.c", &.{}); | 78 | exe.addCSourceFile("main.c", &.{}); |
| 73 | exe.linkSystemLibraryName("a"); | 79 | exe.linkSystemLibraryName(name); |
| 74 | exe.linkLibC(); | 80 | exe.linkLibC(); |
| 75 | exe.addLibraryPath(b.pathFromRoot("zig-out/static")); | 81 | exe.addLibraryPathDirectorySource(static.getOutputDirectorySource()); |
| 76 | exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); | 82 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); |
| 77 | exe.addRPath(b.pathFromRoot("zig-out/dynamic")); | 83 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); |
| 78 | return exe; | 84 | return exe; |
| 79 | } | 85 | } |
test/link/macho/stack_size/build.zig+14-5| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const exe = b.addExecutable(.{ | 18 | const exe = b.addExecutable(.{ |
| 11 | .name = "main", | 19 | .name = "main", |
| ... | @@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void { |
| 16 | exe.linkLibC(); | 24 | exe.linkLibC(); |
| 17 | exe.stack_size = 0x100000000; | 25 | exe.stack_size = 0x100000000; |
| 18 | 26 | ||
| 19 | const check_exe = exe.checkObject(.macho); | 27 | const check_exe = exe.checkObject(); |
| 20 | check_exe.checkStart("cmd MAIN"); | 28 | check_exe.checkStart("cmd MAIN"); |
| 21 | check_exe.checkNext("stacksize 100000000"); | 29 | check_exe.checkNext("stacksize 100000000"); |
| 22 | 30 | ||
| 23 | const run = check_exe.runAndCompare(); | 31 | const run = check_exe.runAndCompare(); |
| 32 | run.expectStdOutEqual(""); | ||
| 24 | test_step.dependOn(&run.step); | 33 | test_step.dependOn(&run.step); |
| 25 | } | 34 | } |
test/link/macho/strict_validation/build.zig+13-5| ... | @@ -1,12 +1,20 @@ | ... | @@ -1,12 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 4 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 8 | b.default_step = test_step; |
| 7 | 9 | ||
| 8 | const test_step = b.step("test", "Test"); | 10 | add(b, test_step, .Debug); |
| 9 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 15 | |||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 17 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 10 | 18 | ||
| 11 | const exe = b.addExecutable(.{ | 19 | const exe = b.addExecutable(.{ |
| 12 | .name = "main", | 20 | .name = "main", |
| ... | @@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void { |
| 16 | }); | 24 | }); |
| 17 | exe.linkLibC(); | 25 | exe.linkLibC(); |
| 18 | 26 | ||
| 19 | const check_exe = exe.checkObject(.macho); | 27 | const check_exe = exe.checkObject(); |
| 20 | 28 | ||
| 21 | check_exe.checkStart("cmd SEGMENT_64"); | 29 | check_exe.checkStart("cmd SEGMENT_64"); |
| 22 | check_exe.checkNext("segname __LINKEDIT"); | 30 | check_exe.checkNext("segname __LINKEDIT"); |
test/link/macho/tls/build.zig+16-3| ... | @@ -1,7 +1,18 @@ | ... | @@ -1,7 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub 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 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 6 | 17 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 18 | const lib = b.addSharedLibrary(.{ |
| ... | @@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void { |
| 21 | test_exe.linkLibrary(lib); | 32 | test_exe.linkLibrary(lib); |
| 22 | test_exe.linkLibC(); | 33 | test_exe.linkLibC(); |
| 23 | 34 | ||
| 24 | const test_step = b.step("test", "Test it"); | 35 | const run = test_exe.run(); |
| 25 | test_step.dependOn(&test_exe.step); | 36 | run.skip_foreign_checks = true; |
| 37 | |||
| 38 | test_step.dependOn(&run.step); | ||
| 26 | } | 39 | } |
test/link/macho/unwind_info/build.zig+19-8| ... | @@ -1,14 +1,23 @@ | ... | @@ -1,14 +1,23 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 4 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 8 | b.default_step = test_step; |
| 7 | 9 | ||
| 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 | |||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 17 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 18 | ||
| 10 | testUnwindInfo(b, test_step, optimize, target, false); | 19 | testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip"); |
| 11 | testUnwindInfo(b, test_step, optimize, target, true); | 20 | testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip"); |
| 12 | } | 21 | } |
| 13 | 22 | ||
| 14 | fn testUnwindInfo( | 23 | fn testUnwindInfo( |
| ... | @@ -17,11 +26,12 @@ fn testUnwindInfo( | ... | @@ -17,11 +26,12 @@ fn testUnwindInfo( |
| 17 | optimize: std.builtin.OptimizeMode, | 26 | optimize: std.builtin.OptimizeMode, |
| 18 | target: std.zig.CrossTarget, | 27 | target: std.zig.CrossTarget, |
| 19 | dead_strip: bool, | 28 | dead_strip: bool, |
| 29 | name: []const u8, | ||
| 20 | ) void { | 30 | ) void { |
| 21 | const exe = createScenario(b, optimize, target); | 31 | const exe = createScenario(b, optimize, target, name); |
| 22 | exe.link_gc_sections = dead_strip; | 32 | exe.link_gc_sections = dead_strip; |
| 23 | 33 | ||
| 24 | const check = exe.checkObject(.macho); | 34 | const check = exe.checkObject(); |
| 25 | check.checkStart("segname __TEXT"); | 35 | check.checkStart("segname __TEXT"); |
| 26 | check.checkNext("sectname __gcc_except_tab"); | 36 | check.checkNext("sectname __gcc_except_tab"); |
| 27 | check.checkNext("sectname __unwind_info"); | 37 | check.checkNext("sectname __unwind_info"); |
| ... | @@ -54,9 +64,10 @@ fn createScenario( | ... | @@ -54,9 +64,10 @@ fn createScenario( |
| 54 | b: *std.Build, | 64 | b: *std.Build, |
| 55 | optimize: std.builtin.OptimizeMode, | 65 | optimize: std.builtin.OptimizeMode, |
| 56 | target: std.zig.CrossTarget, | 66 | target: std.zig.CrossTarget, |
| 67 | name: []const u8, | ||
| 57 | ) *std.Build.CompileStep { | 68 | ) *std.Build.CompileStep { |
| 58 | const exe = b.addExecutable(.{ | 69 | const exe = b.addExecutable(.{ |
| 59 | .name = "test", | 70 | .name = name, |
| 60 | .optimize = optimize, | 71 | .optimize = optimize, |
| 61 | .target = target, | 72 | .target = target, |
| 62 | }); | 73 | }); |
test/link/macho/uuid/build.zig+23-62| ... | @@ -1,14 +1,16 @@ | ... | @@ -1,14 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Builder = std.Build.Builder; | ||
| 3 | const CompileStep = std.Build.CompileStep; | 2 | const CompileStep = std.Build.CompileStep; |
| 4 | const FileSource = std.Build.FileSource; | 3 | const FileSource = std.Build.FileSource; |
| 5 | const Step = std.Build.Step; | 4 | const Step = std.Build.Step; |
| 6 | 5 | ||
| 6 | pub const requires_symlinks = true; | ||
| 7 | |||
| 7 | pub fn build(b: *std.Build) void { | 8 | pub fn build(b: *std.Build) void { |
| 8 | const test_step = b.step("test", "Test"); | 9 | const test_step = b.step("test", "Test"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 10 | b.default_step = test_step; |
| 10 | 11 | ||
| 11 | // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features. | 12 | // We force cross-compilation to ensure we always pick a generic CPU with |
| 13 | // constant set of CPU features. | ||
| 12 | const aarch64_macos = std.zig.CrossTarget{ | 14 | const aarch64_macos = std.zig.CrossTarget{ |
| 13 | .cpu_arch = .aarch64, | 15 | .cpu_arch = .aarch64, |
| 14 | .os_tag = .macos, | 16 | .os_tag = .macos, |
| ... | @@ -38,13 +40,15 @@ fn testUuid( | ... | @@ -38,13 +40,15 @@ fn testUuid( |
| 38 | // stay the same across builds. | 40 | // stay the same across builds. |
| 39 | { | 41 | { |
| 40 | const dylib = simpleDylib(b, optimize, target); | 42 | const dylib = simpleDylib(b, optimize, target); |
| 41 | const install_step = installWithRename(dylib, "test1.dylib"); | 43 | const install_step = b.addInstallArtifact(dylib); |
| 44 | install_step.dest_sub_path = "test1.dylib"; | ||
| 42 | install_step.step.dependOn(&dylib.step); | 45 | install_step.step.dependOn(&dylib.step); |
| 43 | } | 46 | } |
| 44 | { | 47 | { |
| 45 | const dylib = simpleDylib(b, optimize, target); | 48 | const dylib = simpleDylib(b, optimize, target); |
| 46 | dylib.strip = true; | 49 | dylib.strip = true; |
| 47 | const install_step = installWithRename(dylib, "test2.dylib"); | 50 | const install_step = b.addInstallArtifact(dylib); |
| 51 | install_step.dest_sub_path = "test2.dylib"; | ||
| 48 | install_step.step.dependOn(&dylib.step); | 52 | install_step.step.dependOn(&dylib.step); |
| 49 | } | 53 | } |
| 50 | 54 | ||
| ... | @@ -68,86 +72,43 @@ fn simpleDylib( | ... | @@ -68,86 +72,43 @@ fn simpleDylib( |
| 68 | return dylib; | 72 | return dylib; |
| 69 | } | 73 | } |
| 70 | 74 | ||
| 71 | fn 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 | |||
| 77 | const 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 | |||
| 114 | const CompareUuid = struct { | 75 | const CompareUuid = struct { |
| 115 | pub const base_id = .custom; | 76 | pub const base_id = .custom; |
| 116 | 77 | ||
| 117 | step: Step, | 78 | step: Step, |
| 118 | builder: *Builder, | ||
| 119 | lhs: []const u8, | 79 | lhs: []const u8, |
| 120 | rhs: []const u8, | 80 | rhs: []const u8, |
| 121 | 81 | ||
| 122 | pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid { | 82 | pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid { |
| 123 | const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); | 83 | const self = owner.allocator.create(CompareUuid) catch @panic("OOM"); |
| 124 | self.* = CompareUuid{ | 84 | self.* = CompareUuid{ |
| 125 | .builder = builder, | 85 | .step = Step.init(.{ |
| 126 | .step = Step.init( | 86 | .id = base_id, |
| 127 | .custom, | 87 | .name = owner.fmt("compare uuid: {s} and {s}", .{ |
| 128 | builder.fmt("compare uuid: {s} and {s}", .{ | ||
| 129 | lhs, | 88 | lhs, |
| 130 | rhs, | 89 | rhs, |
| 131 | }), | 90 | }), |
| 132 | builder.allocator, | 91 | .owner = owner, |
| 133 | make, | 92 | .makeFn = make, |
| 134 | ), | 93 | }), |
| 135 | .lhs = lhs, | 94 | .lhs = lhs, |
| 136 | .rhs = rhs, | 95 | .rhs = rhs, |
| 137 | }; | 96 | }; |
| 138 | return self; | 97 | return self; |
| 139 | } | 98 | } |
| 140 | 99 | ||
| 141 | fn make(step: *Step) anyerror!void { | 100 | fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { |
| 101 | _ = prog_node; | ||
| 102 | const b = step.owner; | ||
| 142 | const self = @fieldParentPtr(CompareUuid, "step", step); | 103 | const self = @fieldParentPtr(CompareUuid, "step", step); |
| 143 | const gpa = self.builder.allocator; | 104 | const gpa = b.allocator; |
| 144 | 105 | ||
| 145 | var lhs_uuid: [16]u8 = undefined; | 106 | var lhs_uuid: [16]u8 = undefined; |
| 146 | const lhs_path = self.builder.getInstallPath(.lib, self.lhs); | 107 | const lhs_path = b.getInstallPath(.lib, self.lhs); |
| 147 | try parseUuid(gpa, lhs_path, &lhs_uuid); | 108 | try parseUuid(gpa, lhs_path, &lhs_uuid); |
| 148 | 109 | ||
| 149 | var rhs_uuid: [16]u8 = undefined; | 110 | var rhs_uuid: [16]u8 = undefined; |
| 150 | const rhs_path = self.builder.getInstallPath(.lib, self.rhs); | 111 | const rhs_path = b.getInstallPath(.lib, self.rhs); |
| 151 | try parseUuid(gpa, rhs_path, &rhs_uuid); | 112 | try parseUuid(gpa, rhs_path, &rhs_uuid); |
| 152 | 113 | ||
| 153 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); | 114 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); |
test/link/macho/weak_framework/build.zig+12-4| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub 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; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 8 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 10 | .name = "test", | 18 | .name = "test", |
| 11 | .optimize = optimize, | 19 | .optimize = optimize, |
| ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 14 | exe.linkLibC(); | 22 | exe.linkLibC(); |
| 15 | exe.linkFrameworkWeak("Cocoa"); | 23 | exe.linkFrameworkWeak("Cocoa"); |
| 16 | 24 | ||
| 17 | const check = exe.checkObject(.macho); | 25 | const check = exe.checkObject(); |
| 18 | check.checkStart("cmd LOAD_WEAK_DYLIB"); | 26 | check.checkStart("cmd LOAD_WEAK_DYLIB"); |
| 19 | check.checkNext("name {*}Cocoa"); | 27 | check.checkNext("name {*}Cocoa"); |
| 20 | test_step.dependOn(&check.step); | 28 | test_step.dependOn(&check.step); |
test/link/macho/weak_library/build.zig+15-7| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const dylib = b.addSharedLibrary(.{ | 18 | const dylib = b.addSharedLibrary(.{ |
| 11 | .name = "a", | 19 | .name = "a", |
| ... | @@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void { |
| 25 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 33 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 26 | exe.linkLibC(); | 34 | exe.linkLibC(); |
| 27 | exe.linkSystemLibraryWeak("a"); | 35 | exe.linkSystemLibraryWeak("a"); |
| 28 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); | 36 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); |
| 29 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | 37 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); |
| 30 | 38 | ||
| 31 | const check = exe.checkObject(.macho); | 39 | const check = exe.checkObject(); |
| 32 | check.checkStart("cmd LOAD_WEAK_DYLIB"); | 40 | check.checkStart("cmd LOAD_WEAK_DYLIB"); |
| 33 | check.checkNext("name @rpath/liba.dylib"); | 41 | check.checkNext("name @rpath/liba.dylib"); |
| 34 | 42 |
test/link/static_lib_as_system_lib/a.c deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | #include "a.h" | ||
| 2 | int32_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> | ||
| 2 | int32_t add(int32_t a, int32_t b); | ||
test/link/static_lib_as_system_lib/build.zig deleted-29| ... | @@ -1,29 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const c = @cImport(@cInclude("a.h")); | ||
| 4 | |||
| 5 | test "import C add" { | ||
| 6 | const result = c.add(2, 1); | ||
| 7 | try expect(result == 3); | ||
| 8 | } | ||
test/link/wasm/archive/build.zig+13-4| ... | @@ -1,22 +1,31 @@ | ... | @@ -1,22 +1,31 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | // The code in question will pull-in compiler-rt, | 16 | // The code in question will pull-in compiler-rt, |
| 8 | // and therefore link with its archive file. | 17 | // and therefore link with its archive file. |
| 9 | const lib = b.addSharedLibrary(.{ | 18 | const lib = b.addSharedLibrary(.{ |
| 10 | .name = "main", | 19 | .name = "main", |
| 11 | .root_source_file = .{ .path = "main.zig" }, | 20 | .root_source_file = .{ .path = "main.zig" }, |
| 12 | .optimize = b.standardOptimizeOption(.{}), | 21 | .optimize = optimize, |
| 13 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 14 | }); | 23 | }); |
| 15 | lib.use_llvm = false; | 24 | lib.use_llvm = false; |
| 16 | lib.use_lld = false; | 25 | lib.use_lld = false; |
| 17 | lib.strip = false; | 26 | lib.strip = false; |
| 18 | 27 | ||
| 19 | const check = lib.checkObject(.wasm); | 28 | const check = lib.checkObject(); |
| 20 | check.checkStart("Section custom"); | 29 | check.checkStart("Section custom"); |
| 21 | check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved | 30 | check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved |
| 22 | 31 |
test/link/wasm/basic-features/build.zig+5-2| ... | @@ -1,11 +1,13 @@ | ... | @@ -1,11 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | // Library with explicitly set cpu features | 6 | // Library with explicitly set cpu features |
| 5 | const lib = b.addSharedLibrary(.{ | 7 | const lib = b.addSharedLibrary(.{ |
| 6 | .name = "lib", | 8 | .name = "lib", |
| 7 | .root_source_file = .{ .path = "main.zig" }, | 9 | .root_source_file = .{ .path = "main.zig" }, |
| 8 | .optimize = b.standardOptimizeOption(.{}), | 10 | .optimize = .Debug, |
| 9 | .target = .{ | 11 | .target = .{ |
| 10 | .cpu_arch = .wasm32, | 12 | .cpu_arch = .wasm32, |
| 11 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | 13 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, |
| ... | @@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void { |
| 17 | lib.use_lld = false; | 19 | lib.use_lld = false; |
| 18 | 20 | ||
| 19 | // Verify the result contains the features explicitly set on the target for the library. | 21 | // Verify the result contains the features explicitly set on the target for the library. |
| 20 | const check = lib.checkObject(.wasm); | 22 | const check = lib.checkObject(); |
| 21 | check.checkStart("name target_features"); | 23 | check.checkStart("name target_features"); |
| 22 | check.checkNext("features 1"); | 24 | check.checkNext("features 1"); |
| 23 | check.checkNext("+ atomics"); | 25 | check.checkNext("+ atomics"); |
| 24 | 26 | ||
| 25 | const test_step = b.step("test", "Run linker test"); | 27 | const test_step = b.step("test", "Run linker test"); |
| 26 | test_step.dependOn(&check.step); | 28 | test_step.dependOn(&check.step); |
| 29 | b.default_step = test_step; | ||
| 27 | } | 30 | } |
test/link/wasm/bss/build.zig+6-3| ... | @@ -1,14 +1,16 @@ | ... | @@ -1,14 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 9 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 10 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 11 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 12 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 13 | .optimize = .Debug, |
| 12 | }); | 14 | }); |
| 13 | lib.use_llvm = false; | 15 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 16 | lib.use_lld = false; |
| ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { |
| 17 | lib.import_memory = true; | 19 | lib.import_memory = true; |
| 18 | lib.install(); | 20 | lib.install(); |
| 19 | 21 | ||
| 20 | const check_lib = lib.checkObject(.wasm); | 22 | const check_lib = lib.checkObject(); |
| 21 | 23 | ||
| 22 | // since we import memory, make sure it exists with the correct naming | 24 | // since we import memory, make sure it exists with the correct naming |
| 23 | check_lib.checkStart("Section import"); | 25 | check_lib.checkStart("Section import"); |
| ... | @@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void { |
| 36 | check_lib.checkNext("name .rodata"); | 38 | check_lib.checkNext("name .rodata"); |
| 37 | check_lib.checkNext("index 1"); // bss section always last | 39 | check_lib.checkNext("index 1"); // bss section always last |
| 38 | check_lib.checkNext("name .bss"); | 40 | check_lib.checkNext("name .bss"); |
| 41 | |||
| 39 | test_step.dependOn(&check_lib.step); | 42 | test_step.dependOn(&check_lib.step); |
| 40 | } | 43 | } |
test/link/wasm/export-data/build.zig+7-2| ... | @@ -2,7 +2,12 @@ const std = @import("std"); | ... | @@ -2,7 +2,12 @@ const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | if (@import("builtin").os.tag == .windows) { | ||
| 8 | // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. | ||
| 9 | return; | ||
| 10 | } | ||
| 6 | 11 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 12 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 13 | .name = "lib", |
| ... | @@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void { |
| 14 | lib.export_symbol_names = &.{ "foo", "bar" }; | 19 | lib.export_symbol_names = &.{ "foo", "bar" }; |
| 15 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse | 20 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse |
| 16 | 21 | ||
| 17 | const check_lib = lib.checkObject(.wasm); | 22 | const check_lib = lib.checkObject(); |
| 18 | 23 | ||
| 19 | check_lib.checkStart("Section global"); | 24 | check_lib.checkStart("Section global"); |
| 20 | check_lib.checkNext("entries 3"); | 25 | check_lib.checkNext("entries 3"); |
test/link/wasm/export/build.zig+14-5| ... | @@ -1,8 +1,18 @@ | ... | @@ -1,8 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub 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 | } | ||
| 5 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 6 | const no_export = b.addSharedLibrary(.{ | 16 | const no_export = b.addSharedLibrary(.{ |
| 7 | .name = "no-export", | 17 | .name = "no-export", |
| 8 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| ... | @@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void { | ... | @@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void { |
| 32 | force_export.use_llvm = false; | 42 | force_export.use_llvm = false; |
| 33 | force_export.use_lld = false; | 43 | force_export.use_lld = false; |
| 34 | 44 | ||
| 35 | const check_no_export = no_export.checkObject(.wasm); | 45 | const check_no_export = no_export.checkObject(); |
| 36 | check_no_export.checkStart("Section export"); | 46 | check_no_export.checkStart("Section export"); |
| 37 | check_no_export.checkNext("entries 1"); | 47 | check_no_export.checkNext("entries 1"); |
| 38 | check_no_export.checkNext("name memory"); | 48 | check_no_export.checkNext("name memory"); |
| 39 | check_no_export.checkNext("kind memory"); | 49 | check_no_export.checkNext("kind memory"); |
| 40 | 50 | ||
| 41 | const check_dynamic_export = dynamic_export.checkObject(.wasm); | 51 | const check_dynamic_export = dynamic_export.checkObject(); |
| 42 | check_dynamic_export.checkStart("Section export"); | 52 | check_dynamic_export.checkStart("Section export"); |
| 43 | check_dynamic_export.checkNext("entries 2"); | 53 | check_dynamic_export.checkNext("entries 2"); |
| 44 | check_dynamic_export.checkNext("name foo"); | 54 | check_dynamic_export.checkNext("name foo"); |
| 45 | check_dynamic_export.checkNext("kind function"); | 55 | check_dynamic_export.checkNext("kind function"); |
| 46 | 56 | ||
| 47 | const check_force_export = force_export.checkObject(.wasm); | 57 | const check_force_export = force_export.checkObject(); |
| 48 | check_force_export.checkStart("Section export"); | 58 | check_force_export.checkStart("Section export"); |
| 49 | check_force_export.checkNext("entries 2"); | 59 | check_force_export.checkNext("entries 2"); |
| 50 | check_force_export.checkNext("name foo"); | 60 | check_force_export.checkNext("name foo"); |
| 51 | check_force_export.checkNext("kind function"); | 61 | check_force_export.checkNext("kind function"); |
| 52 | 62 | ||
| 53 | const test_step = b.step("test", "Run linker test"); | ||
| 54 | test_step.dependOn(&check_no_export.step); | 63 | test_step.dependOn(&check_no_export.step); |
| 55 | test_step.dependOn(&check_dynamic_export.step); | 64 | test_step.dependOn(&check_dynamic_export.step); |
| 56 | test_step.dependOn(&check_force_export.step); | 65 | test_step.dependOn(&check_force_export.step); |
test/link/wasm/extern-mangle/build.zig+11-5| ... | @@ -1,20 +1,26 @@ | ... | @@ -1,20 +1,26 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 5 | b.default_step = test_step; |
| 6 | 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 | } | ||
| 12 | |||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 14 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 15 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 16 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 17 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 18 | .optimize = optimize, |
| 12 | }); | 19 | }); |
| 13 | lib.import_symbols = true; // import `a` and `b` | 20 | lib.import_symbols = true; // import `a` and `b` |
| 14 | lib.rdynamic = true; // export `foo` | 21 | lib.rdynamic = true; // export `foo` |
| 15 | lib.install(); | ||
| 16 | 22 | ||
| 17 | const check_lib = lib.checkObject(.wasm); | 23 | const check_lib = lib.checkObject(); |
| 18 | check_lib.checkStart("Section import"); | 24 | check_lib.checkStart("Section import"); |
| 19 | check_lib.checkNext("entries 2"); // a.hello & b.hello | 25 | check_lib.checkNext("entries 2"); // a.hello & b.hello |
| 20 | check_lib.checkNext("module a"); | 26 | check_lib.checkNext("module a"); |
test/link/wasm/extern/build.zig+15-3| ... | @@ -1,19 +1,31 @@ | ... | @@ -1,19 +1,31 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub 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 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 4 | const exe = b.addExecutable(.{ | 16 | const exe = b.addExecutable(.{ |
| 5 | .name = "extern", | 17 | .name = "extern", |
| 6 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = b.standardOptimizeOption(.{}), | 19 | .optimize = optimize, |
| 8 | .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, |
| 9 | }); | 21 | }); |
| 10 | exe.addCSourceFile("foo.c", &.{}); | 22 | exe.addCSourceFile("foo.c", &.{}); |
| 11 | exe.use_llvm = false; | 23 | exe.use_llvm = false; |
| 12 | exe.use_lld = false; | 24 | exe.use_lld = false; |
| 13 | 25 | ||
| 14 | const run = exe.runEmulatable(); | 26 | const run = b.addRunArtifact(exe); |
| 27 | run.skip_foreign_checks = true; | ||
| 15 | run.expectStdOutEqual("Result: 30"); | 28 | run.expectStdOutEqual("Result: 30"); |
| 16 | 29 | ||
| 17 | const test_step = b.step("test", "Run linker test"); | ||
| 18 | test_step.dependOn(&run.step); | 30 | test_step.dependOn(&run.step); |
| 19 | } | 31 | } |
test/link/wasm/function-table/build.zig+16-9| ... | @@ -1,13 +1,20 @@ | ... | @@ -1,13 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub 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; | ||
| 5 | 8 | ||
| 6 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 8 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const import_table = b.addSharedLibrary(.{ | 16 | const import_table = b.addSharedLibrary(.{ |
| 10 | .name = "lib", | 17 | .name = "import_table", |
| 11 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 12 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 13 | .optimize = optimize, | 20 | .optimize = optimize, |
| ... | @@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void { |
| 17 | import_table.import_table = true; | 24 | import_table.import_table = true; |
| 18 | 25 | ||
| 19 | const export_table = b.addSharedLibrary(.{ | 26 | const export_table = b.addSharedLibrary(.{ |
| 20 | .name = "lib", | 27 | .name = "export_table", |
| 21 | .root_source_file = .{ .path = "lib.zig" }, | 28 | .root_source_file = .{ .path = "lib.zig" }, |
| 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 29 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 23 | .optimize = optimize, | 30 | .optimize = optimize, |
| ... | @@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void { |
| 27 | export_table.export_table = true; | 34 | export_table.export_table = true; |
| 28 | 35 | ||
| 29 | const regular_table = b.addSharedLibrary(.{ | 36 | const regular_table = b.addSharedLibrary(.{ |
| 30 | .name = "lib", | 37 | .name = "regular_table", |
| 31 | .root_source_file = .{ .path = "lib.zig" }, | 38 | .root_source_file = .{ .path = "lib.zig" }, |
| 32 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 39 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 33 | .optimize = optimize, | 40 | .optimize = optimize, |
| ... | @@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void { |
| 35 | regular_table.use_llvm = false; | 42 | regular_table.use_llvm = false; |
| 36 | regular_table.use_lld = false; | 43 | regular_table.use_lld = false; |
| 37 | 44 | ||
| 38 | const check_import = import_table.checkObject(.wasm); | 45 | const check_import = import_table.checkObject(); |
| 39 | const check_export = export_table.checkObject(.wasm); | 46 | const check_export = export_table.checkObject(); |
| 40 | const check_regular = regular_table.checkObject(.wasm); | 47 | const check_regular = regular_table.checkObject(); |
| 41 | 48 | ||
| 42 | check_import.checkStart("Section import"); | 49 | check_import.checkStart("Section import"); |
| 43 | check_import.checkNext("entries 1"); | 50 | check_import.checkNext("entries 1"); |
test/link/wasm/infer-features/build.zig+6-5| ... | @@ -1,12 +1,12 @@ | ... | @@ -1,12 +1,12 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub const requires_stage2 = true; |
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | ||
| 6 | // Wasm Object file which we will use to infer the features from | 6 | // Wasm Object file which we will use to infer the features from |
| 7 | const c_obj = b.addObject(.{ | 7 | const c_obj = b.addObject(.{ |
| 8 | .name = "c_obj", | 8 | .name = "c_obj", |
| 9 | .optimize = optimize, | 9 | .optimize = .Debug, |
| 10 | .target = .{ | 10 | .target = .{ |
| 11 | .cpu_arch = .wasm32, | 11 | .cpu_arch = .wasm32, |
| 12 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, | 12 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, |
| ... | @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { |
| 20 | const lib = b.addSharedLibrary(.{ | 20 | const lib = b.addSharedLibrary(.{ |
| 21 | .name = "lib", | 21 | .name = "lib", |
| 22 | .root_source_file = .{ .path = "main.zig" }, | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 23 | .optimize = optimize, | 23 | .optimize = .Debug, |
| 24 | .target = .{ | 24 | .target = .{ |
| 25 | .cpu_arch = .wasm32, | 25 | .cpu_arch = .wasm32, |
| 26 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | 26 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, |
| ... | @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { |
| 32 | lib.addObject(c_obj); | 32 | lib.addObject(c_obj); |
| 33 | 33 | ||
| 34 | // Verify the result contains the features from the C Object file. | 34 | // Verify the result contains the features from the C Object file. |
| 35 | const check = lib.checkObject(.wasm); | 35 | const check = lib.checkObject(); |
| 36 | check.checkStart("name target_features"); | 36 | check.checkStart("name target_features"); |
| 37 | check.checkNext("features 7"); | 37 | check.checkNext("features 7"); |
| 38 | check.checkNext("+ atomics"); | 38 | check.checkNext("+ atomics"); |
| ... | @@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void { |
| 45 | 45 | ||
| 46 | const test_step = b.step("test", "Run linker test"); | 46 | const test_step = b.step("test", "Run linker test"); |
| 47 | test_step.dependOn(&check.step); | 47 | test_step.dependOn(&check.step); |
| 48 | b.default_step = test_step; | ||
| 48 | } | 49 | } |
test/link/wasm/producers/build.zig+14-7| ... | @@ -1,26 +1,33 @@ | ... | @@ -1,26 +1,33 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_stage2 = true; | ||
| 5 | |||
| 4 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 5 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test it"); |
| 6 | test_step.dependOn(b.getInstallStep()); | 8 | b.default_step = test_step; |
| 9 | |||
| 10 | add(b, test_step, .Debug); | ||
| 11 | add(b, test_step, .ReleaseFast); | ||
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 7 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 8 | const lib = b.addSharedLibrary(.{ | 17 | const lib = b.addSharedLibrary(.{ |
| 9 | .name = "lib", | 18 | .name = "lib", |
| 10 | .root_source_file = .{ .path = "lib.zig" }, | 19 | .root_source_file = .{ .path = "lib.zig" }, |
| 11 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 12 | .optimize = b.standardOptimizeOption(.{}), | 21 | .optimize = optimize, |
| 13 | }); | 22 | }); |
| 14 | lib.use_llvm = false; | 23 | lib.use_llvm = false; |
| 15 | lib.use_lld = false; | 24 | lib.use_lld = false; |
| 16 | lib.strip = false; | 25 | lib.strip = false; |
| 17 | lib.install(); | 26 | lib.install(); |
| 18 | 27 | ||
| 19 | const zig_version = builtin.zig_version; | 28 | const version_fmt = "version " ++ builtin.zig_version_string; |
| 20 | var version_buf: [100]u8 = undefined; | ||
| 21 | const version_fmt = std.fmt.bufPrint(&version_buf, "version {}", .{zig_version}) catch unreachable; | ||
| 22 | 29 | ||
| 23 | const check_lib = lib.checkObject(.wasm); | 30 | const check_lib = lib.checkObject(); |
| 24 | check_lib.checkStart("name producers"); | 31 | check_lib.checkStart("name producers"); |
| 25 | check_lib.checkNext("fields 2"); | 32 | check_lib.checkNext("fields 2"); |
| 26 | check_lib.checkNext("field_name language"); | 33 | check_lib.checkNext("field_name language"); |
test/link/wasm/segments/build.zig+13-4| ... | @@ -1,21 +1,30 @@ | ... | @@ -1,21 +1,30 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 16 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 17 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 20 | .optimize = optimize, |
| 12 | }); | 21 | }); |
| 13 | lib.use_llvm = false; | 22 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 23 | lib.use_lld = false; |
| 15 | lib.strip = false; | 24 | lib.strip = false; |
| 16 | lib.install(); | 25 | lib.install(); |
| 17 | 26 | ||
| 18 | const check_lib = lib.checkObject(.wasm); | 27 | const check_lib = lib.checkObject(); |
| 19 | check_lib.checkStart("Section data"); | 28 | check_lib.checkStart("Section data"); |
| 20 | check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory | 29 | check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory |
| 21 | 30 |
test/link/wasm/stack_pointer/build.zig+13-4| ... | @@ -1,14 +1,23 @@ | ... | @@ -1,14 +1,23 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 16 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 17 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 20 | .optimize = optimize, |
| 12 | }); | 21 | }); |
| 13 | lib.use_llvm = false; | 22 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 23 | lib.use_lld = false; |
| ... | @@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void { |
| 16 | lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size | 25 | lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size |
| 17 | lib.install(); | 26 | lib.install(); |
| 18 | 27 | ||
| 19 | const check_lib = lib.checkObject(.wasm); | 28 | const check_lib = lib.checkObject(); |
| 20 | 29 | ||
| 21 | // ensure global exists and its initial value is equal to explitic stack size | 30 | // ensure global exists and its initial value is equal to explitic stack size |
| 22 | check_lib.checkStart("Section global"); | 31 | check_lib.checkStart("Section global"); |
test/link/wasm/type/build.zig+13-4| ... | @@ -1,21 +1,30 @@ | ... | @@ -1,21 +1,30 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 16 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 17 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 20 | .optimize = optimize, |
| 12 | }); | 21 | }); |
| 13 | lib.use_llvm = false; | 22 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 23 | lib.use_lld = false; |
| 15 | lib.strip = false; | 24 | lib.strip = false; |
| 16 | lib.install(); | 25 | lib.install(); |
| 17 | 26 | ||
| 18 | const check_lib = lib.checkObject(.wasm); | 27 | const check_lib = lib.checkObject(); |
| 19 | check_lib.checkStart("Section type"); | 28 | check_lib.checkStart("Section type"); |
| 20 | // only 2 entries, although we have 3 functions. | 29 | // only 2 entries, although we have 3 functions. |
| 21 | // This is to test functions with the same function signature | 30 | // This is to test functions with the same function signature |
test/nvptx.zig created+106| ... | @@ -0,0 +1,106 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Cases = @import("src/Cases.zig"); | ||
| 3 | |||
| 4 | pub 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 | |||
| 85 | const nvptx_target = std.zig.CrossTarget{ | ||
| 86 | .cpu_arch = .nvptx64, | ||
| 87 | .os_tag = .cuda, | ||
| 88 | }; | ||
| 89 | |||
| 90 | pub 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 @@ | ||
| 1 | gpa: Allocator, | ||
| 2 | arena: Allocator, | ||
| 3 | cases: std.ArrayList(Case), | ||
| 4 | incremental_cases: std.ArrayList(IncrementalCase), | ||
| 5 | |||
| 6 | pub const IncrementalCase = struct { | ||
| 7 | base_path: []const u8, | ||
| 8 | }; | ||
| 9 | |||
| 10 | pub 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 | |||
| 48 | pub const File = struct { | ||
| 49 | src: [:0]const u8, | ||
| 50 | path: []const u8, | ||
| 51 | }; | ||
| 52 | |||
| 53 | pub const DepModule = struct { | ||
| 54 | name: []const u8, | ||
| 55 | path: []const u8, | ||
| 56 | }; | ||
| 57 | |||
| 58 | pub 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. | ||
| 67 | pub 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 | |||
| 145 | pub 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 | ||
| 161 | pub fn exe(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 162 | return ctx.addExe(name, target); | ||
| 163 | } | ||
| 164 | |||
| 165 | pub 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. | ||
| 181 | pub 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 | |||
| 194 | pub 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 | |||
| 209 | pub 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. | ||
| 226 | pub 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. | ||
| 231 | pub 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. | ||
| 236 | pub 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 | |||
| 249 | pub 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` | ||
| 260 | pub 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 | |||
| 269 | pub 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` | ||
| 281 | pub 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 | |||
| 291 | pub 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`. | ||
| 304 | pub 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. | ||
| 316 | pub 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. | ||
| 332 | pub 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 | |||
| 341 | fn 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 | |||
| 440 | pub 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 | |||
| 449 | pub 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. | ||
| 565 | fn 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. | ||
| 603 | const 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> | ||
| 673 | fn 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 | |||
| 697 | const 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 | ||
| 712 | const 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 | ||
| 772 | const 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 | |||
| 967 | const Cases = @This(); | ||
| 968 | const builtin = @import("builtin"); | ||
| 969 | const std = @import("std"); | ||
| 970 | const assert = std.debug.assert; | ||
| 971 | const Allocator = std.mem.Allocator; | ||
| 972 | const CrossTarget = std.zig.CrossTarget; | ||
| 973 | const Compilation = @import("../../src/Compilation.zig"); | ||
| 974 | const zig_h = @import("../../src/link.zig").File.C.zig_h; | ||
| 975 | const introspect = @import("../../src/introspect.zig"); | ||
| 976 | const ThreadPool = std.Thread.Pool; | ||
| 977 | const WaitGroup = std.Thread.WaitGroup; | ||
| 978 | const build_options = @import("build_options"); | ||
| 979 | const Package = @import("../../src/Package.zig"); | ||
| 980 | |||
| 981 | pub const std_options = struct { | ||
| 982 | pub const log_level: std.log.Level = .err; | ||
| 983 | }; | ||
| 984 | |||
| 985 | var 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. | ||
| 991 | pub 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 | |||
| 1125 | fn 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 | |||
| 1194 | fn 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 | |||
| 1582 | fn 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 | |||
| 4 | b: *std.Build, | ||
| 5 | step: *std.Build.Step, | ||
| 6 | test_index: usize, | ||
| 7 | test_filter: ?[]const u8, | ||
| 8 | optimize_modes: []const OptimizeMode, | ||
| 9 | |||
| 10 | const Special = enum { | ||
| 11 | None, | ||
| 12 | Asm, | ||
| 13 | RuntimeSafety, | ||
| 14 | }; | ||
| 15 | |||
| 16 | const 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 | |||
| 41 | pub 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 | |||
| 55 | pub 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 | |||
| 59 | pub 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 | |||
| 65 | pub 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 | |||
| 70 | pub 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 | |||
| 75 | pub 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 | |||
| 80 | pub 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 | |||
| 168 | const CompareOutput = @This(); | ||
| 169 | const std = @import("std"); | ||
| 170 | const ArrayList = std.ArrayList; | ||
| 171 | const fmt = std.fmt; | ||
| 172 | const mem = std.mem; | ||
| 173 | const fs = std.fs; | ||
| 174 | const OptimizeMode = std.builtin.OptimizeMode; | ||
test/src/StackTrace.zig created+107| ... | @@ -0,0 +1,107 @@ | ||
| 1 | b: *std.Build, | ||
| 2 | step: *Step, | ||
| 3 | test_index: usize, | ||
| 4 | test_filter: ?[]const u8, | ||
| 5 | optimize_modes: []const OptimizeMode, | ||
| 6 | check_exe: *std.Build.CompileStep, | ||
| 7 | |||
| 8 | const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; | ||
| 9 | |||
| 10 | pub 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 | |||
| 48 | fn 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 | |||
| 101 | const StackTrace = @This(); | ||
| 102 | const std = @import("std"); | ||
| 103 | const builtin = @import("builtin"); | ||
| 104 | const Step = std.Build.Step; | ||
| 105 | const OptimizeMode = std.builtin.OptimizeMode; | ||
| 106 | const fmt = std.fmt; | ||
| 107 | const mem = std.mem; | ||
test/src/check-stack-trace.zig created+79| ... | @@ -0,0 +1,79 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const mem = std.mem; | ||
| 4 | const fs = std.fs; | ||
| 5 | |||
| 6 | pub 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. | ||
| 3 | const std = @import("std"); | ||
| 4 | const ArrayList = std.ArrayList; | ||
| 5 | const fmt = std.fmt; | ||
| 6 | const mem = std.mem; | ||
| 7 | const fs = std.fs; | ||
| 8 | const OptimizeMode = std.builtin.OptimizeMode; | ||
| 9 | |||
| 10 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const 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. | ||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | ||
| 3 | |||
| 4 | pub 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 | |||
| 85 | const nvptx_target = std.zig.CrossTarget{ | ||
| 86 | .cpu_arch = .nvptx64, | ||
| 87 | .os_tag = .cuda, | ||
| 88 | }; | ||
| 89 | |||
| 90 | pub fn addPtx( | ||
| 91 | ctx: *TestContext, | ||
| 92 | name: []const u8, | ||
| 93 | ) *TestContext.Case { | ||
| 94 | ctx.cases.append(TestContext.Case{ | ||
| 95 | .name = name, | ||
| 96 | .target = nvptx_target, | ||
| 97 | .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), | ||
| 98 | .output_mode = .Obj, | ||
| 99 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), | ||
| 100 | .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator), | ||
| 101 | .link_libc = false, | ||
| 102 | .backend = .llvm, | ||
| 103 | // Bug in Debug mode | ||
| 104 | .optimize_mode = .ReleaseSafe, | ||
| 105 | }) catch @panic("out of memory"); | ||
| 106 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 107 | } | ||
test/standalone.zig+208-106| ... | @@ -1,116 +1,218 @@ | ... | @@ -1,116 +1,218 @@ |
| 1 | const std = @import("std"); | 1 | pub const SimpleCase = struct { |
| 2 | const builtin = @import("builtin"); | 2 | src_path: []const u8, |
| 3 | const tests = @import("tests.zig"); | 3 | link_libc: bool = false, |
| 4 | all_modes: bool = false, | ||
| 5 | target: std.zig.CrossTarget = .{}, | ||
| 6 | is_test: bool = false, | ||
| 7 | is_exe: bool = true, | ||
| 8 | /// Run only on this OS. | ||
| 9 | os_filter: ?std.Target.Os.Tag = null, | ||
| 10 | }; | ||
| 4 | 11 | ||
| 5 | pub fn addCases(cases: *tests.StandaloneContext) void { | 12 | pub const BuildCase = struct { |
| 6 | cases.add("test/standalone/hello_world/hello.zig"); | 13 | build_root: []const u8, |
| 7 | cases.addC("test/standalone/hello_world/hello_libc.zig"); | 14 | import: type, |
| 15 | }; | ||
| 8 | 16 | ||
| 9 | cases.addBuildFile("test/standalone/options/build.zig", .{ | 17 | pub const simple_cases = [_]SimpleCase{ |
| 10 | .extra_argv = &.{ | 18 | .{ |
| 11 | "-Dbool_true", | 19 | .src_path = "test/standalone/hello_world/hello.zig", |
| 12 | "-Dbool_false=false", | 20 | .all_modes = true, |
| 13 | "-Dint=1234", | 21 | }, |
| 14 | "-De=two", | 22 | .{ |
| 15 | "-Dstring=hello", | 23 | .src_path = "test/standalone/hello_world/hello_libc.zig", |
| 24 | .link_libc = true, | ||
| 25 | .all_modes = true, | ||
| 26 | }, | ||
| 27 | .{ | ||
| 28 | .src_path = "test/standalone/cat/main.zig", | ||
| 29 | }, | ||
| 30 | // https://github.com/ziglang/zig/issues/6025 | ||
| 31 | //.{ | ||
| 32 | // .src_path = "test/standalone/issue_9693/main.zig", | ||
| 33 | //}, | ||
| 34 | .{ | ||
| 35 | .src_path = "test/standalone/brace_expansion.zig", | ||
| 36 | .is_test = true, | ||
| 37 | }, | ||
| 38 | .{ | ||
| 39 | .src_path = "test/standalone/issue_7030.zig", | ||
| 40 | .target = .{ | ||
| 41 | .cpu_arch = .wasm32, | ||
| 42 | .os_tag = .freestanding, | ||
| 16 | }, | 43 | }, |
| 17 | }); | 44 | }, |
| 18 | |||
| 19 | cases.add("test/standalone/cat/main.zig"); | ||
| 20 | if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/6025 | ||
| 21 | cases.add("test/standalone/issue_9693/main.zig"); | ||
| 22 | } | ||
| 23 | cases.add("test/standalone/issue_12471/main.zig"); | ||
| 24 | cases.add("test/standalone/guess_number/main.zig"); | ||
| 25 | cases.add("test/standalone/main_return_error/error_u8.zig"); | ||
| 26 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); | ||
| 27 | cases.add("test/standalone/noreturn_call/inline.zig"); | ||
| 28 | cases.add("test/standalone/noreturn_call/as_arg.zig"); | ||
| 29 | cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); | ||
| 30 | cases.addBuildFile("test/standalone/issue_13970/build.zig", .{}); | ||
| 31 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); | ||
| 32 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); | ||
| 33 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); | ||
| 34 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | ||
| 35 | .build_modes = true, | ||
| 36 | .cross_targets = true, | ||
| 37 | }); | ||
| 38 | cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); | ||
| 39 | cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); | ||
| 40 | cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); | ||
| 41 | cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); | ||
| 42 | cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); | ||
| 43 | cases.addBuildFile("test/standalone/issue_5825/build.zig", .{}); | ||
| 44 | cases.addBuildFile("test/standalone/pkg_import/build.zig", .{}); | ||
| 45 | cases.addBuildFile("test/standalone/use_alias/build.zig", .{}); | ||
| 46 | cases.addBuildFile("test/standalone/brace_expansion/build.zig", .{}); | ||
| 47 | if (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64) { | ||
| 48 | // https://github.com/ziglang/zig/issues/13685 | ||
| 49 | cases.addBuildFile("test/standalone/empty_env/build.zig", .{}); | ||
| 50 | } | ||
| 51 | cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); | ||
| 52 | cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); | ||
| 53 | if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194 | ||
| 54 | cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); | ||
| 55 | } | ||
| 56 | if (builtin.os.tag != .windows) { | ||
| 57 | // https://github.com/ziglang/zig/issues/12419 | ||
| 58 | cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (builtin.os.tag != .wasi and | ||
| 62 | // https://github.com/ziglang/zig/issues/13550 | ||
| 63 | (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and | ||
| 64 | // https://github.com/ziglang/zig/issues/13686 | ||
| 65 | (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)) | ||
| 66 | { | ||
| 67 | cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); | ||
| 68 | } | ||
| 69 | 45 | ||
| 70 | if (builtin.os.tag == .windows) { | 46 | .{ .src_path = "test/standalone/issue_12471/main.zig" }, |
| 71 | cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{}); | 47 | .{ .src_path = "test/standalone/guess_number/main.zig" }, |
| 72 | } | 48 | .{ .src_path = "test/standalone/main_return_error/error_u8.zig" }, |
| 49 | .{ .src_path = "test/standalone/main_return_error/error_u8_non_zero.zig" }, | ||
| 50 | .{ .src_path = "test/standalone/noreturn_call/inline.zig" }, | ||
| 51 | .{ .src_path = "test/standalone/noreturn_call/as_arg.zig" }, | ||
| 73 | 52 | ||
| 74 | cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ | 53 | .{ |
| 75 | .build_modes = true, | 54 | .src_path = "test/standalone/issue_9402/main.zig", |
| 76 | .cross_targets = true, | 55 | .os_filter = .windows, |
| 77 | }); | 56 | .link_libc = true, |
| 78 | 57 | }, | |
| 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 | } | ||
| 90 | 58 | ||
| 91 | // Ensure the development tools are buildable. Alphabetically sorted. | 59 | // Ensure the development tools are buildable. Alphabetically sorted. |
| 92 | // No need to build `tools/spirv/grammar.zig`. | 60 | // No need to build `tools/spirv/grammar.zig`. |
| 93 | cases.add("tools/extract-grammar.zig"); | 61 | .{ .src_path = "tools/extract-grammar.zig" }, |
| 94 | cases.add("tools/gen_outline_atomics.zig"); | 62 | .{ .src_path = "tools/gen_outline_atomics.zig" }, |
| 95 | cases.add("tools/gen_spirv_spec.zig"); | 63 | .{ .src_path = "tools/gen_spirv_spec.zig" }, |
| 96 | cases.add("tools/gen_stubs.zig"); | 64 | .{ .src_path = "tools/gen_stubs.zig" }, |
| 97 | cases.add("tools/generate_linux_syscalls.zig"); | 65 | .{ .src_path = "tools/generate_linux_syscalls.zig" }, |
| 98 | cases.add("tools/process_headers.zig"); | 66 | .{ .src_path = "tools/process_headers.zig" }, |
| 99 | cases.add("tools/update-license-headers.zig"); | 67 | .{ .src_path = "tools/update-license-headers.zig" }, |
| 100 | cases.add("tools/update-linux-headers.zig"); | 68 | .{ .src_path = "tools/update-linux-headers.zig" }, |
| 101 | cases.add("tools/update_clang_options.zig"); | 69 | .{ .src_path = "tools/update_clang_options.zig" }, |
| 102 | cases.add("tools/update_cpu_features.zig"); | 70 | .{ .src_path = "tools/update_cpu_features.zig" }, |
| 103 | cases.add("tools/update_glibc.zig"); | 71 | .{ .src_path = "tools/update_glibc.zig" }, |
| 104 | cases.add("tools/update_spirv_features.zig"); | 72 | .{ .src_path = "tools/update_spirv_features.zig" }, |
| 73 | }; | ||
| 105 | 74 | ||
| 106 | cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); | 75 | pub const build_cases = [_]BuildCase{ |
| 107 | cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); | 76 | .{ |
| 108 | cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); | 77 | .build_root = "test/standalone/test_runner_path", |
| 109 | cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); | 78 | .import = @import("standalone/test_runner_path/build.zig"), |
| 79 | }, | ||
| 80 | .{ | ||
| 81 | .build_root = "test/standalone/issue_13970", | ||
| 82 | .import = @import("standalone/issue_13970/build.zig"), | ||
| 83 | }, | ||
| 84 | .{ | ||
| 85 | .build_root = "test/standalone/main_pkg_path", | ||
| 86 | .import = @import("standalone/main_pkg_path/build.zig"), | ||
| 87 | }, | ||
| 88 | .{ | ||
| 89 | .build_root = "test/standalone/shared_library", | ||
| 90 | .import = @import("standalone/shared_library/build.zig"), | ||
| 91 | }, | ||
| 92 | .{ | ||
| 93 | .build_root = "test/standalone/mix_o_files", | ||
| 94 | .import = @import("standalone/mix_o_files/build.zig"), | ||
| 95 | }, | ||
| 96 | .{ | ||
| 97 | .build_root = "test/standalone/mix_c_files", | ||
| 98 | .import = @import("standalone/mix_c_files/build.zig"), | ||
| 99 | }, | ||
| 100 | .{ | ||
| 101 | .build_root = "test/standalone/global_linkage", | ||
| 102 | .import = @import("standalone/global_linkage/build.zig"), | ||
| 103 | }, | ||
| 104 | .{ | ||
| 105 | .build_root = "test/standalone/static_c_lib", | ||
| 106 | .import = @import("standalone/static_c_lib/build.zig"), | ||
| 107 | }, | ||
| 108 | .{ | ||
| 109 | .build_root = "test/standalone/issue_339", | ||
| 110 | .import = @import("standalone/issue_339/build.zig"), | ||
| 111 | }, | ||
| 112 | .{ | ||
| 113 | .build_root = "test/standalone/issue_8550", | ||
| 114 | .import = @import("standalone/issue_8550/build.zig"), | ||
| 115 | }, | ||
| 116 | .{ | ||
| 117 | .build_root = "test/standalone/issue_794", | ||
| 118 | .import = @import("standalone/issue_794/build.zig"), | ||
| 119 | }, | ||
| 120 | .{ | ||
| 121 | .build_root = "test/standalone/issue_5825", | ||
| 122 | .import = @import("standalone/issue_5825/build.zig"), | ||
| 123 | }, | ||
| 124 | .{ | ||
| 125 | .build_root = "test/standalone/pkg_import", | ||
| 126 | .import = @import("standalone/pkg_import/build.zig"), | ||
| 127 | }, | ||
| 128 | .{ | ||
| 129 | .build_root = "test/standalone/use_alias", | ||
| 130 | .import = @import("standalone/use_alias/build.zig"), | ||
| 131 | }, | ||
| 132 | .{ | ||
| 133 | .build_root = "test/standalone/install_raw_hex", | ||
| 134 | .import = @import("standalone/install_raw_hex/build.zig"), | ||
| 135 | }, | ||
| 136 | // TODO take away EmitOption.emit_to option and make it give a FileSource | ||
| 137 | //.{ | ||
| 138 | // .build_root = "test/standalone/emit_asm_and_bin", | ||
| 139 | // .import = @import("standalone/emit_asm_and_bin/build.zig"), | ||
| 140 | //}, | ||
| 141 | // TODO take away EmitOption.emit_to option and make it give a FileSource | ||
| 142 | //.{ | ||
| 143 | // .build_root = "test/standalone/issue_12588", | ||
| 144 | // .import = @import("standalone/issue_12588/build.zig"), | ||
| 145 | //}, | ||
| 146 | .{ | ||
| 147 | .build_root = "test/standalone/embed_generated_file", | ||
| 148 | .import = @import("standalone/embed_generated_file/build.zig"), | ||
| 149 | }, | ||
| 150 | .{ | ||
| 151 | .build_root = "test/standalone/extern", | ||
| 152 | .import = @import("standalone/extern/build.zig"), | ||
| 153 | }, | ||
| 154 | .{ | ||
| 155 | .build_root = "test/standalone/dep_diamond", | ||
| 156 | .import = @import("standalone/dep_diamond/build.zig"), | ||
| 157 | }, | ||
| 158 | .{ | ||
| 159 | .build_root = "test/standalone/dep_triangle", | ||
| 160 | .import = @import("standalone/dep_triangle/build.zig"), | ||
| 161 | }, | ||
| 162 | .{ | ||
| 163 | .build_root = "test/standalone/dep_recursive", | ||
| 164 | .import = @import("standalone/dep_recursive/build.zig"), | ||
| 165 | }, | ||
| 166 | .{ | ||
| 167 | .build_root = "test/standalone/dep_mutually_recursive", | ||
| 168 | .import = @import("standalone/dep_mutually_recursive/build.zig"), | ||
| 169 | }, | ||
| 170 | .{ | ||
| 171 | .build_root = "test/standalone/dep_shared_builtin", | ||
| 172 | .import = @import("standalone/dep_shared_builtin/build.zig"), | ||
| 173 | }, | ||
| 174 | .{ | ||
| 175 | .build_root = "test/standalone/empty_env", | ||
| 176 | .import = @import("standalone/empty_env/build.zig"), | ||
| 177 | }, | ||
| 178 | .{ | ||
| 179 | .build_root = "test/standalone/issue_9812", | ||
| 180 | .import = @import("standalone/issue_9812/build.zig"), | ||
| 181 | }, | ||
| 182 | .{ | ||
| 183 | .build_root = "test/standalone/issue_11595", | ||
| 184 | .import = @import("standalone/issue_11595/build.zig"), | ||
| 185 | }, | ||
| 186 | .{ | ||
| 187 | .build_root = "test/standalone/load_dynamic_library", | ||
| 188 | .import = @import("standalone/load_dynamic_library/build.zig"), | ||
| 189 | }, | ||
| 190 | .{ | ||
| 191 | .build_root = "test/standalone/windows_spawn", | ||
| 192 | .import = @import("standalone/windows_spawn/build.zig"), | ||
| 193 | }, | ||
| 194 | .{ | ||
| 195 | .build_root = "test/standalone/c_compiler", | ||
| 196 | .import = @import("standalone/c_compiler/build.zig"), | ||
| 197 | }, | ||
| 198 | .{ | ||
| 199 | .build_root = "test/standalone/pie", | ||
| 200 | .import = @import("standalone/pie/build.zig"), | ||
| 201 | }, | ||
| 202 | .{ | ||
| 203 | .build_root = "test/standalone/issue_12706", | ||
| 204 | .import = @import("standalone/issue_12706/build.zig"), | ||
| 205 | }, | ||
| 206 | // TODO This test is disabled for doing naughty things in the build script. | ||
| 207 | // The logic needs to get moved to a child process instead of build.zig. | ||
| 208 | //.{ | ||
| 209 | // .build_root = "test/standalone/sigpipe", | ||
| 210 | // .import = @import("standalone/sigpipe/build.zig"), | ||
| 211 | //}, | ||
| 212 | .{ | ||
| 213 | .build_root = "test/standalone/issue_13030", | ||
| 214 | .import = @import("standalone/issue_13030/build.zig"), | ||
| 215 | }, | ||
| 216 | }; | ||
| 110 | 217 | ||
| 111 | cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); | 218 | const std = @import("std"); |
| 112 | cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); | ||
| 113 | cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); | ||
| 114 | cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); | ||
| 115 | cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); | ||
| 116 | } |
test/standalone/brace_expansion.zig created+292| ... | @@ -0,0 +1,292 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const io = std.io; | ||
| 3 | const mem = std.mem; | ||
| 4 | const debug = std.debug; | ||
| 5 | const assert = debug.assert; | ||
| 6 | const testing = std.testing; | ||
| 7 | const ArrayList = std.ArrayList; | ||
| 8 | const maxInt = std.math.maxInt; | ||
| 9 | |||
| 10 | const Token = union(enum) { | ||
| 11 | Word: []const u8, | ||
| 12 | OpenBrace, | ||
| 13 | CloseBrace, | ||
| 14 | Comma, | ||
| 15 | Eof, | ||
| 16 | }; | ||
| 17 | |||
| 18 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 19 | var global_allocator = gpa.allocator(); | ||
| 20 | |||
| 21 | fn 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 | |||
| 68 | const 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 | |||
| 91 | const ParseError = error{ | ||
| 92 | InvalidInput, | ||
| 93 | OutOfMemory, | ||
| 94 | }; | ||
| 95 | |||
| 96 | fn 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 | |||
| 137 | fn 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 | |||
| 170 | const ExpandNodeError = error{OutOfMemory}; | ||
| 171 | |||
| 172 | fn 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 | |||
| 226 | pub 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 | |||
| 241 | test "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 | |||
| 255 | fn 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 | |||
| 262 | test "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 | |||
| 285 | fn 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const io = std.io; | ||
| 3 | const mem = std.mem; | ||
| 4 | const debug = std.debug; | ||
| 5 | const assert = debug.assert; | ||
| 6 | const testing = std.testing; | ||
| 7 | const ArrayList = std.ArrayList; | ||
| 8 | const maxInt = std.math.maxInt; | ||
| 9 | |||
| 10 | const Token = union(enum) { | ||
| 11 | Word: []const u8, | ||
| 12 | OpenBrace, | ||
| 13 | CloseBrace, | ||
| 14 | Comma, | ||
| 15 | Eof, | ||
| 16 | }; | ||
| 17 | |||
| 18 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 19 | var global_allocator = gpa.allocator(); | ||
| 20 | |||
| 21 | fn 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 | |||
| 68 | const 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 | |||
| 91 | const ParseError = error{ | ||
| 92 | InvalidInput, | ||
| 93 | OutOfMemory, | ||
| 94 | }; | ||
| 95 | |||
| 96 | fn 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 | |||
| 137 | fn 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 | |||
| 170 | const ExpandNodeError = error{OutOfMemory}; | ||
| 171 | |||
| 172 | fn 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 | |||
| 226 | pub 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 | |||
| 241 | test "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 | |||
| 255 | fn 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 | |||
| 262 | test "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 | |||
| 285 | fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void { | ||
| 286 | var result = ArrayList(u8).init(global_allocator); | ||
| 287 | defer result.deinit(); | ||
| 288 | |||
| 289 | expandString(test_input, &result) catch unreachable; | ||
| 290 | |||
| 291 | try testing.expectEqualSlices(u8, expected_result, result.items); | ||
| 292 | } | ||
test/standalone/c_compiler/build.zig+18-21| ... | @@ -1,27 +1,24 @@ | ... | @@ -1,27 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | 3 | ||
| 5 | // TODO integrate this with the std.Build executor API | 4 | pub fn build(b: *std.Build) void { |
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | 5 | const test_step = b.step("test", "Test it"); |
| 7 | if (t.isNative()) return true; | 6 | b.default_step = test_step; |
| 8 | 7 | ||
| 9 | return (t.getOsTag() == builtin.os.tag and | 8 | add(b, test_step, .Debug); |
| 10 | t.getCpuArch() == builtin.cpu.arch); | 9 | add(b, test_step, .ReleaseFast); |
| 10 | add(b, test_step, .ReleaseSmall); | ||
| 11 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | 12 | } |
| 12 | 13 | ||
| 13 | pub fn build(b: *std.Build) void { | 14 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | 15 | const target: std.zig.CrossTarget = .{}; |
| 15 | const target = b.standardTargetOptions(.{}); | ||
| 16 | |||
| 17 | const test_step = b.step("test", "Test the program"); | ||
| 18 | 16 | ||
| 19 | const exe_c = b.addExecutable(.{ | 17 | const exe_c = b.addExecutable(.{ |
| 20 | .name = "test_c", | 18 | .name = "test_c", |
| 21 | .optimize = optimize, | 19 | .optimize = optimize, |
| 22 | .target = target, | 20 | .target = target, |
| 23 | }); | 21 | }); |
| 24 | b.default_step.dependOn(&exe_c.step); | ||
| 25 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); | 22 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); |
| 26 | exe_c.linkLibC(); | 23 | exe_c.linkLibC(); |
| 27 | 24 | ||
| ... | @@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void { | ... | @@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void { |
| 47 | else => {}, | 44 | else => {}, |
| 48 | } | 45 | } |
| 49 | 46 | ||
| 50 | if (isRunnableTarget(target)) { | 47 | const run_c_cmd = b.addRunArtifact(exe_c); |
| 51 | const run_c_cmd = exe_c.run(); | 48 | run_c_cmd.expectExitCode(0); |
| 52 | test_step.dependOn(&run_c_cmd.step); | 49 | run_c_cmd.skip_foreign_checks = true; |
| 53 | const run_cpp_cmd = exe_cpp.run(); | 50 | test_step.dependOn(&run_c_cmd.step); |
| 54 | test_step.dependOn(&run_cpp_cmd.step); | 51 | |
| 55 | } else { | 52 | const run_cpp_cmd = b.addRunArtifact(exe_cpp); |
| 56 | test_step.dependOn(&exe_c.step); | 53 | run_cpp_cmd.expectExitCode(0); |
| 57 | test_step.dependOn(&exe_cpp.step); | 54 | run_cpp_cmd.skip_foreign_checks = true; |
| 58 | } | 55 | test_step.dependOn(&run_cpp_cmd.step); |
| 59 | } | 56 | } |
test/standalone/dep_diamond/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const shared = b.createModule(.{ | 9 | const shared = b.createModule(.{ |
| 7 | .source_file = .{ .path = "shared.zig" }, | 10 | .source_file = .{ .path = "shared.zig" }, |
| ... | @@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void { |
| 23 | 26 | ||
| 24 | const run = exe.run(); | 27 | const run = exe.run(); |
| 25 | 28 | ||
| 26 | const test_step = b.step("test", "Test it"); | ||
| 27 | test_step.dependOn(&run.step); | 29 | test_step.dependOn(&run.step); |
| 28 | } | 30 | } |
test/standalone/dep_mutually_recursive/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const foo = b.createModule(.{ | 9 | const foo = b.createModule(.{ |
| 7 | .source_file = .{ .path = "foo.zig" }, | 10 | .source_file = .{ .path = "foo.zig" }, |
| ... | @@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void { |
| 21 | 24 | ||
| 22 | const run = exe.run(); | 25 | const run = exe.run(); |
| 23 | 26 | ||
| 24 | const test_step = b.step("test", "Test it"); | ||
| 25 | test_step.dependOn(&run.step); | 27 | test_step.dependOn(&run.step); |
| 26 | } | 28 | } |
test/standalone/dep_recursive/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const foo = b.createModule(.{ | 9 | const foo = b.createModule(.{ |
| 7 | .source_file = .{ .path = "foo.zig" }, | 10 | .source_file = .{ .path = "foo.zig" }, |
| ... | @@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void { |
| 17 | 20 | ||
| 18 | const run = exe.run(); | 21 | const run = exe.run(); |
| 19 | 22 | ||
| 20 | const test_step = b.step("test", "Test it"); | ||
| 21 | test_step.dependOn(&run.step); | 23 | test_step.dependOn(&run.step); |
| 22 | } | 24 | } |
test/standalone/dep_shared_builtin/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const exe = b.addExecutable(.{ | 9 | const exe = b.addExecutable(.{ |
| 7 | .name = "test", | 10 | .name = "test", |
| ... | @@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void { |
| 14 | 17 | ||
| 15 | const run = exe.run(); | 18 | const run = exe.run(); |
| 16 | 19 | ||
| 17 | const test_step = b.step("test", "Test it"); | ||
| 18 | test_step.dependOn(&run.step); | 20 | test_step.dependOn(&run.step); |
| 19 | } | 21 | } |
test/standalone/dep_triangle/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const shared = b.createModule(.{ | 9 | const shared = b.createModule(.{ |
| 7 | .source_file = .{ .path = "shared.zig" }, | 10 | .source_file = .{ .path = "shared.zig" }, |
| ... | @@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void { |
| 20 | 23 | ||
| 21 | const run = exe.run(); | 24 | const run = exe.run(); |
| 22 | 25 | ||
| 23 | const test_step = b.step("test", "Test it"); | ||
| 24 | test_step.dependOn(&run.step); | 26 | test_step.dependOn(&run.step); |
| 25 | } | 27 | } |
test/standalone/embed_generated_file/build.zig+3-5| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const target = b.standardTargetOptions(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 5 | b.default_step = test_step; |
| 6 | 6 | ||
| 7 | const bootloader = b.addExecutable(.{ | 7 | const bootloader = b.addExecutable(.{ |
| 8 | .name = "bootloader", | 8 | .name = "bootloader", |
| ... | @@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void { |
| 16 | 16 | ||
| 17 | const exe = b.addTest(.{ | 17 | const exe = b.addTest(.{ |
| 18 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 19 | .target = target, | 19 | .optimize = .Debug, |
| 20 | .optimize = optimize, | ||
| 21 | }); | 20 | }); |
| 22 | exe.addAnonymousModule("bootloader.elf", .{ | 21 | exe.addAnonymousModule("bootloader.elf", .{ |
| 23 | .source_file = bootloader.getOutputSource(), | 22 | .source_file = bootloader.getOutputSource(), |
| 24 | }); | 23 | }); |
| 25 | 24 | ||
| 26 | const test_step = b.step("test", "Test the program"); | ||
| 27 | test_step.dependOn(&exe.step); | 25 | test_step.dependOn(&exe.step); |
| 28 | } | 26 | } |
test/standalone/emit_asm_and_bin/build.zig+4-2| ... | @@ -1,6 +1,9 @@ | ... | @@ -1,6 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const main = b.addTest(.{ | 7 | const main = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 8 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | 9 | .optimize = b.standardOptimizeOption(.{}), |
| ... | @@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void { |
| 8 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; | 11 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; |
| 9 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; | 12 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; |
| 10 | 13 | ||
| 11 | const test_step = b.step("test", "Run test"); | 14 | test_step.dependOn(&main.run().step); |
| 12 | test_step.dependOn(&main.step); | ||
| 13 | } | 15 | } |
test/standalone/empty_env/build.zig+13-3| ... | @@ -1,15 +1,25 @@ | ... | @@ -1,15 +1,25 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | 3 | ||
| 3 | pub fn build(b: *std.Build) void { | 4 | pub fn build(b: *std.Build) void { |
| 5 | const test_step = b.step("test", "Test it"); | ||
| 6 | b.default_step = test_step; | ||
| 7 | |||
| 8 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 9 | |||
| 10 | if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) { | ||
| 11 | // https://github.com/ziglang/zig/issues/13685 | ||
| 12 | return; | ||
| 13 | } | ||
| 14 | |||
| 4 | const main = b.addExecutable(.{ | 15 | const main = b.addExecutable(.{ |
| 5 | .name = "main", | 16 | .name = "main", |
| 6 | .root_source_file = .{ .path = "main.zig" }, | 17 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = b.standardOptimizeOption(.{}), | 18 | .optimize = optimize, |
| 8 | }); | 19 | }); |
| 9 | 20 | ||
| 10 | const run = main.run(); | 21 | const run = b.addRunArtifact(main); |
| 11 | run.clearEnvironment(); | 22 | run.clearEnvironment(); |
| 12 | 23 | ||
| 13 | const test_step = b.step("test", "Test it"); | ||
| 14 | test_step.dependOn(&run.step); | 24 | test_step.dependOn(&run.step); |
| 15 | } | 25 | } |
test/standalone/extern/build.zig created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | var hidden: u32 = 0; | ||
| 2 | export fn updateHidden(val: u32) void { | ||
| 3 | hidden = val; | ||
| 4 | } | ||
| 5 | export fn getHidden() u32 { | ||
| 6 | return hidden; | ||
| 7 | } | ||
| 8 | |||
| 9 | const T = extern struct { x: u32 }; | ||
| 10 | |||
| 11 | export var mut_val: f64 = 1.23; | ||
| 12 | export const const_val: T = .{ .x = 42 }; | ||
test/standalone/extern/main.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | |||
| 3 | const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" }); | ||
| 4 | const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" }); | ||
| 5 | |||
| 6 | const T = extern struct { x: u32 }; | ||
| 7 | |||
| 8 | test { | ||
| 9 | var mut_val_ptr = @extern(*f64, .{ .name = "mut_val" }); | ||
| 10 | var const_val_ptr = @extern(*const T, .{ .name = "const_val" }); | ||
| 11 | |||
| 12 | assert(getHidden() == 0); | ||
| 13 | updateHidden(123); | ||
| 14 | assert(getHidden() == 123); | ||
| 15 | |||
| 16 | assert(mut_val_ptr.* == 1.23); | ||
| 17 | mut_val_ptr.* = 10.0; | ||
| 18 | assert(mut_val_ptr.* == 10.0); | ||
| 19 | |||
| 20 | assert(const_val_ptr.x == 42); | ||
| 21 | } | ||
test/standalone/global_linkage/build.zig+8-5| ... | @@ -1,20 +1,24 @@ | ... | @@ -1,20 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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 = .{}; | ||
| 5 | 9 | ||
| 6 | const obj1 = b.addStaticLibrary(.{ | 10 | const obj1 = b.addStaticLibrary(.{ |
| 7 | .name = "obj1", | 11 | .name = "obj1", |
| 8 | .root_source_file = .{ .path = "obj1.zig" }, | 12 | .root_source_file = .{ .path = "obj1.zig" }, |
| 9 | .optimize = optimize, | 13 | .optimize = optimize, |
| 10 | .target = .{}, | 14 | .target = target, |
| 11 | }); | 15 | }); |
| 12 | 16 | ||
| 13 | const obj2 = b.addStaticLibrary(.{ | 17 | const obj2 = b.addStaticLibrary(.{ |
| 14 | .name = "obj2", | 18 | .name = "obj2", |
| 15 | .root_source_file = .{ .path = "obj2.zig" }, | 19 | .root_source_file = .{ .path = "obj2.zig" }, |
| 16 | .optimize = optimize, | 20 | .optimize = optimize, |
| 17 | .target = .{}, | 21 | .target = target, |
| 18 | }); | 22 | }); |
| 19 | 23 | ||
| 20 | const main = b.addTest(.{ | 24 | const main = b.addTest(.{ |
| ... | @@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void { |
| 24 | main.linkLibrary(obj1); | 28 | main.linkLibrary(obj1); |
| 25 | main.linkLibrary(obj2); | 29 | main.linkLibrary(obj2); |
| 26 | 30 | ||
| 27 | const test_step = b.step("test", "Test it"); | 31 | test_step.dependOn(&main.run().step); |
| 28 | test_step.dependOn(&main.step); | ||
| 29 | } | 32 | } |
test/standalone/install_raw_hex/build.zig+3-3| ... | @@ -3,8 +3,8 @@ const std = @import("std"); | ... | @@ -3,8 +3,8 @@ const std = @import("std"); |
| 3 | const CheckFileStep = std.Build.CheckFileStep; | 3 | const CheckFileStep = std.Build.CheckFileStep; |
| 4 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test the program"); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step.dependOn(test_step); | 7 | b.default_step = test_step; |
| 8 | 8 | ||
| 9 | const target = .{ | 9 | const target = .{ |
| 10 | .cpu_arch = .thumb, | 10 | .cpu_arch = .thumb, |
| ... | @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { |
| 13 | .abi = .gnueabihf, | 13 | .abi = .gnueabihf, |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | const optimize = b.standardOptimizeOption(.{}); | 16 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 17 | 17 | ||
| 18 | const elf = b.addExecutable(.{ | 18 | const elf = b.addExecutable(.{ |
| 19 | .name = "zig-nrf52-blink.elf", | 19 | .name = "zig-nrf52-blink.elf", |
test/standalone/issue_11595/build.zig+14-17| ... | @@ -1,18 +1,17 @@ | ... | @@ -1,18 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | 3 | ||
| 5 | // TODO integrate this with the std.Build executor API | 4 | pub fn build(b: *std.Build) void { |
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | 5 | const test_step = b.step("test", "Test it"); |
| 7 | if (t.isNative()) return true; | 6 | b.default_step = test_step; |
| 8 | 7 | ||
| 9 | return (t.getOsTag() == builtin.os.tag and | 8 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 10 | t.getCpuArch() == builtin.cpu.arch); | 9 | const target: std.zig.CrossTarget = .{}; |
| 11 | } | ||
| 12 | 10 | ||
| 13 | pub fn build(b: *std.Build) void { | 11 | if (builtin.os.tag == .windows) { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | 12 | // https://github.com/ziglang/zig/issues/12419 |
| 15 | const target = b.standardTargetOptions(.{}); | 13 | return; |
| 14 | } | ||
| 16 | 15 | ||
| 17 | const exe = b.addExecutable(.{ | 16 | const exe = b.addExecutable(.{ |
| 18 | .name = "zigtest", | 17 | .name = "zigtest", |
| ... | @@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void { |
| 44 | 43 | ||
| 45 | b.default_step.dependOn(&exe.step); | 44 | b.default_step.dependOn(&exe.step); |
| 46 | 45 | ||
| 47 | const test_step = b.step("test", "Test the program"); | 46 | const run_cmd = b.addRunArtifact(exe); |
| 48 | if (isRunnableTarget(target)) { | 47 | run_cmd.skip_foreign_checks = true; |
| 49 | const run_cmd = exe.run(); | 48 | run_cmd.expectExitCode(0); |
| 50 | test_step.dependOn(&run_cmd.step); | 49 | |
| 51 | } else { | 50 | test_step.dependOn(&run_cmd.step); |
| 52 | test_step.dependOn(&exe.step); | ||
| 53 | } | ||
| 54 | } | 51 | } |
test/standalone/issue_12588/build.zig+5-3| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 6 | 9 | ||
| 7 | const obj = b.addObject(.{ | 10 | const obj = b.addObject(.{ |
| 8 | .name = "main", | 11 | .name = "main", |
| ... | @@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void { |
| 15 | obj.emit_bin = .no_emit; | 18 | obj.emit_bin = .no_emit; |
| 16 | b.default_step.dependOn(&obj.step); | 19 | b.default_step.dependOn(&obj.step); |
| 17 | 20 | ||
| 18 | const test_step = b.step("test", "Test the program"); | ||
| 19 | test_step.dependOn(&obj.step); | 21 | test_step.dependOn(&obj.step); |
| 20 | } | 22 | } |
test/standalone/issue_12706/build.zig+9-21| ... | @@ -2,17 +2,12 @@ const std = @import("std"); | ... | @@ -2,17 +2,12 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | // TODO integrate this with the std.Build executor API | ||
| 6 | fn 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 | |||
| 13 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 15 | const target = b.standardTargetOptions(.{}); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 10 | const target: std.zig.CrossTarget = .{}; | ||
| 16 | 11 | ||
| 17 | const exe = b.addExecutable(.{ | 12 | const exe = b.addExecutable(.{ |
| 18 | .name = "main", | 13 | .name = "main", |
| ... | @@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void { |
| 20 | .optimize = optimize, | 15 | .optimize = optimize, |
| 21 | .target = target, | 16 | .target = target, |
| 22 | }); | 17 | }); |
| 23 | exe.install(); | ||
| 24 | 18 | ||
| 25 | const c_sources = [_][]const u8{ | 19 | const c_sources = [_][]const u8{ |
| 26 | "test.c", | 20 | "test.c", |
| 27 | }; | 21 | }; |
| 28 | |||
| 29 | exe.addCSourceFiles(&c_sources, &.{}); | 22 | exe.addCSourceFiles(&c_sources, &.{}); |
| 30 | exe.linkLibC(); | 23 | exe.linkLibC(); |
| 31 | 24 | ||
| 32 | b.default_step.dependOn(&exe.step); | 25 | const run_cmd = b.addRunArtifact(exe); |
| 33 | 26 | run_cmd.expectExitCode(0); | |
| 34 | const test_step = b.step("test", "Test the program"); | 27 | run_cmd.skip_foreign_checks = true; |
| 35 | if (isRunnableTarget(target)) { | 28 | test_step.dependOn(&run_cmd.step); |
| 36 | const run_cmd = exe.run(); | ||
| 37 | test_step.dependOn(&run_cmd.step); | ||
| 38 | } else { | ||
| 39 | test_step.dependOn(&exe.step); | ||
| 40 | } | ||
| 41 | } | 29 | } |
test/standalone/issue_13030/build.zig+10-5| ... | @@ -3,17 +3,22 @@ const builtin = @import("builtin"); | ... | @@ -3,17 +3,22 @@ const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | const target = b.standardTargetOptions(.{}); | 7 | b.default_step = test_step; |
| 8 | 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 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const obj = b.addObject(.{ | 16 | const obj = b.addObject(.{ |
| 10 | .name = "main", | 17 | .name = "main", |
| 11 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 12 | .optimize = optimize, | 19 | .optimize = optimize, |
| 13 | .target = target, | 20 | .target = .{}, |
| 14 | }); | 21 | }); |
| 15 | b.default_step.dependOn(&obj.step); | ||
| 16 | 22 | ||
| 17 | const test_step = b.step("test", "Test the program"); | ||
| 18 | test_step.dependOn(&obj.step); | 23 | test_step.dependOn(&obj.step); |
| 19 | } | 24 | } |
test/standalone/issue_13970/build.zig+6-4| ... | @@ -1,6 +1,9 @@ | ... | @@ -1,6 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const test1 = b.addTest(.{ | 7 | const test1 = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "test_root/empty.zig" }, | 8 | .root_source_file = .{ .path = "test_root/empty.zig" }, |
| 6 | }); | 9 | }); |
| ... | @@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void { |
| 14 | test2.setTestRunner("src/main.zig"); | 17 | test2.setTestRunner("src/main.zig"); |
| 15 | test3.setTestRunner("src/main.zig"); | 18 | test3.setTestRunner("src/main.zig"); |
| 16 | 19 | ||
| 17 | const test_step = b.step("test", "Test package path resolution of custom test runner"); | 20 | test_step.dependOn(&test1.run().step); |
| 18 | test_step.dependOn(&test1.step); | 21 | test_step.dependOn(&test2.run().step); |
| 19 | test_step.dependOn(&test2.step); | 22 | test_step.dependOn(&test3.run().step); |
| 20 | test_step.dependOn(&test3.step); | ||
| 21 | } | 23 | } |
test/standalone/issue_339/build.zig+8-3| ... | @@ -1,13 +1,18 @@ | ... | @@ -1,13 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 9 | |||
| 4 | const obj = b.addObject(.{ | 10 | const obj = b.addObject(.{ |
| 5 | .name = "test", | 11 | .name = "test", |
| 6 | .root_source_file = .{ .path = "test.zig" }, | 12 | .root_source_file = .{ .path = "test.zig" }, |
| 7 | .target = b.standardTargetOptions(.{}), | 13 | .target = target, |
| 8 | .optimize = b.standardOptimizeOption(.{}), | 14 | .optimize = optimize, |
| 9 | }); | 15 | }); |
| 10 | 16 | ||
| 11 | const test_step = b.step("test", "Test the program"); | ||
| 12 | test_step.dependOn(&obj.step); | 17 | test_step.dependOn(&obj.step); |
| 13 | } | 18 | } |
test/standalone/issue_5825/build.zig+4-2| ... | @@ -1,12 +1,15 @@ | ... | @@ -1,12 +1,15 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const target = .{ | 7 | const target = .{ |
| 5 | .cpu_arch = .x86_64, | 8 | .cpu_arch = .x86_64, |
| 6 | .os_tag = .windows, | 9 | .os_tag = .windows, |
| 7 | .abi = .msvc, | 10 | .abi = .msvc, |
| 8 | }; | 11 | }; |
| 9 | const optimize = b.standardOptimizeOption(.{}); | 12 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 10 | const obj = b.addObject(.{ | 13 | const obj = b.addObject(.{ |
| 11 | .name = "issue_5825", | 14 | .name = "issue_5825", |
| 12 | .root_source_file = .{ .path = "main.zig" }, | 15 | .root_source_file = .{ .path = "main.zig" }, |
| ... | @@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void { |
| 24 | exe.linkSystemLibrary("ntdll"); | 27 | exe.linkSystemLibrary("ntdll"); |
| 25 | exe.addObject(obj); | 28 | exe.addObject(obj); |
| 26 | 29 | ||
| 27 | const test_step = b.step("test", "Test the program"); | ||
| 28 | test_step.dependOn(&exe.step); | 30 | test_step.dependOn(&exe.step); |
| 29 | } | 31 | } |
test/standalone/issue_7030.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const std_options = struct { | ||
| 4 | pub const logFn = log; | ||
| 5 | }; | ||
| 6 | |||
| 7 | pub 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 | |||
| 19 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const std_options = struct { | ||
| 4 | pub const logFn = log; | ||
| 5 | }; | ||
| 6 | |||
| 7 | pub 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 | |||
| 19 | pub fn main() anyerror!void { | ||
| 20 | std.log.info("All your codebase are belong to us.", .{}); | ||
| 21 | } | ||
test/standalone/issue_794/build.zig+3-3| ... | @@ -1,13 +1,13 @@ | ... | @@ -1,13 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const test_artifact = b.addTest(.{ | 7 | const test_artifact = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 8 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | }); | 9 | }); |
| 7 | test_artifact.addIncludePath("a_directory"); | 10 | test_artifact.addIncludePath("a_directory"); |
| 8 | 11 | ||
| 9 | b.default_step.dependOn(&test_artifact.step); | ||
| 10 | |||
| 11 | const test_step = b.step("test", "Test the program"); | ||
| 12 | test_step.dependOn(&test_artifact.step); | 12 | test_step.dependOn(&test_artifact.step); |
| 13 | } | 13 | } |
test/standalone/issue_8550/build.zig+5-2| ... | @@ -1,6 +1,10 @@ | ... | @@ -1,6 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) !void { | 3 | pub fn build(b: *std.Build) !void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 4 | const target = std.zig.CrossTarget{ | 8 | const target = std.zig.CrossTarget{ |
| 5 | .os_tag = .freestanding, | 9 | .os_tag = .freestanding, |
| 6 | .cpu_arch = .arm, | 10 | .cpu_arch = .arm, |
| ... | @@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void { |
| 8 | .explicit = &std.Target.arm.cpu.arm1176jz_s, | 12 | .explicit = &std.Target.arm.cpu.arm1176jz_s, |
| 9 | }, | 13 | }, |
| 10 | }; | 14 | }; |
| 11 | const optimize = b.standardOptimizeOption(.{}); | 15 | |
| 12 | const kernel = b.addExecutable(.{ | 16 | const kernel = b.addExecutable(.{ |
| 13 | .name = "kernel", | 17 | .name = "kernel", |
| 14 | .root_source_file = .{ .path = "./main.zig" }, | 18 | .root_source_file = .{ .path = "./main.zig" }, |
| ... | @@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void { | ... | @@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void { |
| 19 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); | 23 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); |
| 20 | kernel.install(); | 24 | kernel.install(); |
| 21 | 25 | ||
| 22 | const test_step = b.step("test", "Test it"); | ||
| 23 | test_step.dependOn(&kernel.step); | 26 | test_step.dependOn(&kernel.step); |
| 24 | } | 27 | } |
test/standalone/issue_9812/build.zig+5-2| ... | @@ -1,7 +1,11 @@ | ... | @@ -1,7 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) !void { | 3 | pub fn build(b: *std.Build) !void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | |||
| 5 | const zip_add = b.addTest(.{ | 9 | const zip_add = b.addTest(.{ |
| 6 | .root_source_file = .{ .path = "main.zig" }, | 10 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = optimize, | 11 | .optimize = optimize, |
| ... | @@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void { | ... | @@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void { |
| 13 | zip_add.addIncludePath("vendor/kuba-zip"); | 17 | zip_add.addIncludePath("vendor/kuba-zip"); |
| 14 | zip_add.linkLibC(); | 18 | zip_add.linkLibC(); |
| 15 | 19 | ||
| 16 | const test_step = b.step("test", "Test it"); | ||
| 17 | test_step.dependOn(&zip_add.step); | 20 | test_step.dependOn(&zip_add.step); |
| 18 | } | 21 | } |
test/standalone/load_dynamic_library/build.zig+16-4| ... | @@ -1,8 +1,19 @@ | ... | @@ -1,8 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | 3 | ||
| 3 | pub fn build(b: *std.Build) void { | 4 | pub fn build(b: *std.Build) void { |
| 4 | const target = b.standardTargetOptions(.{}); | 5 | const test_step = b.step("test", "Test it"); |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 6 | b.default_step = test_step; |
| 7 | |||
| 8 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 9 | const target: std.zig.CrossTarget = .{}; | ||
| 10 | |||
| 11 | const ok = (builtin.os.tag != .wasi and | ||
| 12 | // https://github.com/ziglang/zig/issues/13550 | ||
| 13 | (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and | ||
| 14 | // https://github.com/ziglang/zig/issues/13686 | ||
| 15 | (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)); | ||
| 16 | if (!ok) return; | ||
| 6 | 17 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 18 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "add", | 19 | .name = "add", |
| ... | @@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void { |
| 19 | .target = target, | 30 | .target = target, |
| 20 | }); | 31 | }); |
| 21 | 32 | ||
| 22 | const run = main.run(); | 33 | const run = b.addRunArtifact(main); |
| 23 | run.addArtifactArg(lib); | 34 | run.addArtifactArg(lib); |
| 35 | run.skip_foreign_checks = true; | ||
| 36 | run.expectExitCode(0); | ||
| 24 | 37 | ||
| 25 | const test_step = b.step("test", "Test the program"); | ||
| 26 | test_step.dependOn(&run.step); | 38 | test_step.dependOn(&run.step); |
| 27 | } | 39 | } |
test/standalone/load_dynamic_library/main.zig+1-5| ... | @@ -11,11 +11,7 @@ pub fn main() !void { | ... | @@ -11,11 +11,7 @@ pub fn main() !void { |
| 11 | var lib = try std.DynLib.open(dynlib_name); | 11 | var lib = try std.DynLib.open(dynlib_name); |
| 12 | defer lib.close(); | 12 | defer lib.close(); |
| 13 | 13 | ||
| 14 | const Add = switch (@import("builtin").zig_backend) { | 14 | const Add = *const fn (i32, i32) callconv(.C) i32; |
| 15 | .stage1 => fn (i32, i32) callconv(.C) i32, | ||
| 16 | else => *const fn (i32, i32) callconv(.C) i32, | ||
| 17 | }; | ||
| 18 | |||
| 19 | const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound; | 15 | const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound; |
| 20 | 16 | ||
| 21 | const result = addFn(12, 34); | 17 | const result = addFn(12, 34); |
test/standalone/main_pkg_path/build.zig+4-2| ... | @@ -1,11 +1,13 @@ | ... | @@ -1,11 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const test_exe = b.addTest(.{ | 7 | const test_exe = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "a/test.zig" }, | 8 | .root_source_file = .{ .path = "a/test.zig" }, |
| 6 | }); | 9 | }); |
| 7 | test_exe.setMainPkgPath("."); | 10 | test_exe.setMainPkgPath("."); |
| 8 | 11 | ||
| 9 | const test_step = b.step("test", "Test the program"); | 12 | test_step.dependOn(&test_exe.run().step); |
| 10 | test_step.dependOn(&test_exe.step); | ||
| 11 | } | 13 | } |
test/standalone/mix_c_files/build.zig+13-19| ... | @@ -1,34 +1,28 @@ | ... | @@ -1,34 +1,28 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | 2 | ||
| 5 | // TODO integrate this with the std.Build executor API | 3 | pub fn build(b: *std.Build) void { |
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | 4 | const test_step = b.step("test", "Test it"); |
| 7 | if (t.isNative()) return true; | 5 | b.default_step = test_step; |
| 8 | 6 | ||
| 9 | return (t.getOsTag() == builtin.os.tag and | 7 | add(b, test_step, .Debug); |
| 10 | t.getCpuArch() == builtin.cpu.arch); | 8 | add(b, test_step, .ReleaseFast); |
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | pub fn build(b: *std.Build) void { | 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | ||
| 15 | const target = b.standardTargetOptions(.{}); | ||
| 16 | |||
| 17 | const exe = b.addExecutable(.{ | 14 | const exe = b.addExecutable(.{ |
| 18 | .name = "test", | 15 | .name = "test", |
| 19 | .root_source_file = .{ .path = "main.zig" }, | 16 | .root_source_file = .{ .path = "main.zig" }, |
| 20 | .optimize = optimize, | 17 | .optimize = optimize, |
| 21 | .target = target, | ||
| 22 | }); | 18 | }); |
| 23 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); | 19 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); |
| 24 | exe.linkLibC(); | 20 | exe.linkLibC(); |
| 25 | b.default_step.dependOn(&exe.step); | 21 | b.default_step.dependOn(&exe.step); |
| 26 | 22 | ||
| 27 | const test_step = b.step("test", "Test the program"); | 23 | const run_cmd = b.addRunArtifact(exe); |
| 28 | if (isRunnableTarget(target)) { | 24 | run_cmd.skip_foreign_checks = true; |
| 29 | const run_cmd = exe.run(); | 25 | run_cmd.expectExitCode(0); |
| 30 | test_step.dependOn(&run_cmd.step); | 26 | |
| 31 | } else { | 27 | test_step.dependOn(&run_cmd.step); |
| 32 | test_step.dependOn(&exe.step); | ||
| 33 | } | ||
| 34 | } | 28 | } |
test/standalone/mix_o_files/build.zig+7-3| ... | @@ -1,18 +1,23 @@ | ... | @@ -1,18 +1,23 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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 = .{}; | ||
| 5 | 9 | ||
| 6 | const obj = b.addObject(.{ | 10 | const obj = b.addObject(.{ |
| 7 | .name = "base64", | 11 | .name = "base64", |
| 8 | .root_source_file = .{ .path = "base64.zig" }, | 12 | .root_source_file = .{ .path = "base64.zig" }, |
| 9 | .optimize = optimize, | 13 | .optimize = optimize, |
| 10 | .target = .{}, | 14 | .target = target, |
| 11 | }); | 15 | }); |
| 12 | 16 | ||
| 13 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 14 | .name = "test", | 18 | .name = "test", |
| 15 | .optimize = optimize, | 19 | .optimize = optimize, |
| 20 | .target = target, | ||
| 16 | }); | 21 | }); |
| 17 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); | 22 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 18 | exe.addObject(obj); | 23 | exe.addObject(obj); |
| ... | @@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void { |
| 22 | 27 | ||
| 23 | const run_cmd = exe.run(); | 28 | const run_cmd = exe.run(); |
| 24 | 29 | ||
| 25 | const test_step = b.step("test", "Test the program"); | ||
| 26 | test_step.dependOn(&run_cmd.step); | 30 | test_step.dependOn(&run_cmd.step); |
| 27 | } | 31 | } |
test/standalone/options/build.zig+1-1| ... | @@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void { |
| 20 | options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?); | 20 | options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?); |
| 21 | 21 | ||
| 22 | const test_step = b.step("test", "Run unit tests"); | 22 | const test_step = b.step("test", "Run unit tests"); |
| 23 | test_step.dependOn(&main.step); | 23 | test_step.dependOn(&main.run().step); |
| 24 | } | 24 | } |
test/standalone/pie/build.zig+14-4| ... | @@ -1,14 +1,24 @@ | ... | @@ -1,14 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{ | ||
| 9 | .os_tag = .linux, | ||
| 10 | .cpu_arch = .x86_64, | ||
| 11 | }; | ||
| 12 | |||
| 4 | const main = b.addTest(.{ | 13 | const main = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 14 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | 15 | .optimize = optimize, |
| 16 | .target = target, | ||
| 7 | }); | 17 | }); |
| 8 | main.pie = true; | 18 | main.pie = true; |
| 9 | 19 | ||
| 10 | const test_step = b.step("test", "Test the program"); | 20 | const run = main.run(); |
| 11 | test_step.dependOn(&main.step); | 21 | run.skip_foreign_checks = true; |
| 12 | 22 | ||
| 13 | b.default_step.dependOn(test_step); | 23 | test_step.dependOn(&run.step); |
| 14 | } | 24 | } |
test/standalone/pkg_import/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const exe = b.addExecutable(.{ | 9 | const exe = b.addExecutable(.{ |
| 7 | .name = "test", | 10 | .name = "test", |
| ... | @@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void { |
| 12 | 15 | ||
| 13 | const run = exe.run(); | 16 | const run = exe.run(); |
| 14 | 17 | ||
| 15 | const test_step = b.step("test", "Test it"); | ||
| 16 | test_step.dependOn(&run.step); | 18 | test_step.dependOn(&run.step); |
| 17 | } | 19 | } |
test/standalone/shared_library/build.zig+5-5| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 6 | const lib = b.addSharedLibrary(.{ | 9 | const lib = b.addSharedLibrary(.{ |
| 7 | .name = "mathtest", | 10 | .name = "mathtest", |
| 8 | .root_source_file = .{ .path = "mathtest.zig" }, | 11 | .root_source_file = .{ .path = "mathtest.zig" }, |
| ... | @@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void { |
| 20 | exe.linkLibrary(lib); | 23 | exe.linkLibrary(lib); |
| 21 | exe.linkSystemLibrary("c"); | 24 | exe.linkSystemLibrary("c"); |
| 22 | 25 | ||
| 23 | b.default_step.dependOn(&exe.step); | ||
| 24 | |||
| 25 | const run_cmd = exe.run(); | 26 | const run_cmd = exe.run(); |
| 26 | 27 | ||
| 27 | const test_step = b.step("test", "Test the program"); | ||
| 28 | test_step.dependOn(&run_cmd.step); | 28 | test_step.dependOn(&run_cmd.step); |
| 29 | } | 29 | } |
test/standalone/sigpipe/build.zig+14-5| ... | @@ -2,7 +2,16 @@ const std = @import("std"); | ... | @@ -2,7 +2,16 @@ const std = @import("std"); |
| 2 | const os = std.os; | 2 | const os = std.os; |
| 3 | 3 | ||
| 4 | pub fn build(b: *std.build.Builder) !void { | 4 | pub 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 | //} | ||
| 6 | 15 | ||
| 7 | // This test runs "breakpipe" as a child process and that process | 16 | // This test runs "breakpipe" as a child process and that process |
| 8 | // depends on inheriting a SIGPIPE disposition of "default". | 17 | // depends on inheriting a SIGPIPE disposition of "default". |
| ... | @@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void { | ... | @@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void { |
| 23 | .root_source_file = .{ .path = "breakpipe.zig" }, | 32 | .root_source_file = .{ .path = "breakpipe.zig" }, |
| 24 | }); | 33 | }); |
| 25 | exe.addOptions("build_options", options); | 34 | exe.addOptions("build_options", options); |
| 26 | const run = exe.run(); | 35 | const run = b.addRunArtifact(exe); |
| 27 | if (keep_sigpipe) { | 36 | if (keep_sigpipe) { |
| 28 | run.expected_term = .{ .Signal = std.os.SIG.PIPE }; | 37 | run.addCheck(.{ .expect_term = .{ .Signal = std.os.SIG.PIPE } }); |
| 29 | } else { | 38 | } else { |
| 30 | run.stdout_action = .{ .expect_exact = "BrokenPipe\n" }; | 39 | run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" }); |
| 31 | run.expected_term = .{ .Exited = 123 }; | 40 | run.addCheck(.{ .expect_term = .{ .Exited = 123 } }); |
| 32 | } | 41 | } |
| 33 | test_step.dependOn(&run.step); | 42 | test_step.dependOn(&run.step); |
| 34 | } | 43 | } |
test/standalone/static_c_lib/build.zig+5-3| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub 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; | ||
| 5 | 8 | ||
| 6 | const foo = b.addStaticLibrary(.{ | 9 | const foo = b.addStaticLibrary(.{ |
| 7 | .name = "foo", | 10 | .name = "foo", |
| ... | @@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void { |
| 18 | test_exe.linkLibrary(foo); | 21 | test_exe.linkLibrary(foo); |
| 19 | test_exe.addIncludePath("."); | 22 | test_exe.addIncludePath("."); |
| 20 | 23 | ||
| 21 | const test_step = b.step("test", "Test it"); | 24 | test_step.dependOn(&test_exe.run().step); |
| 22 | test_step.dependOn(&test_exe.step); | ||
| 23 | } | 25 | } |
test/standalone/test_runner_module_imports/build.zig+1-1| ... | @@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void { |
| 15 | t.addModule("module2", module2); | 15 | t.addModule("module2", module2); |
| 16 | 16 | ||
| 17 | const test_step = b.step("test", "Run unit tests"); | 17 | const test_step = b.step("test", "Run unit tests"); |
| 18 | test_step.dependOn(&t.step); | 18 | test_step.dependOn(&t.run().step); |
| 19 | } | 19 | } |
test/standalone/test_runner_path/build.zig+5-2| ... | @@ -1,14 +1,17 @@ | ... | @@ -1,14 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test the program"); | ||
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 4 | const test_exe = b.addTest(.{ | 9 | const test_exe = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "test.zig" }, | 10 | .root_source_file = .{ .path = "test.zig" }, |
| 6 | .kind = .test_exe, | ||
| 7 | }); | 11 | }); |
| 8 | test_exe.test_runner = "test_runner.zig"; | 12 | test_exe.test_runner = "test_runner.zig"; |
| 9 | 13 | ||
| 10 | const test_run = test_exe.run(); | 14 | const test_run = test_exe.run(); |
| 11 | 15 | ||
| 12 | const test_step = b.step("test", "Test the program"); | ||
| 13 | test_step.dependOn(&test_run.step); | 16 | test_step.dependOn(&test_run.step); |
| 14 | } | 17 | } |
test/standalone/test_runner_path/test_runner.zig+4-36| ... | @@ -1,51 +1,19 @@ | ... | @@ -1,51 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const io = std.io; | ||
| 3 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 4 | 3 | ||
| 5 | pub const io_mode: io.Mode = builtin.test_io_mode; | ||
| 6 | |||
| 7 | pub fn main() void { | 4 | pub fn main() void { |
| 8 | const test_fn_list = builtin.test_functions; | ||
| 9 | var ok_count: usize = 0; | 5 | var ok_count: usize = 0; |
| 10 | var skip_count: usize = 0; | 6 | var skip_count: usize = 0; |
| 11 | var fail_count: usize = 0; | 7 | var fail_count: usize = 0; |
| 12 | 8 | ||
| 13 | var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined; | 9 | for (builtin.test_functions) |test_fn| { |
| 14 | // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly | 10 | if (test_fn.func()) |_| { |
| 15 | // ignores the alignment of the slice. | ||
| 16 | async_frame_buffer = &[_]u8{}; | ||
| 17 | |||
| 18 | for (test_fn_list) |test_fn| { | ||
| 19 | const result = if (test_fn.async_frame_size) |size| switch (io_mode) { | ||
| 20 | .evented => blk: { | ||
| 21 | if (async_frame_buffer.len < size) { | ||
| 22 | std.heap.page_allocator.free(async_frame_buffer); | ||
| 23 | async_frame_buffer = std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size) catch @panic("out of memory"); | ||
| 24 | } | ||
| 25 | const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func); | ||
| 26 | break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{}); | ||
| 27 | }, | ||
| 28 | .blocking => { | ||
| 29 | skip_count += 1; | ||
| 30 | continue; | ||
| 31 | }, | ||
| 32 | } else test_fn.func(); | ||
| 33 | if (result) |_| { | ||
| 34 | ok_count += 1; | 11 | ok_count += 1; |
| 35 | } else |err| switch (err) { | 12 | } else |err| switch (err) { |
| 36 | error.SkipZigTest => { | 13 | error.SkipZigTest => skip_count += 1, |
| 37 | skip_count += 1; | 14 | else => fail_count += 1, |
| 38 | }, | ||
| 39 | else => { | ||
| 40 | fail_count += 1; | ||
| 41 | }, | ||
| 42 | } | 15 | } |
| 43 | } | 16 | } |
| 44 | if (ok_count == test_fn_list.len) { | ||
| 45 | std.debug.print("All {d} tests passed.\n", .{ok_count}); | ||
| 46 | } else { | ||
| 47 | std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count }); | ||
| 48 | } | ||
| 49 | if (ok_count != 1 or skip_count != 1 or fail_count != 1) { | 17 | if (ok_count != 1 or skip_count != 1 or fail_count != 1) { |
| 50 | std.process.exit(1); | 18 | std.process.exit(1); |
| 51 | } | 19 | } |
test/standalone/use_alias/build.zig+7-3| ... | @@ -1,12 +1,16 @@ | ... | @@ -1,12 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | |||
| 4 | const main = b.addTest(.{ | 9 | const main = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 10 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | 11 | .optimize = optimize, |
| 7 | }); | 12 | }); |
| 8 | main.addIncludePath("."); | 13 | main.addIncludePath("."); |
| 9 | 14 | ||
| 10 | const test_step = b.step("test", "Test it"); | 15 | test_step.dependOn(&main.run().step); |
| 11 | test_step.dependOn(&main.step); | ||
| 12 | } | 16 | } |
test/standalone/windows_spawn/build.zig+13-3| ... | @@ -1,23 +1,33 @@ | ... | @@ -1,23 +1,33 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | 3 | ||
| 3 | pub fn build(b: *std.Build) void { | 4 | pub 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; | ||
| 5 | 12 | ||
| 6 | const hello = b.addExecutable(.{ | 13 | const hello = b.addExecutable(.{ |
| 7 | .name = "hello", | 14 | .name = "hello", |
| 8 | .root_source_file = .{ .path = "hello.zig" }, | 15 | .root_source_file = .{ .path = "hello.zig" }, |
| 9 | .optimize = optimize, | 16 | .optimize = optimize, |
| 17 | .target = target, | ||
| 10 | }); | 18 | }); |
| 11 | 19 | ||
| 12 | const main = b.addExecutable(.{ | 20 | const main = b.addExecutable(.{ |
| 13 | .name = "main", | 21 | .name = "main", |
| 14 | .root_source_file = .{ .path = "main.zig" }, | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 15 | .optimize = optimize, | 23 | .optimize = optimize, |
| 24 | .target = target, | ||
| 16 | }); | 25 | }); |
| 17 | 26 | ||
| 18 | const run = main.run(); | 27 | const run = b.addRunArtifact(main); |
| 19 | run.addArtifactArg(hello); | 28 | run.addArtifactArg(hello); |
| 29 | run.expectExitCode(0); | ||
| 30 | run.skip_foreign_checks = true; | ||
| 20 | 31 | ||
| 21 | const test_step = b.step("test", "Test it"); | ||
| 22 | test_step.dependOn(&run.step); | 32 | test_step.dependOn(&run.step); |
| 23 | } | 33 | } |
test/tests.zig+449-745| ... | @@ -1,16 +1,9 @@ | ... | @@ -1,16 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const debug = std.debug; | 3 | const assert = std.debug.assert; |
| 4 | const CrossTarget = std.zig.CrossTarget; | 4 | const CrossTarget = std.zig.CrossTarget; |
| 5 | const io = std.io; | ||
| 6 | const fs = std.fs; | ||
| 7 | const mem = std.mem; | 5 | const mem = std.mem; |
| 8 | const fmt = std.fmt; | ||
| 9 | const ArrayList = std.ArrayList; | ||
| 10 | const OptimizeMode = std.builtin.OptimizeMode; | 6 | const OptimizeMode = std.builtin.OptimizeMode; |
| 11 | const CompileStep = std.Build.CompileStep; | ||
| 12 | const Allocator = mem.Allocator; | ||
| 13 | const ExecError = std.Build.ExecError; | ||
| 14 | const Step = std.Build.Step; | 7 | const Step = std.Build.Step; |
| 15 | 8 | ||
| 16 | // Cases | 9 | // Cases |
| ... | @@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig"); | ... | @@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig"); |
| 20 | const assemble_and_link = @import("assemble_and_link.zig"); | 13 | const assemble_and_link = @import("assemble_and_link.zig"); |
| 21 | const translate_c = @import("translate_c.zig"); | 14 | const translate_c = @import("translate_c.zig"); |
| 22 | const run_translated_c = @import("run_translated_c.zig"); | 15 | const run_translated_c = @import("run_translated_c.zig"); |
| 23 | const gen_h = @import("gen_h.zig"); | ||
| 24 | const link = @import("link.zig"); | 16 | const link = @import("link.zig"); |
| 25 | 17 | ||
| 26 | // Implementations | 18 | // Implementations |
| 27 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; | 19 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; |
| 28 | pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; | 20 | pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; |
| 29 | pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; | 21 | pub const CompareOutputContext = @import("src/CompareOutput.zig"); |
| 22 | pub const StackTracesContext = @import("src/StackTrace.zig"); | ||
| 30 | 23 | ||
| 31 | const TestTarget = struct { | 24 | const TestTarget = struct { |
| 32 | target: CrossTarget = @as(CrossTarget, .{}), | 25 | target: CrossTarget = @as(CrossTarget, .{}), |
| ... | @@ -460,10 +453,71 @@ const test_targets = blk: { | ... | @@ -460,10 +453,71 @@ const test_targets = blk: { |
| 460 | }; | 453 | }; |
| 461 | }; | 454 | }; |
| 462 | 455 | ||
| 463 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB | 456 | const 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 | }; | ||
| 464 | 514 | ||
| 465 | pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 515 | pub fn addCompareOutputTests( |
| 466 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; | 516 | b: *std.Build, |
| 517 | test_filter: ?[]const u8, | ||
| 518 | optimize_modes: []const OptimizeMode, | ||
| 519 | ) *Step { | ||
| 520 | const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM"); | ||
| 467 | cases.* = CompareOutputContext{ | 521 | cases.* = CompareOutputContext{ |
| 468 | .b = b, | 522 | .b = b, |
| 469 | .step = b.step("test-compare-output", "Run the compare output tests"), | 523 | .step = b.step("test-compare-output", "Run the compare output tests"), |
| ... | @@ -477,14 +531,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m | ... | @@ -477,14 +531,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m |
| 477 | return cases.step; | 531 | return cases.step; |
| 478 | } | 532 | } |
| 479 | 533 | ||
| 480 | pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 534 | pub fn addStackTraceTests( |
| 481 | const cases = b.allocator.create(StackTracesContext) catch unreachable; | 535 | b: *std.Build, |
| 482 | cases.* = StackTracesContext{ | 536 | test_filter: ?[]const u8, |
| 537 | optimize_modes: []const OptimizeMode, | ||
| 538 | ) *Step { | ||
| 539 | const check_exe = b.addExecutable(.{ | ||
| 540 | .name = "check-stack-trace", | ||
| 541 | .root_source_file = .{ .path = "test/src/check-stack-trace.zig" }, | ||
| 542 | .target = .{}, | ||
| 543 | .optimize = .Debug, | ||
| 544 | }); | ||
| 545 | |||
| 546 | const cases = b.allocator.create(StackTracesContext) catch @panic("OOM"); | ||
| 547 | cases.* = .{ | ||
| 483 | .b = b, | 548 | .b = b, |
| 484 | .step = b.step("test-stack-traces", "Run the stack trace tests"), | 549 | .step = b.step("test-stack-traces", "Run the stack trace tests"), |
| 485 | .test_index = 0, | 550 | .test_index = 0, |
| 486 | .test_filter = test_filter, | 551 | .test_filter = test_filter, |
| 487 | .optimize_modes = optimize_modes, | 552 | .optimize_modes = optimize_modes, |
| 553 | .check_exe = check_exe, | ||
| 488 | }; | 554 | }; |
| 489 | 555 | ||
| 490 | stack_traces.addCases(cases); | 556 | stack_traces.addCases(cases); |
| ... | @@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode | ... | @@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode |
| 494 | 560 | ||
| 495 | pub fn addStandaloneTests( | 561 | pub fn addStandaloneTests( |
| 496 | b: *std.Build, | 562 | b: *std.Build, |
| 497 | test_filter: ?[]const u8, | ||
| 498 | optimize_modes: []const OptimizeMode, | 563 | optimize_modes: []const OptimizeMode, |
| 499 | skip_non_native: bool, | ||
| 500 | enable_macos_sdk: bool, | 564 | enable_macos_sdk: bool, |
| 501 | target: std.zig.CrossTarget, | ||
| 502 | omit_stage2: bool, | 565 | omit_stage2: bool, |
| 503 | enable_darling: bool, | ||
| 504 | enable_qemu: bool, | ||
| 505 | enable_rosetta: bool, | ||
| 506 | enable_wasmtime: bool, | ||
| 507 | enable_wine: bool, | ||
| 508 | enable_symlinks_windows: bool, | 566 | enable_symlinks_windows: bool, |
| 509 | ) *Step { | 567 | ) *Step { |
| 510 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | 568 | const step = b.step("test-standalone", "Run the standalone tests"); |
| 511 | cases.* = StandaloneContext{ | 569 | const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; |
| 512 | .b = b, | 570 | |
| 513 | .step = b.step("test-standalone", "Run the standalone tests"), | 571 | for (standalone.simple_cases) |case| { |
| 514 | .test_index = 0, | 572 | for (optimize_modes) |optimize| { |
| 515 | .test_filter = test_filter, | 573 | if (!case.all_modes and optimize != .Debug) continue; |
| 516 | .optimize_modes = optimize_modes, | 574 | if (case.os_filter) |os_tag| { |
| 517 | .skip_non_native = skip_non_native, | 575 | if (os_tag != builtin.os.tag) continue; |
| 518 | .enable_macos_sdk = enable_macos_sdk, | 576 | } |
| 519 | .target = target, | ||
| 520 | .omit_stage2 = omit_stage2, | ||
| 521 | .enable_darling = enable_darling, | ||
| 522 | .enable_qemu = enable_qemu, | ||
| 523 | .enable_rosetta = enable_rosetta, | ||
| 524 | .enable_wasmtime = enable_wasmtime, | ||
| 525 | .enable_wine = enable_wine, | ||
| 526 | .enable_symlinks_windows = enable_symlinks_windows, | ||
| 527 | }; | ||
| 528 | 577 | ||
| 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(); | ||
| 530 | 586 | ||
| 531 | return cases.step; | 587 | step.dependOn(&exe.step); |
| 588 | } | ||
| 589 | |||
| 590 | if (case.is_test) { | ||
| 591 | const exe = b.addTest(.{ | ||
| 592 | .name = std.fs.path.stem(case.src_path), | ||
| 593 | .root_source_file = .{ .path = case.src_path }, | ||
| 594 | .optimize = optimize, | ||
| 595 | .target = case.target, | ||
| 596 | }); | ||
| 597 | if (case.link_libc) exe.linkLibC(); | ||
| 598 | |||
| 599 | step.dependOn(&exe.run().step); | ||
| 600 | } | ||
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | inline for (standalone.build_cases) |case| { | ||
| 605 | const requires_stage2 = @hasDecl(case.import, "requires_stage2") and | ||
| 606 | case.import.requires_stage2; | ||
| 607 | const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and | ||
| 608 | case.import.requires_symlinks; | ||
| 609 | const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and | ||
| 610 | case.import.requires_macos_sdk; | ||
| 611 | const bad = | ||
| 612 | (requires_stage2 and omit_stage2) or | ||
| 613 | (requires_symlinks and omit_symlinks) or | ||
| 614 | (requires_macos_sdk and !enable_macos_sdk); | ||
| 615 | if (!bad) { | ||
| 616 | const dep = b.anonymousDependency(case.build_root, case.import, .{}); | ||
| 617 | const dep_step = dep.builder.default_step; | ||
| 618 | assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); | ||
| 619 | const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; | ||
| 620 | dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); | ||
| 621 | step.dependOn(dep_step); | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | return step; | ||
| 532 | } | 626 | } |
| 533 | 627 | ||
| 534 | pub fn addLinkTests( | 628 | pub fn addLinkTests( |
| 535 | b: *std.Build, | 629 | b: *std.Build, |
| 536 | test_filter: ?[]const u8, | ||
| 537 | optimize_modes: []const OptimizeMode, | ||
| 538 | enable_macos_sdk: bool, | 630 | enable_macos_sdk: bool, |
| 539 | omit_stage2: bool, | 631 | omit_stage2: bool, |
| 540 | enable_symlinks_windows: bool, | 632 | enable_symlinks_windows: bool, |
| 541 | ) *Step { | 633 | ) *Step { |
| 542 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | 634 | const step = b.step("test-link", "Run the linker tests"); |
| 543 | cases.* = StandaloneContext{ | 635 | const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; |
| 544 | .b = b, | 636 | |
| 545 | .step = b.step("test-link", "Run the linker tests"), | 637 | inline for (link.cases) |case| { |
| 546 | .test_index = 0, | 638 | const requires_stage2 = @hasDecl(case.import, "requires_stage2") and |
| 547 | .test_filter = test_filter, | 639 | case.import.requires_stage2; |
| 548 | .optimize_modes = optimize_modes, | 640 | const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and |
| 549 | .skip_non_native = true, | 641 | case.import.requires_symlinks; |
| 550 | .enable_macos_sdk = enable_macos_sdk, | 642 | const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and |
| 551 | .target = .{}, | 643 | case.import.requires_macos_sdk; |
| 552 | .omit_stage2 = omit_stage2, | 644 | const bad = |
| 553 | .enable_symlinks_windows = enable_symlinks_windows, | 645 | (requires_stage2 and omit_stage2) or |
| 554 | }; | 646 | (requires_symlinks and omit_symlinks) or |
| 555 | link.addCases(cases); | 647 | (requires_macos_sdk and !enable_macos_sdk); |
| 556 | return cases.step; | 648 | if (!bad) { |
| 649 | const dep = b.anonymousDependency(case.build_root, case.import, .{}); | ||
| 650 | const dep_step = dep.builder.default_step; | ||
| 651 | assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); | ||
| 652 | const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; | ||
| 653 | dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); | ||
| 654 | step.dependOn(dep_step); | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | return step; | ||
| 557 | } | 659 | } |
| 558 | 660 | ||
| 559 | pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 661 | pub fn addCliTests(b: *std.Build) *Step { |
| 560 | _ = test_filter; | ||
| 561 | _ = optimize_modes; | ||
| 562 | const step = b.step("test-cli", "Test the command line interface"); | 662 | const step = b.step("test-cli", "Test the command line interface"); |
| 663 | const s = std.fs.path.sep_str; | ||
| 664 | |||
| 665 | { | ||
| 666 | |||
| 667 | // Test `zig init-lib`. | ||
| 668 | const tmp_path = b.makeTempPath(); | ||
| 669 | const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" }); | ||
| 670 | init_lib.cwd = tmp_path; | ||
| 671 | init_lib.setName("zig init-lib"); | ||
| 672 | init_lib.expectStdOutEqual(""); | ||
| 673 | init_lib.expectStdErrEqual("info: Created build.zig\n" ++ | ||
| 674 | "info: Created src" ++ s ++ "main.zig\n" ++ | ||
| 675 | "info: Next, try `zig build --help` or `zig build test`\n"); | ||
| 676 | |||
| 677 | const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); | ||
| 678 | run_test.cwd = tmp_path; | ||
| 679 | run_test.setName("zig build test"); | ||
| 680 | run_test.expectStdOutEqual(""); | ||
| 681 | run_test.step.dependOn(&init_lib.step); | ||
| 682 | |||
| 683 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 684 | cleanup.step.dependOn(&run_test.step); | ||
| 685 | |||
| 686 | step.dependOn(&cleanup.step); | ||
| 687 | } | ||
| 563 | 688 | ||
| 564 | const exe = b.addExecutable(.{ | 689 | { |
| 565 | .name = "test-cli", | 690 | // Test `zig init-exe`. |
| 566 | .root_source_file = .{ .path = "test/cli.zig" }, | 691 | const tmp_path = b.makeTempPath(); |
| 567 | .target = .{}, | 692 | const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" }); |
| 568 | .optimize = .Debug, | 693 | init_exe.cwd = tmp_path; |
| 569 | }); | 694 | init_exe.setName("zig init-exe"); |
| 570 | const run_cmd = exe.run(); | 695 | init_exe.expectStdOutEqual(""); |
| 571 | run_cmd.addArgs(&[_][]const u8{ | 696 | init_exe.expectStdErrEqual("info: Created build.zig\n" ++ |
| 572 | fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, | 697 | "info: Created src" ++ s ++ "main.zig\n" ++ |
| 573 | b.pathFromRoot(b.cache_root.path orelse "."), | 698 | "info: Next, try `zig build --help` or `zig build run`\n"); |
| 574 | }); | 699 | |
| 700 | // Test missing output path. | ||
| 701 | const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe"; | ||
| 702 | const ok_src_arg = "src" ++ s ++ "main.zig"; | ||
| 703 | const expected = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; | ||
| 704 | const run_bad = b.addSystemCommand(&.{ b.zig_exe, "build-exe", ok_src_arg, bad_out_arg }); | ||
| 705 | run_bad.setName("zig build-exe error message for bad -femit-bin arg"); | ||
| 706 | run_bad.expectExitCode(1); | ||
| 707 | run_bad.expectStdErrEqual(expected); | ||
| 708 | run_bad.expectStdOutEqual(""); | ||
| 709 | run_bad.step.dependOn(&init_exe.step); | ||
| 710 | |||
| 711 | const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); | ||
| 712 | run_test.cwd = tmp_path; | ||
| 713 | run_test.setName("zig build test"); | ||
| 714 | run_test.expectStdOutEqual(""); | ||
| 715 | run_test.step.dependOn(&init_exe.step); | ||
| 716 | |||
| 717 | const run_run = b.addSystemCommand(&.{ b.zig_exe, "build", "run" }); | ||
| 718 | run_run.cwd = tmp_path; | ||
| 719 | run_run.setName("zig build run"); | ||
| 720 | run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n"); | ||
| 721 | run_run.expectStdErrEqual("All your codebase are belong to us.\n"); | ||
| 722 | run_run.step.dependOn(&init_exe.step); | ||
| 723 | |||
| 724 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 725 | cleanup.step.dependOn(&run_test.step); | ||
| 726 | cleanup.step.dependOn(&run_run.step); | ||
| 727 | cleanup.step.dependOn(&run_bad.step); | ||
| 728 | |||
| 729 | step.dependOn(&cleanup.step); | ||
| 730 | } | ||
| 731 | |||
| 732 | // Test Godbolt API | ||
| 733 | if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) { | ||
| 734 | const tmp_path = b.makeTempPath(); | ||
| 735 | |||
| 736 | const writefile = b.addWriteFile("example.zig", | ||
| 737 | \\// Type your code here, or load an example. | ||
| 738 | \\export fn square(num: i32) i32 { | ||
| 739 | \\ return num * num; | ||
| 740 | \\} | ||
| 741 | \\extern fn zig_panic() noreturn; | ||
| 742 | \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn { | ||
| 743 | \\ _ = msg; | ||
| 744 | \\ _ = error_return_trace; | ||
| 745 | \\ zig_panic(); | ||
| 746 | \\} | ||
| 747 | ); | ||
| 748 | |||
| 749 | // This is intended to be the exact CLI usage used by godbolt.org. | ||
| 750 | const run = b.addSystemCommand(&.{ | ||
| 751 | b.zig_exe, "build-obj", | ||
| 752 | "--cache-dir", tmp_path, | ||
| 753 | "--name", "example", | ||
| 754 | "-fno-emit-bin", "-fno-emit-h", | ||
| 755 | "-fstrip", "-OReleaseFast", | ||
| 756 | }); | ||
| 757 | run.addFileSourceArg(writefile.getFileSource("example.zig").?); | ||
| 758 | const example_s = run.addPrefixedOutputFileArg("-femit-asm=", "example.s"); | ||
| 759 | |||
| 760 | const checkfile = b.addCheckFile(example_s, .{ | ||
| 761 | .expected_matches = &.{ | ||
| 762 | "square:", | ||
| 763 | "mov\teax, edi", | ||
| 764 | "imul\teax, edi", | ||
| 765 | }, | ||
| 766 | }); | ||
| 767 | checkfile.setName("check godbolt.org CLI usage generating valid asm"); | ||
| 768 | |||
| 769 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 770 | cleanup.step.dependOn(&checkfile.step); | ||
| 771 | |||
| 772 | step.dependOn(&cleanup.step); | ||
| 773 | } | ||
| 774 | |||
| 775 | { | ||
| 776 | // Test `zig fmt`. | ||
| 777 | // This test must use a temporary directory rather than a cache | ||
| 778 | // directory because this test will be mutating the files. The cache | ||
| 779 | // system relies on cache directories being mutated only by their | ||
| 780 | // owners. | ||
| 781 | const tmp_path = b.makeTempPath(); | ||
| 782 | const unformatted_code = " // no reason for indent"; | ||
| 783 | |||
| 784 | var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); | ||
| 785 | defer dir.close(); | ||
| 786 | dir.writeFile("fmt1.zig", unformatted_code) catch @panic("unhandled"); | ||
| 787 | dir.writeFile("fmt2.zig", unformatted_code) catch @panic("unhandled"); | ||
| 788 | |||
| 789 | // Test zig fmt affecting only the appropriate files. | ||
| 790 | const run1 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "fmt1.zig" }); | ||
| 791 | run1.setName("run zig fmt one file"); | ||
| 792 | run1.cwd = tmp_path; | ||
| 793 | run1.has_side_effects = true; | ||
| 794 | // stdout should be file path + \n | ||
| 795 | run1.expectStdOutEqual("fmt1.zig\n"); | ||
| 796 | |||
| 797 | // running it on the dir, only the new file should be changed | ||
| 798 | const run2 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); | ||
| 799 | run2.setName("run zig fmt the directory"); | ||
| 800 | run2.cwd = tmp_path; | ||
| 801 | run2.has_side_effects = true; | ||
| 802 | run2.expectStdOutEqual("." ++ s ++ "fmt2.zig\n"); | ||
| 803 | run2.step.dependOn(&run1.step); | ||
| 804 | |||
| 805 | // both files have been formatted, nothing should change now | ||
| 806 | const run3 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); | ||
| 807 | run3.setName("run zig fmt with nothing to do"); | ||
| 808 | run3.cwd = tmp_path; | ||
| 809 | run3.has_side_effects = true; | ||
| 810 | run3.expectStdOutEqual(""); | ||
| 811 | run3.step.dependOn(&run2.step); | ||
| 812 | |||
| 813 | const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; | ||
| 814 | const fmt4_path = std.fs.path.join(b.allocator, &.{ tmp_path, "fmt4.zig" }) catch @panic("OOM"); | ||
| 815 | const write4 = b.addWriteFiles(); | ||
| 816 | write4.addBytesToSource(unformatted_code_utf16, fmt4_path); | ||
| 817 | write4.step.dependOn(&run3.step); | ||
| 818 | |||
| 819 | // Test `zig fmt` handling UTF-16 decoding. | ||
| 820 | const run4 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); | ||
| 821 | run4.setName("run zig fmt convert UTF-16 to UTF-8"); | ||
| 822 | run4.cwd = tmp_path; | ||
| 823 | run4.has_side_effects = true; | ||
| 824 | run4.expectStdOutEqual("." ++ s ++ "fmt4.zig\n"); | ||
| 825 | run4.step.dependOn(&write4.step); | ||
| 826 | |||
| 827 | // TODO change this to an exact match | ||
| 828 | const check4 = b.addCheckFile(.{ .path = fmt4_path }, .{ | ||
| 829 | .expected_matches = &.{ | ||
| 830 | "// no reason", | ||
| 831 | }, | ||
| 832 | }); | ||
| 833 | check4.step.dependOn(&run4.step); | ||
| 834 | |||
| 835 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 836 | cleanup.step.dependOn(&check4.step); | ||
| 837 | |||
| 838 | step.dependOn(&cleanup.step); | ||
| 839 | } | ||
| 840 | |||
| 841 | { | ||
| 842 | // TODO this should move to become a CLI test rather than standalone | ||
| 843 | // cases.addBuildFile("test/standalone/options/build.zig", .{ | ||
| 844 | // .extra_argv = &.{ | ||
| 845 | // "-Dbool_true", | ||
| 846 | // "-Dbool_false=false", | ||
| 847 | // "-Dint=1234", | ||
| 848 | // "-De=two", | ||
| 849 | // "-Dstring=hello", | ||
| 850 | // }, | ||
| 851 | // }); | ||
| 852 | } | ||
| 575 | 853 | ||
| 576 | step.dependOn(&run_cmd.step); | ||
| 577 | return step; | 854 | return step; |
| 578 | } | 855 | } |
| 579 | 856 | ||
| 580 | pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 857 | pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { |
| 581 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; | 858 | const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM"); |
| 582 | cases.* = CompareOutputContext{ | 859 | cases.* = CompareOutputContext{ |
| 583 | .b = b, | 860 | .b = b, |
| 584 | .step = b.step("test-asm-link", "Run the assemble and link tests"), | 861 | .step = b.step("test-asm-link", "Run the assemble and link tests"), |
| ... | @@ -593,7 +870,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize | ... | @@ -593,7 +870,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize |
| 593 | } | 870 | } |
| 594 | 871 | ||
| 595 | pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { | 872 | pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { |
| 596 | const cases = b.allocator.create(TranslateCContext) catch unreachable; | 873 | const cases = b.allocator.create(TranslateCContext) catch @panic("OOM"); |
| 597 | cases.* = TranslateCContext{ | 874 | cases.* = TranslateCContext{ |
| 598 | .b = b, | 875 | .b = b, |
| 599 | .step = b.step("test-translate-c", "Run the C translation tests"), | 876 | .step = b.step("test-translate-c", "Run the C translation tests"), |
| ... | @@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests( | ... | @@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests( |
| 611 | test_filter: ?[]const u8, | 888 | test_filter: ?[]const u8, |
| 612 | target: std.zig.CrossTarget, | 889 | target: std.zig.CrossTarget, |
| 613 | ) *Step { | 890 | ) *Step { |
| 614 | const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; | 891 | const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM"); |
| 615 | cases.* = .{ | 892 | cases.* = .{ |
| 616 | .b = b, | 893 | .b = b, |
| 617 | .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), | 894 | .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), |
| ... | @@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests( | ... | @@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests( |
| 625 | return cases.step; | 902 | return cases.step; |
| 626 | } | 903 | } |
| 627 | 904 | ||
| 628 | pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step { | 905 | const ModuleTestOptions = struct { |
| 629 | const cases = b.allocator.create(GenHContext) catch unreachable; | ||
| 630 | cases.* = GenHContext{ | ||
| 631 | .b = b, | ||
| 632 | .step = b.step("test-gen-h", "Run the C header file generation tests"), | ||
| 633 | .test_index = 0, | ||
| 634 | .test_filter = test_filter, | ||
| 635 | }; | ||
| 636 | |||
| 637 | gen_h.addCases(cases); | ||
| 638 | |||
| 639 | return cases.step; | ||
| 640 | } | ||
| 641 | |||
| 642 | pub fn addPkgTests( | ||
| 643 | b: *std.Build, | ||
| 644 | test_filter: ?[]const u8, | 906 | test_filter: ?[]const u8, |
| 645 | root_src: []const u8, | 907 | root_src: []const u8, |
| 646 | name: []const u8, | 908 | name: []const u8, |
| ... | @@ -651,14 +913,17 @@ pub fn addPkgTests( | ... | @@ -651,14 +913,17 @@ pub fn addPkgTests( |
| 651 | skip_libc: bool, | 913 | skip_libc: bool, |
| 652 | skip_stage1: bool, | 914 | skip_stage1: bool, |
| 653 | skip_stage2: bool, | 915 | skip_stage2: bool, |
| 654 | ) *Step { | 916 | max_rss: usize = 0, |
| 655 | const step = b.step(b.fmt("test-{s}", .{name}), desc); | 917 | }; |
| 918 | |||
| 919 | pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { | ||
| 920 | const step = b.step(b.fmt("test-{s}", .{options.name}), options.desc); | ||
| 656 | 921 | ||
| 657 | for (test_targets) |test_target| { | 922 | for (test_targets) |test_target| { |
| 658 | if (skip_non_native and !test_target.target.isNative()) | 923 | if (options.skip_non_native and !test_target.target.isNative()) |
| 659 | continue; | 924 | continue; |
| 660 | 925 | ||
| 661 | if (skip_libc and test_target.link_libc) | 926 | if (options.skip_libc and test_target.link_libc) |
| 662 | continue; | 927 | continue; |
| 663 | 928 | ||
| 664 | if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { | 929 | if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { |
| ... | @@ -666,7 +931,7 @@ pub fn addPkgTests( | ... | @@ -666,7 +931,7 @@ pub fn addPkgTests( |
| 666 | continue; | 931 | continue; |
| 667 | } | 932 | } |
| 668 | 933 | ||
| 669 | if (skip_single_threaded and test_target.single_threaded) | 934 | if (options.skip_single_threaded and test_target.single_threaded) |
| 670 | continue; | 935 | continue; |
| 671 | 936 | ||
| 672 | if (test_target.disable_native and | 937 | if (test_target.disable_native and |
| ... | @@ -677,12 +942,12 @@ pub fn addPkgTests( | ... | @@ -677,12 +942,12 @@ pub fn addPkgTests( |
| 677 | } | 942 | } |
| 678 | 943 | ||
| 679 | if (test_target.backend) |backend| switch (backend) { | 944 | if (test_target.backend) |backend| switch (backend) { |
| 680 | .stage1 => if (skip_stage1) continue, | 945 | .stage1 => if (options.skip_stage1) continue, |
| 681 | .stage2_llvm => {}, | 946 | .stage2_llvm => {}, |
| 682 | else => if (skip_stage2) continue, | 947 | else => if (options.skip_stage2) continue, |
| 683 | }; | 948 | }; |
| 684 | 949 | ||
| 685 | const want_this_mode = for (optimize_modes) |m| { | 950 | const want_this_mode = for (options.optimize_modes) |m| { |
| 686 | if (m == test_target.optimize_mode) break true; | 951 | if (m == test_target.optimize_mode) break true; |
| 687 | } else false; | 952 | } else false; |
| 688 | if (!want_this_mode) continue; | 953 | if (!want_this_mode) continue; |
| ... | @@ -694,25 +959,24 @@ pub fn addPkgTests( | ... | @@ -694,25 +959,24 @@ pub fn addPkgTests( |
| 694 | else | 959 | else |
| 695 | "bare"; | 960 | "bare"; |
| 696 | 961 | ||
| 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; | ||
| 698 | 969 | ||
| 699 | const these_tests = b.addTest(.{ | 970 | const these_tests = b.addTest(.{ |
| 700 | .root_source_file = .{ .path = root_src }, | 971 | .root_source_file = .{ .path = options.root_src }, |
| 701 | .optimize = test_target.optimize_mode, | 972 | .optimize = test_target.optimize_mode, |
| 702 | .target = test_target.target, | 973 | .target = test_target.target, |
| 974 | .max_rss = max_rss, | ||
| 703 | }); | 975 | }); |
| 704 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; | 976 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; |
| 705 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; | 977 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; |
| 706 | these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ | ||
| 707 | name, | ||
| 708 | triple_prefix, | ||
| 709 | @tagName(test_target.optimize_mode), | ||
| 710 | libc_prefix, | ||
| 711 | single_threaded_txt, | ||
| 712 | backend_txt, | ||
| 713 | })); | ||
| 714 | these_tests.single_threaded = test_target.single_threaded; | 978 | these_tests.single_threaded = test_target.single_threaded; |
| 715 | these_tests.setFilter(test_filter); | 979 | these_tests.setFilter(options.test_filter); |
| 716 | if (test_target.link_libc) { | 980 | if (test_target.link_libc) { |
| 717 | these_tests.linkSystemLibrary("c"); | 981 | these_tests.linkSystemLibrary("c"); |
| 718 | } | 982 | } |
| ... | @@ -736,654 +1000,94 @@ pub fn addPkgTests( | ... | @@ -736,654 +1000,94 @@ pub fn addPkgTests( |
| 736 | }, | 1000 | }, |
| 737 | }; | 1001 | }; |
| 738 | 1002 | ||
| 739 | step.dependOn(&these_tests.step); | 1003 | const run = these_tests.run(); |
| 1004 | run.skip_foreign_checks = true; | ||
| 1005 | run.setName(b.fmt("run test {s}-{s}-{s}-{s}-{s}-{s}", .{ | ||
| 1006 | options.name, | ||
| 1007 | triple_prefix, | ||
| 1008 | @tagName(test_target.optimize_mode), | ||
| 1009 | libc_prefix, | ||
| 1010 | single_threaded_txt, | ||
| 1011 | backend_txt, | ||
| 1012 | })); | ||
| 1013 | |||
| 1014 | step.dependOn(&run.step); | ||
| 740 | } | 1015 | } |
| 741 | return step; | 1016 | return step; |
| 742 | } | 1017 | } |
| 743 | 1018 | ||
| 744 | pub const StackTracesContext = struct { | 1019 | pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { |
| 745 | b: *std.Build, | 1020 | const step = b.step("test-c-abi", "Run the C ABI tests"); |
| 746 | step: *Step, | ||
| 747 | test_index: usize, | ||
| 748 | test_filter: ?[]const u8, | ||
| 749 | optimize_modes: []const OptimizeMode, | ||
| 750 | |||
| 751 | const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; | ||
| 752 | |||
| 753 | pub fn addCase(self: *StackTracesContext, config: anytype) void { | ||
| 754 | if (@hasField(@TypeOf(config), "exclude")) { | ||
| 755 | if (config.exclude.exclude()) return; | ||
| 756 | } | ||
| 757 | if (@hasField(@TypeOf(config), "exclude_arch")) { | ||
| 758 | const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch; | ||
| 759 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | ||
| 760 | } | ||
| 761 | if (@hasField(@TypeOf(config), "exclude_os")) { | ||
| 762 | const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; | ||
| 763 | for (exclude_os) |os| if (os == builtin.os.tag) return; | ||
| 764 | } | ||
| 765 | for (self.optimize_modes) |optimize_mode| { | ||
| 766 | switch (optimize_mode) { | ||
| 767 | .Debug => { | ||
| 768 | if (@hasField(@TypeOf(config), "Debug")) { | ||
| 769 | self.addExpect(config.name, config.source, optimize_mode, config.Debug); | ||
| 770 | } | ||
| 771 | }, | ||
| 772 | .ReleaseSafe => { | ||
| 773 | if (@hasField(@TypeOf(config), "ReleaseSafe")) { | ||
| 774 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); | ||
| 775 | } | ||
| 776 | }, | ||
| 777 | .ReleaseFast => { | ||
| 778 | if (@hasField(@TypeOf(config), "ReleaseFast")) { | ||
| 779 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); | ||
| 780 | } | ||
| 781 | }, | ||
| 782 | .ReleaseSmall => { | ||
| 783 | if (@hasField(@TypeOf(config), "ReleaseSmall")) { | ||
| 784 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); | ||
| 785 | } | ||
| 786 | }, | ||
| 787 | } | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 791 | fn addExpect( | ||
| 792 | self: *StackTracesContext, | ||
| 793 | name: []const u8, | ||
| 794 | source: []const u8, | ||
| 795 | optimize_mode: OptimizeMode, | ||
| 796 | mode_config: anytype, | ||
| 797 | ) void { | ||
| 798 | if (@hasField(@TypeOf(mode_config), "exclude")) { | ||
| 799 | if (mode_config.exclude.exclude()) return; | ||
| 800 | } | ||
| 801 | if (@hasField(@TypeOf(mode_config), "exclude_arch")) { | ||
| 802 | const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch; | ||
| 803 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | ||
| 804 | } | ||
| 805 | if (@hasField(@TypeOf(mode_config), "exclude_os")) { | ||
| 806 | const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os; | ||
| 807 | for (exclude_os) |os| if (os == builtin.os.tag) return; | ||
| 808 | } | ||
| 809 | |||
| 810 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | ||
| 811 | "stack-trace", | ||
| 812 | name, | ||
| 813 | @tagName(optimize_mode), | ||
| 814 | }) catch unreachable; | ||
| 815 | if (self.test_filter) |filter| { | ||
| 816 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 817 | } | ||
| 818 | |||
| 819 | const b = self.b; | ||
| 820 | const src_basename = "source.zig"; | ||
| 821 | const write_src = b.addWriteFile(src_basename, source); | ||
| 822 | const exe = b.addExecutable(.{ | ||
| 823 | .name = "test", | ||
| 824 | .root_source_file = write_src.getFileSource(src_basename).?, | ||
| 825 | .optimize = optimize_mode, | ||
| 826 | .target = .{}, | ||
| 827 | }); | ||
| 828 | |||
| 829 | const run_and_compare = RunAndCompareStep.create( | ||
| 830 | self, | ||
| 831 | exe, | ||
| 832 | annotated_case_name, | ||
| 833 | optimize_mode, | ||
| 834 | mode_config.expect, | ||
| 835 | ); | ||
| 836 | |||
| 837 | self.step.dependOn(&run_and_compare.step); | ||
| 838 | } | ||
| 839 | |||
| 840 | const RunAndCompareStep = struct { | ||
| 841 | pub const base_id = .custom; | ||
| 842 | |||
| 843 | step: Step, | ||
| 844 | context: *StackTracesContext, | ||
| 845 | exe: *CompileStep, | ||
| 846 | name: []const u8, | ||
| 847 | optimize_mode: OptimizeMode, | ||
| 848 | expect_output: []const u8, | ||
| 849 | test_index: usize, | ||
| 850 | |||
| 851 | pub fn create( | ||
| 852 | context: *StackTracesContext, | ||
| 853 | exe: *CompileStep, | ||
| 854 | name: []const u8, | ||
| 855 | optimize_mode: OptimizeMode, | ||
| 856 | expect_output: []const u8, | ||
| 857 | ) *RunAndCompareStep { | ||
| 858 | const allocator = context.b.allocator; | ||
| 859 | const ptr = allocator.create(RunAndCompareStep) catch unreachable; | ||
| 860 | ptr.* = RunAndCompareStep{ | ||
| 861 | .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), | ||
| 862 | .context = context, | ||
| 863 | .exe = exe, | ||
| 864 | .name = name, | ||
| 865 | .optimize_mode = optimize_mode, | ||
| 866 | .expect_output = expect_output, | ||
| 867 | .test_index = context.test_index, | ||
| 868 | }; | ||
| 869 | ptr.step.dependOn(&exe.step); | ||
| 870 | context.test_index += 1; | ||
| 871 | return ptr; | ||
| 872 | } | ||
| 873 | |||
| 874 | fn make(step: *Step) !void { | ||
| 875 | const self = @fieldParentPtr(RunAndCompareStep, "step", step); | ||
| 876 | const b = self.context.b; | ||
| 877 | |||
| 878 | const full_exe_path = self.exe.getOutputSource().getPath(b); | ||
| 879 | var args = ArrayList([]const u8).init(b.allocator); | ||
| 880 | defer args.deinit(); | ||
| 881 | args.append(full_exe_path) catch unreachable; | ||
| 882 | |||
| 883 | std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); | ||
| 884 | |||
| 885 | if (!std.process.can_spawn) { | ||
| 886 | const cmd = try std.mem.join(b.allocator, " ", args.items); | ||
| 887 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); | ||
| 888 | b.allocator.free(cmd); | ||
| 889 | return ExecError.ExecNotSupported; | ||
| 890 | } | ||
| 891 | |||
| 892 | var child = std.ChildProcess.init(args.items, b.allocator); | ||
| 893 | child.stdin_behavior = .Ignore; | ||
| 894 | child.stdout_behavior = .Pipe; | ||
| 895 | child.stderr_behavior = .Pipe; | ||
| 896 | child.env_map = b.env_map; | ||
| 897 | |||
| 898 | if (b.verbose) { | ||
| 899 | printInvocation(args.items); | ||
| 900 | } | ||
| 901 | child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); | ||
| 902 | |||
| 903 | const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; | ||
| 904 | defer b.allocator.free(stdout); | ||
| 905 | const stderrFull = child.stderr.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; | ||
| 906 | defer b.allocator.free(stderrFull); | ||
| 907 | var stderr = stderrFull; | ||
| 908 | |||
| 909 | const term = child.wait() catch |err| { | ||
| 910 | debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); | ||
| 911 | }; | ||
| 912 | |||
| 913 | switch (term) { | ||
| 914 | .Exited => |code| { | ||
| 915 | const expect_code: u32 = 1; | ||
| 916 | if (code != expect_code) { | ||
| 917 | std.debug.print("Process {s} exited with error code {d} but expected code {d}\n", .{ | ||
| 918 | full_exe_path, | ||
| 919 | code, | ||
| 920 | expect_code, | ||
| 921 | }); | ||
| 922 | printInvocation(args.items); | ||
| 923 | return error.TestFailed; | ||
| 924 | } | ||
| 925 | }, | ||
| 926 | .Signal => |signum| { | ||
| 927 | std.debug.print("Process {s} terminated on signal {d}\n", .{ full_exe_path, signum }); | ||
| 928 | printInvocation(args.items); | ||
| 929 | return error.TestFailed; | ||
| 930 | }, | ||
| 931 | .Stopped => |signum| { | ||
| 932 | std.debug.print("Process {s} stopped on signal {d}\n", .{ full_exe_path, signum }); | ||
| 933 | printInvocation(args.items); | ||
| 934 | return error.TestFailed; | ||
| 935 | }, | ||
| 936 | .Unknown => |code| { | ||
| 937 | std.debug.print("Process {s} terminated unexpectedly with error code {d}\n", .{ full_exe_path, code }); | ||
| 938 | printInvocation(args.items); | ||
| 939 | return error.TestFailed; | ||
| 940 | }, | ||
| 941 | } | ||
| 942 | |||
| 943 | // process result | ||
| 944 | // - keep only basename of source file path | ||
| 945 | // - replace address with symbolic string | ||
| 946 | // - replace function name with symbolic string when optimize_mode != .Debug | ||
| 947 | // - skip empty lines | ||
| 948 | const got: []const u8 = got_result: { | ||
| 949 | var buf = ArrayList(u8).init(b.allocator); | ||
| 950 | defer buf.deinit(); | ||
| 951 | if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1]; | ||
| 952 | var it = mem.split(u8, stderr, "\n"); | ||
| 953 | process_lines: while (it.next()) |line| { | ||
| 954 | if (line.len == 0) continue; | ||
| 955 | |||
| 956 | // offset search past `[drive]:` on windows | ||
| 957 | var pos: usize = if (builtin.os.tag == .windows) 2 else 0; | ||
| 958 | // locate delims/anchor | ||
| 959 | const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" }; | ||
| 960 | var marks = [_]usize{0} ** delims.len; | ||
| 961 | for (delims, 0..) |delim, i| { | ||
| 962 | marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { | ||
| 963 | // unexpected pattern: emit raw line and cont | ||
| 964 | try buf.appendSlice(line); | ||
| 965 | try buf.appendSlice("\n"); | ||
| 966 | continue :process_lines; | ||
| 967 | }; | ||
| 968 | pos = marks[i] + delim.len; | ||
| 969 | } | ||
| 970 | // locate source basename | ||
| 971 | pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { | ||
| 972 | // unexpected pattern: emit raw line and cont | ||
| 973 | try buf.appendSlice(line); | ||
| 974 | try buf.appendSlice("\n"); | ||
| 975 | continue :process_lines; | ||
| 976 | }; | ||
| 977 | // end processing if source basename changes | ||
| 978 | if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break; | ||
| 979 | // emit substituted line | ||
| 980 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); | ||
| 981 | try buf.appendSlice(" [address]"); | ||
| 982 | if (self.optimize_mode == .Debug) { | ||
| 983 | // On certain platforms (windows) or possibly depending on how we choose to link main | ||
| 984 | // the object file extension may be present so we simply strip any extension. | ||
| 985 | if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { | ||
| 986 | try buf.appendSlice(line[marks[3] .. marks[4] + idot]); | ||
| 987 | try buf.appendSlice(line[marks[5]..]); | ||
| 988 | } else { | ||
| 989 | try buf.appendSlice(line[marks[3]..]); | ||
| 990 | } | ||
| 991 | } else { | ||
| 992 | try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]); | ||
| 993 | try buf.appendSlice("[function]"); | ||
| 994 | } | ||
| 995 | try buf.appendSlice("\n"); | ||
| 996 | } | ||
| 997 | break :got_result try buf.toOwnedSlice(); | ||
| 998 | }; | ||
| 999 | |||
| 1000 | if (!mem.eql(u8, self.expect_output, got)) { | ||
| 1001 | std.debug.print( | ||
| 1002 | \\ | ||
| 1003 | \\========= Expected this output: ========= | ||
| 1004 | \\{s} | ||
| 1005 | \\================================================ | ||
| 1006 | \\{s} | ||
| 1007 | \\ | ||
| 1008 | , .{ self.expect_output, got }); | ||
| 1009 | return error.TestFailed; | ||
| 1010 | } | ||
| 1011 | std.debug.print("OK\n", .{}); | ||
| 1012 | } | ||
| 1013 | }; | ||
| 1014 | }; | ||
| 1015 | |||
| 1016 | pub 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 | } | ||
| 1082 | 1021 | ||
| 1083 | if (features.use_emulation) { | 1022 | const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; |
| 1084 | if (self.enable_darling) { | ||
| 1085 | zig_args.append("-fdarling") catch unreachable; | ||
| 1086 | } | ||
| 1087 | if (self.enable_qemu) { | ||
| 1088 | zig_args.append("-fqemu") catch unreachable; | ||
| 1089 | } | ||
| 1090 | if (self.enable_rosetta) { | ||
| 1091 | zig_args.append("-frosetta") catch unreachable; | ||
| 1092 | } | ||
| 1093 | if (self.enable_wasmtime) { | ||
| 1094 | zig_args.append("-fwasmtime") catch unreachable; | ||
| 1095 | } | ||
| 1096 | if (self.enable_wine) { | ||
| 1097 | zig_args.append("-fwine") catch unreachable; | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | 1023 | ||
| 1101 | const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; | 1024 | for (optimize_modes) |optimize_mode| { |
| 1102 | for (optimize_modes) |optimize_mode| { | 1025 | if (optimize_mode != .Debug and skip_release) continue; |
| 1103 | const arg = switch (optimize_mode) { | ||
| 1104 | .Debug => "", | ||
| 1105 | .ReleaseFast => "-Doptimize=ReleaseFast", | ||
| 1106 | .ReleaseSafe => "-Doptimize=ReleaseSafe", | ||
| 1107 | .ReleaseSmall => "-Doptimize=ReleaseSmall", | ||
| 1108 | }; | ||
| 1109 | const zig_args_base_len = zig_args.items.len; | ||
| 1110 | if (arg.len > 0) | ||
| 1111 | zig_args.append(arg) catch unreachable; | ||
| 1112 | defer zig_args.resize(zig_args_base_len) catch unreachable; | ||
| 1113 | |||
| 1114 | const run_cmd = b.addSystemCommand(zig_args.items); | ||
| 1115 | const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) }); | ||
| 1116 | log_step.step.dependOn(&run_cmd.step); | ||
| 1117 | |||
| 1118 | self.step.dependOn(&log_step.step); | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | 1026 | ||
| 1122 | pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void { | 1027 | for (c_abi_targets) |c_abi_target| { |
| 1123 | const b = self.b; | 1028 | if (skip_non_native and !c_abi_target.isNative()) continue; |
| 1124 | 1029 | ||
| 1125 | for (self.optimize_modes) |optimize| { | 1030 | if (c_abi_target.isWindows() and c_abi_target.getCpuArch() == .aarch64) { |
| 1126 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ | 1031 | // https://github.com/ziglang/zig/issues/14908 |
| 1127 | root_src, | 1032 | continue; |
| 1128 | @tagName(optimize), | ||
| 1129 | }) catch unreachable; | ||
| 1130 | if (self.test_filter) |filter| { | ||
| 1131 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | ||
| 1132 | } | 1033 | } |
| 1133 | 1034 | ||
| 1134 | const exe = b.addExecutable(.{ | 1035 | const test_step = b.addTest(.{ |
| 1135 | .name = "test", | 1036 | .root_source_file = .{ .path = "test/c_abi/main.zig" }, |
| 1136 | .root_source_file = .{ .path = root_src }, | 1037 | .optimize = optimize_mode, |
| 1137 | .optimize = optimize, | 1038 | .target = c_abi_target, |
| 1138 | .target = .{}, | ||
| 1139 | }); | 1039 | }); |
| 1140 | if (link_libc) { | 1040 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { |
| 1141 | exe.linkSystemLibrary("c"); | 1041 | // TODO NativeTargetInfo insists on dynamically linking musl |
| 1042 | // for some reason? | ||
| 1043 | test_step.target_info.dynamic_linker.max_byte = null; | ||
| 1142 | } | 1044 | } |
| 1143 | 1045 | test_step.linkLibC(); | |
| 1144 | const log_step = b.addLog("PASS {s}", .{annotated_case_name}); | 1046 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); |
| 1145 | log_step.step.dependOn(&exe.step); | 1047 | |
| 1146 | 1048 | // test-c-abi should test both with LTO on and with LTO off. Only | |
| 1147 | self.step.dependOn(&log_step.step); | 1049 | // some combinations are passing currently: |
| 1148 | } | 1050 | // https://github.com/ziglang/zig/issues/14908 |
| 1149 | } | 1051 | if (c_abi_target.isWindows()) { |
| 1150 | }; | 1052 | test_step.want_lto = false; |
| 1151 | |||
| 1152 | pub const GenHContext = struct { | ||
| 1153 | b: *std.Build, | ||
| 1154 | step: *Step, | ||
| 1155 | test_index: usize, | ||
| 1156 | test_filter: ?[]const u8, | ||
| 1157 | |||
| 1158 | const TestCase = struct { | ||
| 1159 | name: []const u8, | ||
| 1160 | sources: ArrayList(SourceFile), | ||
| 1161 | expected_lines: ArrayList([]const u8), | ||
| 1162 | |||
| 1163 | const SourceFile = struct { | ||
| 1164 | filename: []const u8, | ||
| 1165 | source: []const u8, | ||
| 1166 | }; | ||
| 1167 | |||
| 1168 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | ||
| 1169 | self.sources.append(SourceFile{ | ||
| 1170 | .filename = filename, | ||
| 1171 | .source = source, | ||
| 1172 | }) catch unreachable; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | pub fn addExpectedLine(self: *TestCase, text: []const u8) void { | ||
| 1176 | self.expected_lines.append(text) catch unreachable; | ||
| 1177 | } | ||
| 1178 | }; | ||
| 1179 | |||
| 1180 | const GenHCmpOutputStep = struct { | ||
| 1181 | step: Step, | ||
| 1182 | context: *GenHContext, | ||
| 1183 | obj: *CompileStep, | ||
| 1184 | name: []const u8, | ||
| 1185 | test_index: usize, | ||
| 1186 | case: *const TestCase, | ||
| 1187 | |||
| 1188 | pub fn create( | ||
| 1189 | context: *GenHContext, | ||
| 1190 | obj: *CompileStep, | ||
| 1191 | name: []const u8, | ||
| 1192 | case: *const TestCase, | ||
| 1193 | ) *GenHCmpOutputStep { | ||
| 1194 | const allocator = context.b.allocator; | ||
| 1195 | const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; | ||
| 1196 | ptr.* = GenHCmpOutputStep{ | ||
| 1197 | .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make), | ||
| 1198 | .context = context, | ||
| 1199 | .obj = obj, | ||
| 1200 | .name = name, | ||
| 1201 | .test_index = context.test_index, | ||
| 1202 | .case = case, | ||
| 1203 | }; | ||
| 1204 | ptr.step.dependOn(&obj.step); | ||
| 1205 | context.test_index += 1; | ||
| 1206 | return ptr; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | fn make(step: *Step) !void { | ||
| 1210 | const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); | ||
| 1211 | const b = self.context.b; | ||
| 1212 | |||
| 1213 | std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); | ||
| 1214 | |||
| 1215 | const full_h_path = self.obj.getOutputHPath(); | ||
| 1216 | const actual_h = try io.readFileAlloc(b.allocator, full_h_path); | ||
| 1217 | |||
| 1218 | for (self.case.expected_lines.items) |expected_line| { | ||
| 1219 | if (mem.indexOf(u8, actual_h, expected_line) == null) { | ||
| 1220 | std.debug.print( | ||
| 1221 | \\ | ||
| 1222 | \\========= Expected this output: ================ | ||
| 1223 | \\{s} | ||
| 1224 | \\========= But found: =========================== | ||
| 1225 | \\{s} | ||
| 1226 | \\ | ||
| 1227 | , .{ expected_line, actual_h }); | ||
| 1228 | return error.TestFailed; | ||
| 1229 | } | ||
| 1230 | } | 1053 | } |
| 1231 | std.debug.print("OK\n", .{}); | ||
| 1232 | } | ||
| 1233 | }; | ||
| 1234 | |||
| 1235 | pub fn create( | ||
| 1236 | self: *GenHContext, | ||
| 1237 | filename: []const u8, | ||
| 1238 | name: []const u8, | ||
| 1239 | source: []const u8, | ||
| 1240 | expected_lines: []const []const u8, | ||
| 1241 | ) *TestCase { | ||
| 1242 | const tc = self.b.allocator.create(TestCase) catch unreachable; | ||
| 1243 | tc.* = TestCase{ | ||
| 1244 | .name = name, | ||
| 1245 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | ||
| 1246 | .expected_lines = ArrayList([]const u8).init(self.b.allocator), | ||
| 1247 | }; | ||
| 1248 | |||
| 1249 | tc.addSourceFile(filename, source); | ||
| 1250 | var arg_i: usize = 0; | ||
| 1251 | while (arg_i < expected_lines.len) : (arg_i += 1) { | ||
| 1252 | tc.addExpectedLine(expected_lines[arg_i]); | ||
| 1253 | } | ||
| 1254 | return tc; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: []const []const u8) void { | ||
| 1258 | const tc = self.create("test.zig", name, source, expected_lines); | ||
| 1259 | self.addCase(tc); | ||
| 1260 | } | ||
| 1261 | 1054 | ||
| 1262 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { | 1055 | const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); |
| 1263 | const b = self.b; | 1056 | test_step.setName(b.fmt("test-c-abi-{s}-{s} ", .{ |
| 1057 | triple_prefix, @tagName(optimize_mode), | ||
| 1058 | })); | ||
| 1264 | 1059 | ||
| 1265 | const optimize_mode = std.builtin.OptimizeMode.Debug; | 1060 | const run = test_step.run(); |
| 1266 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable; | 1061 | run.skip_foreign_checks = true; |
| 1267 | if (self.test_filter) |filter| { | 1062 | step.dependOn(&run.step); |
| 1268 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 1269 | } | 1063 | } |
| 1270 | |||
| 1271 | const write_src = b.addWriteFiles(); | ||
| 1272 | for (case.sources.items) |src_file| { | ||
| 1273 | write_src.add(src_file.filename, src_file.source); | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); | ||
| 1277 | obj.setBuildMode(optimize_mode); | ||
| 1278 | |||
| 1279 | const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); | ||
| 1280 | |||
| 1281 | self.step.dependOn(&cmp_h.step); | ||
| 1282 | } | 1064 | } |
| 1283 | }; | 1065 | return step; |
| 1284 | |||
| 1285 | fn printInvocation(args: []const []const u8) void { | ||
| 1286 | for (args) |arg| { | ||
| 1287 | std.debug.print("{s} ", .{arg}); | ||
| 1288 | } | ||
| 1289 | std.debug.print("\n", .{}); | ||
| 1290 | } | 1066 | } |
| 1291 | 1067 | ||
| 1292 | const c_abi_targets = [_]CrossTarget{ | 1068 | pub fn addCases( |
| 1293 | .{}, | 1069 | b: *std.Build, |
| 1294 | .{ | 1070 | parent_step: *Step, |
| 1295 | .cpu_arch = .x86_64, | 1071 | opt_test_filter: ?[]const u8, |
| 1296 | .os_tag = .linux, | 1072 | check_case_exe: *std.Build.CompileStep, |
| 1297 | .abi = .musl, | 1073 | ) !void { |
| 1298 | }, | 1074 | const arena = b.allocator; |
| 1299 | .{ | 1075 | const gpa = b.allocator; |
| 1300 | .cpu_arch = .x86, | 1076 | |
| 1301 | .os_tag = .linux, | 1077 | var cases = @import("src/Cases.zig").init(gpa, arena); |
| 1302 | .abi = .musl, | 1078 | |
| 1303 | }, | 1079 | var dir = try b.build_root.handle.openIterableDir("test/cases", .{}); |
| 1304 | .{ | 1080 | defer dir.close(); |
| 1305 | .cpu_arch = .aarch64, | 1081 | |
| 1306 | .os_tag = .linux, | 1082 | cases.addFromDir(dir); |
| 1307 | .abi = .musl, | 1083 | try @import("cases.zig").addCases(&cases); |
| 1308 | }, | 1084 | |
| 1309 | .{ | 1085 | const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" }); |
| 1310 | .cpu_arch = .arm, | 1086 | cases.lowerToBuildSteps( |
| 1311 | .os_tag = .linux, | 1087 | b, |
| 1312 | .abi = .musleabihf, | 1088 | parent_step, |
| 1313 | }, | 1089 | opt_test_filter, |
| 1314 | .{ | 1090 | cases_dir_path, |
| 1315 | .cpu_arch = .mips, | 1091 | check_case_exe, |
| 1316 | .os_tag = .linux, | 1092 | ); |
| 1317 | .abi = .musl, | ||
| 1318 | }, | ||
| 1319 | .{ | ||
| 1320 | .cpu_arch = .riscv64, | ||
| 1321 | .os_tag = .linux, | ||
| 1322 | .abi = .musl, | ||
| 1323 | }, | ||
| 1324 | .{ | ||
| 1325 | .cpu_arch = .wasm32, | ||
| 1326 | .os_tag = .wasi, | ||
| 1327 | .abi = .musl, | ||
| 1328 | }, | ||
| 1329 | .{ | ||
| 1330 | .cpu_arch = .powerpc, | ||
| 1331 | .os_tag = .linux, | ||
| 1332 | .abi = .musl, | ||
| 1333 | }, | ||
| 1334 | .{ | ||
| 1335 | .cpu_arch = .powerpc64le, | ||
| 1336 | .os_tag = .linux, | ||
| 1337 | .abi = .musl, | ||
| 1338 | }, | ||
| 1339 | .{ | ||
| 1340 | .cpu_arch = .x86, | ||
| 1341 | .os_tag = .windows, | ||
| 1342 | .abi = .gnu, | ||
| 1343 | }, | ||
| 1344 | .{ | ||
| 1345 | .cpu_arch = .x86_64, | ||
| 1346 | .os_tag = .windows, | ||
| 1347 | .abi = .gnu, | ||
| 1348 | }, | ||
| 1349 | }; | ||
| 1350 | |||
| 1351 | pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { | ||
| 1352 | const step = b.step("test-c-abi", "Run the C ABI tests"); | ||
| 1353 | |||
| 1354 | const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; | ||
| 1355 | |||
| 1356 | for (optimize_modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |optimize_mode| for (c_abi_targets) |c_abi_target| { | ||
| 1357 | if (skip_non_native and !c_abi_target.isNative()) | ||
| 1358 | continue; | ||
| 1359 | |||
| 1360 | const test_step = b.addTest(.{ | ||
| 1361 | .root_source_file = .{ .path = "test/c_abi/main.zig" }, | ||
| 1362 | .optimize = optimize_mode, | ||
| 1363 | .target = c_abi_target, | ||
| 1364 | }); | ||
| 1365 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { | ||
| 1366 | // TODO NativeTargetInfo insists on dynamically linking musl | ||
| 1367 | // for some reason? | ||
| 1368 | test_step.target_info.dynamic_linker.max_byte = null; | ||
| 1369 | } | ||
| 1370 | test_step.linkLibC(); | ||
| 1371 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); | ||
| 1372 | |||
| 1373 | if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { | ||
| 1374 | // LTO currently incorrectly strips stdcall name-mangled functions | ||
| 1375 | // LLD crashes in LTO here when cross compiling for windows on linux | ||
| 1376 | test_step.want_lto = false; | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | const triple_prefix = c_abi_target.zigTriple(b.allocator) catch unreachable; | ||
| 1380 | test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ | ||
| 1381 | "test-c-abi", | ||
| 1382 | triple_prefix, | ||
| 1383 | @tagName(optimize_mode), | ||
| 1384 | })); | ||
| 1385 | |||
| 1386 | step.dependOn(&test_step.step); | ||
| 1387 | }; | ||
| 1388 | return step; | ||
| 1389 | } | 1093 | } |
test/translate_c.zig+8| ... | @@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3948 | \\} | 3948 | \\} |
| 3949 | }); | 3949 | }); |
| 3950 | } | 3950 | } |
| 3951 | |||
| 3952 | cases.add("extern array of unknown length", | ||
| 3953 | \\extern int foo[]; | ||
| 3954 | , &[_][]const u8{ | ||
| 3955 | \\const foo: [*c]c_int = @extern([*c]c_int, .{ | ||
| 3956 | \\ .name = "foo", | ||
| 3957 | \\}); | ||
| 3958 | }); | ||
| 3951 | } | 3959 | } |
tools/lldb_pretty_printers.py+38-5| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | # pretty printing for the zig language, zig standard library, and zig stage 2 compiler. | 1 | # pretty printing for the zig language, zig standard library, and zig stage 2 compiler. |
| 2 | # put commands in ~/.lldbinit to run them automatically when starting lldb | 2 | # 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 |
| 4 | # `type category enable zig` to enable pretty printing for the zig language | 4 | # `type category enable zig` to enable pretty printing for the zig language |
| 5 | # `type category enable zig.std` to enable pretty printing for the zig standard library | 5 | # `type category enable zig.std` to enable pretty printing for the zig standard library |
| 6 | # `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler | 6 | # `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler |
| ... | @@ -201,7 +201,7 @@ class std_MultiArrayList_SynthProvider: | ... | @@ -201,7 +201,7 @@ class std_MultiArrayList_SynthProvider: |
| 201 | 201 | ||
| 202 | value_type = self.value.type | 202 | value_type = self.value.type |
| 203 | for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull): | 203 | for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull): |
| 204 | ptr_self_type, ptr_child_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes() | 204 | ptr_self_type, ptr_child_type, ptr_field_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes() |
| 205 | if ptr_self_type.GetPointeeType() == value_type: break | 205 | if ptr_self_type.GetPointeeType() == value_type: break |
| 206 | else: return | 206 | else: return |
| 207 | 207 | ||
| ... | @@ -221,12 +221,44 @@ class std_MultiArrayList_SynthProvider: | ... | @@ -221,12 +221,44 @@ class std_MultiArrayList_SynthProvider: |
| 221 | offset = 0 | 221 | offset = 0 |
| 222 | data = lldb.SBData() | 222 | data = lldb.SBData() |
| 223 | for field in self.entry_type.fields: | 223 | for field in self.entry_type.fields: |
| 224 | ptr_field_type = field.type | 224 | field_type = field.type.GetPointeeType() |
| 225 | field_size = ptr_field_type.GetPointeeType().size | 225 | field_size = field_type.size |
| 226 | data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, ptr_field_type).address_of.data) | 226 | data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, field_type).address_of.data) |
| 227 | offset += self.capacity * field_size | 227 | offset += self.capacity * field_size |
| 228 | return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type) | 228 | return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type) |
| 229 | except: return None | 229 | except: return None |
| 230 | class 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 | ||
| 230 | 262 | ||
| 231 | class std_HashMapUnmanaged_SynthProvider: | 263 | class std_HashMapUnmanaged_SynthProvider: |
| 232 | def __init__(self, value, _=None): self.value = value | 264 | def __init__(self, value, _=None): self.value = value |
| ... | @@ -556,6 +588,7 @@ def __lldb_init_module(debugger, _=None): | ... | @@ -556,6 +588,7 @@ def __lldb_init_module(debugger, _=None): |
| 556 | add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}') | 588 | add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}') |
| 557 | add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}') | 589 | add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}') |
| 558 | add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}') | 590 | add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}') |
| 591 | add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)\\.Slice$', identifier='std_MultiArrayList_Slice', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}') | ||
| 559 | add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True) | 592 | add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True) |
| 560 | add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True) | 593 | add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True) |
| 561 | add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True) | 594 | add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True) |